@agung_dhewe/webapps 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +2 -0
- package/jsconfig.json +10 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.css +2 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js +11 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js.map +1 -0
- package/libs/fgta5js-dist/fonts/karla-italic-latin-ext.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-italic-latin.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-normal-latin-ext.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-normal-latin.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla.css +142 -0
- package/libs/webmodule/module-edit.css +163 -0
- package/libs/webmodule/module-footer.css +22 -0
- package/libs/webmodule/module-list.css +25 -0
- package/libs/webmodule/module.css +52 -0
- package/libs/webmodule/module.js +195 -0
- package/libs/webmodule/pagehelper.mjs +45 -0
- package/modules/generator/appgen-components.mjs +142 -0
- package/modules/generator/appgen-icons.mjs +6 -0
- package/modules/generator/appgen-io.mjs +784 -0
- package/modules/generator/appgen-ui-search.mjs +173 -0
- package/modules/generator/appgen-ui-unique.mjs +153 -0
- package/modules/generator/appgen-ui.mjs +1181 -0
- package/modules/generator/generator-context.mjs +18 -0
- package/modules/generator/generator-designtemplate.html +1508 -0
- package/modules/generator/generator-ext.html +0 -0
- package/modules/generator/generator-ext.mjs +3 -0
- package/modules/generator/generator.css +642 -0
- package/modules/generator/generator.mjs +195 -0
- package/modules/generator/generator.png +0 -0
- package/modules/generator/generatorEdit.html +185 -0
- package/modules/generator/generatorEdit.mjs +238 -0
- package/modules/generator/generatorList.html +32 -0
- package/modules/generator/generatorList.mjs +243 -0
- package/modules/login/login.css +11 -0
- package/modules/login/login.html +12 -0
- package/modules/login/login.mjs +111 -0
- package/package.json +46 -0
- package/percobaan/simmpan-ke-minio.js +24 -0
- package/src/api.js +80 -0
- package/src/apis/generator.api.js +226 -0
- package/src/apis/login.api.js +109 -0
- package/src/bucket.js +24 -0
- package/src/context.js +26 -0
- package/src/datalog.sql +22 -0
- package/src/datarecords.js +0 -0
- package/src/db.js +61 -0
- package/src/generator/createApiExtenderModule.js +54 -0
- package/src/generator/createApiModule.js +218 -0
- package/src/generator/createIcon.js +62 -0
- package/src/generator/createInfoAboutExtender.js +42 -0
- package/src/generator/createInfoLogs.js +41 -0
- package/src/generator/createInfoRecordExtender.js +41 -0
- package/src/generator/createModuleContext.js +48 -0
- package/src/generator/createModuleDetilEditHtml.js +110 -0
- package/src/generator/createModuleDetilEditMjs.js +172 -0
- package/src/generator/createModuleDetilListHtml.js +146 -0
- package/src/generator/createModuleDetilListMjs.js +73 -0
- package/src/generator/createModuleEjs.js +51 -0
- package/src/generator/createModuleExtenderHtml.js +43 -0
- package/src/generator/createModuleExtenderMjs.js +43 -0
- package/src/generator/createModuleHeaderEditHtml.js +148 -0
- package/src/generator/createModuleHeaderEditMjs.js +197 -0
- package/src/generator/createModuleHeaderListHtml.js +144 -0
- package/src/generator/createModuleHeaderListMjs.js +67 -0
- package/src/generator/createModuleMjs.js +67 -0
- package/src/generator/createModuleRollup.js +42 -0
- package/src/generator/createProgramData.js +96 -0
- package/src/generator/createTable.js +156 -0
- package/src/generator/ddl.js +475 -0
- package/src/generator/helper.js +149 -0
- package/src/generator/templates/__rollup-module.ejs +90 -0
- package/src/generator/templates/api-extender-module.js.ejs +0 -0
- package/src/generator/templates/api-module.js.ejs +818 -0
- package/src/generator/templates/module-context.ejs +16 -0
- package/src/generator/templates/module-ext-about.ejs +1 -0
- package/src/generator/templates/module-ext-record.ejs +1 -0
- package/src/generator/templates/module-ext.html.ejs +3 -0
- package/src/generator/templates/module-ext.mjs.ejs +21 -0
- package/src/generator/templates/module-logs.ejs +14 -0
- package/src/generator/templates/module.ejs.ejs +48 -0
- package/src/generator/templates/module.mjs.ejs +256 -0
- package/src/generator/templates/moduleDetilEdit.html.ejs +34 -0
- package/src/generator/templates/moduleDetilEdit.mjs.ejs +792 -0
- package/src/generator/templates/moduleDetilList.html.ejs +26 -0
- package/src/generator/templates/moduleDetilList.mjs.ejs +319 -0
- package/src/generator/templates/moduleHeaderEdit.html.ejs +53 -0
- package/src/generator/templates/moduleHeaderEdit.mjs.ejs +807 -0
- package/src/generator/templates/moduleHeaderList.html.ejs +24 -0
- package/src/generator/templates/moduleHeaderList.mjs.ejs +308 -0
- package/src/generator/templates/sqlAddField.ejs +3 -0
- package/src/generator/templates/sqlAddForeignKey.ejs +12 -0
- package/src/generator/templates/sqlAddUniqueIndex.ejs +4 -0
- package/src/generator/templates/sqlCreateTable.ejs +9 -0
- package/src/generator/templates/sqlDropForeignKey.ejs +3 -0
- package/src/generator/templates/sqlDropUniqueIndex.ejs +4 -0
- package/src/generator/templates/sqlModifyField.ejs +6 -0
- package/src/generator/trygenerate.js +83 -0
- package/src/generator/worker.js +389 -0
- package/src/helper.js +82 -0
- package/src/logger.js +39 -0
- package/src/router.js +84 -0
- package/src/routers/defaultLoginApi.js +29 -0
- package/src/routers/defaultLoginAsset.js +18 -0
- package/src/routers/defaultLoginPage.js +36 -0
- package/src/routers/defaultRootIndex.js +16 -0
- package/src/routers/downloadHandler.js +51 -0
- package/src/routers/fileUploadApi.js +15 -0
- package/src/routers/generatorApi.js +30 -0
- package/src/routers/generatorAsset.js +18 -0
- package/src/routers/generatorPage.js +37 -0
- package/src/routers/handleError.js +43 -0
- package/src/routers/handleModuleNotfound.js +12 -0
- package/src/routers/moduleApi.js +34 -0
- package/src/routers/modulePage.js +102 -0
- package/src/sequencerdoc.js +311 -0
- package/src/sequencerline.js +214 -0
- package/src/session.js +57 -0
- package/src/startup.js +59 -0
- package/src/webapps.js +239 -0
- package/src/workermanager.js +83 -0
- package/templates/_lib_debug.ejs +11 -0
- package/templates/_lib_production.ejs +5 -0
- package/templates/application.page.ejs +143 -0
- package/templates/generator.page.ejs +131 -0
- package/templates/index.page.ejs +24 -0
- package/templates/login.page.ejs +102 -0
- package/templates/moduleError.ejs +16 -0
- package/templates/moduleNotfound.ejs +14 -0
- package/webapps.code-workspace +11 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Api from '../api.js'
|
|
2
|
+
import bucket from '../bucket.js'
|
|
3
|
+
|
|
4
|
+
export async function publicDownloadHandler(req, res, next) {
|
|
5
|
+
// hanya bisa dari public bucket
|
|
6
|
+
const bucketname = 'public'
|
|
7
|
+
|
|
8
|
+
// test: /images/profiles/agung-lurik-jogja.jpg
|
|
9
|
+
const objectname = req.query.path;
|
|
10
|
+
if (objectname==null) {
|
|
11
|
+
res.send('object tidak ditemukan')
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const stat = await bucket.statObject(bucketname, objectname);
|
|
16
|
+
const contentType = stat.metaData['content-type']
|
|
17
|
+
const originalname = stat.metaData['originalname']
|
|
18
|
+
|
|
19
|
+
res.setHeader('Content-Type', contentType);
|
|
20
|
+
res.setHeader('Content-Disposition', `attachment; filename="${originalname}"`);
|
|
21
|
+
|
|
22
|
+
// Stream file dari MinIO ke response
|
|
23
|
+
const stream = await bucket.getObject(bucketname, objectname);
|
|
24
|
+
stream.pipe(res);
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export async function privateDownloadHandler(req, res, next) {
|
|
31
|
+
// harus login
|
|
32
|
+
const { bucketname, objectname } = req.body
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
Api.cekLogin(req)
|
|
36
|
+
|
|
37
|
+
const stat = await bucket.statObject(bucketname, objectname);
|
|
38
|
+
const contentType = stat.metaData['content-type']
|
|
39
|
+
const originalname = stat.metaData['originalname']
|
|
40
|
+
|
|
41
|
+
res.setHeader('Content-Type', contentType);
|
|
42
|
+
res.setHeader('Content-Disposition', `attachment; filename="${originalname}"`);
|
|
43
|
+
|
|
44
|
+
// Stream file dari MinIO ke response
|
|
45
|
+
const stream = await bucket.getObject(bucketname, objectname);
|
|
46
|
+
stream.pipe(res);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
throw err
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export async function fileUploadApi(req, res, next) {
|
|
4
|
+
|
|
5
|
+
const moduleName = 'xxxxxx'
|
|
6
|
+
const ModuleClass = await importModule(moduleName)
|
|
7
|
+
const module = new ModuleClass(req, res, next)
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const response = await module.handleRequest('upload', req.body)
|
|
11
|
+
res.send(response)
|
|
12
|
+
} catch (err) {
|
|
13
|
+
next(err);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as helper from './../helper.js'
|
|
2
|
+
import { handleError } from './handleError.js'
|
|
3
|
+
|
|
4
|
+
export async function generatorApi(req, res, next) {
|
|
5
|
+
// res.status(200).send(`{"code":"1", "message":"api not implemented"}`)
|
|
6
|
+
const moduleName = 'generator'
|
|
7
|
+
const methodName = req.params.method
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
|
|
11
|
+
const ModuleClass = await helper.importApiModule(moduleName)
|
|
12
|
+
const method = helper.kebabToCamel(methodName);
|
|
13
|
+
if (ModuleClass===undefined) {
|
|
14
|
+
throw new Error(`invalid module: '${moduleName}'`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const requestedBody = req.body
|
|
18
|
+
const module = new ModuleClass(req, res, next)
|
|
19
|
+
const result = await module.handleRequest(method, requestedBody)
|
|
20
|
+
const response = {
|
|
21
|
+
code: 0,
|
|
22
|
+
result: result
|
|
23
|
+
}
|
|
24
|
+
res.json(response)
|
|
25
|
+
} catch (err) {
|
|
26
|
+
handleError(err, req, res)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import context from '../context.js'
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import * as helper from '../helper.js'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function generatorAsset(req, res, next) {
|
|
8
|
+
const __dirname = context.getWebappsDirectory()
|
|
9
|
+
const requestedFile = req.params.requestedAsset;
|
|
10
|
+
const filePath = path.join(__dirname, 'modules', 'generator', requestedFile)
|
|
11
|
+
|
|
12
|
+
const assetExists = await helper.isFileExists(filePath)
|
|
13
|
+
if (assetExists) {
|
|
14
|
+
res.sendFile(filePath)
|
|
15
|
+
} else {
|
|
16
|
+
res.status(404).send(`'${requestedFile}' is not found`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import context from '../context.js'
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import * as helper from '../helper.js'
|
|
5
|
+
|
|
6
|
+
import { handleError } from './handleError.js';
|
|
7
|
+
|
|
8
|
+
export async function generatorPage(req, res, next) {
|
|
9
|
+
const __dirname = context.getWebappsDirectory()
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
|
|
13
|
+
const generatorListPath = path.join(__dirname, 'modules', 'generator', 'generatorList.html')
|
|
14
|
+
const generatorEditPath = path.join(__dirname, 'modules', 'generator', 'generatorEdit.html')
|
|
15
|
+
const generatorDesignPath = path.join(__dirname, 'modules', 'generator', 'generator-designtemplate.html')
|
|
16
|
+
|
|
17
|
+
// load halaman html-nya
|
|
18
|
+
const variables = {
|
|
19
|
+
...helper.createDefaultEjsVariable(req),
|
|
20
|
+
...{
|
|
21
|
+
generatorListPath,
|
|
22
|
+
generatorEditPath,
|
|
23
|
+
generatorDesignPath
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const tplFilePath = path.join(context.getWebappsDirectory(), 'templates', 'generator.page.ejs')
|
|
28
|
+
const content = await helper.parseTemplate(tplFilePath, variables)
|
|
29
|
+
|
|
30
|
+
res.status(200).send(content)
|
|
31
|
+
} catch (err) {
|
|
32
|
+
handleError(err, req, res)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import context from './../context.js'
|
|
2
|
+
import * as helper from './../helper.js'
|
|
3
|
+
import * as http from 'node:http'
|
|
4
|
+
import * as path from 'node:path'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function handleError(err, req, res) {
|
|
8
|
+
const appName = req.app.locals.appConfig.appName || ''
|
|
9
|
+
const moduleName = req.params.modulename || req.url
|
|
10
|
+
const status = err.status || 500
|
|
11
|
+
const statusText = http.STATUS_CODES[status]
|
|
12
|
+
const code = err.code ?? 1
|
|
13
|
+
|
|
14
|
+
console.log(err)
|
|
15
|
+
console.log(moduleName)
|
|
16
|
+
|
|
17
|
+
const response = {
|
|
18
|
+
code,
|
|
19
|
+
status,
|
|
20
|
+
statusText,
|
|
21
|
+
appName,
|
|
22
|
+
moduleName
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (req.method=='POST') {
|
|
26
|
+
// kalau post (api), kirimkan berupa json
|
|
27
|
+
response.message = "API: " + err.message
|
|
28
|
+
res.status(status).json(response)
|
|
29
|
+
|
|
30
|
+
} else {
|
|
31
|
+
// kalau selain post, kirimkan halaman error
|
|
32
|
+
const variables = {
|
|
33
|
+
...response,
|
|
34
|
+
...{
|
|
35
|
+
message: err.message
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const tplFilePath = path.join(context.getWebappsDirectory(), 'templates', 'moduleError.ejs')
|
|
39
|
+
const content = await helper.parseTemplate(tplFilePath, variables)
|
|
40
|
+
res.status(status).send(content)
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import context from './../context.js'
|
|
2
|
+
import * as helper from './../helper.js'
|
|
3
|
+
import * as http from 'node:http'
|
|
4
|
+
import * as path from 'node:path'
|
|
5
|
+
|
|
6
|
+
export async function handleModuleNotfound(req, res) {
|
|
7
|
+
const appName = req.app.locals.appConfig.appName || ''
|
|
8
|
+
const moduleName = req.params.modulename
|
|
9
|
+
const tplFilePath = path.join(context.getWebappsDirectory(), 'templates', 'moduleNotfound.ejs')
|
|
10
|
+
const content = await helper.parseTemplate(tplFilePath, {appName, moduleName})
|
|
11
|
+
res.status(404).send(content)
|
|
12
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import context from './../context.js'
|
|
2
|
+
import * as helper from './../helper.js'
|
|
3
|
+
import { handleError } from './handleError.js'
|
|
4
|
+
import * as path from 'path'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function moduleApi(req, res, next) {
|
|
8
|
+
const moduleName = req.params.modulename;
|
|
9
|
+
const methodName = req.params.method
|
|
10
|
+
const options = {
|
|
11
|
+
apiDir: path.join(context.getRootDirectory(), 'src', 'apis')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
|
|
16
|
+
const ModuleClass = await helper.importApiModule(moduleName, options)
|
|
17
|
+
const method = helper.kebabToCamel(methodName);
|
|
18
|
+
if (ModuleClass===undefined) {
|
|
19
|
+
throw new Error(`invalid module: '${moduleName}'`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const requestedBody = req.body
|
|
23
|
+
const module = new ModuleClass(req, res, next)
|
|
24
|
+
const result = await module.handleRequest(method, requestedBody)
|
|
25
|
+
const response = {
|
|
26
|
+
code: 0,
|
|
27
|
+
result: result
|
|
28
|
+
}
|
|
29
|
+
res.json(response)
|
|
30
|
+
} catch (err) {
|
|
31
|
+
handleError(err, req, res)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import context from './../context.js'
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import * as helper from './../helper.js'
|
|
5
|
+
|
|
6
|
+
import { handleError } from './handleError.js';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async function getCurrentVersion(filepath) {
|
|
11
|
+
const previousVersionNumber = await fs.readFile(filepath, 'utf8');
|
|
12
|
+
return previousVersionNumber
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function modulePage(req, res) {
|
|
16
|
+
const moduleName = req.params.modulename;
|
|
17
|
+
const fullUrlWithHostHeader = `${req.protocol}://${req.headers.host}${req.originalUrl}`;
|
|
18
|
+
const __rootDir = context.getRootDirectory()
|
|
19
|
+
|
|
20
|
+
const fgta5jsDebugMode = req.app.locals.appConfig.fgta5jsDebugMode
|
|
21
|
+
const fgta5jsVersion = req.app.locals.appConfig.fgta5jsVersion
|
|
22
|
+
const appDebugMode = req.app.locals.appConfig.appDebugMode
|
|
23
|
+
|
|
24
|
+
const moduleDir = path.join(__rootDir, 'public', 'modules', moduleName)
|
|
25
|
+
const ejsPath = path.join(__rootDir, 'public', 'modules', moduleName, `${moduleName}.ejs`)
|
|
26
|
+
const cssPath = path.join(__rootDir, 'public', 'modules', moduleName, `${moduleName}.css`);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// const mjsFileName = appDebugMode ? `${moduleName}.mjs` : `${moduleName}.min.mjs`
|
|
30
|
+
let mjsFileName
|
|
31
|
+
if (appDebugMode) {
|
|
32
|
+
// Default Debug
|
|
33
|
+
if (req.query.mode=='release') {
|
|
34
|
+
const version = await getCurrentVersion(path.join(__rootDir, 'public', 'modules', moduleName, 'version.txt'))
|
|
35
|
+
mjsFileName = `${moduleName}-${version}.min.mjs`
|
|
36
|
+
} else {
|
|
37
|
+
mjsFileName = `${moduleName}.mjs`
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
// Default Production
|
|
41
|
+
if (req.query.mode=='debug') {
|
|
42
|
+
mjsFileName = `${moduleName}.mjs`
|
|
43
|
+
} else {
|
|
44
|
+
const version = await getCurrentVersion(path.join(__rootDir, 'public', 'modules', moduleName, 'version.txt'))
|
|
45
|
+
mjsFileName = `${moduleName}-${version}.min.mjs`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const mjsPath = path.join(__rootDir, 'public', 'modules', moduleName, mjsFileName);
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
const htmlExtenderFile = `${moduleName}-ext.html`
|
|
52
|
+
const htmlExtenderPath = path.join(__rootDir, 'public', 'modules', moduleName, htmlExtenderFile)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const cssExists = await helper.isFileExists(cssPath)
|
|
56
|
+
const mjsExists = await helper.isFileExists(mjsPath);
|
|
57
|
+
const htmlExtenderExists = await helper.isFileExists(htmlExtenderPath);
|
|
58
|
+
|
|
59
|
+
const mjsPrerenderPath = path.join(__rootDir, 'public', 'modules', moduleName, `${moduleName}-prerender.mjs`);
|
|
60
|
+
const mjsPrerenderExists = await helper.isFileExists(mjsPrerenderPath)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const ejsModuleExist = await helper.isFileExists(ejsPath)
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
|
|
67
|
+
// coba cek request halaman
|
|
68
|
+
const fnParseModuleRequest = context.getFnParseModuleRequest()
|
|
69
|
+
if (typeof fnParseModuleRequest==='function') {
|
|
70
|
+
await fnParseModuleRequest(req, res)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// load halaman html-nya
|
|
75
|
+
if (!ejsModuleExist) {
|
|
76
|
+
const err = new Error(`requested module '${moduleName}' is not found`)
|
|
77
|
+
err.status = 404
|
|
78
|
+
throw err
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const variables = {
|
|
82
|
+
...helper.createDefaultEjsVariable(req),
|
|
83
|
+
...{
|
|
84
|
+
moduleDir,
|
|
85
|
+
ejsPath,
|
|
86
|
+
mjsPrerenderExists,
|
|
87
|
+
cssExists,
|
|
88
|
+
mjsExists,
|
|
89
|
+
mjsFileName,
|
|
90
|
+
htmlExtenderExists,
|
|
91
|
+
htmlExtenderPath,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const tplFilePath = path.join(context.getWebappsDirectory(), 'templates', 'application.page.ejs')
|
|
96
|
+
const content = await helper.parseTemplate(tplFilePath, variables)
|
|
97
|
+
|
|
98
|
+
res.status(200).send(content)
|
|
99
|
+
} catch (err) {
|
|
100
|
+
handleError(err, req, res)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import sqlUtil from '@agung_dhewe/pgsqlc'
|
|
2
|
+
|
|
3
|
+
const MAX_LENGTH = 19
|
|
4
|
+
const BLOCK_MAX_LENGTH = 5
|
|
5
|
+
const CLUSTER_MAX_LENGTH = 3
|
|
6
|
+
const tablename = "core.sequencer_doc"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export function createSequencerDocument(db, options) {
|
|
10
|
+
return new Sequencer(db, options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Sequencer {
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
#defaultOptions = {
|
|
18
|
+
COMPANY_CODE: '00',
|
|
19
|
+
blockLength: 3,
|
|
20
|
+
clusterLength: 2,
|
|
21
|
+
numberLength: 6
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
db
|
|
26
|
+
options
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
constructor(db, opt) {
|
|
30
|
+
this.db = db
|
|
31
|
+
this.options = {
|
|
32
|
+
...this.#defaultOptions,
|
|
33
|
+
...opt
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
setBlock() {
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
setCluster() {
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async yearly(doc_id, block=0, cluster=0) {
|
|
48
|
+
const self = this
|
|
49
|
+
const db = self.db
|
|
50
|
+
try {
|
|
51
|
+
const { year } = await getDbCurrentDate(db)
|
|
52
|
+
const month = 0
|
|
53
|
+
return await generateId(self, year, month, doc_id, block, cluster)
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw err
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async monthly(doc_id, block=0, cluster=0) {
|
|
60
|
+
const self = this
|
|
61
|
+
const db = self.db
|
|
62
|
+
try {
|
|
63
|
+
const { year, month } = await getDbCurrentDate(db)
|
|
64
|
+
return await generateId(self, year, month, doc_id, block, cluster)
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw err
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
function isValidDigitMax(n, length) {
|
|
74
|
+
if (!Number.isInteger(n) || !Number.isInteger(length) || length <= 0) return false;
|
|
75
|
+
|
|
76
|
+
const maxAllowed = Number('9'.repeat(length));
|
|
77
|
+
return n <= maxAllowed;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async function generateId(self, year, month, doc_id, block, cluster) {
|
|
82
|
+
const options = self.options
|
|
83
|
+
const db = self.db
|
|
84
|
+
const searchMap = {
|
|
85
|
+
seqnum: `sequencer_seqnum=\${seqnum}`,
|
|
86
|
+
block: `sequencer_block=\${block}`,
|
|
87
|
+
cluster: 'sequencer_cluster=\${cluster}',
|
|
88
|
+
year: `sequencer_year=\${year}`,
|
|
89
|
+
month: `sequencer_month=\${month}`
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
sqlUtil.connect(db)
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
if (!Number.isInteger(block)) {
|
|
96
|
+
throw new Error(`block value: '${block}' is not integer`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!Number.isInteger(cluster)) {
|
|
100
|
+
throw new Error(`cluster value: '${cluster}' is not integer`)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (block<0 || !isValidDigitMax(block, options.blockLength)) {
|
|
104
|
+
throw new Error(`block value: '${block}' is invalid. max length: ${options.blockLength} `)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (cluster<0 || !isValidDigitMax(cluster, options.clusterLength)) {
|
|
108
|
+
throw new Error(`cluster value: '${cluster}' is invalid. max length: ${options.clusterLength} `)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
// ambil code doc
|
|
115
|
+
const docparam = { doc_id }
|
|
116
|
+
const sqldoc = `select doc_seqnum from core.doc where doc_id=\${doc_id}`
|
|
117
|
+
const row = await db.oneOrNone(sqldoc, docparam)
|
|
118
|
+
const seqnum = row!=null ? row.doc_seqnum : 0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if (seqnum<0 || seqnum>99) {
|
|
122
|
+
throw new Error(`doc_id: '${doc_id}' has doc seqnum '${seqnum}' that is invalid. doc seqnum have to in range 1-99 `)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
// hitung total panjang bigint yang akan dihailkan
|
|
127
|
+
let nlength = 0
|
|
128
|
+
if (month==0) {
|
|
129
|
+
// yearly
|
|
130
|
+
nlength += 2
|
|
131
|
+
} else {
|
|
132
|
+
// montly
|
|
133
|
+
nlength += 4
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (seqnum>0) {
|
|
137
|
+
nlength += 2
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (block>0) {
|
|
141
|
+
nlength += self.options.blockLength
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (cluster>0) {
|
|
145
|
+
nlength += self.options.clusterLength
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
// 250901000402 0000009
|
|
150
|
+
|
|
151
|
+
const ln = nlength + self.options.numberLength
|
|
152
|
+
if (ln > MAX_LENGTH) {
|
|
153
|
+
throw new Error(`Total length of sequencer (${ln}) is mre than max length allowed(${MAX_LENGTH})`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
// ambil data sequencer
|
|
161
|
+
{
|
|
162
|
+
const criteria = { year, month, seqnum, block, cluster }
|
|
163
|
+
if (block==null) {
|
|
164
|
+
criteria.block = 0
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (cluster==null) {
|
|
168
|
+
criteria.cluster = 0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
|
|
173
|
+
// console.log(year, month, whereClause, queryParams)
|
|
174
|
+
|
|
175
|
+
const columns = [
|
|
176
|
+
'sequencer_id',
|
|
177
|
+
'sequencer_year',
|
|
178
|
+
'sequencer_month',
|
|
179
|
+
'sequencer_seqnum',
|
|
180
|
+
'sequencer_block',
|
|
181
|
+
'sequencer_cluster',
|
|
182
|
+
'sequencer_number',
|
|
183
|
+
'EXTRACT(YEAR FROM sequencer_lastdate) AS lastyear',
|
|
184
|
+
'EXTRACT(MONTH FROM sequencer_lastdate) AS lastmonth'
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
const sql = sqlUtil.createSqlSelect({tablename, columns, whereClause, sort:{}, limit:0, offset:0, queryParams}) + ' for update'
|
|
188
|
+
const row = await db.oneOrNone(sql, queryParams)
|
|
189
|
+
|
|
190
|
+
const obj = {}
|
|
191
|
+
|
|
192
|
+
if (row!=null) {
|
|
193
|
+
// console.log(row)
|
|
194
|
+
obj.sequencer_id = row.sequencer_id
|
|
195
|
+
obj._modifyby = 0
|
|
196
|
+
obj._modifydate = (new Date()).toISOString()
|
|
197
|
+
obj.sequencer_number = row.sequencer_number+1
|
|
198
|
+
const cmd = sqlUtil.createUpdateCommand(tablename, obj, ['sequencer_id'])
|
|
199
|
+
await cmd.execute(obj)
|
|
200
|
+
} else {
|
|
201
|
+
obj._createby = 0
|
|
202
|
+
obj._createdate = (new Date()).toISOString()
|
|
203
|
+
obj.sequencer_year = year,
|
|
204
|
+
obj.sequencer_month = month
|
|
205
|
+
obj.sequencer_seqnum = seqnum
|
|
206
|
+
obj.sequencer_block = block
|
|
207
|
+
obj.sequencer_cluster = cluster
|
|
208
|
+
obj.sequencer_number = 1
|
|
209
|
+
obj.sequencer_lastdate = (new Date()).toISOString()
|
|
210
|
+
obj.sequencer_remark = doc_id
|
|
211
|
+
const cmd = sqlUtil.createInsertCommand(tablename, obj, ['sequencer_id'])
|
|
212
|
+
await cmd.execute(obj)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// compose generated id
|
|
216
|
+
// DD05.YYMM.000.00.00000
|
|
217
|
+
const YY = String(year-2000).padStart(2, '0')
|
|
218
|
+
const MM = String(month).padStart(2, '0')
|
|
219
|
+
|
|
220
|
+
const tokendoc = []
|
|
221
|
+
const tokennum = []
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if (options.COMPANY_CODE!='00') {
|
|
225
|
+
if (seqnum>0) {
|
|
226
|
+
tokendoc.push(doc_id)
|
|
227
|
+
tokendoc.push(options.COMPANY_CODE)
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
if (seqnum>0) {
|
|
231
|
+
tokendoc.push(doc_id)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if (month>0) {
|
|
237
|
+
// monthly
|
|
238
|
+
tokendoc.push('.')
|
|
239
|
+
tokendoc.push(YY)
|
|
240
|
+
tokendoc.push(MM)
|
|
241
|
+
|
|
242
|
+
tokennum.push(YY)
|
|
243
|
+
tokennum.push(MM)
|
|
244
|
+
} else {
|
|
245
|
+
// yearly
|
|
246
|
+
tokendoc.push('.')
|
|
247
|
+
tokendoc.push(YY)
|
|
248
|
+
|
|
249
|
+
tokennum.push(YY)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (seqnum>0) {
|
|
253
|
+
tokennum.push(String(seqnum).padStart(2, '0'))
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if (block>0) {
|
|
259
|
+
const codeBlock = String(block).padStart(self.options.blockLength, '0')
|
|
260
|
+
tokendoc.push('.')
|
|
261
|
+
tokendoc.push(codeBlock)
|
|
262
|
+
|
|
263
|
+
tokennum.push(codeBlock)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (cluster>0) {
|
|
267
|
+
const codeCluster = String(cluster).padStart(self.options.clusterLength, '0')
|
|
268
|
+
tokendoc.push('.')
|
|
269
|
+
tokendoc.push(codeCluster)
|
|
270
|
+
|
|
271
|
+
tokennum.push(codeCluster)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
tokendoc.push('.')
|
|
276
|
+
tokendoc.push(String(obj.sequencer_number).padStart(self.options.numberLength, '0'))
|
|
277
|
+
|
|
278
|
+
const idpref = tokennum.join('')
|
|
279
|
+
const numlen = MAX_LENGTH - idpref.length
|
|
280
|
+
tokennum.push(String(obj.sequencer_number).padStart(numlen, '0'))
|
|
281
|
+
|
|
282
|
+
const ret = {
|
|
283
|
+
id: tokennum.join(''),
|
|
284
|
+
doc: tokendoc.join('')
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return ret
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
} catch (err) {
|
|
291
|
+
throw err
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function getDbCurrentDate(db) {
|
|
296
|
+
try {
|
|
297
|
+
const sql = "SELECT EXTRACT('year' FROM CURRENT_DATE) AS year, EXTRACT('month' FROM CURRENT_DATE) AS month"
|
|
298
|
+
const row = await db.one(sql)
|
|
299
|
+
|
|
300
|
+
const ret = {
|
|
301
|
+
year: row.year,
|
|
302
|
+
month: row.month
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return ret
|
|
306
|
+
} catch (err) {
|
|
307
|
+
throw err
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|