@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 CHANGED
@@ -1,16 +1 @@
1
1
 
2
- # Migration initiale
3
-
4
- npx prisma migrate dev --name init
5
-
6
-
7
- # Run dev
8
-
9
- npx nodemon src/app.js
10
-
11
-
12
- # Run AdminJS
13
-
14
- npx ts-node src/admin.ts
15
-
16
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcbuisson/express-x",
3
- "version": "1.0.1",
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
- "nodemailer": "^6.9.1",
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 enhanceExpress(app) {
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
- const service = {
23
+ return createService({
26
24
  name,
27
25
  entity,
28
26
 
29
- create: async (data) => {
30
- // TODO...before hooks
31
- const value = await prisma[entity].create({ data })
32
- // TODO...after hook
33
- return value
34
- },
27
+ methods: {
28
+ create: (data) => prisma[entity].create({
29
+ data,
30
+ }),
35
31
 
36
- get: async (id) => {
37
- // TODO...before hooks
38
- const value = await prisma[entity].findUnique({
32
+ get: (id) => prisma[entity].findUnique({
39
33
  where: {
40
- id,
34
+ id,
41
35
  },
42
- })
43
- // TODO...after hook
44
- return value
45
- },
46
-
47
- find: async (query = {}) => {
48
- // ...before hooks
49
- const values = await prisma[entity].findMany(query)
50
- // ...after hook
51
- return values
52
- },
53
-
54
- // pub/sub
55
- publish: async (func) => {
56
- publishCallbacks[name] = func
57
- },
58
- }
59
- // cache service in `services`
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 custom service `name`
57
+ * create a service `name` with given `methods`
66
58
  */
67
- function createCustomService({ name, create, get, find, patch, update, remove }) {
68
- const service = {
69
- name,
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
- create: async (data) => {
72
- // TODO...before hooks
73
- const value = await create(data)
74
- // TODO...after hook
75
- return value
76
- },
77
-
78
- patch: async (id, data) => {
79
- // TODO...before hooks
80
- const value = await patch(id, data)
81
- // TODO...after hook
82
- return value
83
- },
84
-
85
- // pub/sub
86
- publish: async (func) => {
87
- publishCallbacks[name] = func
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
- * provide a REST endpoint at `path`, based on `service`
126
+ * add an HTTP REST endpoint at `path`, based on `service`
107
127
  */
108
- function httpRestService(path, service) {
109
- // console.log('path', path, 'service', service.name)
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, argList }) => {
161
- console.log("client-request", uid, name, action, argList)
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
- // ?? BOF
170
- if (name === 'authenticate' && result && !result.error) {
171
- console.log('### mark connection as secured', result)
172
- connection.accessToken = result.accessToken
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 = publishCallbacks[name]
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
- createCustomService,
256
+ createService,
222
257
  service,
223
258
  configure,
224
- httpRestService,
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
@@ -1 +0,0 @@
1
- AdminJS.UserComponents = {}
package/.env DELETED
@@ -1,2 +0,0 @@
1
- DATABASE_URL="postgresql://chris:eureka31@localhost:5432/blog?schema=public"
2
- # DATABASE_URL="postgres://chris:eureka31@localhost:5432/mitab-dev"
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
- }
Binary file
@@ -1,3 +0,0 @@
1
- {
2
- "recommendations": ["Vue.volar"]
3
- }
@@ -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)
@@ -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>
@@ -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
@@ -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
@@ -1,4 +0,0 @@
1
- import { createApp } from 'vue'
2
- import App from './App.vue'
3
-
4
- createApp(App).mount('#app')
@@ -1,9 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "sourceMap": true,
4
- "outDir": "dist",
5
- "strict": true,
6
- "lib": ["esnext"],
7
- "esModuleInterop": true
8
- }
9
- }
@@ -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;
@@ -1,5 +0,0 @@
1
- -- DropForeignKey
2
- ALTER TABLE "Post" DROP CONSTRAINT "Post_authorId_fkey";
3
-
4
- -- AddForeignKey
5
- ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1,3 +0,0 @@
1
- # Please do not edit this file manually
2
- # It should be added in your version-control system (i.e. Git)
3
- provider = "postgresql"
@@ -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>
@@ -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
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "sourceMap": true,
4
- "outDir": "dist",
5
- "strict": true,
6
- "lib": ["esnext"],
7
- "esModuleInterop": true
8
- }
9
- }