Spaces:
Sleeping
Sleeping
| /** | |
| * Copyright (c) 2017~2022, OBCon Inc. | |
| * All rights reserved. | |
| */ | |
| /** | |
| * @file | |
| * @copyright 2017~2022, OBCon Inc. | |
| * @author gye hyun james kim [pnuskgh@gmail.com] | |
| */ | |
| // var multer = require('multer'); | |
| // var cors = require('cors'); //--- CORS(Cross-Origin Resource Sharing) | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const moment = require('moment'); | |
| const View = utils.getClass('include', 'view.js'); | |
| const markdown = utils.getModule('include', 'Markdown.js'); | |
| //--- Facebook 등으로 공유할 때 사용 한다. | |
| const share = utils.getModule('openapis', 'share.js'); | |
| //--- MediaWiki 등으로 배포할 때 사용 한다. | |
| const deploy = utils.getModule('openapis', 'deploy.js'); | |
| class MarkdownsView extends View { | |
| constructor() { | |
| super(); | |
| this._name = 'markdowns'; | |
| this._title = '마크다운'; | |
| this._model = modules.markdowns.model; | |
| this._msgs = []; //--- msg 저장을 위한 임시 영역 | |
| this._dataSitemap = {}; //--- Outfile 내용 저장을 위한 임시 영역 | |
| } | |
| //--- 권한 설정 반환 | |
| getRights() { | |
| return { | |
| get: { | |
| list : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| detail : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| create : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| update : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| convert : { isLogin: false, isAdmin: false, isGlobalAdmin: false }, | |
| createSitemap : { isLogin: true, isAdmin: true, isGlobalAdmin: true } | |
| }, | |
| post: { | |
| create : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| update : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| delete : { isLogin: true, isAdmin: true, isGlobalAdmin: true } | |
| }, | |
| ajaxGet: { | |
| list : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| detail : { isLogin: true, isAdmin: true, isGlobalAdmin: true } | |
| }, | |
| ajaxPost: { | |
| create : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| update : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| delete : { isLogin: true, isAdmin: true, isGlobalAdmin: true }, | |
| deploy : { isLogin: true, isAdmin: true, isGlobalAdmin: true } | |
| }, | |
| //--- To-Do : SocketIO의 권한을 설정 한다. socketio | |
| default: false | |
| }; | |
| } | |
| //--- Router 설정 반환 | |
| getRouters() { | |
| let routers = super.getRouters(); | |
| routers.get.convert = this.convert.bind(this); | |
| routers.get.createSitemap = this.createSitemap.bind(this); //--- sitemap.md 파일을 생성 한다. | |
| routers.ajaxPost.deploy = this.ajaxDeploy.bind(this); | |
| return routers; | |
| } | |
| //--- Markdown 파일을 읽어서 화면에 표시 한다. | |
| //--- pages 모듈의 view() 참조 | |
| convert(req, res, next) { | |
| (req.query.record) ? this._convert_record(req, res, next):this._convert_file(req, res, next); | |
| } | |
| _convert_file(req, res, next) { | |
| const theme = this.getTheme('convert', req, 'convert', 'convert.ejs'); | |
| theme.javascriptFiles.push(this.name + '/js/markdown.js'); | |
| theme.cssFiles.push(this.name + '/css/markdown.css'); | |
| const options = { | |
| req: req, | |
| res: res, | |
| file: req.query.source || '/README.md', //--- Markdown 파일 | |
| language: req.query.language || 'ko_KR', //--- 사용 언어 | |
| service: req.query.service || '' //--- 강제로 지정한 서비스 이름 | |
| } | |
| markdown.initialize(options); | |
| const filename = path.dirname(markdown.options.file) + '/' + path.basename(markdown.options.file, '.md'); | |
| theme.javascriptFiles.push(`${this.name}/js/${config.service.name}/${filename}.js`); | |
| theme.cssFiles.push(`${this.name}/css/${config.service.name}/${filename}.css`); | |
| //--- Markdown 파일 조회시 로그인이 필요한 경우, 로그인 화면을 표시 한다. | |
| if ((markdown.config.isPublic == false) && | |
| ((!req.session.isLogined) || (req.session.isLogined == false))) { | |
| res.redirect(config.http.path + '/users?action=login'); | |
| return; | |
| } | |
| theme.layout = markdown.config.layout || theme.layout; | |
| share.url = utils.getFullUrl(req.url); | |
| share.title = config.openAPI.share.title; | |
| const info = markdown.convert({ source: 'file', target: 'info' }); | |
| info.filestat = fs.statSync(info.filename); | |
| info.filestat.mtimeStr = moment(info.filestat.mtime).format('YYYY-MM-DD HH:mm:ss'); | |
| const data = { | |
| folder: markdown.folder || '', | |
| menu: markdown.convert({ source: 'menu', target: 'menu' }), | |
| index: JSON.parse('[' + (' ' + markdown.convert({ source: 'file', target: 'index' })).slice(0, -1) + ']'), | |
| content: markdown.convert({ source: 'file', target: 'html' }), | |
| info: info, | |
| config: markdown.config, | |
| item: null, | |
| share: share.getHtml(), | |
| deploy: deploy.getHtml(), | |
| record: -1, | |
| source: options.file, | |
| language: options.language | |
| }; | |
| res.render(utils.getTemplateFullPath(theme.serviceInfo.site.id, theme.layout), { theme: theme, baseUrl: req.baseUrl, session: req.session, data: data }); | |
| } | |
| //--- 사용하는 곳이 없음 | |
| _convert_record(req, res, next) { | |
| let theme = this.getTheme('convert', req, 'convert', 'convert.ejs'); | |
| theme.javascriptFiles.push(this.name + '/js/markdown.js'); | |
| theme.cssFiles.push(this.name + '/css/markdown.css'); | |
| let record = req.query.record; | |
| let query = { where: { id: record, deleted: false } }; | |
| this.model.table.findOne(query) | |
| .then(function(item) { | |
| if (!item) { | |
| next(); | |
| return; | |
| } | |
| if (item.isPublic == false) { | |
| //--- Site ID가 일치하여야 한다. | |
| if (item.site != req.session.site.id) { | |
| next(); | |
| return; | |
| } | |
| //--- 로그인이 되어 있어야 한다. | |
| if ((!req.session.isLogined) || (req.session.isLogined == false)) { | |
| res.redirect(config.http.path + '/users?action=login'); | |
| return; | |
| } | |
| } | |
| //--- To-Do : service 항목 추가에 따른 수정을 할 것. _convert_file() 함수 참조 | |
| const options = { | |
| req: req, | |
| res: res, | |
| source: path.join(item.folder, `record=${record}.md`), //--- ???, markdown 파일 이름 (예, /HAProxy/README.md) | |
| language: item.language, //--- markdown 파일 언어 | |
| service: '' //--- Service 이름 | |
| } | |
| markdown.initialize(options); | |
| share.url = utils.getFullUrl(req.url); | |
| share.title = config.openAPI.share.title; | |
| const data = { | |
| folder: item.folder, | |
| menu: markdown.convert({ source: 'menu', target: 'menu' }), | |
| index: markdown.convert({ source: 'content', content: item.content, target: 'index' }), | |
| content: markdown.convert({ source: 'content', content: item.content, target: 'html' }), | |
| config: { | |
| layout: config.theme.layout, //--- layout | |
| isPublic: item.isPublic //--- false. 로그인 필요 | |
| }, | |
| item: item, | |
| share: share.getHtml(), | |
| deploy: deploy.getHtml(), | |
| record: record, | |
| source: options.source, | |
| language: options.language | |
| }; | |
| theme.javascriptFiles.push(`${this.name}/js/${config.service.name}/record_${record}.js`); | |
| theme.cssFiles.push(`${this.name}/css/${config.service.name}/record_${record}.css`); | |
| res.render(utils.getTemplateFullPath(theme.serviceInfo.site.id, config.theme.layout), { theme: theme, baseUrl: req.baseUrl, session: req.session, data: data }); | |
| }).catch(utils.obj.loggerError); | |
| } | |
| //--- 사용하는 곳이 없음 | |
| createSitemap(req, res, next) { | |
| let theme = this.getTheme('createSitemap', req, 'createSitemap', 'createSitemap.ejs'); | |
| //--- manual/${service}/default/ko_KR/sitemap.md | |
| //--- manual/${service}/${site}/ko_KR/sitemap.md | |
| //--- manual/${service}/${site}/${lang}/sitemap.md | |
| let folderManual = path.join(config.folder_root, 'manual'); | |
| let service = ''; | |
| let site = ''; | |
| let lang = ''; | |
| this._msgs = []; | |
| this._dataSitemap = {}; | |
| let dataService = fs.readdirSync(folderManual); | |
| for (let idxService = 0; idxService < dataService.length; idxService++) { | |
| service = dataService[idxService]; | |
| let folderService = path.join(folderManual, service); | |
| if (fs.statSync(folderService).isDirectory() == false) { | |
| continue; | |
| } | |
| let dataSite = fs.readdirSync(folderService); | |
| for (let idxSite = 0; idxSite < dataSite.length; idxSite++) { | |
| site = dataSite[idxSite]; | |
| let folderSite = path.join(folderService, site); | |
| if (fs.statSync(folderSite).isDirectory() == false) { | |
| continue; | |
| } | |
| let dataLang = fs.readdirSync(folderSite); | |
| for (let idxLang = 0; idxLang < dataLang.length; idxLang++) { | |
| lang = dataLang[idxLang]; | |
| let folderLang = path.join(folderSite, lang); | |
| if (fs.statSync(folderLang).isDirectory() == false) { | |
| continue; | |
| } | |
| this._createSitemap(req, res, next, folderManual, service, site, lang); | |
| } | |
| } | |
| } | |
| let data = { | |
| code: '0', | |
| msg: this._msgs.join('<br/>') | |
| }; | |
| res.render(utils.getTemplateFullPath(theme.serviceInfo.site.id, config.theme.layout), { theme: theme, baseUrl: req.baseUrl, session: req.session, data: data }); | |
| } | |
| //--- 언어별로 사이트맵을 작성 한다. | |
| //--- To-Do: 1 단계 - default/ko_KR/에 대한 사이트맵을 작성 한다. | |
| //--- To-Do: 2 단계 - 언어별 사이트맵을 작성 한다. | |
| //--- To-Do: 3 단계 - 사이트별 사이트맵을 작성 한다. | |
| _createSitemap(req, res, next, folderManual, service, site, lang) { | |
| this._msgs.push(' service: ' + service + ', site: ' + site + ', lang: ' + lang + ' sitemap 작성 시작'); | |
| if (utils.existField(this._dataSitemap, service) == false) { | |
| this._dataSitemap[service] = {}; | |
| } | |
| if (utils.existField(this._dataSitemap[service], site) == false) { | |
| this._dataSitemap[service][site] = {}; | |
| } | |
| if (utils.existField(this._dataSitemap[service][site], lang) == false) { | |
| this._dataSitemap[service][site][lang] = []; | |
| } | |
| //--- 폴더와 파일 정보를 읽어서 this._dataSitemap[service][site][lang]에 저장 한다. | |
| logger.info(`Create sitemap : service - ${service}, site - ${site}, lang - ${lang}`); | |
| this._searchFolder(folderManual, service, site, lang, path.join(folderManual, service, site, lang)); | |
| // logger.info('Create sitemap : dataSitemap - ' + JSON.stringify(this._dataSitemap[service][site][lang])); | |
| let saveFolder = ''; | |
| let contents = [] ; | |
| contents.push('## Sitemap'); | |
| contents.push(''); | |
| for (let idx = 0; idx < this._dataSitemap[service][site][lang].length; idx++) { | |
| let item = this._dataSitemap[service][site][lang][idx]; | |
| logger.info('Sitemap : ' + JSON.stringify(item)); | |
| if (item.folder == '/') { | |
| if (item.conf.isPublic) { | |
| contents.push(item.ident + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)'); | |
| } else { | |
| contents.push(item.ident + '- [<span style="color: green;">' + item.file_name + '</span>](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident + '- [<span style="color: green;">' + item.file_name + '</span>](' + item.folder + item.file_name + '.md)'); | |
| } | |
| } else { | |
| if (saveFolder != item.folder) { | |
| saveFolder = item.folder; | |
| if (item.file_name == 'README') { | |
| if (item.conf.isPublic) { | |
| contents.push(item.ident.substring(2) + '- [' + item.folder.substring(1, item.folder.length - 1) + '](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident.substring(2) + '- [' + item.folder.substring(1, item.folder.length - 1) + '](' + item.folder + item.file_name + '.md)'); | |
| } else { | |
| contents.push(item.ident.substring(2) + '- [<span style="color: green;">' + item.folder.substring(1, item.folder.length - 1) + '</span>](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident.substring(2) + '- [<span style="color: green;">' + item.folder.substring(1, item.folder.length - 1) + '</span>](' + item.folder + item.file_name + '.md)'); | |
| } | |
| continue; | |
| } else { | |
| contents.push(item.ident.substring(2) + '- ' + item.folder.substring(1, item.folder.length - 1)); | |
| logger.info(' ' + item.ident.substring(2) + '- ' + item.folder.substring(1, item.folder.length - 1)); | |
| } | |
| } | |
| if (item.conf.isPublic) { | |
| contents.push(item.ident + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)'); | |
| } else { | |
| contents.push(item.ident + '- [<span style="color: green;">' + item.file_name + '</span>](' + item.folder + item.file_name + '.md)'); | |
| logger.info(' ' + item.ident + '- [<span style="color: green;">' + item.file_name + '</span>](' + item.folder + item.file_name + '.md)'); | |
| } | |
| } | |
| } | |
| // let fout_filename = path.join(folderManual, service, site, lang, '_sitemap.md'); | |
| let fout_filename = path.join(folderManual, service, site, lang, 'sitemap.md'); | |
| if (fs.existsSync(fout_filename)) { | |
| fs.unlinkSync(fout_filename); | |
| } | |
| if (utils.isLinux()) { | |
| fs.writeFileSync(fout_filename, contents.join('\n'), 'utf8'); | |
| } else { | |
| fs.writeFileSync(fout_filename, contents.join('\r\n'), 'utf8'); | |
| } | |
| } | |
| _searchFolder(folderManual, service, site, lang, folder=null, folderName='/', ident='') { | |
| // this._msgs.push(ident + 'Folder : ' + folderName); | |
| let data = fs.readdirSync(folder); | |
| //--- 파일 처리 | |
| let file = path.join(folder, 'README.md'); | |
| if ((fs.existsSync(file)) && (fs.statSync(file).isFile())) { | |
| let conf = this._getMarkdownConfig(file); | |
| this._dataSitemap[service][site][lang].push({ | |
| ident: ident, | |
| folder: folderName, | |
| file_name: 'README', | |
| conf: conf | |
| }); | |
| } | |
| for (let idx = 0; idx < data.length; idx++) { | |
| let file = path.join(folder, data[idx]); | |
| if ((fs.statSync(file).isFile()) && (file.endsWith('.md'))) { | |
| let filename = data[idx]; | |
| let file_ext = path.extname(filename); //--- .md | |
| let file_name = path.basename(file, file_ext); | |
| if ((file_name.startsWith('zz')) || (file_name.startsWith('_'))) { | |
| continue; | |
| } | |
| if ((file_name == 'README') || (file_name.startsWith('MENU_')) || (file_name == 'sitemap')) { | |
| continue; | |
| } | |
| try { | |
| let conf = this._getMarkdownConfig(file); | |
| // this._msgs.push(ident + 'Service : ' + service + ', Folder : ' + folderName + ', file : ' + file_name + ', conf : ' + conf.isPublic); | |
| this._dataSitemap[service][site][lang].push({ | |
| ident: ident, | |
| folder: folderName, | |
| file_name: file_name, | |
| conf: conf | |
| }); | |
| } catch (e) { | |
| console.error(file + ' :: error.'); | |
| console.error(e.stack); | |
| } | |
| } | |
| } | |
| //--- 폴더 처리 | |
| for (let idx = 0; idx < data.length; idx++) { | |
| let file = path.join(folder, data[idx]); | |
| if (data[idx].startsWith('zz')) { | |
| continue; | |
| } | |
| if (fs.statSync(file).isDirectory()) { | |
| this._searchFolder(folderManual, service, site, lang, file, folderName + data[idx] + '/', ident + ' '); | |
| } | |
| } | |
| } | |
| _getMarkdownConfig(file) { | |
| let rtConfig = markdown._defaultConfig; | |
| let tmpFile = path.join(path.dirname(file), 'config.js'); | |
| if (fs.existsSync(tmpFile)) { | |
| delete require.cache[tmpFile]; | |
| let conf = require(tmpFile); | |
| Object.assign(rtConfig, conf); | |
| } | |
| tmpFile = path.join(path.dirname(file), path.basename(file, '.md') + '.js'); | |
| if (fs.existsSync(tmpFile)) { | |
| delete require.cache[tmpFile]; | |
| let conf = require(tmpFile); | |
| Object.assign(rtConfig, conf); | |
| } | |
| return rtConfig; | |
| } | |
| ajaxDeploy(req, res, next) { | |
| let id = req.body.id; | |
| if ((typeof(id) == 'undefined') || (id == '')) { | |
| utils.obj.resJson(res, 1, 'ID가 없습니다.', {}, {}); | |
| return; | |
| } | |
| let service = null; | |
| for (let idx = 0; idx < config.openAPI.deploy.services.length; idx++) { | |
| let tmpService = config.openAPI.deploy.services[idx]; | |
| if (tmpService.id == id) { | |
| service = tmpService; | |
| break; | |
| } | |
| } | |
| if (service == null) { | |
| utils.obj.resJson(res, 2, 'ID에 해당하는 서비스가 없습니다.', {}, {}); | |
| return; | |
| } | |
| let opts = { | |
| req: req, | |
| res: res, | |
| service: service, | |
| record: req.body.record, | |
| source: req.body.source, | |
| language: req.body.language | |
| } | |
| deploy.deploy(opts, function(code, message, data, err) { | |
| utils.obj.resJson(res, code, message, data, err); | |
| }); | |
| } | |
| } | |
| module.exports = MarkdownsView; | |