'use strict'
/**
* 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('
')
};
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 + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)');
logger.info(' ' + item.ident + '- [' + item.file_name + '](' + 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) + '- [' + 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)');
}
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 + '- [' + item.file_name + '](' + item.folder + item.file_name + '.md)');
logger.info(' ' + item.ident + '- [' + item.file_name + '](' + 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;