@jcbuisson/express-x 1.0.1 → 1.0.3
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/README.md +0 -15
- package/package.json +2 -9
- package/src/expressX.js +115 -80
- package/.adminjs/.entry.js +0 -1
- package/.env +0 -2
- package/app.js +0 -28
- package/config/default.js +0 -36
- package/express-ws-1.0.0.tgz +0 -0
- package/frontend/.vscode/extensions.json +0 -3
- package/frontend/README.md +0 -7
- package/frontend/index.html +0 -13
- package/frontend/package.json +0 -20
- package/frontend/public/favicon.ico +0 -0
- package/frontend/src/App.vue +0 -83
- package/frontend/src/expressx-client.js +0 -83
- package/frontend/src/main.js +0 -4
- package/frontend/tsconfig.json +0 -9
- package/frontend/vite.config.js +0 -28
- package/index.html +0 -106
- package/prisma/migrations/20230222122754_init/migration.sql +0 -24
- package/prisma/migrations/20230222150825_init/migration.sql +0 -5
- package/prisma/migrations/migration_lock.toml +0 -3
- package/prisma/schema.prisma +0 -26
- package/src/admin.ts +0 -42
- package/src/app.js +0 -42
- package/src/index.html +0 -106
- package/src/node-client.js +0 -28
- package/src/services/authenticate.service.js +0 -53
- package/src/services/mail.service.js +0 -25
- package/tsconfig.json +0 -9
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jcbuisson/express-x",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/expressX.js",
|
|
6
6
|
"repository": {
|
|
@@ -11,21 +11,14 @@
|
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"private": false,
|
|
13
13
|
"scripts": {
|
|
14
|
-
"dev": "npx nodemon src/app.js",
|
|
15
|
-
"migrate": "npx prisma migrate dev --name init",
|
|
16
|
-
"admin": "npx ts-node src/admin.ts"
|
|
17
14
|
},
|
|
18
15
|
"keywords": [],
|
|
19
16
|
"dependencies": {
|
|
20
|
-
"@adminjs/express": "^5.1.0",
|
|
21
|
-
"@adminjs/prisma": "^3.0.1",
|
|
22
17
|
"@prisma/client": "^4.10.1",
|
|
23
18
|
"config": "^3.3.9",
|
|
24
19
|
"express": "^4.18.2",
|
|
25
20
|
"jsonwebtoken": "^9.0.0",
|
|
26
|
-
"
|
|
27
|
-
"socket.io": "^4.6.0",
|
|
28
|
-
"socket.io-client": "^4.6.0"
|
|
21
|
+
"socket.io": "^4.6.0"
|
|
29
22
|
},
|
|
30
23
|
"devDependencies": {
|
|
31
24
|
"@types/express": "^4.17.17"
|
package/src/expressX.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
const http = require('http')
|
|
3
3
|
const socketio = require('socket.io')
|
|
4
|
-
const config = require('config')
|
|
5
4
|
|
|
6
5
|
const { PrismaClient } = require('@prisma/client')
|
|
7
6
|
|
|
8
7
|
/*
|
|
9
8
|
* Enhance `app` express application with Feathers-like services
|
|
10
9
|
*/
|
|
11
|
-
function
|
|
10
|
+
function expressX(app) {
|
|
12
11
|
const prisma = new PrismaClient()
|
|
13
12
|
app.set('prisma', prisma) // ?? BOF
|
|
14
13
|
|
|
15
14
|
const services = {}
|
|
16
|
-
const publishCallbacks = {}
|
|
17
15
|
const connections = {}
|
|
18
16
|
|
|
19
17
|
let lastConnectionId = 1
|
|
@@ -22,71 +20,93 @@ function enhanceExpress(app) {
|
|
|
22
20
|
* create a service `name` based on Prisma table `entity`
|
|
23
21
|
*/
|
|
24
22
|
function createDatabaseService({ name, entity=name, client='prisma' }) {
|
|
25
|
-
|
|
23
|
+
return createService({
|
|
26
24
|
name,
|
|
27
25
|
entity,
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return value
|
|
34
|
-
},
|
|
27
|
+
methods: {
|
|
28
|
+
create: (data) => prisma[entity].create({
|
|
29
|
+
data,
|
|
30
|
+
}),
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
// TODO...before hooks
|
|
38
|
-
const value = await prisma[entity].findUnique({
|
|
32
|
+
get: (id) => prisma[entity].findUnique({
|
|
39
33
|
where: {
|
|
40
|
-
|
|
34
|
+
id,
|
|
41
35
|
},
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
services[name] = service
|
|
61
|
-
return service
|
|
36
|
+
}),
|
|
37
|
+
|
|
38
|
+
patch: (id, data) => prisma[entity].update({
|
|
39
|
+
where: {
|
|
40
|
+
id,
|
|
41
|
+
},
|
|
42
|
+
data,
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
remove: (id, data) => prisma[entity].delete({
|
|
46
|
+
where: {
|
|
47
|
+
id,
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
|
|
51
|
+
find: (options) => prisma[entity].findMany(options),
|
|
52
|
+
}
|
|
53
|
+
})
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
/*
|
|
65
|
-
* create a
|
|
57
|
+
* create a service `name` with given `methods`
|
|
66
58
|
*/
|
|
67
|
-
function
|
|
68
|
-
const service = {
|
|
69
|
-
|
|
59
|
+
function createService({ name, methods }) {
|
|
60
|
+
const service = { name }
|
|
61
|
+
|
|
62
|
+
for (const methodName in methods) {
|
|
63
|
+
const method = methods[methodName]
|
|
64
|
+
|
|
65
|
+
// `context` is the context of execution (transport type, connection, app)
|
|
66
|
+
// `args` is the list of arguments of the method
|
|
67
|
+
service[methodName] = async (context, ...args) => {
|
|
68
|
+
context.args = args
|
|
69
|
+
|
|
70
|
+
// if a hook or the method throws an error, it will be caught by `socket.on('client-request'`
|
|
71
|
+
// and the client will get a rejected promise
|
|
72
|
+
|
|
73
|
+
// call 'before' hooks, modifying `context.args`
|
|
74
|
+
const beforeMethodHooks = service?.hooks?.before && service.hooks.before[methodName] || []
|
|
75
|
+
const beforeAllHooks = service?.hooks?.before?.all || []
|
|
76
|
+
for (const hook of [...beforeMethodHooks, ...beforeAllHooks]) {
|
|
77
|
+
context = await hook({ ...context, args })
|
|
78
|
+
}
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
// call method
|
|
81
|
+
const result = await method(...context.args)
|
|
82
|
+
// console.log('result', result)
|
|
83
|
+
|
|
84
|
+
// call 'after' hooks
|
|
85
|
+
const afterMethodHooks = service?.hooks?.after && service.hooks.after[methodName] || []
|
|
86
|
+
const afterAllHooks = service?.hooks?.after?.all || []
|
|
87
|
+
for (const hook of [...afterMethodHooks, ...afterAllHooks]) {
|
|
88
|
+
context = await hook({ ...context, result })
|
|
89
|
+
}
|
|
90
|
+
return result
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// un-hooked versions of methods: `_create`, etc.
|
|
95
|
+
for (const methodName in methods) {
|
|
96
|
+
const method = methods[methodName]
|
|
97
|
+
service['_' + methodName] = method
|
|
89
98
|
}
|
|
99
|
+
|
|
100
|
+
// attach pub/sub publish callback
|
|
101
|
+
service.publish = async (func) => {
|
|
102
|
+
service.publishCallback = func
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// attach hooks
|
|
106
|
+
service.hooks = (hooks) => {
|
|
107
|
+
service.hooks = hooks
|
|
108
|
+
}
|
|
109
|
+
|
|
90
110
|
// cache service in `services`
|
|
91
111
|
services[name] = service
|
|
92
112
|
return service
|
|
@@ -103,25 +123,37 @@ function enhanceExpress(app) {
|
|
|
103
123
|
}
|
|
104
124
|
|
|
105
125
|
/*
|
|
106
|
-
*
|
|
126
|
+
* add an HTTP REST endpoint at `path`, based on `service`
|
|
107
127
|
*/
|
|
108
|
-
function
|
|
109
|
-
|
|
128
|
+
function addHttpRestRoutes(path, service) {
|
|
129
|
+
|
|
130
|
+
const context = {
|
|
131
|
+
app,
|
|
132
|
+
transport: 'http',
|
|
133
|
+
}
|
|
110
134
|
|
|
111
135
|
app.post(path, async (req, res) => {
|
|
112
|
-
const value = await service.create(req.body)
|
|
136
|
+
const value = await service.create(context, req.body)
|
|
113
137
|
res.json(value)
|
|
114
138
|
})
|
|
115
139
|
|
|
116
140
|
app.get(path, async (req, res) => {
|
|
117
|
-
const values = await service.find(
|
|
118
|
-
where: req.body,
|
|
119
|
-
})
|
|
141
|
+
const values = await service.find(context, req.body)
|
|
120
142
|
res.json(values)
|
|
121
143
|
})
|
|
122
144
|
|
|
123
145
|
app.get(`${path}/:id`, async (req, res) => {
|
|
124
|
-
const value = await service.get(parseInt(req.params.id))
|
|
146
|
+
const value = await service.get(context, parseInt(req.params.id))
|
|
147
|
+
res.json(value)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
app.patch(`${path}/:id`, async (req, res) => {
|
|
151
|
+
const value = await service.patch(context, parseInt(req.params.id), req.body)
|
|
152
|
+
res.json(value)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
app.delete(`${path}/:id`, async (req, res) => {
|
|
156
|
+
const value = await service.remove(context, parseInt(req.params.id))
|
|
125
157
|
res.json(value)
|
|
126
158
|
})
|
|
127
159
|
}
|
|
@@ -145,6 +177,7 @@ function enhanceExpress(app) {
|
|
|
145
177
|
// emit 'connection' event for app (expressjs extends EventEmitter)
|
|
146
178
|
app.emit('connection', connection)
|
|
147
179
|
|
|
180
|
+
// send 'connected' event to client
|
|
148
181
|
socket.emit('connected', connection.id)
|
|
149
182
|
|
|
150
183
|
socket.on('disconnect', () => {
|
|
@@ -157,27 +190,26 @@ function enhanceExpress(app) {
|
|
|
157
190
|
* Handle websocket client request
|
|
158
191
|
* Emit in return a 'client-response' message
|
|
159
192
|
*/
|
|
160
|
-
socket.on('client-request', async ({ uid, name, action,
|
|
161
|
-
console.log("client-request", uid, name, action,
|
|
193
|
+
socket.on('client-request', async ({ uid, name, action, args }) => {
|
|
194
|
+
console.log("client-request", uid, name, action, args)
|
|
162
195
|
if (name in services) {
|
|
163
196
|
const service = services[name]
|
|
164
197
|
try {
|
|
165
198
|
const serviceMethod = service[action]
|
|
166
|
-
const result = await serviceMethod(...argList)
|
|
167
|
-
console.log('result', result)
|
|
168
199
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
connection
|
|
200
|
+
const context = {
|
|
201
|
+
app,
|
|
202
|
+
transport: 'ws',
|
|
203
|
+
connection,
|
|
173
204
|
}
|
|
205
|
+
const result = await serviceMethod(context, ...args)
|
|
174
206
|
|
|
175
207
|
io.emit('client-response', {
|
|
176
208
|
uid,
|
|
177
209
|
result,
|
|
178
210
|
})
|
|
179
|
-
// send event on associated channels
|
|
180
|
-
const publishFunc =
|
|
211
|
+
// pub/sub: send event on associated channels
|
|
212
|
+
const publishFunc = service.publishCallback
|
|
181
213
|
if (publishFunc) {
|
|
182
214
|
const channelNames = await publishFunc(result, app)
|
|
183
215
|
console.log('publish channels', name, action, channelNames)
|
|
@@ -195,7 +227,6 @@ function enhanceExpress(app) {
|
|
|
195
227
|
}
|
|
196
228
|
}
|
|
197
229
|
} catch(error) {
|
|
198
|
-
console.log('error', error)
|
|
199
230
|
io.emit('client-response', {
|
|
200
231
|
uid,
|
|
201
232
|
error,
|
|
@@ -215,18 +246,22 @@ function enhanceExpress(app) {
|
|
|
215
246
|
connection.channelNames.add(channelName)
|
|
216
247
|
}
|
|
217
248
|
|
|
249
|
+
function leaveChannel(channelName, connection) {
|
|
250
|
+
connection.channelNames.delete(channelName)
|
|
251
|
+
}
|
|
252
|
+
|
|
218
253
|
// enhance `app` with objects and methods
|
|
219
254
|
Object.assign(app, {
|
|
220
255
|
createDatabaseService,
|
|
221
|
-
|
|
256
|
+
createService,
|
|
222
257
|
service,
|
|
223
258
|
configure,
|
|
224
|
-
|
|
259
|
+
addHttpRestRoutes,
|
|
225
260
|
server,
|
|
226
261
|
joinChannel,
|
|
262
|
+
leaveChannel,
|
|
227
263
|
})
|
|
264
|
+
return app
|
|
228
265
|
}
|
|
229
266
|
|
|
230
|
-
module.exports =
|
|
231
|
-
enhanceExpress,
|
|
232
|
-
}
|
|
267
|
+
module.exports = expressX
|
package/.adminjs/.entry.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
AdminJS.UserComponents = {}
|
package/.env
DELETED
package/app.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
var express = require('express')
|
|
2
|
-
var app = express()
|
|
3
|
-
var http = require('http').createServer(app);
|
|
4
|
-
var io = require('socket.io')(http);
|
|
5
|
-
|
|
6
|
-
app.get('/', function (req, res) {
|
|
7
|
-
res.sendFile(__dirname + '/index.html');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
http.listen(process.env.PORT || 3000, function() {
|
|
11
|
-
var host = http.address().address
|
|
12
|
-
var port = http.address().port
|
|
13
|
-
console.log('App listening at http://%s:%s', host, port)
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
io.on('connection', function(socket) {
|
|
17
|
-
console.log('Client connected to the WebSocket');
|
|
18
|
-
|
|
19
|
-
socket.on('disconnect', () => {
|
|
20
|
-
console.log('Client disconnected');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
socket.on('chat message', function(msg) {
|
|
24
|
-
console.log("Received a chat message");
|
|
25
|
-
io.emit('chat message', msg);
|
|
26
|
-
});
|
|
27
|
-
})
|
|
28
|
-
|
package/config/default.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = {
|
|
3
|
-
|
|
4
|
-
authentication: {
|
|
5
|
-
entity: "user",
|
|
6
|
-
service: "user",
|
|
7
|
-
secret: "A3lxcjmVD0af6Hjd1fksZyzFGtE=",
|
|
8
|
-
authStrategies: [
|
|
9
|
-
"local"
|
|
10
|
-
],
|
|
11
|
-
jwtOptions: {
|
|
12
|
-
header: {
|
|
13
|
-
typ: "access"
|
|
14
|
-
},
|
|
15
|
-
audience: "https://mitab.com",
|
|
16
|
-
issuer: "feathers",
|
|
17
|
-
algorithm: "HS256",
|
|
18
|
-
expiresIn: "100y"
|
|
19
|
-
},
|
|
20
|
-
local: {
|
|
21
|
-
usernameField: "pseudo",
|
|
22
|
-
passwordField: "password"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
NODEMAILER: {
|
|
27
|
-
host: "smtp.online.net",
|
|
28
|
-
port: 587,
|
|
29
|
-
secure: false,
|
|
30
|
-
auth: {
|
|
31
|
-
user: "contact@shdl.fr",
|
|
32
|
-
pass: "Eurek@31",
|
|
33
|
-
},
|
|
34
|
-
name: "shdl.fr",
|
|
35
|
-
},
|
|
36
|
-
}
|
package/express-ws-1.0.0.tgz
DELETED
|
Binary file
|
package/frontend/README.md
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Vue 3 + Vite
|
|
2
|
-
|
|
3
|
-
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
4
|
-
|
|
5
|
-
## Recommended IDE Setup
|
|
6
|
-
|
|
7
|
-
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
|
package/frontend/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" href="/favicon.ico" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>Vite App</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"></div>
|
|
11
|
-
<script type="module" src="/src/main.js"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
package/frontend/package.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "frontend",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "vite",
|
|
7
|
-
"build": "vite build",
|
|
8
|
-
"preview": "vite preview",
|
|
9
|
-
"admin": "npx ts-node src/admin.ts"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"socket.io-client": "^4.6.0",
|
|
13
|
-
"uuid": "^9.0.0",
|
|
14
|
-
"vue": "^3.2.25"
|
|
15
|
-
},
|
|
16
|
-
"devDependencies": {
|
|
17
|
-
"@vitejs/plugin-vue": "^2.3.4",
|
|
18
|
-
"vite": "^2.9.15"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
Binary file
|
package/frontend/src/App.vue
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div> <input v-model="pseudo"> pseudo </div>
|
|
3
|
-
<div> <input v-model="password"> password </div>
|
|
4
|
-
|
|
5
|
-
<button @click="signin">signin</button>
|
|
6
|
-
<button @click="addUser">add user</button>
|
|
7
|
-
<button @click="sendMail">send mail</button>
|
|
8
|
-
<hr>
|
|
9
|
-
<p>Is authenticated: {{ isAuthenticated }}</p>
|
|
10
|
-
|
|
11
|
-
<div v-for="user in users">
|
|
12
|
-
<li>{{ user.name }}</li>
|
|
13
|
-
</div>
|
|
14
|
-
|
|
15
|
-
</template>
|
|
16
|
-
|
|
17
|
-
<script setup>
|
|
18
|
-
import { ref, onMounted } from 'vue'
|
|
19
|
-
import expressxClient from './expressx-client'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const app = expressxClient()
|
|
23
|
-
// app.configure(feathers.authentication({ storage: window.sessionStorage }))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const users = ref([])
|
|
27
|
-
const pseudo = ref()
|
|
28
|
-
const password = ref()
|
|
29
|
-
|
|
30
|
-
const isAuthenticated = ref(false)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
app.service('user').on('create', user => {
|
|
34
|
-
console.log('USER EVENT created', user)
|
|
35
|
-
users.value.push(user)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
app.service('authenticate').on('create', user => {
|
|
39
|
-
console.log('AUTHENTICATE EVENT created', user)
|
|
40
|
-
isAuthenticated.value = true
|
|
41
|
-
})
|
|
42
|
-
app.service('authenticate').on('patch', () => {
|
|
43
|
-
console.log('(RE)AUTHENTICATE EVENT patch')
|
|
44
|
-
isAuthenticated.value = true
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
onMounted(async () => {
|
|
49
|
-
users.value = await app.service('user').find()
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
const addUser = async () => {
|
|
53
|
-
const user = await app.service('user').create({
|
|
54
|
-
pseudo: pseudo.value,
|
|
55
|
-
password: password.value,
|
|
56
|
-
})
|
|
57
|
-
console.log('created user', user)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const signin = async () => {
|
|
61
|
-
const { error, accessToken, user } = await app.service('authenticate').create({
|
|
62
|
-
strategy: 'local',
|
|
63
|
-
username: pseudo.value,
|
|
64
|
-
password: password.value,
|
|
65
|
-
})
|
|
66
|
-
console.log('authenticate', error, accessToken, user)
|
|
67
|
-
if (error) {
|
|
68
|
-
alert(error)
|
|
69
|
-
} else {
|
|
70
|
-
window.sessionStorage.setItem('feathers-jwt', accessToken)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const sendMail = () => {
|
|
75
|
-
app.service('mailer').create({
|
|
76
|
-
to: "buisson@n7.fr",
|
|
77
|
-
from: "buisson@nasa.gov",
|
|
78
|
-
subject: "Fake",
|
|
79
|
-
text: "Hello from NASA",
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
</script>
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { io } from 'socket.io-client'
|
|
3
|
-
import { v4 } from 'uuid'
|
|
4
|
-
|
|
5
|
-
function expressxClient() {
|
|
6
|
-
|
|
7
|
-
const socket = io()
|
|
8
|
-
|
|
9
|
-
const waitingPromises = {}
|
|
10
|
-
const action2service2handlers = {}
|
|
11
|
-
|
|
12
|
-
// on connection
|
|
13
|
-
socket.on("connected", async (connectionId) => {
|
|
14
|
-
console.log('connected', connectionId)
|
|
15
|
-
const token = window.sessionStorage.getItem('feathers-jwt')
|
|
16
|
-
if (token) {
|
|
17
|
-
// disconnect/reconnect: reauthenticate
|
|
18
|
-
console.log('reauthenticate with', token)
|
|
19
|
-
serviceMethodRequest('authenticate', 'patch', [token])
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// on receiving response from service request
|
|
24
|
-
socket.on('client-response', ({ uid, error, result }) => {
|
|
25
|
-
console.log('client-response', uid, error, result)
|
|
26
|
-
if (!uid in waitingPromises) return // ???
|
|
27
|
-
const [resolve, reject] = waitingPromises[uid]
|
|
28
|
-
if (error) {
|
|
29
|
-
reject(error)
|
|
30
|
-
} else {
|
|
31
|
-
resolve(result)
|
|
32
|
-
}
|
|
33
|
-
delete waitingPromises[uid]
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
// on receiving events from pub/sub
|
|
37
|
-
socket.on('service-event', ({ name, action, result }) => {
|
|
38
|
-
if (!action2service2handlers[action]) action2service2handlers[action] = {}
|
|
39
|
-
const serviceHandlers = action2service2handlers[action]
|
|
40
|
-
const handler = serviceHandlers[name]
|
|
41
|
-
if (handler) handler(result)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
async function serviceMethodRequest(name, action, argList) {
|
|
45
|
-
const uid = v4()
|
|
46
|
-
const promise = new Promise((resolve, reject) => {
|
|
47
|
-
waitingPromises[uid] = [resolve, reject]
|
|
48
|
-
})
|
|
49
|
-
socket.emit('client-request', {
|
|
50
|
-
uid,
|
|
51
|
-
name,
|
|
52
|
-
action,
|
|
53
|
-
argList,
|
|
54
|
-
})
|
|
55
|
-
return promise
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function service(name) {
|
|
59
|
-
return {
|
|
60
|
-
create: (data) => serviceMethodRequest(name, 'create', [data]),
|
|
61
|
-
get: (id) => serviceMethodRequest(name, 'create', [id]),
|
|
62
|
-
patch: (id, data) => serviceMethodRequest(name, 'create', [id, data]),
|
|
63
|
-
update: (id, data) => serviceMethodRequest(name, 'update', [id, data]),
|
|
64
|
-
remove: (id) => serviceMethodRequest(name, 'remove', [id]),
|
|
65
|
-
find: (data) => serviceMethodRequest(name, 'find', [data]),
|
|
66
|
-
|
|
67
|
-
// associate a handler to a pub/sub event for this service
|
|
68
|
-
on: (action, handler) => {
|
|
69
|
-
if (!action2service2handlers[action]) action2service2handlers[action] = {}
|
|
70
|
-
const serviceHandlers = action2service2handlers[action]
|
|
71
|
-
serviceHandlers[name] = handler
|
|
72
|
-
console.log('action2service2handlers', action2service2handlers)
|
|
73
|
-
},
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
service,
|
|
79
|
-
socket,
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export default expressxClient
|
package/frontend/src/main.js
DELETED
package/frontend/tsconfig.json
DELETED
package/frontend/vite.config.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import vue from '@vitejs/plugin-vue'
|
|
3
|
-
|
|
4
|
-
// https://vitejs.dev/config/
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
plugins: [
|
|
7
|
-
vue()
|
|
8
|
-
],
|
|
9
|
-
server: {
|
|
10
|
-
port: 3000,
|
|
11
|
-
open: true,
|
|
12
|
-
host: true, // allows for external device connection on local network
|
|
13
|
-
proxy: {
|
|
14
|
-
'^/socket.io/.*': {
|
|
15
|
-
target: 'http://localhost:3030',
|
|
16
|
-
changeOrigin: true,
|
|
17
|
-
ws: true,
|
|
18
|
-
secure: false,
|
|
19
|
-
},
|
|
20
|
-
'/api': {
|
|
21
|
-
target: 'http://localhost:3030',
|
|
22
|
-
changeOrigin: true,
|
|
23
|
-
ws: false,
|
|
24
|
-
secure: false,
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
})
|
package/index.html
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<title>Greetings from a sample Node.js app built with the Socket.IO framework!</title>
|
|
5
|
-
<meta content='website' property='og:type'>
|
|
6
|
-
<meta content='https://cdn.scalingo.com/homepage/assets/fb-33a6a93ddbf90bfdae57407481aa05a4.png' property='og:image'>
|
|
7
|
-
<meta content="Deploying your own Node.js/Socket.IO app on Scalingo is as easy as pie: JUST CLICK THIS BUTTON! It will be up in less than 2 minutes!" property='og:description'>
|
|
8
|
-
<meta charset="utf-8">
|
|
9
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
10
|
-
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
11
|
-
<link rel="shortcut icon" href="https://scalingo.com/favicon.ico" type="image/vnd.microsoft.icon">
|
|
12
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css">
|
|
13
|
-
<link href="https://fonts.googleapis.com/css?family=Roboto:300,500" rel="stylesheet">
|
|
14
|
-
<style>
|
|
15
|
-
body { color: #373a3c; font-family: 'Roboto', sans-serif; font-size: 1rem; line-height: 1.5; font-weight: 300; }
|
|
16
|
-
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
|
17
|
-
color: inherit;
|
|
18
|
-
font-family: inherit;
|
|
19
|
-
font-weight: 500;
|
|
20
|
-
line-height: 1.1;
|
|
21
|
-
margin-bottom: 1rem;
|
|
22
|
-
margin-top: 0;
|
|
23
|
-
}
|
|
24
|
-
a { color: #0275d8; text-decoration: none; background-color: transparent; }
|
|
25
|
-
a:focus, a:hover { color: #014c8c; text-decoration: underline; text-decoration: none; }
|
|
26
|
-
a:focus { outline: thin dotted; outline-offset: -2px; }
|
|
27
|
-
.container { margin: 0 auto; text-align: center; }
|
|
28
|
-
.hero { padding: 3rem 1.5rem; }
|
|
29
|
-
.love { margin-bottom: 2rem; }
|
|
30
|
-
h1 { font-size: 2.5rem; margin-bottom: 2rem; }
|
|
31
|
-
h2 { font-size: 1.6rem; font-weight: 300; }
|
|
32
|
-
small { margin-top: 0; }
|
|
33
|
-
.btn {
|
|
34
|
-
overflow: visible;
|
|
35
|
-
text-transform: uppercase;
|
|
36
|
-
text-decoration: none;
|
|
37
|
-
margin: 1em 0;
|
|
38
|
-
-moz-user-select: none;
|
|
39
|
-
border-radius: 0.25rem;
|
|
40
|
-
cursor: pointer;
|
|
41
|
-
display: inline-block;
|
|
42
|
-
font-size: 1rem;
|
|
43
|
-
font-weight: 400;
|
|
44
|
-
line-height: 1.5;
|
|
45
|
-
padding: 0.375rem 1rem;
|
|
46
|
-
text-align: center;
|
|
47
|
-
vertical-align: middle;
|
|
48
|
-
white-space: nowrap;
|
|
49
|
-
background-color: #0275d8;
|
|
50
|
-
border-color: #0275d8;
|
|
51
|
-
color: #fff;
|
|
52
|
-
border-radius: 0.3rem;
|
|
53
|
-
font-size: 1.25rem;
|
|
54
|
-
line-height: 1.33333;
|
|
55
|
-
padding: 0.75rem 1.25rem;
|
|
56
|
-
}
|
|
57
|
-
.btn:hover, .btn:focus, .btn:active { background-color: #025aa5; border-color: #01549b; color: #fff; background-image: none; }
|
|
58
|
-
.btn:active:focus, .btn:active:hover { background-color: #014682; border-color: #01315a; color: #fff; }
|
|
59
|
-
.scalingo-logo { max-width: 100%; height: auto; width: 330px; }
|
|
60
|
-
.heart-logo { margin: 0 20px; width:56px;position:relative; top:-18px; }
|
|
61
|
-
.tech-logo { width:56px;height:72px; }
|
|
62
|
-
@media (min-width:768px){
|
|
63
|
-
.container { width: 800px; }
|
|
64
|
-
.one-liner { display: block; }
|
|
65
|
-
}
|
|
66
|
-
@media (max-width:767px){
|
|
67
|
-
.scalingo-logo, .heart-logo, .tech-logo { display: block; position: unset; margin: 0 auto; }
|
|
68
|
-
.scalingo-logo, .heart-logo { margin-bottom: 1rem; }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/* * { margin: 0; padding: 0; box-sizing: border-box; } */
|
|
74
|
-
/* body { font: 13px Helvetica, Arial; } */
|
|
75
|
-
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
|
|
76
|
-
form input { border: 0; padding: 10px; width: 90%; margin-right: 0.5%; }
|
|
77
|
-
form button { width: 8%; background: rgb(130, 224, 255); border: none; padding: 10px; cursor: pointer; }
|
|
78
|
-
#messages { list-style-type: none; margin: 0; padding: 0; }
|
|
79
|
-
#messages li { padding: 5px 10px; }
|
|
80
|
-
#messages li:nth-child(odd) { background: #eee; }
|
|
81
|
-
</style>
|
|
82
|
-
</head>
|
|
83
|
-
<body>
|
|
84
|
-
<ul id="messages"></ul>
|
|
85
|
-
<form action="">
|
|
86
|
-
<input id="m" autocomplete="off" /><button>Send</button>
|
|
87
|
-
</form>
|
|
88
|
-
|
|
89
|
-
<script src="/socket.io/socket.io.js"></script>
|
|
90
|
-
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
|
91
|
-
<script>
|
|
92
|
-
$(function () {
|
|
93
|
-
var socket = io();
|
|
94
|
-
$('form').submit(function(e) {
|
|
95
|
-
e.preventDefault(); // prevents page reloading
|
|
96
|
-
socket.emit('chat message', $('#m').val());
|
|
97
|
-
$('#m').val('');
|
|
98
|
-
return false;
|
|
99
|
-
});
|
|
100
|
-
socket.on('chat message', function(msg){
|
|
101
|
-
$('#messages').append($('<li>').text(msg));
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
</script>
|
|
105
|
-
</body>
|
|
106
|
-
</html>
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
CREATE TABLE "User" (
|
|
3
|
-
"id" SERIAL NOT NULL,
|
|
4
|
-
"pseudo" TEXT NOT NULL,
|
|
5
|
-
"password" TEXT NOT NULL,
|
|
6
|
-
"name" TEXT,
|
|
7
|
-
|
|
8
|
-
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
-- CreateTable
|
|
12
|
-
CREATE TABLE "Post" (
|
|
13
|
-
"id" SERIAL NOT NULL,
|
|
14
|
-
"title" TEXT NOT NULL,
|
|
15
|
-
"authorId" INTEGER,
|
|
16
|
-
|
|
17
|
-
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
-- CreateIndex
|
|
21
|
-
CREATE UNIQUE INDEX "User_pseudo_key" ON "User"("pseudo");
|
|
22
|
-
|
|
23
|
-
-- AddForeignKey
|
|
24
|
-
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
package/prisma/schema.prisma
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// This is your Prisma schema file,
|
|
2
|
-
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
-
|
|
4
|
-
generator client {
|
|
5
|
-
provider = "prisma-client-js"
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
model User {
|
|
9
|
-
id Int @default(autoincrement()) @id
|
|
10
|
-
pseudo String @unique
|
|
11
|
-
password String
|
|
12
|
-
name String?
|
|
13
|
-
posts Post[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
model Post {
|
|
17
|
-
id Int @default(autoincrement()) @id
|
|
18
|
-
title String
|
|
19
|
-
author User? @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
20
|
-
authorId Int?
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
datasource db {
|
|
24
|
-
provider = "postgresql"
|
|
25
|
-
url = env("DATABASE_URL")
|
|
26
|
-
}
|
package/src/admin.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import AdminJS from 'adminjs'
|
|
3
|
-
import AdminJSExpress from '@adminjs/express'
|
|
4
|
-
import { Database, Resource } from '@adminjs/prisma'
|
|
5
|
-
import { PrismaClient } from '@prisma/client'
|
|
6
|
-
import { DMMFClass } from '@prisma/client/runtime'
|
|
7
|
-
|
|
8
|
-
const PORT = process.env.port || 3020
|
|
9
|
-
|
|
10
|
-
const prisma = new PrismaClient()
|
|
11
|
-
|
|
12
|
-
AdminJS.registerAdapter({ Database, Resource })
|
|
13
|
-
|
|
14
|
-
const run = async () => {
|
|
15
|
-
const app = express()
|
|
16
|
-
|
|
17
|
-
// `_baseDmmf` contains necessary Model metadata. `PrismaClient` type doesn't have it included
|
|
18
|
-
const dmmf = ((prisma as any)._baseDmmf as DMMFClass)
|
|
19
|
-
|
|
20
|
-
const admin = new AdminJS({
|
|
21
|
-
resources: [{
|
|
22
|
-
resource: { model: dmmf.modelMap.User, client: prisma },
|
|
23
|
-
options: {},
|
|
24
|
-
}, {
|
|
25
|
-
resource: { model: dmmf.modelMap.Post, client: prisma },
|
|
26
|
-
options: {},
|
|
27
|
-
}],
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
const router = AdminJSExpress.buildRouter(admin)
|
|
31
|
-
|
|
32
|
-
app.use(admin.options.rootPath, router)
|
|
33
|
-
|
|
34
|
-
app.listen(PORT, () => {
|
|
35
|
-
console.log(`Example app listening at http://localhost:${PORT}`)
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
run()
|
|
40
|
-
.finally(async () => {
|
|
41
|
-
await prisma.$disconnect()
|
|
42
|
-
})
|
package/src/app.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const express = require('express')
|
|
3
|
-
const { enhanceExpress } = require('./expressX')
|
|
4
|
-
|
|
5
|
-
const mailService = require('./services/mail.service')
|
|
6
|
-
const authenticateService = require('./services/authenticate.service')
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const app = express()
|
|
10
|
-
enhanceExpress(app)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
app.createDatabaseService({name: 'user', client: 'prisma' })
|
|
14
|
-
|
|
15
|
-
// app.createAuthService(config.get('authentication'))
|
|
16
|
-
|
|
17
|
-
app.configure(mailService)
|
|
18
|
-
app.configure(authenticateService)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
app.httpRestService('/api/user', app.service('user'))
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// serve index.html
|
|
25
|
-
app.get('/', function (req, res) {
|
|
26
|
-
res.sendFile(__dirname + '/index.html')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
app.server.listen(3030, () => console.log('App listening at http://localhost:3030'))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
app.on('connection', (connection) => {
|
|
33
|
-
console.log('connection', connection.id)
|
|
34
|
-
app.joinChannel('everyone', connection)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
app.service('user').publish(async (user, context) => {
|
|
38
|
-
return ['everyone']
|
|
39
|
-
})
|
|
40
|
-
app.service('authenticate').publish(async (user, context) => {
|
|
41
|
-
return ['everyone']
|
|
42
|
-
})
|
package/src/index.html
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<title>Greetings from a sample Node.js app built with the Socket.IO framework!</title>
|
|
5
|
-
<meta content='website' property='og:type'>
|
|
6
|
-
<meta content='https://cdn.scalingo.com/homepage/assets/fb-33a6a93ddbf90bfdae57407481aa05a4.png' property='og:image'>
|
|
7
|
-
<meta content="Deploying your own Node.js/Socket.IO app on Scalingo is as easy as pie: JUST CLICK THIS BUTTON! It will be up in less than 2 minutes!" property='og:description'>
|
|
8
|
-
<meta charset="utf-8">
|
|
9
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
10
|
-
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
11
|
-
<link rel="shortcut icon" href="https://scalingo.com/favicon.ico" type="image/vnd.microsoft.icon">
|
|
12
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css">
|
|
13
|
-
<link href="https://fonts.googleapis.com/css?family=Roboto:300,500" rel="stylesheet">
|
|
14
|
-
<style>
|
|
15
|
-
body { color: #373a3c; font-family: 'Roboto', sans-serif; font-size: 1rem; line-height: 1.5; font-weight: 300; }
|
|
16
|
-
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
|
17
|
-
color: inherit;
|
|
18
|
-
font-family: inherit;
|
|
19
|
-
font-weight: 500;
|
|
20
|
-
line-height: 1.1;
|
|
21
|
-
margin-bottom: 1rem;
|
|
22
|
-
margin-top: 0;
|
|
23
|
-
}
|
|
24
|
-
a { color: #0275d8; text-decoration: none; background-color: transparent; }
|
|
25
|
-
a:focus, a:hover { color: #014c8c; text-decoration: underline; text-decoration: none; }
|
|
26
|
-
a:focus { outline: thin dotted; outline-offset: -2px; }
|
|
27
|
-
.container { margin: 0 auto; text-align: center; }
|
|
28
|
-
.hero { padding: 3rem 1.5rem; }
|
|
29
|
-
.love { margin-bottom: 2rem; }
|
|
30
|
-
h1 { font-size: 2.5rem; margin-bottom: 2rem; }
|
|
31
|
-
h2 { font-size: 1.6rem; font-weight: 300; }
|
|
32
|
-
small { margin-top: 0; }
|
|
33
|
-
.btn {
|
|
34
|
-
overflow: visible;
|
|
35
|
-
text-transform: uppercase;
|
|
36
|
-
text-decoration: none;
|
|
37
|
-
margin: 1em 0;
|
|
38
|
-
-moz-user-select: none;
|
|
39
|
-
border-radius: 0.25rem;
|
|
40
|
-
cursor: pointer;
|
|
41
|
-
display: inline-block;
|
|
42
|
-
font-size: 1rem;
|
|
43
|
-
font-weight: 400;
|
|
44
|
-
line-height: 1.5;
|
|
45
|
-
padding: 0.375rem 1rem;
|
|
46
|
-
text-align: center;
|
|
47
|
-
vertical-align: middle;
|
|
48
|
-
white-space: nowrap;
|
|
49
|
-
background-color: #0275d8;
|
|
50
|
-
border-color: #0275d8;
|
|
51
|
-
color: #fff;
|
|
52
|
-
border-radius: 0.3rem;
|
|
53
|
-
font-size: 1.25rem;
|
|
54
|
-
line-height: 1.33333;
|
|
55
|
-
padding: 0.75rem 1.25rem;
|
|
56
|
-
}
|
|
57
|
-
.btn:hover, .btn:focus, .btn:active { background-color: #025aa5; border-color: #01549b; color: #fff; background-image: none; }
|
|
58
|
-
.btn:active:focus, .btn:active:hover { background-color: #014682; border-color: #01315a; color: #fff; }
|
|
59
|
-
.scalingo-logo { max-width: 100%; height: auto; width: 330px; }
|
|
60
|
-
.heart-logo { margin: 0 20px; width:56px;position:relative; top:-18px; }
|
|
61
|
-
.tech-logo { width:56px;height:72px; }
|
|
62
|
-
@media (min-width:768px){
|
|
63
|
-
.container { width: 800px; }
|
|
64
|
-
.one-liner { display: block; }
|
|
65
|
-
}
|
|
66
|
-
@media (max-width:767px){
|
|
67
|
-
.scalingo-logo, .heart-logo, .tech-logo { display: block; position: unset; margin: 0 auto; }
|
|
68
|
-
.scalingo-logo, .heart-logo { margin-bottom: 1rem; }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/* * { margin: 0; padding: 0; box-sizing: border-box; } */
|
|
74
|
-
/* body { font: 13px Helvetica, Arial; } */
|
|
75
|
-
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
|
|
76
|
-
form input { border: 0; padding: 10px; width: 90%; margin-right: 0.5%; }
|
|
77
|
-
form button { width: 8%; background: rgb(130, 224, 255); border: none; padding: 10px; cursor: pointer; }
|
|
78
|
-
#messages { list-style-type: none; margin: 0; padding: 0; }
|
|
79
|
-
#messages li { padding: 5px 10px; }
|
|
80
|
-
#messages li:nth-child(odd) { background: #eee; }
|
|
81
|
-
</style>
|
|
82
|
-
</head>
|
|
83
|
-
<body>
|
|
84
|
-
<ul id="messages"></ul>
|
|
85
|
-
<form action="">
|
|
86
|
-
<input id="m" autocomplete="off" /><button>Send</button>
|
|
87
|
-
</form>
|
|
88
|
-
|
|
89
|
-
<script src="/socket.io/socket.io.js"></script>
|
|
90
|
-
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
|
91
|
-
<script>
|
|
92
|
-
$(function () {
|
|
93
|
-
var socket = io();
|
|
94
|
-
$('form').submit(function(e) {
|
|
95
|
-
e.preventDefault(); // prevents page reloading
|
|
96
|
-
socket.emit('chat message', $('#m').val());
|
|
97
|
-
$('#m').val('');
|
|
98
|
-
return false;
|
|
99
|
-
});
|
|
100
|
-
socket.on('chat message', function(msg){
|
|
101
|
-
$('#messages').append($('<li>').text(msg));
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
</script>
|
|
105
|
-
</body>
|
|
106
|
-
</html>
|
package/src/node-client.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { io } = require("socket.io-client");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
async function main() {
|
|
6
|
-
try {
|
|
7
|
-
const socket = io(`http://localhost:3030`)
|
|
8
|
-
|
|
9
|
-
socket.on("hello", (arg) => {
|
|
10
|
-
console.log(arg)
|
|
11
|
-
|
|
12
|
-
socket.emit('find-request', {
|
|
13
|
-
uid: 'azer',
|
|
14
|
-
name: 'user',
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
socket.on("find-response", ({ uid, values }) => {
|
|
19
|
-
console.log('find-response', uid, values)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
} catch(err) {
|
|
23
|
-
console.log("*** error", err)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
main()
|
|
28
|
-
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const config = require('config')
|
|
3
|
-
const jwt = require('jsonwebtoken')
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
module.exports = function (app) {
|
|
7
|
-
|
|
8
|
-
const prisma = app.get('prisma') // BOF
|
|
9
|
-
|
|
10
|
-
app.createCustomService({
|
|
11
|
-
name: 'authenticate',
|
|
12
|
-
|
|
13
|
-
create: async ({ uid, strategy, username, password }) => {
|
|
14
|
-
console.log('authenticate data', strategy, username, password)
|
|
15
|
-
try {
|
|
16
|
-
const entity = config.authentication.entity
|
|
17
|
-
const usernameField = config.authentication[strategy].usernameField
|
|
18
|
-
const passwordField = config.authentication[strategy].passwordField
|
|
19
|
-
console.log("authenticate-request", uid, strategy, username, password, usernameField, passwordField)
|
|
20
|
-
// check if a user exists with this username
|
|
21
|
-
const where = {}
|
|
22
|
-
where[usernameField] = username
|
|
23
|
-
const user = await prisma[entity].findUnique({ where })
|
|
24
|
-
if (user) {
|
|
25
|
-
// user exists; check password
|
|
26
|
-
if (user.password === password) {
|
|
27
|
-
const accessToken = jwt.sign({ sub: user.id }, config.authentication.secret, config.authentication.jwtOptions)
|
|
28
|
-
return {
|
|
29
|
-
accessToken,
|
|
30
|
-
user,
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
return {
|
|
34
|
-
error: "Incorrect credentials",
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
return {
|
|
39
|
-
error: "Incorrect credentials",
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
} catch(err) {
|
|
43
|
-
return {
|
|
44
|
-
error: err.toString(),
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
patch: async (accessToken) => {
|
|
50
|
-
return 'ok'
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const nodemailer = require('nodemailer')
|
|
3
|
-
const config = require('config')
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
module.exports = function (app) {
|
|
7
|
-
|
|
8
|
-
app.createCustomService({
|
|
9
|
-
name: 'mailer',
|
|
10
|
-
create: ({ from, to, subject, text }) => {
|
|
11
|
-
try {
|
|
12
|
-
const transporter = nodemailer.createTransport(config.NODEMAILER)
|
|
13
|
-
return transporter.sendMail({
|
|
14
|
-
from,
|
|
15
|
-
to,
|
|
16
|
-
subject,
|
|
17
|
-
text,
|
|
18
|
-
html: text,
|
|
19
|
-
})
|
|
20
|
-
} catch(err) {
|
|
21
|
-
console.log('err mail', err)
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
}
|