Skip to content

Commit baaed40

Browse files
authored
v0.3.3 (#90)
* wip * wip * wip * wip * wip * wip * wip * wip * remove vue-apollo, upgrade apollo/client to v3 * removed json2csv on FE * wip * wip * wip * add oai sample to implement * wip * fix mongo seed * fix nexmo * component input event listener by gusmanwidodo * wip * wip * vite mwc wip * wip * vite wip * add menu * wip * wip * improve npm scripts to handle Win & Unix * package updates * wip * wip * remove * restore * wip * wip vite * wip * wip vite * wip vite * wip vite * wip vite * wip * wip vite * wip vite * wip vite * wip vite * wip vite * vite fix * autocomplete * wip * wip vite * add sorting * update packages * wip documentation * mongodb documentation * wip * wip * documentation * wip * documentation improvement * wip * v0.3.3 update * update README * improve documents
1 parent c3bf505 commit baaed40

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+3806
-1514
lines changed

Diff for: .dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ npm-debug.log
33
docker-compose*.yml
44
**/node_modules
55
**/dist
6+

Diff for: .editorconfig

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+

Diff for: Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ FROM node:12-alpine
1010
RUN apk update && apk add python make g++ && rm -rf /var/cache/apk/*
1111

1212
# available in build time only
13-
ARG ARG_API_PORT=8080
13+
ARG ARG_API_PORT=3000
1414
ARG ARG_NODE_ENV=development
1515
ARG ARG_APP_NAME=example-app
1616

@@ -32,7 +32,7 @@ ENV API_PORT $ARG_API_PORT
3232
ENV NODE_ENV $ARG_NODE_ENV
3333
ENV APP_NAME $ARG_APP_NAME
3434

35-
# for cloud run
35+
# PORT for GCP Cloud Run
3636
ENV PORT $ARG_API_PORT
3737

3838
# Copy dependency definitions

Diff for: README.md

+117-234
Large diffs are not rendered by default.

Diff for: RELEASE.md

+30-22
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
### Version 0.3.2
1+
### Version 0.3.3
22
- [chore] update packages & cleanup & work on improving documentation
3-
- make it CI/CD friendly [re-organized folders again...]
4-
- renamed common-app to common-lib and it contain reusable stuff
5-
- use NodeJS globals for CONFIG, LIB_PATH, APP_PATH, APP_NAME
6-
- long running process support such as tcp servers
7-
- cronjobs (better that cron call an API rather than run code)
8-
- deployment
9-
- Small scale - all in one server - vm, pm2, SSH (express also serves the frontend)
10-
- Medium to large
11-
- Frontend deploy to GCP Storage / AWS S3 etc.
12-
- Backend as docker container, Google Cloud Run
3+
- improve npm scripts to handler Windows and Unix environments
4+
- spa
5+
- removed vue-apollo wrapper to apollo client, just use apollo client directly, apollo client v2 to v3 has broken vue-apollo v3
6+
- vite
7+
- web components or no bundler required UI framework, also make code more framework agnostic
8+
- generic table crud
9+
- add echarts and leaflet example
10+
- backend
11+
- generic table crud
1312

1413
### Ongoing
1514
- [Work-in-progress]
@@ -18,32 +17,41 @@
1817
- deploy SPA frontend to GCP Storage
1918
- backend
2019
- GKE, Kubernetes
21-
- generic table crud frontend
20+
- add kafka working example
2221
- frontend
23-
- generic table crud backend
24-
- cypress e2e testing
2522
- Preparing for VueJS 3
26-
- PWA cookbook (many things to consider)
27-
- web components or no bundler required UI framework, also make code more framework agnostic
23+
- cypress e2e testing
2824
- [TODO]
25+
- Make VueCrudX ES module build (only possible when UI framework e.g. Vuetify has ES module version)
2926
- backend
30-
- add kafka working example
3127
- research websocket testing, improve coverage
3228
- research auto generated REST API and Testing (keep in view dredd.io)
3329
- JsonSchema
3430
- frontend
3531
- Handle/Test Signature & Webcam input
36-
- Remove date-fns library and use native JS libraries for handling date, time
37-
- explore use of fetch API instead of axios (abort fetch, etc)
38-
- ant design version (kiv until web components / more UI framework agnostic code is implemened)
32+
- Fetch API abort / retry
33+
- PWA cookbook (many things to consider)
3934
- others
40-
- Make ES module build (only possible when UI framework e.g. Vuetify has ES module version)
41-
- Should we change Vuex action setLayout to setPublic and setSecure?
4235
- graphql security & performance review
36+
- use monorepo when npm 7 is released ?
37+
- improve structure further
4338
- [Findings]
4439
- Logging (Use APM instead?) - use console.log & morgan
4540
- use native html5 validation rather than Vuelidate / Vee-validate (major version changes is painful)
4641

42+
### Version 0.3.2
43+
- [chore] update packages & cleanup & work on improving documentation
44+
- make it CI/CD friendly [re-organized folders again...]
45+
- renamed common-app to common-lib and it contain reusable stuff
46+
- use NodeJS globals for CONFIG, LIB_PATH, APP_PATH, APP_NAME
47+
- long running process support such as tcp servers
48+
- cronjobs (better that cron call an API rather than run code)
49+
- deployment
50+
- Small scale - all in one server - vm, pm2, SSH (express also serves the frontend)
51+
- Medium to large
52+
- Frontend deploy to GCP Storage / AWS S3 etc.
53+
- Backend as docker container, Google Cloud Run
54+
4755
### Version 0.3.1
4856
- update packages & cleanup
4957
- work on improving documentation

Diff for: common-lib/auth/index.js

+22-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const jwt = require('jsonwebtoken')
44
// const uuid = require('uuid/v4')
55
// const qrcode = require('qrcode')
66
const { USE_OTP, OTP_EXPIRY, HTTPONLY_TOKEN, AUTH_USER_STORE, AUTH_USER_STORE_NAME } = global.CONFIG
7-
const { AUTH_USER_FIELD_LOGIN, AUTH_USER_FIELD_PASSWORD, AUTH_USER_FIELD_GAKEY, AUTH_USER_FIELD_ID_FOR_JWT, AUTH_USER_FIELD_GROUPS_FOR_JWT } = global.CONFIG
7+
const { AUTH_USER_FIELD_LOGIN, AUTH_USER_FIELD_PASSWORD, AUTH_USER_FIELD_GAKEY, AUTH_USER_FIELD_ID_FOR_JWT, AUTH_USER_FIELDS_JWT_PAYLOAD = ''} = global.CONFIG
88
const { JWT_ALG, JWT_SECRET, JWT_EXPIRY, JWT_REFRESH_EXPIRY, JWT_REFRESH_STORE ='keyv', jwtCerts } = global.CONFIG
99

1010
const mongo = require('../services/db/mongodb')
@@ -82,13 +82,13 @@ const authUser = async (req, res, next) => {
8282
if (req.baseUrl + req.path === '/api/auth/refresh') {
8383
try {
8484
// check refresh token & user - always stateful
85-
const { id, groups, exp } = jwt.decode(token)
85+
const { id, exp, iat, verified, ...payload } = jwt.decode(token)
8686
let refreshToken = await getToken(id)
8787
if (refreshToken) {
8888
// console.log('ggg', req.baseUrl, req.path, parseInt(Date.now() / 1000) - exp, JWT_REFRESH_EXPIRY, e.toString(), parseInt(Date.now() / 1000) < exp + JWT_REFRESH_EXPIRY, token)
8989
if (parseInt(Date.now() / 1000) < exp + JWT_REFRESH_EXPIRY) { // not too expired... exp is in seconds, iat is not used
9090
if (refreshToken === req.body.refresh_token) { // ok... generate new access token & refresh token?
91-
const tokens = await createToken({ id, verified: true, groups }, { expiresIn: JWT_EXPIRY }) // 5 minute expire for login
91+
const tokens = await createToken({ id, verified: true, ...payload }, { expiresIn: JWT_EXPIRY }) // 5 minute expire for login
9292
if (HTTPONLY_TOKEN) res.setHeader('Set-Cookie', [`token=${tokens.token}; HttpOnly; Path=/;`]); // may need to restart browser, TBD set Max-Age, ALTERNATE use res.cookie, Signed?, Secure?, SameSite=true?
9393
return res.status(200).json(tokens)
9494
}
@@ -133,11 +133,22 @@ const refresh = async (req, res) => {
133133
return res.status(401).json({ message: 'Error token revoked' })
134134
}
135135

136+
const addPayloadFromUserData = (user) => { // local method
137+
const keys = AUTH_USER_FIELDS_JWT_PAYLOAD.split(',')
138+
const payloadItems = {}
139+
if (keys && keys.length) {
140+
for (const key of keys) {
141+
if (key && user[key] !== undefined) payloadItems[key] = user[key]
142+
}
143+
}
144+
return payloadItems
145+
}
146+
136147
const login = async (req, res) => {
137148
try {
138149
// console.log(AUTH_USER_FIELD_LOGIN, req.body)
139150
const user = await findUser({
140-
[AUTH_USER_FIELD_LOGIN]: req.body[AUTH_USER_FIELD_LOGIN] // email
151+
[AUTH_USER_FIELD_LOGIN]: req.body[AUTH_USER_FIELD_LOGIN]
141152
})
142153
const password = req.body[AUTH_USER_FIELD_PASSWORD]
143154

@@ -146,7 +157,8 @@ const login = async (req, res) => {
146157
if (user.revoked) return res.status(401).json({ message: 'Authorization Revoked' })
147158
let verified = true
148159
const id = user[AUTH_USER_FIELD_ID_FOR_JWT] || ''
149-
const groups = user[AUTH_USER_FIELD_GROUPS_FOR_JWT] || ''
160+
const additionalPayload = addPayloadFromUserData(user)
161+
150162
if (!id) return res.status(401).json({ message: 'Authorization Format Error' })
151163
if (USE_OTP) {
152164
verified = false
@@ -158,7 +170,7 @@ const login = async (req, res) => {
158170
// // set user SMS & send it
159171
// }
160172
}
161-
const tokens = await createToken({ id, verified, groups }, { expiresIn: USE_OTP ? OTP_EXPIRY : JWT_EXPIRY }) // 5 minute expire for login
173+
const tokens = await createToken({ id, verified, ...additionalPayload }, { expiresIn: USE_OTP ? OTP_EXPIRY : JWT_EXPIRY }) // 5 minute expire for login
162174
if (HTTPONLY_TOKEN) res.setHeader('Set-Cookie', [`token=${tokens.token}; HttpOnly; Path=/;`]); // may need to restart browser, TBD set Max-Age, ALTERNATE use res.cookie, Signed?, Secure?, SameSite=true?
163175
return res.status(200).json(tokens)
164176
} catch (e) {
@@ -169,15 +181,16 @@ const login = async (req, res) => {
169181

170182
const otp = async (req, res) => { // need to be authentication, body { pin: '123456' }
171183
try {
172-
const { id, groups } = req.decoded
184+
const { id } = req.decoded
173185
const user = await findUser({ id })
174186
if (user) {
175187
const { pin } = req.body
176188
const gaKey = user[AUTH_USER_FIELD_GAKEY]
177189
const isValid = USE_OTP !== 'TEST' ? otplib.authenticator.check(pin, gaKey) : String(pin) === '111111'
178190
if (isValid) {
179191
await revokeToken(id)
180-
const tokens = await createToken({ id, verified: true, groups }, {expiresIn: JWT_EXPIRY})
192+
const additionalPayload = addPayloadFromUserData(user)
193+
const tokens = await createToken({ id, verified: true, ...additionalPayload }, {expiresIn: JWT_EXPIRY})
181194
if (HTTPONLY_TOKEN) res.setHeader('Set-Cookie', [`token=${tokens.token}; HttpOnly; Path=/;`]); // may need to restart browser, TBD set Max-Age, ALTERNATE use res.cookie, Signed?, Secure?, SameSite=true?
182195
return res.status(200).json(tokens)
183196
} else {
@@ -248,4 +261,4 @@ console.log('Encrypted: ' + encText)
248261
249262
const decText = decryptText(algorithm, key, iv, encText, 'base64')
250263
console.log('Decrypted: ' + decText);
251-
*/
264+
*/

Diff for: common-lib/comms/nexmo.js

+26-17
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
11
const axios = require('axios')
22

3-
const { NEXMO_KEY, NEXMO_SECRET, NEXMO_FROM = '' } = global.CONFIG
3+
const { NEXMO_KEY, NEXMO_SECRET, NEXMO_FROM = 'SMSnotice' } = global.CONFIG
44

55
const nexmo = {
6-
send: async function (sms, message) {
6+
// sms = 6511112222
7+
// one at a time...
8+
send: async function (sms, message, from = NEXMO_FROM) {
79
try {
8-
return await axios.get(`https://rest.nexmo.com/sms/json?api_key=${NEXMO_KEY}&api_secret=${NEXMO_SECRET}&to=${sms}&from=${NEXMO_FROM}&text=${message}`)
10+
if (sms && message) {
11+
return await axios.get(`https://rest.nexmo.com/sms/json?api_key=${NEXMO_KEY}&api_secret=${NEXMO_SECRET}&to=${sms}&from=${from}&text=${message}`)
12+
}
913
} catch (e) {
10-
return null
14+
console.log('send', e.toString())
1115
}
16+
return null
1217
},
13-
ismsSend: async function (sms, message) {
18+
// sms = 6511112222
19+
// one at a time...
20+
ismsSend: async function (sms, message, from = NEXMO_FROM) {
21+
const url = 'https://sms.era.sg/isms_mt.php?'
1422
try {
15-
const url = 'https://sms.era.sg/isms_mt.php?'
16-
const options = {
17-
params: {
18-
uid: NEXMO_KEY,
19-
// pwd: 'xxxxxxxxyyyyyyyyzzzzzzzzxxxxxxxx',
20-
pwd: md5( NEXMO_SECRET ),
21-
dnr: sms,
22-
snr: NEXMO_FROM,
23-
msg: message,
24-
split: 5
23+
if (sms && message) {
24+
const options = {
25+
params: {
26+
uid: NEXMO_KEY,
27+
pwd: require('crypto').createHash('md5').update( NEXMO_SECRET ).digest('hex'),
28+
dnr: sms,
29+
snr: from,
30+
msg: message,
31+
split: 5
32+
}
2533
}
34+
return await axios.get(url, options)
2635
}
27-
return await axios.get(url, options)
2836
} catch (e) {
29-
return null
37+
console.log('ismsSend', e.toString())
3038
}
39+
return null
3140
}
3241
}
3342

0 commit comments

Comments
 (0)