@es-labs/jslib 0.0.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +42 -0
  3. package/__test__/services.test.js +32 -0
  4. package/auth/index.js +226 -0
  5. package/auth/keyv.js +23 -0
  6. package/auth/knex.js +29 -0
  7. package/auth/redis.js +23 -0
  8. package/comms/email.js +123 -0
  9. package/comms/nexmo.js +44 -0
  10. package/comms/telegram.js +43 -0
  11. package/comms/telegram2/inbound.js +314 -0
  12. package/comms/telegram2/outbound.js +574 -0
  13. package/comms/webpush.js +60 -0
  14. package/config.js +37 -0
  15. package/express/controller/auth/oauth.js +39 -0
  16. package/express/controller/auth/oidc.js +87 -0
  17. package/express/controller/auth/own.js +100 -0
  18. package/express/controller/auth/saml.js +74 -0
  19. package/express/upload.js +48 -0
  20. package/index.js +1 -0
  21. package/iso/README.md +4 -0
  22. package/iso/__tests__/csv-utils.spec.js +128 -0
  23. package/iso/__tests__/datetime.spec.js +101 -0
  24. package/iso/__tests__/fetch.spec.js +270 -0
  25. package/iso/csv-utils.js +206 -0
  26. package/iso/datetime.js +103 -0
  27. package/iso/fetch.js +129 -0
  28. package/iso/fetch2.js +180 -0
  29. package/iso/log-filter.js +17 -0
  30. package/iso/sleep.js +6 -0
  31. package/iso/ws.js +63 -0
  32. package/node/oss-files/oss-uploader-client-fetch.js +258 -0
  33. package/node/oss-files/oss-uploader-client-fetch.md +31 -0
  34. package/node/oss-files/oss-uploader-client.js +219 -0
  35. package/node/oss-files/oss-uploader-server.js +199 -0
  36. package/node/oss-files/oss-uploader-usage.js +121 -0
  37. package/node/oss-files/oss-uploader-usage.md +34 -0
  38. package/node/oss-files/s3-uploader-client.js +217 -0
  39. package/node/oss-files/s3-uploader-server.js +123 -0
  40. package/node/oss-files/s3-uploader-usage.js +77 -0
  41. package/node/oss-files/s3-uploader-usage.md +34 -0
  42. package/package.json +53 -0
  43. package/packageInfo.js +9 -0
  44. package/services/ali.js +279 -0
  45. package/services/aws.js +194 -0
  46. package/services/db/__tests__/keyv.spec.js +31 -0
  47. package/services/db/keyv.js +14 -0
  48. package/services/db/knex.js +67 -0
  49. package/services/db/redis.js +51 -0
  50. package/services/index.js +57 -0
  51. package/services/mq/README.md +8 -0
  52. package/services/websocket.js +139 -0
  53. package/t4t/README.md +1 -0
  54. package/traps.js +20 -0
  55. package/utils/__tests__/aes.spec.js +52 -0
  56. package/utils/aes.js +23 -0
  57. package/web/UI.md +71 -0
  58. package/web/bwc-autocomplete.js +211 -0
  59. package/web/bwc-combobox.js +343 -0
  60. package/web/bwc-fileupload.js +87 -0
  61. package/web/bwc-loading-overlay.js +54 -0
  62. package/web/bwc-t4t-form.js +511 -0
  63. package/web/bwc-table.js +756 -0
  64. package/web/fetch.js +129 -0
  65. package/web/i18n.js +24 -0
  66. package/web/idle.js +49 -0
  67. package/web/parse-jwt.js +15 -0
  68. package/web/pwa.js +84 -0
  69. package/web/sign-pad.js +164 -0
  70. package/web/t4t-fe.js +164 -0
  71. package/web/util.js +126 -0
  72. package/web/web-cam.js +182 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.0.1
2
+
3
+ - merging of `@es-labs/node` and `@es-labs/esm` packages
4
+
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ ## Description
2
+
3
+ Shared Javascript ES Module library for Javascript applications (node, express, web, isomorphic)
4
+
5
+ - web
6
+ - node
7
+ - iso
8
+ - express
9
+ - auth: authorization
10
+ - comms: for communications
11
+ - express: for express JS
12
+ - services: various services
13
+ - db:
14
+ - mysql
15
+ - postgres
16
+ - mq: TODO
17
+ - others:
18
+ - keyv
19
+ - redis/valkey
20
+ - webpush
21
+ - websocket
22
+ - cloud:
23
+ - aliyun:
24
+ - bucket (oss)
25
+ - aws:
26
+ - bucket (s3)
27
+ - t4t: table editor
28
+ - utils: various utilities
29
+ - aes: encrypt & decrypt
30
+ - config.js: config loader
31
+ - package.json
32
+ - packageInfo.js: some package information for now
33
+ - README.md
34
+ - traps.js: error trapping
35
+
36
+ Refer to [https://github.com/es-labs/express-template]() project for usage
37
+
38
+ ## TODO
39
+
40
+ - setup github actions to auto increment package json version for `libs/node` & `libs/esm` when deploying to npm
41
+ - https://aboutbits.it/blog/2021-03-11-using-github-actions-to-perfom-npm-version-increment
42
+ - setup unit tests
@@ -0,0 +1,32 @@
1
+ import '@express-template/common/env' // setup env vars
2
+ import { describe, it, before, after } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import StoreKnex from '@es-labs/node/services/db/knex.js';
5
+
6
+ let sqldb;
7
+
8
+ const RUN_TEST = false;
9
+ if (RUN_TEST) {
10
+ before(async () => {
11
+ sqldb = new StoreKnex();
12
+ await sqldb.open();
13
+ });
14
+
15
+ after(async () => {
16
+ await sqldb.close();
17
+ });
18
+
19
+ describe('Test Services', () => {
20
+ it.skip('Test Knex', async () => {
21
+ let knex = sqldb.get();
22
+ const rv = (await knex('users').where({ username: 'ais-one' }).first()).githubId;
23
+ assert.strictEqual(rv, 4284574);
24
+ });
25
+ });
26
+
27
+ describe('Services Test', () => {
28
+ it.skip('should pass', () => {
29
+ assert.strictEqual(true, true);
30
+ });
31
+ });
32
+ }
package/auth/index.js ADDED
@@ -0,0 +1,226 @@
1
+ import bcrypt from 'bcryptjs'
2
+ import jwt from 'jsonwebtoken'
3
+ import * as keyv from './keyv.js'
4
+ import * as knex from './knex.js'
5
+ import * as redis from './redis.js'
6
+
7
+ //NOSONAR import uuid from 'uuid/v4'
8
+ //NOSONAR import qrcode from 'qrcode'
9
+
10
+ let setRefreshToken, getRefreshToken, revokeRefreshToken,
11
+ setRefreshTokenStoreName, setTokenService, setUserService,
12
+ findUser, updateUser,
13
+ setAuthUserStoreName
14
+
15
+ const {
16
+ COOKIE_HTTPONLY, COOKIE_SAMESITE, COOKIE_SECURE, COOKIE_MAXAGE, COOKIE_DOMAIN,
17
+ AUTH_REFRESH_URL, AUTH_USER_FIELD_LOGIN, AUTH_USER_FIELD_PASSWORD, AUTH_USER_FIELD_GAKEY, AUTH_USER_FIELD_ID_FOR_JWT, AUTH_USER_FIELDS_JWT_PAYLOAD = '',
18
+ JWT_REFRESH_STORE='keyv',
19
+ AUTH_USER_STORE,
20
+ AUTH_USER_STORE_NAME,
21
+ JWT_REFRESH_STORE_NAME,
22
+
23
+ USE_OTP,
24
+ JWT_ALG, JWT_EXPIRY, JWT_REFRESH_EXPIRY,
25
+ JWT_PRIVATE_KEY, JWT_CERTIFICATE, JWT_REFRESH_PRIVATE_KEY, JWT_REFRESH_CERTIFICATE, JWT_SECRET, JWT_REFRESH_SECRET,
26
+ JWT_ALLOW_INSECURE_KEY_SIZES
27
+ } = process.env
28
+
29
+ const authFns = { // rename to authFns
30
+ findUser: null,
31
+ updateUser: null,
32
+ revokeRefreshToken: null
33
+ }
34
+
35
+ // const services = (await import('@es-labs/node/services')).default;
36
+ // const authService = (await import('@es-labs/node/auth')).default;
37
+
38
+ const store = {
39
+ keyv,
40
+ knex,
41
+ redis
42
+ }
43
+
44
+ const setup = (tokenService, userService) => {
45
+ //NOSONAR ({ } = process.env);
46
+ // ({ setRefreshToken, getRefreshToken, revokeRefreshToken, setRefreshTokenStoreName, setTokenService } = require('./' + JWT_REFRESH_STORE)); // keyv, redis, knex
47
+ // ({ findUser, updateUser, setAuthUserStoreName, setUserService } = require('./' + AUTH_USER_STORE)); // knex
48
+ ({ setRefreshToken, getRefreshToken, revokeRefreshToken, setRefreshTokenStoreName, setTokenService } = store[JWT_REFRESH_STORE]); // keyv, redis, knex
49
+ ({ findUser, updateUser, setAuthUserStoreName, setUserService } = store[AUTH_USER_STORE]); // knex
50
+ authFns.findUser = findUser
51
+ authFns.updateUser = updateUser
52
+ authFns.revokeRefreshToken = revokeRefreshToken
53
+ if (setTokenService) setTokenService(tokenService)
54
+ if (setUserService) setUserService(userService)
55
+ if (setRefreshTokenStoreName) setRefreshTokenStoreName(JWT_REFRESH_STORE_NAME)
56
+ if (setAuthUserStoreName) setAuthUserStoreName(AUTH_USER_STORE_NAME)
57
+ }
58
+
59
+ // SameSite=None; must use with Secure;
60
+ // may need to restart browser, TODO set Max-Age, ALTERNATE use res.cookie, Signed?
61
+ const httpOnlyCookie = () => `HttpOnly;SameSite=${COOKIE_SAMESITE};`
62
+ + (COOKIE_SECURE ? 'Secure;':'')
63
+ + (COOKIE_MAXAGE ? 'MaxAge='+COOKIE_MAXAGE+';':'')
64
+ + (COOKIE_DOMAIN ? 'domain='+COOKIE_DOMAIN+';':'')
65
+
66
+ //NOSONAR algorithm
67
+ // expiresIn
68
+ // issuer = 'Mysoft corp'
69
+ // subject = 'some@user.com'
70
+ // audience = 'http://mysoftcorp.in'
71
+ // ip
72
+ // We implement stateful refresh_token not stateless
73
+
74
+ //NOSONAR
75
+ // mode: sign, verify
76
+ // type: access, refresh
77
+ const getSecret = (mode, type) => {
78
+ if (JWT_ALG.substring(0,2) === 'RS') {
79
+ if (mode === 'sign') {
80
+ return type === 'refresh' ? JWT_REFRESH_PRIVATE_KEY : JWT_PRIVATE_KEY
81
+ } else {
82
+ return type === 'refresh' ? JWT_REFRESH_CERTIFICATE : JWT_CERTIFICATE
83
+ }
84
+ }
85
+ return type === 'refresh' ? JWT_REFRESH_SECRET : JWT_SECRET
86
+ }
87
+
88
+ // should use:
89
+ // sub - for user id (access_token & refresh_token)
90
+ // groups - for user groups (access_token only)
91
+ // all other user related information sent on initial login and stored using local storage
92
+ // do not catch exception here, let functions above handle
93
+ const createToken = async (user) => { // Create a tokens & data from user
94
+ const user_meta = { }
95
+ const options = { }
96
+
97
+ const id = user[AUTH_USER_FIELD_ID_FOR_JWT]
98
+
99
+ if (!id) throw Error('User ID Not Found')
100
+ if (user.revoked) throw Error('User Revoked')
101
+
102
+ const groups = user.groups
103
+
104
+ const keys = AUTH_USER_FIELDS_JWT_PAYLOAD.split(',')
105
+ for (const key of keys) {
106
+ if (key && user[key] !== undefined) user_meta[key] = user[key]
107
+ }
108
+
109
+ options.allowInsecureKeySizes = !!JWT_ALLOW_INSECURE_KEY_SIZES
110
+ options.algorithm = JWT_ALG
111
+ options.expiresIn = JWT_EXPIRY
112
+ const access_token = jwt.sign({ id, groups }, getSecret('sign', 'access'), options)
113
+
114
+ options.expiresIn = JWT_REFRESH_EXPIRY
115
+ const refresh_token = jwt.sign({ id }, getSecret('sign', 'refresh'), options) // store only ID in refresh token?
116
+ await setRefreshToken(id, refresh_token) // store in DB or Cache
117
+ return {
118
+ access_token,
119
+ refresh_token,
120
+ user_meta
121
+ }
122
+ }
123
+
124
+ const setTokensToHeader = (res, {access_token, refresh_token}) => {
125
+ const _access_token = `Bearer ${access_token}`
126
+ if (COOKIE_HTTPONLY) {
127
+ res.setHeader('Set-Cookie', [
128
+ `Authorization=${_access_token};Path=/;`+ httpOnlyCookie(),
129
+ `refresh_token=${refresh_token};Path=${AUTH_REFRESH_URL};`+ httpOnlyCookie() // send only if path contains refresh
130
+ ])
131
+ } else {
132
+ res.setHeader('Authorization', `${_access_token}`)
133
+ res.setHeader('refresh_token', `${refresh_token}`)
134
+ }
135
+ }
136
+
137
+ const authUser = async (req, res, next) => {
138
+ // console.log('auth express', req.baseUrl, req.path, req.cookies, req.signedCookies)
139
+ let access_token = null
140
+ try {
141
+ let tmp = req.cookies?.Authorization || req.header('Authorization') || req.query?.Authorization
142
+ access_token = tmp.split(' ')[1]
143
+ } catch (e) {
144
+ return res.status(401).json({ message: 'Token Format Error' })
145
+ }
146
+ if (access_token) {
147
+ try {
148
+ let access_result = jwt.verify(access_token, getSecret('verify', 'access'), { algorithm: [JWT_ALG] }) // and options
149
+ if (access_result) {
150
+ req.decoded = access_result
151
+ return next()
152
+ } else {
153
+ return res.status(401).json({ message: 'Access Error' })
154
+ }
155
+ } catch (e) {
156
+ if (e.name === 'TokenExpiredError') {
157
+ return res.status(401).json({ message: 'Token Expired Error' })
158
+ } else {
159
+ console.log('auth err', e.name)
160
+ return res.status(401).json({ message: 'Token Error' })
161
+ }
162
+ }
163
+ } else {
164
+ return res.status(401).json({ message: 'Token Missing' })
165
+ }
166
+ }
167
+
168
+ const authRefresh = async (req, res) => { // get refresh token
169
+ try {
170
+ const refresh_token = req.cookies?.refresh_token || req.header('refresh_token') || req.query?.refresh_token // check refresh token & user - always stateful
171
+ const refresh_result = jwt.verify(refresh_token, getSecret('verify', 'refresh'), { algorithm: [JWT_ALG] }) // throw if expired or invalid
172
+ const { id } = refresh_result
173
+ let refreshToken = await getRefreshToken(id)
174
+ if (String(refreshToken) === String(refresh_token)) { // ok... generate new access token & refresh token?
175
+ const user = await findUser({ id })
176
+ const tokens = await createToken(user) // 5 minute expire for login
177
+ setTokensToHeader(res, tokens)
178
+ return res.status(200).json(tokens)
179
+ } else {
180
+ return res.status(401).json({ message: 'Refresh Token Error: Uncaught' })
181
+ }
182
+ } catch (err) { // use err instead of e (fix no-catch-shadow issue)
183
+ console.log('authRefresh', err)
184
+ return res.status(401).json({ message: 'Refresh Token Error' })
185
+ }
186
+ }
187
+
188
+ export {
189
+ setup,
190
+ authFns,
191
+ // findUser, updateUser,
192
+ getSecret,
193
+ createToken,
194
+ setTokensToHeader,
195
+ authUser,
196
+ authRefresh,
197
+ bcrypt
198
+ }
199
+
200
+ // do refresh token check from backend ?
201
+ /*
202
+ Signout across tabs
203
+ window.addEventListener('storage', this.syncLogout)
204
+ //....
205
+ syncLogout (event) {
206
+ if (event.key === 'logout') {
207
+ console.log('logged out from storage!')
208
+ Router.push('/login')
209
+ }
210
+ }
211
+ async function logout () {
212
+ inMemoryToken = null;
213
+ const url = 'http://localhost:3010/auth/logout'
214
+ const response = await fetch(url, { method: 'POST', credentials: 'include', })
215
+ // to support logging out from all windows
216
+ window.localStorage.setItem('logout', Date.now())
217
+ }
218
+ */
219
+
220
+ // The user logs in with a login API call.
221
+ // Server generates JWT Token and refresh_token
222
+ // Server sets a HttpOnly cookie with refresh_token. jwt_token and jwt_token_expiry are returned back to the client as a JSON payload.
223
+ // The jwt_token is stored in memory.
224
+ // A countdown to a future silent refresh is started based on jwt_token_expiry
225
+
226
+ // https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/
package/auth/keyv.js ADDED
@@ -0,0 +1,23 @@
1
+ let keyv
2
+ const setTokenService = (service) => keyv = service
3
+ const setRefreshToken = async (id, refresh_token) => keyv.set(id, refresh_token)
4
+ const getRefreshToken = async (id) => keyv.get(id)
5
+ const revokeRefreshToken = async(id) => keyv.delete(id)
6
+
7
+ const setUserService = () => {}
8
+ const setRefreshTokenStoreName = () => {}
9
+ const setAuthUserStoreName = () => {}
10
+ const findUser = () => {}
11
+ const updateUser = () => {}
12
+
13
+ export {
14
+ setTokenService,
15
+ setUserService,
16
+ setRefreshTokenStoreName,
17
+ setAuthUserStoreName,
18
+ setRefreshToken,
19
+ getRefreshToken,
20
+ revokeRefreshToken,
21
+ findUser,
22
+ updateUser
23
+ }
package/auth/knex.js ADDED
@@ -0,0 +1,29 @@
1
+ let knex
2
+ let JWT_REFRESH_STORE_NAME
3
+ let AUTH_USER_STORE_NAME
4
+
5
+
6
+ const setTokenService = (service) => knex = service
7
+ const setUserService = (service) => knex = service
8
+ const setRefreshTokenStoreName = (name) => JWT_REFRESH_STORE_NAME = name
9
+ const setAuthUserStoreName = (name) => AUTH_USER_STORE_NAME = name
10
+
11
+ // id field must be unique, upsert for PostgreSQL, MySQL, and SQLite only
12
+ const setRefreshToken = async (id, refresh_token) => knex(JWT_REFRESH_STORE_NAME).insert({ id, refresh_token }).onConflict('id').merge()
13
+ const getRefreshToken = async (id) => ( await knex(JWT_REFRESH_STORE_NAME).where({ id: id }).first() ).refresh_token
14
+ const revokeRefreshToken = async(id) => knex(JWT_REFRESH_STORE_NAME).where({ id: id }).delete()
15
+
16
+ const findUser = async (where) => knex(AUTH_USER_STORE_NAME).where(where).first()
17
+ const updateUser = async (where, payload) => knex(AUTH_USER_STORE_NAME).where(where).first().update(payload)
18
+
19
+ export {
20
+ setTokenService,
21
+ setUserService,
22
+ setRefreshTokenStoreName,
23
+ setAuthUserStoreName,
24
+ setRefreshToken,
25
+ getRefreshToken,
26
+ revokeRefreshToken,
27
+ findUser,
28
+ updateUser
29
+ }
package/auth/redis.js ADDED
@@ -0,0 +1,23 @@
1
+ let redis
2
+ const setTokenService = (service) => redis = service
3
+ const setRefreshToken = async (id, refresh_token) => redis.set(id, refresh_token)
4
+ const getRefreshToken = async (id) => redis.get(id)
5
+ const revokeRefreshToken = async(id) => redis.del(id)
6
+
7
+ const setUserService = () => {}
8
+ const setRefreshTokenStoreName = () => {}
9
+ const setAuthUserStoreName = () => {}
10
+ const findUser = () => {}
11
+ const updateUser = () => {}
12
+
13
+ export {
14
+ setTokenService,
15
+ setUserService,
16
+ setRefreshTokenStoreName,
17
+ setAuthUserStoreName,
18
+ setRefreshToken,
19
+ getRefreshToken,
20
+ revokeRefreshToken,
21
+ findUser,
22
+ updateUser
23
+ }
package/comms/email.js ADDED
@@ -0,0 +1,123 @@
1
+ import crypto from 'crypto'
2
+
3
+ const { SENDGRID_KEY, SENDGRID_SENDER_NAME, SENDGRID_SENDER_EMAIL, SENDGRID_TEMPLATE_ID, SENDGRID_URL = 'https://api.sendgrid.com/v3/mail/send' } = process.env
4
+
5
+ // generate random hash to prevent duplicate emails
6
+ const generateRandomHash = () => {
7
+ return crypto.createHash('sha256').update(new Date().toString()).digest('hex').slice(0, 15)
8
+ }
9
+
10
+ /**
11
+ * Hit SendGrid API to send email
12
+ *
13
+ * Full documentation: https://docs.sendgrid.com/api-reference/mail-send/mail-send#body
14
+ *
15
+ * @param {Object} body needed parameters to send email
16
+ */
17
+ const sendMail = async (body) => {
18
+ try {
19
+ const headers = {
20
+ Authorization: `Bearer ${SENDGRID_KEY}`,
21
+ 'Content-Type': 'application/json'
22
+ }
23
+
24
+ const response = await fetch(SENDGRID_URL, {
25
+ method: 'POST',
26
+ headers,
27
+ body: JSON.stringify(body)
28
+ })
29
+
30
+ return response
31
+ } catch (error) {
32
+ throw error
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Send html email using SendGrid
38
+ *
39
+ * @param {String} to a recepient email address
40
+ * @param {String} subject the subject of the email
41
+ * @param {String} content concatenated of the email content in html format
42
+ *
43
+ * @example sendDynamicEmail('user@mail.com', 'Test Email', '<p>Hello world!</p>')
44
+ */
45
+ export const sendEmail = async (to, subject, content) => {
46
+ try {
47
+ if (!SENDGRID_KEY) throw new Error('SENDGRID_KEY is not defined')
48
+ if (!SENDGRID_SENDER_NAME) throw new Error('SENDGRID_SENDER_NAME is not defined')
49
+ if (!SENDGRID_SENDER_EMAIL) throw new Error('SENDGRID_SENDER_EMAIL is not defined')
50
+
51
+ // generate random hash to prevent duplicate emails
52
+ const hash = generateRandomHash()
53
+
54
+ // request body
55
+ const body = {
56
+ personalizations: [{ to: [{ email: to }] }],
57
+ from: {
58
+ name: SENDGRID_SENDER_NAME,
59
+ email: SENDGRID_SENDER_EMAIL
60
+ },
61
+ subject,
62
+ content: [
63
+ {
64
+ type: 'text/html',
65
+ value: content
66
+ }
67
+ ],
68
+ headers: {
69
+ 'X-Entity-Ref-ID': hash
70
+ }
71
+ }
72
+
73
+ const response = await sendMail(body)
74
+ return response
75
+ } catch (error) {
76
+ throw error
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {String} to a recepient email address
82
+ * @param {String} subject the subject of the email
83
+ * @param {String} content concatenated of the email content in html format
84
+ *
85
+ * @example sendDynamicEmail('user@mail.com', 'Test Email', '<p>Hello world!</p>')
86
+ */
87
+ export const sendDynamicEmail = async (to, subject, content) => {
88
+ try {
89
+ if (!SENDGRID_KEY) throw new Error('SENDGRID_KEY is not defined')
90
+ if (!SENDGRID_SENDER_NAME) throw new Error('SENDGRID_SENDER_NAME is not defined')
91
+ if (!SENDGRID_SENDER_EMAIL) throw new Error('SENDGRID_SENDER_EMAIL is not defined')
92
+ if (!SENDGRID_TEMPLATE_ID) throw new Error('SENDGRID_TEMPLATE_ID is not defined')
93
+
94
+ // generate random hash to prevent duplicate emails
95
+ const hash = generateRandomHash()
96
+
97
+ // request body
98
+ const body = {
99
+ personalizations: [
100
+ {
101
+ to: [{ email: to }],
102
+ dynamic_template_data: {
103
+ subject: subject,
104
+ content: content
105
+ }
106
+ }
107
+ ],
108
+ from: {
109
+ name: SENDGRID_SENDER_NAME,
110
+ email: SENDGRID_SENDER_EMAIL
111
+ },
112
+ template_id: SENDGRID_TEMPLATE_ID,
113
+ headers: {
114
+ 'X-Entity-Ref-ID': hash
115
+ }
116
+ }
117
+
118
+ const response = await sendMail(body)
119
+ return response
120
+ } catch (error) {
121
+ throw error
122
+ }
123
+ }
package/comms/nexmo.js ADDED
@@ -0,0 +1,44 @@
1
+ import crypto from 'crypto'
2
+
3
+ //NOSONAR
4
+ // nexmo.isms('6596935500', 'Blah ' + new Date())
5
+ // nexmo.ismsSend('6596935500', 'Blah ' + new Date())
6
+ const { NEXMO_KEY, NEXMO_SECRET, NEXMO_SENDER = 'SMSnotice' } = process.env
7
+
8
+ // sms = 6511112222
9
+ // one at a time...
10
+ export const send = async (sms, message, from) => {
11
+ try {
12
+ if (!from) from = NEXMO_SENDER
13
+ if (sms && message) {
14
+ return await fetch(`https://rest.nexmo.com/sms/json?api_key=${NEXMO_KEY}&api_secret=${NEXMO_SECRET}&to=${sms}&from=${from}&text=${message}`)
15
+ }
16
+ } catch (e) {
17
+ console.log('send', e.toString())
18
+ }
19
+ return null
20
+ }
21
+
22
+ // sms = 6511112222
23
+ // one at a time...
24
+ export const ismsSend = async (sms, message, from) => {
25
+ const url = 'https://sms.era.sg/isms_mt.php?'
26
+ try {
27
+ if (sms && message) {
28
+ const options = {
29
+ params: {
30
+ uid: NEXMO_KEY,
31
+ pwd: crypto.createHash('md5').update( NEXMO_SECRET ).digest('hex'),
32
+ dnr: sms,
33
+ snr: from || NEXMO_SENDER,
34
+ msg: message,
35
+ split: 5
36
+ }
37
+ }
38
+ return await fetch(url, options)
39
+ }
40
+ } catch (e) {
41
+ console.log('ismsSend', e.toString())
42
+ }
43
+ return null
44
+ }
@@ -0,0 +1,43 @@
1
+ // Setting up webhook: https://api.telegram.org/bot{my_bot_token}/setWebhook?url={url_to_send_updates_to}
2
+ // Querying webhook: https://api.telegram.org/bot{my_bot_token}/getWebhookInfo
3
+ /*
4
+ {
5
+ update_id: 165679876,
6
+ message: {
7
+ message_id: 3,
8
+ from: {
9
+ id: 123456789,
10
+ is_bot: false,
11
+ first_name: 'A',
12
+ last_name: 'G',
13
+ username: 'aaronjxz',
14
+ language_code: 'en'
15
+ },
16
+ chat: {
17
+ id: 123456789,
18
+ first_name: 'A',
19
+ last_name: 'G',
20
+ username: 'aaronjxz',
21
+ type: 'private'
22
+ },
23
+ date: 1694045266,
24
+ text: 'test'
25
+ }
26
+ }
27
+ */
28
+
29
+
30
+ const { TELEGRAM_API_KEY, TELEGRAM_CHANNEL_ID } = process.env
31
+
32
+ export const sendMsg = async (text, chatId = '') => {
33
+ try {
34
+ // console.log('text, chatId', text, chatId)
35
+ //NOSONAR { id, date, pts, seq }
36
+ if (!chatId) chatId = TELEGRAM_CHANNEL_ID // channel message
37
+ return await fetch(`https://api.telegram.org/bot${TELEGRAM_API_KEY}/sendMessage?chat_id=${chatId}&text=${text}`)
38
+ } catch (e) {
39
+ return { err: e.toString() }
40
+ }
41
+ }
42
+
43
+ export const sendChannelMsg = async (text) => await sendMsg(text) // TODEPRECATE