@mixd-id/web-scaffold 0.1.230406236 → 0.1.230406238

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406236",
4
+ "version": "0.1.230406238",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -11,8 +11,7 @@
11
11
  "exports": {
12
12
  ".": "./src/index.js",
13
13
  "./themes/default": "./src/themes/default/index.js",
14
- "./components/Modal.vue": "./src/components/Modal.vue",
15
- "./components/Textbox.vue": "./src/components/Textbox.vue",
14
+ "./components/*": "./src/components/*",
16
15
  "./mixin/component": "./src/mixin/component.js",
17
16
  "./mixin/edit-mode": "./src/mixin/edit-mode.js",
18
17
  "./middleware/http/trim-string": "./src/middleware/http/trim-string.js",
@@ -20,6 +19,10 @@
20
19
  "require": "./src/utils/helpers.js",
21
20
  "import": "./src/utils/helpers.mjs"
22
21
  },
22
+ "./wss": {
23
+ "require": "./src/utils/wss.js",
24
+ "import": "./src/utils/wss.mjs"
25
+ },
23
26
  "./importer": "./src/utils/importer.js",
24
27
  "./listpage1": "./src/utils/listpage1.js",
25
28
  "./listview": "./src/utils/listview.js",
@@ -36,10 +39,12 @@
36
39
  "compression": "^1.7.4",
37
40
  "cookie-parser": "^1.4.6",
38
41
  "cors": "^2.8.5",
42
+ "crypto-js": "^4.2.0",
39
43
  "daisyui": "^2.19.0",
40
44
  "dayjs": "^1.11.2",
41
45
  "exceljs": "^4.3.0",
42
46
  "express": "^4.18.1",
47
+ "eventemitter2": "^6.4.7",
43
48
  "file-type": "^18.2.1",
44
49
  "glob": "^8.0.3",
45
50
  "lodash": "^4.17.21",
@@ -51,7 +56,8 @@
51
56
  "tailwindcss": "^3.2.4",
52
57
  "vue": "^3.2.25",
53
58
  "vue-chartjs": "^5.2.0",
54
- "vue-router": "^4.0.14"
59
+ "vue-router": "^4.0.14",
60
+ "ws": "^8.16.0"
55
61
  },
56
62
  "devDependencies": {
57
63
  "@vitejs/plugin-vue": "^2.2.0",
@@ -0,0 +1,238 @@
1
+ const WebSocket = require('ws');
2
+ const CryptoJS = require("crypto-js");
3
+ const crypto = require("crypto-js");
4
+ const EventEmitter2 = require("eventemitter2");
5
+
6
+ const convertDataUrlObj = async(obj) => {
7
+
8
+ const processObjectProperties = async (inputObj) => {
9
+
10
+ let outputObj
11
+
12
+ if(Array.isArray(inputObj)){
13
+ outputObj = []
14
+
15
+ for(let i = 0; i < inputObj.length; i++){
16
+ const value = inputObj[i];
17
+
18
+ if (Buffer.isBuffer(value)) {
19
+ outputObj[i] = value.toString('base64')
20
+ } else if (typeof value === 'object' && value !== null) {
21
+ outputObj[i] = await processObjectProperties(value);
22
+ } else {
23
+ outputObj[i] = value;
24
+ }
25
+ }
26
+ }
27
+ else if(typeof inputObj === 'object' && inputObj !== null){
28
+ outputObj = {};
29
+ for (const key in inputObj) {
30
+ if (inputObj.hasOwnProperty(key)) {
31
+ const value = inputObj[key];
32
+
33
+ if (Buffer.isBuffer(value)) {
34
+ outputObj[key] = value.toString('base64')
35
+ } else if (typeof value === 'object' && value !== null) {
36
+ outputObj[key] = await processObjectProperties(value);
37
+ } else {
38
+ outputObj[key] = value;
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ return outputObj;
45
+ };
46
+
47
+ return processObjectProperties(obj);
48
+ }
49
+
50
+ const revertDataUrlObj = (obj) => {
51
+
52
+ // Recursive function to process each property of the object
53
+ const processObjectProperties = async (inputObj) => {
54
+ let outputObj;
55
+
56
+ if(Array.isArray(inputObj)){
57
+ outputObj = []
58
+ for (let i = 0; i < inputObj.length; i++) {
59
+ let value = inputObj[i];
60
+
61
+ if (typeof value === 'string' && value.startsWith('data:')) {
62
+ if(value.indexOf(',') >= 0){
63
+ value = value.substring(value.indexOf(',') + 1)
64
+ }
65
+ outputObj[i] = Buffer.from(value, 'base64');
66
+ } else if (typeof value === 'object' && value !== null) {
67
+ outputObj[i] = await processObjectProperties(value);
68
+ } else {
69
+ outputObj[i] = value;
70
+ }
71
+ }
72
+ }
73
+ else{
74
+ outputObj = {}
75
+
76
+ for (const key in inputObj) {
77
+ if (inputObj.hasOwnProperty(key)) {
78
+ let value = inputObj[key];
79
+
80
+ if (typeof value === 'string' && value.startsWith('data:')) {
81
+ if(value.indexOf(',') >= 0){
82
+ value = value.substring(value.indexOf(',') + 1)
83
+ }
84
+ outputObj[key] = Buffer.from(value, 'base64');
85
+ } else if (typeof value === 'object' && value !== null) {
86
+ outputObj[key] = await processObjectProperties(value);
87
+ } else {
88
+ outputObj[key] = value;
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+
95
+ return outputObj;
96
+ };
97
+
98
+ // Start processing the top-level object
99
+ return processObjectProperties(obj);
100
+ }
101
+
102
+
103
+ class WSS extends EventEmitter2{
104
+
105
+ _instance
106
+ _opt
107
+
108
+ emits = [ ]
109
+
110
+ _authFn = []
111
+ _reqFn = []
112
+
113
+ async fromBinaryData(binaryData){
114
+ const secretKey = this._opt.key;
115
+ const encryptedString = Buffer.from(binaryData).toString('utf-8');
116
+ const decryptedData = CryptoJS.AES.decrypt(encryptedString, secretKey).toString(CryptoJS.enc.Utf8);
117
+ const receivedObject = JSON.parse(decryptedData);
118
+ return await revertDataUrlObj(receivedObject);
119
+ }
120
+
121
+ async toBinaryData(obj){
122
+ const secretKey = this._opt.key;
123
+ const dataUrlObj = await convertDataUrlObj(obj)
124
+ const encryptedData = crypto.AES.encrypt(JSON.stringify(dataUrlObj), secretKey).toString();
125
+ return new TextEncoder().encode(encryptedData);
126
+ }
127
+
128
+ constructor(opt) {
129
+ super()
130
+
131
+ this._opt = opt
132
+ this._instance = new WebSocket.Server(opt);
133
+
134
+ this._instance.on('connection', (socket, req) => {
135
+
136
+ socket.leave = (channel) => {
137
+ if((socket.channels ?? {})[channel]){
138
+ delete socket.channels[channel]
139
+ //console.log('leave', channel)
140
+ }
141
+ }
142
+
143
+ socket.join = (channel) => {
144
+ if(!socket.channels){
145
+ socket.channels = {}
146
+ }
147
+
148
+ socket.channels[channel] = 1
149
+ //console.log('join', channel)
150
+ }
151
+
152
+ socket.to = (channel) => {
153
+ return {
154
+ emit: (model, event, items) => {
155
+ this.broadcast(channel, { model, event, items }).then()
156
+ }
157
+ }
158
+ }
159
+
160
+ socket.on('message', async (binaryData) => {
161
+
162
+ const { _requestId, path, params } = await this.fromBinaryData(binaryData);
163
+
164
+ let status = 200
165
+ let data
166
+ switch(path){
167
+
168
+ case '_auth':
169
+ try{
170
+ for(let fn of this._authFn){
171
+ await fn(params, socket)
172
+ }
173
+ }
174
+ catch(e){
175
+ socket.close(1002, e.message);
176
+ }
177
+ break
178
+
179
+ case 'ping':
180
+ data = { type:'pong' }
181
+ break
182
+
183
+ default:
184
+ try{
185
+ const arr = (await Promise.all(this._reqFn.map(fn => fn({ path, params }, socket))))
186
+ .filter(_ => _)
187
+
188
+ data = arr.length > 0 ? JSON.parse(JSON.stringify(arr.pop())) : data
189
+ }
190
+ catch(e){
191
+ status = 500
192
+ data = {
193
+ name: e.name,
194
+ message: e.message,
195
+ errors: e.errors,
196
+ stack: process.env.APP_DEBUG === 'true' ?
197
+ e.stack : undefined
198
+ }
199
+ }
200
+ break
201
+ }
202
+
203
+ socket.send(await this.toBinaryData({
204
+ _requestId,
205
+ status,
206
+ data
207
+ }))
208
+ });
209
+
210
+ socket.on('close', (e) => {
211
+ this.emit('close', e, socket)
212
+ });
213
+ })
214
+ }
215
+
216
+ auth(fn){
217
+ this._authFn.push(fn)
218
+ }
219
+
220
+ req(fn){
221
+ this._reqFn.push(fn)
222
+ }
223
+
224
+ async broadcast(channel, { model, event, items }){
225
+ //console.log('broadcast', channel, JSON.stringify({ model, event, items }).substring(0, 70))
226
+
227
+ for(let socket of this._instance.clients){
228
+ if(socket.readyState === WebSocket.OPEN && (socket.channels ?? {})[channel]){
229
+ socket.send(await this.toBinaryData({ data:{ model, event, items } }))
230
+ }
231
+ }
232
+ }
233
+
234
+ }
235
+
236
+ module.exports = {
237
+ WSS
238
+ }
@@ -0,0 +1,289 @@
1
+ import CryptoJS from "crypto-js";
2
+ import EventEmitter2 from "eventemitter2";
3
+
4
+ class WSS extends EventEmitter2{
5
+
6
+ _instance
7
+ _opt
8
+ _counter = 0
9
+ _callbacks = {}
10
+ _pendingSend = []
11
+ _lastBlurAt
12
+
13
+ static async convertDataUrlObj(obj){
14
+
15
+ const fileToDataURL = (file) => {
16
+ return new Promise((resolve, reject) => {
17
+ const reader = new FileReader();
18
+ reader.onload = (event) => resolve(event.target.result);
19
+ reader.onerror = (error) => reject(error);
20
+ reader.readAsDataURL(file);
21
+ });
22
+ };
23
+
24
+ const processObjectProperties = async (inputObj) => {
25
+ let outputObj;
26
+
27
+ if(Array.isArray(inputObj)){
28
+ outputObj = []
29
+ for(let i = 0; i < inputObj.length; i++){
30
+ const value = inputObj[i];
31
+
32
+ if (value instanceof File) {
33
+ outputObj[i] = await fileToDataURL(value);
34
+ } else if (typeof value === 'object' && value !== null) {
35
+ outputObj[i] = await processObjectProperties(value);
36
+ } else {
37
+ outputObj[i] = value;
38
+ }
39
+ }
40
+ }
41
+ else{
42
+ outputObj = {}
43
+
44
+ for (const key in inputObj) {
45
+ if (inputObj.hasOwnProperty(key)) {
46
+ const value = inputObj[key];
47
+
48
+ if (value instanceof File) {
49
+ outputObj[key] = await fileToDataURL(value);
50
+ } else if (typeof value === 'object' && value !== null) {
51
+ outputObj[key] = await processObjectProperties(value);
52
+ } else {
53
+ outputObj[key] = value;
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+
60
+ return outputObj;
61
+ };
62
+
63
+ return processObjectProperties(obj);
64
+ }
65
+
66
+ static async revertDataUrlObj(obj){
67
+
68
+ const processObjectProperties = async (inputObj) => {
69
+ let outputObj;
70
+
71
+ if(Array.isArray(inputObj)){
72
+ outputObj = []
73
+ for (let i = 0; i < inputObj.length; i++) {
74
+ let value = inputObj[i];
75
+
76
+ if (typeof value === 'object' && value !== null) {
77
+ outputObj[i] = await processObjectProperties(value);
78
+ } else {
79
+ outputObj[i] = value;
80
+ }
81
+ }
82
+ }
83
+ else if(typeof inputObj === 'object' && inputObj !== null){
84
+ outputObj = {}
85
+ for (const key in inputObj) {
86
+ if (inputObj.hasOwnProperty(key)) {
87
+ let value = inputObj[key];
88
+
89
+ if (typeof value === 'object' && value !== null) {
90
+ outputObj[key] = await processObjectProperties(value);
91
+ } else {
92
+ outputObj[key] = value;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ return outputObj;
99
+ };
100
+
101
+ return processObjectProperties(obj);
102
+ }
103
+
104
+ constructor(opt) {
105
+ super();
106
+
107
+ this._opt = Object.assign({
108
+ auth: {},
109
+ debug: false,
110
+ key: '',
111
+ onConnect: null,
112
+ onConnectError: null,
113
+ onDisconnect: null,
114
+ timeout: 30000,
115
+ url: '',
116
+ }, opt ?? {})
117
+
118
+ if(typeof window !== 'undefined'){
119
+ window.addEventListener('online', this._onOnlineChanged)
120
+ window.addEventListener('offline', this._onOnlineChanged)
121
+ window.addEventListener('blur', this._onBlur)
122
+ window.addEventListener('focus', this._onFocus)
123
+ }
124
+
125
+ this.connect().then()
126
+ }
127
+
128
+ _onBlur = () => {
129
+ this._lastBlurAt = new Date().getTime()
130
+ }
131
+
132
+ _onFocus = () => {
133
+ this.emit('focus')
134
+
135
+ if(this._lastBlurAt){
136
+ this.send('ping', {}, { timeout:1000 })
137
+ .catch(_ => {
138
+ this.reconnect().then()
139
+ })
140
+ }
141
+ }
142
+
143
+ _onOnlineChanged = (e) => {
144
+ if(!navigator.onLine){
145
+ this.emit('disconnect', e)
146
+ }
147
+ else{
148
+ this.emit('connect')
149
+ }
150
+ }
151
+
152
+ async fromBinaryData(binaryData){
153
+ const secretKey = this._opt.key;
154
+ const encryptedString = new TextDecoder().decode(binaryData)
155
+ const decryptedData = CryptoJS.AES.decrypt(encryptedString, secretKey).toString(CryptoJS.enc.Utf8);
156
+ const receivedObject = JSON.parse(decryptedData);
157
+ return await WSS.revertDataUrlObj(receivedObject);
158
+ }
159
+
160
+ async toBinaryData(obj){
161
+ const secretKey = this._opt.key;
162
+ const encryptedData = CryptoJS.AES.encrypt(JSON.stringify(obj), secretKey).toString();
163
+ return new TextEncoder().encode(encryptedData)
164
+ }
165
+
166
+ async connect(reconnect = false){
167
+
168
+ this._instance = new WebSocket(this._opt.url)
169
+
170
+ this._instance.binaryType = 'arraybuffer';
171
+
172
+ this._instance.onopen = () => {
173
+ this.send('_auth', this._opt.auth)
174
+ .then(() => {
175
+ reconnect ? this.emit('reconnect') : this.emit('connect')
176
+
177
+ for(let sendParams of this._pendingSend){
178
+ this.sendSync(sendParams.path, sendParams.params, sendParams.cb, sendParams.err)
179
+ }
180
+ this._pendingSend = []
181
+ })
182
+ };
183
+
184
+ this._instance.onmessage = async (event) => {
185
+ const obj = await this.fromBinaryData(event.data);
186
+
187
+ const { _requestId, status, data } = obj
188
+
189
+ if(_requestId){
190
+ if(this._callbacks[_requestId]){
191
+ const { cb, err, path, params, t1 } = this._callbacks[_requestId]
192
+ status === 200 ? cb(data) : err(data)
193
+ delete this._callbacks[_requestId]
194
+
195
+ if(this._opt.debug){
196
+ console.log(new Date().getTime() - t1, path, params, data)
197
+ }
198
+ }
199
+ }
200
+ else{
201
+ const { model, event, items } = data
202
+ this.emit(model, event, items)
203
+
204
+ if(this._opt.debug){
205
+ console.log('SIGNAL', model, event, items)
206
+ }
207
+ }
208
+
209
+ };
210
+
211
+ this._instance.onerror = (e) => {
212
+ this.emit('error', e)
213
+ }
214
+
215
+ this._instance.onclose = (e) => {
216
+
217
+ switch(e.code){
218
+
219
+ case 1002:
220
+ this.emit('connect_error', e)
221
+ break
222
+
223
+ default:
224
+ this.emit('disconnect')
225
+ break
226
+ }
227
+ };
228
+ }
229
+
230
+ async reconnect(){
231
+ if(this._instance){
232
+ this._instance.close()
233
+ }
234
+
235
+ await this.connect(true)
236
+ }
237
+
238
+ sendSync(path, params, cb, err, override){
239
+ if(this._instance.readyState !== 1){
240
+ this._pendingSend.push({ path, params, cb, err })
241
+ return
242
+ }
243
+
244
+ if(!navigator.onLine){
245
+ err({ message: 'Unable to send, network disconnected' })
246
+ return
247
+ }
248
+
249
+ const _requestId = ++this._counter
250
+
251
+ WSS.convertDataUrlObj({
252
+ _requestId,
253
+ path,
254
+ params
255
+ })
256
+ .then(async(dataUrlObj) => {
257
+ const binaryData = await this.toBinaryData(dataUrlObj)
258
+ this._instance.send(binaryData)
259
+
260
+ this._callbacks[_requestId] = {
261
+ cb,
262
+ err,
263
+ path,
264
+ params,
265
+ t1: new Date().getTime()
266
+ }
267
+
268
+ setTimeout(() => {
269
+ if(this._callbacks[_requestId]){
270
+ err({ message: 'Timeout' })
271
+ delete this._callbacks[_requestId]
272
+
273
+ this.reconnect()
274
+ }
275
+ }, (override ?? {}).timeout ?? this._opt.timeout)
276
+ })
277
+ }
278
+
279
+ send(path, params, override){
280
+ return new Promise((resolve, reject) => {
281
+ this.sendSync(path, params, resolve, reject, override)
282
+ })
283
+ }
284
+
285
+ }
286
+
287
+ export {
288
+ WSS
289
+ }