@agung_dhewe/webapps 1.1.2 → 1.2.1
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.
Potentially problematic release.
This version of @agung_dhewe/webapps might be problematic. Click here for more details.
- package/lib/fgta5js-dist/fgta5js-v1.8.5.min.js +11 -0
- package/lib/fgta5js-dist/fgta5js-v1.8.5.min.js.map +1 -0
- package/{libs → lib}/webmodule/module-edit.css +73 -18
- package/{libs → lib}/webmodule/module-list.css +13 -0
- package/lib/webmodule/module-print.css +28 -0
- package/{libs → lib}/webmodule/module.css +13 -6
- package/{libs → lib}/webmodule/module.js +14 -4
- package/lib/webmodule/pagehelper.mjs +129 -0
- package/modules/generator/appgen-io.mjs +153 -76
- package/modules/generator/appgen-ui.mjs +234 -167
- package/modules/generator/generator-designtemplate-def.html +38 -0
- package/modules/generator/generator-designtemplate.html +11 -1492
- package/modules/generator/generator.css +103 -65
- package/modules/generator/generator.mjs +1 -1
- package/modules/generator/generator.svg +98 -0
- package/modules/generator/generatorEdit.mjs +43 -35
- package/modules/generator/generatorList.mjs +27 -0
- package/modules/generator/tpl-designerinfo.html +100 -0
- package/modules/generator/tpl-field-checkbox.html +200 -0
- package/modules/generator/tpl-field-combobox.html +228 -0
- package/modules/generator/tpl-field-datepicker.html +192 -0
- package/modules/generator/tpl-field-filebox.html +189 -0
- package/modules/generator/tpl-field-numberbox.html +218 -0
- package/modules/generator/tpl-field-textbox.html +255 -0
- package/modules/generator/tpl-field-timepicker.html +192 -0
- package/modules/generator/tpl-searchdesign.html +32 -0
- package/modules/generator/tpl-uniquedesign.html +25 -0
- package/modules/login/login.css +10 -2
- package/package.json +3 -1
- package/percobaan/coba-sequencer.js +16 -0
- package/src/api.js +12 -9
- package/src/apis/generator.api.js +35 -23
- package/src/apis/login.api.js +1 -0
- package/src/db.js +58 -32
- package/src/generator/createApiModule.js +4 -1
- package/src/generator/createIcon.js +24 -2
- package/src/generator/createLayoutCss.js +107 -0
- package/src/generator/createModuleDetilEditHtml.js +12 -1
- package/src/generator/createModuleDetilEditMjs.js +32 -28
- package/src/generator/createModuleDetilListHtml.js +14 -7
- package/src/generator/createModuleDetilListMjs.js +13 -1
- package/src/generator/createModuleHeaderEditHtml.js +13 -1
- package/src/generator/createModuleHeaderEditMjs.js +23 -2
- package/src/generator/createProgramData.js +3 -2
- package/src/generator/createTable.js +45 -38
- package/src/generator/helper.js +45 -27
- package/src/generator/templates/__rollup-module.ejs +1 -1
- package/src/generator/templates/api-module.js.ejs +171 -32
- package/src/generator/templates/layout.css.ejs +24 -0
- package/src/generator/templates/module-ext.html.ejs +1 -1
- package/src/generator/templates/module-ext.mjs.ejs +19 -1
- package/src/generator/templates/module.ejs.ejs +8 -0
- package/src/generator/templates/module.mjs.ejs +42 -5
- package/src/generator/templates/moduleDetilEdit.html.ejs +11 -0
- package/src/generator/templates/moduleDetilEdit.mjs.ejs +135 -30
- package/src/generator/templates/moduleDetilList.html.ejs +2 -1
- package/src/generator/templates/moduleDetilList.mjs.ejs +86 -11
- package/src/generator/templates/moduleHeaderEdit.html.ejs +8 -1
- package/src/generator/templates/moduleHeaderEdit.mjs.ejs +123 -36
- package/src/generator/templates/moduleHeaderList.html.ejs +5 -1
- package/src/generator/templates/moduleHeaderList.mjs.ejs +47 -15
- package/src/generator/trygenerate.js +18 -2
- package/src/generator/worker.js +83 -72
- package/src/logger.js +12 -12
- package/src/notifier.js +29 -0
- package/src/routers/generatorPage.js +3 -1
- package/src/routers/modulePage.js +32 -7
- package/src/sequencerdoc.js +22 -46
- package/src/sequencerline.js +16 -4
- package/src/session.js +69 -33
- package/src/startup.js +47 -10
- package/src/webapps.js +61 -18
- package/templates/_lib_debug.ejs +8 -8
- package/templates/_lib_production.ejs +2 -2
- package/templates/application.page.ejs +39 -6
- package/templates/generator.page.ejs +4 -3
- package/templates/index.page.ejs +2 -2
- package/templates/login.page.ejs +3 -3
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js +0 -11
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js.map +0 -1
- package/libs/webmodule/pagehelper.mjs +0 -45
- package/modules/generator/generator.png +0 -0
- /package/{libs/fgta5js-dist/fgta5js-v1.8.3.min.css → lib/fgta5js-dist/fgta5js-v1.8.5.min.css} +0 -0
- /package/{libs → lib}/fgta5js-dist/fonts/karla-italic-latin-ext.woff2 +0 -0
- /package/{libs → lib}/fgta5js-dist/fonts/karla-italic-latin.woff2 +0 -0
- /package/{libs → lib}/fgta5js-dist/fonts/karla-normal-latin-ext.woff2 +0 -0
- /package/{libs → lib}/fgta5js-dist/fonts/karla-normal-latin.woff2 +0 -0
- /package/{libs → lib}/fgta5js-dist/fonts/karla.css +0 -0
- /package/{libs → lib}/webmodule/module-footer.css +0 -0
package/src/sequencerdoc.js
CHANGED
|
@@ -17,7 +17,6 @@ class Sequencer {
|
|
|
17
17
|
#defaultOptions = {
|
|
18
18
|
COMPANY_CODE: '00',
|
|
19
19
|
blockLength: 3,
|
|
20
|
-
clusterLength: 2,
|
|
21
20
|
numberLength: 6
|
|
22
21
|
}
|
|
23
22
|
|
|
@@ -44,24 +43,24 @@ class Sequencer {
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
async yearly(doc_id, block=0
|
|
46
|
+
async yearly(doc_id, block=0) {
|
|
48
47
|
const self = this
|
|
49
48
|
const db = self.db
|
|
50
49
|
try {
|
|
51
50
|
const { year } = await getDbCurrentDate(db)
|
|
52
51
|
const month = 0
|
|
53
|
-
return await generateId(self, year, month, doc_id, block
|
|
52
|
+
return await generateId(self, year, month, doc_id, block)
|
|
54
53
|
} catch (err) {
|
|
55
54
|
throw err
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
async monthly(doc_id, block=0
|
|
58
|
+
async monthly(doc_id, block=0) {
|
|
60
59
|
const self = this
|
|
61
60
|
const db = self.db
|
|
62
61
|
try {
|
|
63
62
|
const { year, month } = await getDbCurrentDate(db)
|
|
64
|
-
return await generateId(self, year, month, doc_id, block
|
|
63
|
+
return await generateId(self, year, month, doc_id, block)
|
|
65
64
|
} catch (err) {
|
|
66
65
|
throw err
|
|
67
66
|
}
|
|
@@ -78,13 +77,13 @@ function isValidDigitMax(n, length) {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
|
|
81
|
-
async function generateId(self, year, month, doc_id, block
|
|
80
|
+
async function generateId(self, year, month, doc_id, block) {
|
|
82
81
|
const options = self.options
|
|
83
82
|
const db = self.db
|
|
84
83
|
const searchMap = {
|
|
85
84
|
seqnum: `sequencer_seqnum=\${seqnum}`,
|
|
86
85
|
block: `sequencer_block=\${block}`,
|
|
87
|
-
|
|
86
|
+
seqclust: 'sequencer_cluster=\${seqclust}',
|
|
88
87
|
year: `sequencer_year=\${year}`,
|
|
89
88
|
month: `sequencer_month=\${month}`
|
|
90
89
|
};
|
|
@@ -96,30 +95,26 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
96
95
|
throw new Error(`block value: '${block}' is not integer`)
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
if (!Number.isInteger(cluster)) {
|
|
100
|
-
throw new Error(`cluster value: '${cluster}' is not integer`)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
98
|
if (block<0 || !isValidDigitMax(block, options.blockLength)) {
|
|
104
99
|
throw new Error(`block value: '${block}' is invalid. max length: ${options.blockLength} `)
|
|
105
100
|
}
|
|
106
101
|
|
|
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
102
|
|
|
114
103
|
// ambil code doc
|
|
115
104
|
const docparam = { doc_id }
|
|
116
|
-
const sqldoc = `select doc_seqnum from core.doc where doc_id=\${doc_id}`
|
|
105
|
+
const sqldoc = `select doc_prefix, doc_seqclust, doc_seqnum from core.doc where doc_id=\${doc_id}`
|
|
117
106
|
const row = await db.oneOrNone(sqldoc, docparam)
|
|
107
|
+
const prefix = row!=null ? row.doc_prefix : null
|
|
108
|
+
const seqclust = row!=null ? row.doc_seqclust : 0
|
|
118
109
|
const seqnum = row!=null ? row.doc_seqnum : 0
|
|
119
110
|
|
|
120
111
|
|
|
112
|
+
if (seqclust<0 || seqclust>999) {
|
|
113
|
+
throw new Error(`doc_id: '${doc_id}' has doc seqclust '${seqclust}' that is invalid. doc seqnum have to in range 0-999 `)
|
|
114
|
+
}
|
|
115
|
+
|
|
121
116
|
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
|
|
117
|
+
throw new Error(`doc_id: '${doc_id}' has doc seqnum '${seqnum}' that is invalid. doc seqnum have to in range 0-99 `)
|
|
123
118
|
}
|
|
124
119
|
|
|
125
120
|
|
|
@@ -141,10 +136,6 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
141
136
|
nlength += self.options.blockLength
|
|
142
137
|
}
|
|
143
138
|
|
|
144
|
-
if (cluster>0) {
|
|
145
|
-
nlength += self.options.clusterLength
|
|
146
|
-
}
|
|
147
|
-
|
|
148
139
|
|
|
149
140
|
// 250901000402 0000009
|
|
150
141
|
|
|
@@ -159,15 +150,11 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
159
150
|
|
|
160
151
|
// ambil data sequencer
|
|
161
152
|
{
|
|
162
|
-
const criteria = { year, month, seqnum, block,
|
|
153
|
+
const criteria = { year, month, seqnum, block, seqclust }
|
|
163
154
|
if (block==null) {
|
|
164
155
|
criteria.block = 0
|
|
165
156
|
}
|
|
166
157
|
|
|
167
|
-
if (cluster==null) {
|
|
168
|
-
criteria.cluster = 0
|
|
169
|
-
}
|
|
170
|
-
|
|
171
158
|
|
|
172
159
|
const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
|
|
173
160
|
// console.log(year, month, whereClause, queryParams)
|
|
@@ -204,7 +191,7 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
204
191
|
obj.sequencer_month = month
|
|
205
192
|
obj.sequencer_seqnum = seqnum
|
|
206
193
|
obj.sequencer_block = block
|
|
207
|
-
obj.sequencer_cluster =
|
|
194
|
+
obj.sequencer_cluster = seqclust
|
|
208
195
|
obj.sequencer_number = 1
|
|
209
196
|
obj.sequencer_lastdate = (new Date()).toISOString()
|
|
210
197
|
obj.sequencer_remark = doc_id
|
|
@@ -221,15 +208,12 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
221
208
|
const tokennum = []
|
|
222
209
|
|
|
223
210
|
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (seqnum>0) {
|
|
231
|
-
tokendoc.push(doc_id)
|
|
232
|
-
}
|
|
211
|
+
if (prefix!=null) {
|
|
212
|
+
tokendoc.push(prefix)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (options.COMPANY_CODE!='00' && options.COMPANY_CODE!=null) {
|
|
216
|
+
tokendoc.push(options.COMPANY_CODE)
|
|
233
217
|
}
|
|
234
218
|
|
|
235
219
|
|
|
@@ -263,14 +247,6 @@ async function generateId(self, year, month, doc_id, block, cluster) {
|
|
|
263
247
|
tokennum.push(codeBlock)
|
|
264
248
|
}
|
|
265
249
|
|
|
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
250
|
|
|
275
251
|
tokendoc.push('.')
|
|
276
252
|
tokendoc.push(String(obj.sequencer_number).padStart(self.options.numberLength, '0'))
|
package/src/sequencerline.js
CHANGED
|
@@ -63,6 +63,7 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
63
63
|
const options = self.options
|
|
64
64
|
const db = self.db
|
|
65
65
|
const searchMap = {
|
|
66
|
+
seqclust: 'sequencer_cluster=\${seqclust}',
|
|
66
67
|
seqnum: `sequencer_seqnum=\${seqnum}`,
|
|
67
68
|
year: `sequencer_year=\${year}`,
|
|
68
69
|
month: `sequencer_month=\${month}`
|
|
@@ -74,10 +75,15 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
74
75
|
|
|
75
76
|
// ambil code doc
|
|
76
77
|
const docparam = { doc_id }
|
|
77
|
-
const sqldoc = `select doc_seqnum from core.doc where doc_id=\${doc_id}`
|
|
78
|
+
const sqldoc = `select doc_prefix, doc_seqclust, doc_seqnum from core.doc where doc_id=\${doc_id}`
|
|
78
79
|
const row = await db.oneOrNone(sqldoc, docparam)
|
|
80
|
+
const prefix = row!=null ? row.doc_prefix : ''
|
|
81
|
+
const seqclust = row!=null ? row.doc_seqclust : 0
|
|
79
82
|
const seqnum = row!=null ? row.doc_seqnum : 0
|
|
80
83
|
|
|
84
|
+
if (seqclust<0 || seqclust>999) {
|
|
85
|
+
throw new Error(`doc_id: '${doc_id}' has doc seqclust '${seqclust}' that is invalid. doc seqnum have to in range 0-999 `)
|
|
86
|
+
}
|
|
81
87
|
|
|
82
88
|
if (seqnum<0 || seqnum>99) {
|
|
83
89
|
throw new Error(`doc_id: '${doc_id}' has doc seqnum '${seqnum}' that is invalid. doc seqnum have to in range 1-99 `)
|
|
@@ -110,6 +116,7 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
110
116
|
throw new Error(`Total length of sequencer (${ln}) is mre than max length allowed(9)`)
|
|
111
117
|
}
|
|
112
118
|
} else {
|
|
119
|
+
|
|
113
120
|
if (ln > MAX_LENGTH) {
|
|
114
121
|
throw new Error(`Total length of sequencer (${ln}) is mre than max length allowed(${MAX_LENGTH})`)
|
|
115
122
|
}
|
|
@@ -118,13 +125,14 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
118
125
|
|
|
119
126
|
// ambil data sequencer
|
|
120
127
|
{
|
|
121
|
-
const criteria = { year, month, seqnum }
|
|
128
|
+
const criteria = { year, month, seqnum, seqclust }
|
|
122
129
|
const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
|
|
123
130
|
|
|
124
131
|
const columns = [
|
|
125
132
|
'sequencer_id',
|
|
126
133
|
'sequencer_year',
|
|
127
134
|
'sequencer_month',
|
|
135
|
+
'sequencer_cluster',
|
|
128
136
|
'sequencer_seqnum',
|
|
129
137
|
'sequencer_number',
|
|
130
138
|
'EXTRACT(YEAR FROM sequencer_lastdate) AS lastyear',
|
|
@@ -150,6 +158,7 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
150
158
|
obj.sequencer_year = year,
|
|
151
159
|
obj.sequencer_month = month
|
|
152
160
|
obj.sequencer_seqnum = seqnum
|
|
161
|
+
obj.sequencer_cluster = seqclust
|
|
153
162
|
obj.sequencer_number = 1
|
|
154
163
|
obj.sequencer_lastdate = (new Date()).toISOString()
|
|
155
164
|
obj.sequencer_remark = doc_id
|
|
@@ -186,8 +195,11 @@ async function generateId(self, year, month, doc_id, short=false) {
|
|
|
186
195
|
const numlen = maxlen - idpref.length
|
|
187
196
|
tokennum.push(String(obj.sequencer_number).padStart(numlen, '0'))
|
|
188
197
|
|
|
189
|
-
const
|
|
190
|
-
|
|
198
|
+
const ret = {
|
|
199
|
+
id: tokennum.join(''),
|
|
200
|
+
doc: `${prefix}${tokennum.join('')}`
|
|
201
|
+
}
|
|
202
|
+
return ret
|
|
191
203
|
}
|
|
192
204
|
} catch (err) {
|
|
193
205
|
throw err
|
package/src/session.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import session from 'express-session'; // session
|
|
2
2
|
import { createClient } from 'redis'; // session
|
|
3
3
|
import * as connectRedis from 'connect-redis'; // session
|
|
4
|
-
|
|
4
|
+
import createFileStore from 'session-file-store'
|
|
5
|
+
import context from './context.js'
|
|
6
|
+
import * as path from 'node:path';
|
|
5
7
|
|
|
6
8
|
export async function createSession(options) {
|
|
7
9
|
|
|
8
|
-
const redisUrl = options.redisUrl
|
|
10
|
+
const redisUrl = options.redisUrl //|| 'redis://localhost:6379'
|
|
9
11
|
const sessionName = options.sessionName || 'sid'
|
|
10
12
|
const sessionSecret = options.sessionSecret || 'rahasia'
|
|
11
13
|
const sessionMaxAge = options.sessionMaxAge || 15 * 50 * 1000 // default 15 menit
|
|
@@ -13,35 +15,72 @@ export async function createSession(options) {
|
|
|
13
15
|
const sessionSecure = options.sessionSecure ?? false
|
|
14
16
|
const sessionHttpOnly = options.sessionHttpOnly ?? true
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
|
|
19
|
+
if (redisUrl!==undefined) {
|
|
20
|
+
const RedisStore = connectRedis.RedisStore;
|
|
21
|
+
|
|
22
|
+
console.log(`connecting to redis ${redisUrl}`)
|
|
23
|
+
const redisClient = createClient({
|
|
24
|
+
url: redisUrl
|
|
25
|
+
});
|
|
26
|
+
await redisClient.connect();
|
|
27
|
+
console.log('connected to redis server.')
|
|
28
|
+
|
|
29
|
+
const redisStore = new RedisStore({
|
|
30
|
+
client: redisClient,
|
|
31
|
+
prefix: 'sess:',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const sessionConfig = {
|
|
36
|
+
name: sessionName,
|
|
37
|
+
store: redisStore,
|
|
38
|
+
secret: sessionSecret,
|
|
39
|
+
resave: false,
|
|
40
|
+
saveUninitialized: false,
|
|
41
|
+
rolling: true,
|
|
42
|
+
cookie: {
|
|
43
|
+
secure: sessionSecure,
|
|
44
|
+
httpOnly: sessionHttpOnly,
|
|
45
|
+
maxAge: sessionMaxAge,
|
|
46
|
+
domain: sessionDomain
|
|
47
|
+
}
|
|
44
48
|
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
console.log('starting redis session manager.')
|
|
52
|
+
return session(sessionConfig)
|
|
53
|
+
|
|
54
|
+
} else {
|
|
55
|
+
|
|
56
|
+
const __rootDirectory = context.getRootDirectory()
|
|
57
|
+
const SessionFileStore = createFileStore(session)
|
|
58
|
+
|
|
59
|
+
const fileStoreOptions = {
|
|
60
|
+
path: path.join(__rootDirectory, 'sessions'),
|
|
61
|
+
reapInterval: 3600, // dalam detik, bersihkan setiap 1 jam
|
|
62
|
+
logFn: () => {} // Bungkam semua log internal
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const fileStore = new SessionFileStore(fileStoreOptions)
|
|
66
|
+
|
|
67
|
+
const sessionConfig = {
|
|
68
|
+
name: sessionName,
|
|
69
|
+
store: fileStore,
|
|
70
|
+
secret: sessionSecret,
|
|
71
|
+
resave: false,
|
|
72
|
+
saveUninitialized: false,
|
|
73
|
+
rolling: true,
|
|
74
|
+
cookie: {
|
|
75
|
+
secure: sessionSecure,
|
|
76
|
+
httpOnly: sessionHttpOnly,
|
|
77
|
+
maxAge: sessionMaxAge,
|
|
78
|
+
domain: sessionDomain
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('starting filebased session manager.')
|
|
83
|
+
return session(sessionConfig)
|
|
45
84
|
}
|
|
46
85
|
|
|
47
86
|
// TODO: tambahkan untuk keperluan ini
|
|
@@ -51,7 +90,4 @@ export async function createSession(options) {
|
|
|
51
90
|
// httpOnly: true, // ✅ Tidak bisa diakses dari JavaScript
|
|
52
91
|
// sameSite: 'none', // ✅ Bisa lintas domain (harus paired dengan secure)
|
|
53
92
|
// maxAge: 15 * MINUTE,
|
|
54
|
-
|
|
55
|
-
console.log('starting session manager.')
|
|
56
|
-
return session(sessionConfig)
|
|
57
93
|
}
|
package/src/startup.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
export async function authorizeRequest(db, req) {
|
|
3
3
|
const moduleName = req.params.modulename;
|
|
4
|
-
const program_id = req.query.prog;
|
|
4
|
+
const program_id = req.query.prog;
|
|
5
5
|
|
|
6
6
|
try {
|
|
7
7
|
|
|
8
8
|
// jika belum login
|
|
9
|
-
if (req.session.user==null) {
|
|
10
|
-
const
|
|
9
|
+
if (req.session.user == null) {
|
|
10
|
+
const nextUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
11
|
+
const err = new Error(`Belum login. Anda harus <a href="login?nexturl=${nextUrl}">login</a> dulu untuk mengakses resource ini`)
|
|
11
12
|
err.status = 401
|
|
12
13
|
err.code = 401
|
|
13
14
|
throw err
|
|
@@ -20,15 +21,15 @@ export async function authorizeRequest(db, req) {
|
|
|
20
21
|
|
|
21
22
|
// jika punya akses developer boleh buka semuanya
|
|
22
23
|
const sqlUser = 'select * from core.user where user_id=${user_id} and user_isdev=true'
|
|
23
|
-
const rowUser = await db.oneOrNone(sqlUser, {user_id})
|
|
24
|
-
if (rowUser!=null) {
|
|
24
|
+
const rowUser = await db.oneOrNone(sqlUser, { user_id })
|
|
25
|
+
if (rowUser != null) {
|
|
25
26
|
return true // user adalah developer
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// jika tidak punya akses developer, cek apakah boleh buka program
|
|
29
30
|
const sql = 'select * from core.get_user_programs(${user_id}) where id=${program_id}'
|
|
30
|
-
const row = await db.oneOrNone(sql, {user_id, program_id});
|
|
31
|
-
if (row!=null) {
|
|
31
|
+
const row = await db.oneOrNone(sql, { user_id, program_id });
|
|
32
|
+
if (row != null) {
|
|
32
33
|
return true // user punya akses program
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -37,23 +38,59 @@ export async function authorizeRequest(db, req) {
|
|
|
37
38
|
err.code = 401
|
|
38
39
|
throw err
|
|
39
40
|
|
|
40
|
-
} catch(err) {
|
|
41
|
+
} catch (err) {
|
|
41
42
|
throw err
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
export async function getApplicationSetting(db) {
|
|
46
|
+
export async function getApplicationSetting(db, tablename = 'core.setting') {
|
|
46
47
|
const setting = {}
|
|
47
48
|
try {
|
|
48
|
-
const sql =
|
|
49
|
+
const sql = `select setting_id, setting_value from ${tablename}`
|
|
49
50
|
const rows = await db.any(sql);
|
|
50
51
|
for (var row of rows) {
|
|
51
52
|
const setting_id = row.setting_id
|
|
52
53
|
const setting_value = row.setting_value
|
|
53
54
|
setting[setting_id] = setting_value
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
setting.SETTING_TABLE_NAME = tablename
|
|
55
58
|
return setting
|
|
56
59
|
} catch (err) {
|
|
57
60
|
throw err
|
|
58
61
|
}
|
|
59
62
|
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export async function requireSetting(db, setting, setting_id, descr) {
|
|
66
|
+
if (setting[setting_id] !== undefined) {
|
|
67
|
+
// setting sudah ada
|
|
68
|
+
return true
|
|
69
|
+
} else {
|
|
70
|
+
const tablename = setting.SETTING_TABLE_NAME
|
|
71
|
+
const unset = '*** unset ***'
|
|
72
|
+
|
|
73
|
+
// cek di database
|
|
74
|
+
const sqlCek = `select setting_value from ${tablename} where setting_id=\${setting_id}`
|
|
75
|
+
const rowCek = await db.oneOrNone(sqlCek, { setting_id })
|
|
76
|
+
if (rowCek == null) {
|
|
77
|
+
throw new Error(`setting '${setting_id}' tidak ditemukan di data setting ${tablename}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const value = rowCek.setting_value
|
|
81
|
+
if (value == unset) {
|
|
82
|
+
throw new Error(`setting '${setting_id}' belum di set`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// buat dulu di database
|
|
86
|
+
const sql = `
|
|
87
|
+
insert into ${tablename}
|
|
88
|
+
(setting_id, setting_value, setting_descr, _createby)
|
|
89
|
+
values
|
|
90
|
+
(\${setting_id}, \${defaultValue}, \${descr}, '240100000')`
|
|
91
|
+
await db.none(sql, { setting_id, defaultValue: unset, descr })
|
|
92
|
+
|
|
93
|
+
throw new Error(`setting ${setting_id} belum diisi di ${tablename}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
package/src/webapps.js
CHANGED
|
@@ -8,6 +8,10 @@ import cors from 'cors';
|
|
|
8
8
|
import favicon from 'serve-favicon';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import * as helper from './helper.js'
|
|
11
|
+
import http from 'http'
|
|
12
|
+
import https from 'https'
|
|
13
|
+
import fs from 'fs'
|
|
14
|
+
import { execSync } from 'node:child_process';
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -51,11 +55,11 @@ export default class WebApplication {
|
|
|
51
55
|
get defaultSessionSecure() { return defaultSessionSecure }
|
|
52
56
|
get defaultSessionHttpOnly() { return defaultSessionHttpOnly }
|
|
53
57
|
|
|
54
|
-
get express() { return this.#express}
|
|
58
|
+
get express() { return this.#express }
|
|
55
59
|
|
|
56
60
|
#__rootDirectory
|
|
57
61
|
get __rootDirectory() { return this.#__rootDirectory }
|
|
58
|
-
setRootDirectory(v) {
|
|
62
|
+
setRootDirectory(v) {
|
|
59
63
|
this.#__rootDirectory = v
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -69,7 +73,7 @@ export default class WebApplication {
|
|
|
69
73
|
throw new Error('__rootDirectory belum didefinisikan')
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
context.setRootDirectory(this.__rootDirectory)
|
|
76
|
+
context.setRootDirectory(this.__rootDirectory)
|
|
73
77
|
context.setFnParseModuleRequest(options.fnParseModuleRequest)
|
|
74
78
|
|
|
75
79
|
if (this.#startedOnce) {
|
|
@@ -81,7 +85,7 @@ export default class WebApplication {
|
|
|
81
85
|
main(this, options)
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
|
|
@@ -120,7 +124,7 @@ async function main(self, options) {
|
|
|
120
124
|
const port = options.port ?? self.defaultPort
|
|
121
125
|
const startingMessage = options.startingMessage ?? `Starting webserver on port \x1b[1;33m${port}\x1b[0m`
|
|
122
126
|
const appConfig = options.appConfig || createDefaultAppConfig()
|
|
123
|
-
|
|
127
|
+
|
|
124
128
|
const basicRouter = createBasicRouter()
|
|
125
129
|
const extendedRouter = options.router || ExpressServer.Router({ mergeParams: true });
|
|
126
130
|
const router = ExpressServer.Router({ mergeParams: true });
|
|
@@ -160,9 +164,10 @@ async function main(self, options) {
|
|
|
160
164
|
]);
|
|
161
165
|
|
|
162
166
|
// setup cors
|
|
163
|
-
if (options.allowedOrigins!=null) {
|
|
167
|
+
if (options.allowedOrigins != null) {
|
|
164
168
|
const allowedOrigins = options.allowedOrigins
|
|
165
169
|
app.use(cors({
|
|
170
|
+
credentials: true,
|
|
166
171
|
origin: function (origin, callback) {
|
|
167
172
|
if (!origin) return callback(null, true); // untuk server-side atau curl
|
|
168
173
|
const isAllowed = allowedOrigins.some(o => {
|
|
@@ -196,28 +201,33 @@ async function main(self, options) {
|
|
|
196
201
|
|
|
197
202
|
// framework ini menggunakan library fgta5 untuk ui di client
|
|
198
203
|
if (appConfig.fgta5jsDebugMode) {
|
|
199
|
-
app.use('/public/
|
|
204
|
+
app.use('/public/lib/fgta5js', ExpressServer.static(path.join(__dirname, '..', 'lib', 'fgta5js')));
|
|
200
205
|
} else {
|
|
201
|
-
app.use('/public/
|
|
206
|
+
app.use('/public/lib/fgta5js', ExpressServer.static(path.join(__dirname, '..', 'lib', 'fgta5js-dist')));
|
|
202
207
|
}
|
|
203
|
-
|
|
204
|
-
app.use('/public/
|
|
205
|
-
|
|
208
|
+
|
|
209
|
+
app.use('/public/lib/webmodule', ExpressServer.static(path.join(__dirname, '..', 'lib', 'webmodule')));
|
|
210
|
+
|
|
206
211
|
// Routing /public untuk serve halaman-halaman static
|
|
207
212
|
app.use('/public', rejectEjsFiles);
|
|
208
213
|
app.use('/public', ExpressServer.static(path.join(__rootDirectory, 'public')));
|
|
209
214
|
app.use('/', router)
|
|
210
215
|
app.use(handleModuleNotfound)
|
|
211
|
-
|
|
212
216
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
|
|
218
|
+
// const server = app.listen(port, ()=>{
|
|
219
|
+
// console.log('\n\n' + startingMessage);
|
|
220
|
+
// });
|
|
221
|
+
|
|
222
|
+
// Buat server
|
|
223
|
+
const server = createApplicationServer(app, port, startingMessage, appConfig)
|
|
224
|
+
|
|
216
225
|
|
|
217
226
|
// Tangani event 'error' pada objek server
|
|
218
227
|
server.on('error', (err) => {
|
|
219
228
|
if (err.code === 'EADDRINUSE') {
|
|
220
|
-
|
|
229
|
+
const pid = getPidByPort(port)
|
|
230
|
+
console.error(`\n\x1b[31mError!\x1b[0m\nPort ${port} sudah digunakan pid ${pid}. Silakan coba port lain.\nCurrent pid: ${process.pid}\n`);
|
|
221
231
|
} else {
|
|
222
232
|
console.error('\n\x1b[31mError!\x1b[0m\nTerjadi kesalahan saat memulai server:', err, "\n");
|
|
223
233
|
}
|
|
@@ -227,8 +237,29 @@ async function main(self, options) {
|
|
|
227
237
|
}
|
|
228
238
|
|
|
229
239
|
|
|
240
|
+
function createApplicationServer(app, port, startingMessage, appConfig) {
|
|
241
|
+
const useSSL = appConfig.useSSL
|
|
242
|
+
const sslKey = appConfig.sslKey
|
|
243
|
+
const sslCertificate = appConfig.sslCertificate
|
|
244
|
+
|
|
245
|
+
if (useSSL) {
|
|
246
|
+
const sslOptions = {
|
|
247
|
+
key: fs.readFileSync(sslKey), // path ke private key
|
|
248
|
+
cert: fs.readFileSync(sslCertificate) // path ke sertifikat
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return https.createServer(sslOptions, app).listen(port, () => {
|
|
252
|
+
console.log('\n\n' + startingMessage + ' (https)');
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
return http.createServer({}, app).listen(port, () => {
|
|
256
|
+
console.log('\n\n' + startingMessage);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
230
261
|
function rejectEjsFiles(req, res, next) {
|
|
231
|
-
const excludedExtensions = ['.ejs'];
|
|
262
|
+
const excludedExtensions = ['.ejs'];
|
|
232
263
|
const ext = path.extname(req.url);
|
|
233
264
|
|
|
234
265
|
if (excludedExtensions.includes(ext)) {
|
|
@@ -236,4 +267,16 @@ function rejectEjsFiles(req, res, next) {
|
|
|
236
267
|
}
|
|
237
268
|
|
|
238
269
|
next();
|
|
239
|
-
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
function getPidByPort(port) {
|
|
274
|
+
try {
|
|
275
|
+
// Menggunakan lsof untuk mendapatkan PID saja (-t)
|
|
276
|
+
const pid = execSync(`lsof -t -i:${port}`).toString().trim();
|
|
277
|
+
return pid ? parseInt(pid) : null;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
// Jika tidak ada proses di port tersebut, lsof mengembalikan error code non-zero
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
};
|
package/templates/_lib_debug.ejs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
<!-- Component -->
|
|
3
|
-
<script type="module" src="public/
|
|
4
|
-
<link rel="stylesheet" href="public/
|
|
5
|
-
<link rel="stylesheet" href="public/
|
|
6
|
-
<link rel="stylesheet" href="public/
|
|
7
|
-
<link rel="stylesheet" href="public/
|
|
8
|
-
<link rel="stylesheet" href="public/
|
|
9
|
-
<link rel="stylesheet" href="public/
|
|
10
|
-
<link rel="stylesheet" href="public/
|
|
3
|
+
<script type="module" src="public/lib/fgta5js/src/main.mjs"></script>
|
|
4
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-root.css" />
|
|
5
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-messagebox.css" />
|
|
6
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-modal.css" />
|
|
7
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-entry.css" />
|
|
8
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-checkbox.css" />
|
|
9
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-gridview.css" />
|
|
10
|
+
<link rel="stylesheet" href="public/lib/fgta5js/styles/fgta5js-appmanager.css" />
|
|
11
11
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
<!-- Component -->
|
|
3
|
-
<script src="public/
|
|
4
|
-
<link rel="stylesheet" href="public/
|
|
3
|
+
<script src="public/lib/fgta5js/fgta5js-<%=fgta5jsVersion%>.min.js"></script>
|
|
4
|
+
<link rel="stylesheet" href="public/lib/fgta5js/fgta5js-<%=fgta5jsVersion%>.min.css" />
|
|
5
5
|
|