@owlmeans/server-socket 0.1.0

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +560 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/consts.d.ts +2 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +2 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/helper.d.ts +7 -0
  9. package/build/helper.d.ts.map +1 -0
  10. package/build/helper.js +14 -0
  11. package/build/helper.js.map +1 -0
  12. package/build/index.d.ts +6 -0
  13. package/build/index.d.ts.map +1 -0
  14. package/build/index.js +5 -0
  15. package/build/index.js.map +1 -0
  16. package/build/middleware.d.ts +3 -0
  17. package/build/middleware.d.ts.map +1 -0
  18. package/build/middleware.js +25 -0
  19. package/build/middleware.js.map +1 -0
  20. package/build/service.d.ts +5 -0
  21. package/build/service.d.ts.map +1 -0
  22. package/build/service.js +109 -0
  23. package/build/service.js.map +1 -0
  24. package/build/types.d.ts +11 -0
  25. package/build/types.d.ts.map +1 -0
  26. package/build/types.js +2 -0
  27. package/build/types.js.map +1 -0
  28. package/build/utils/connection.d.ts +6 -0
  29. package/build/utils/connection.d.ts.map +1 -0
  30. package/build/utils/connection.js +104 -0
  31. package/build/utils/connection.js.map +1 -0
  32. package/build/utils/index.d.ts +3 -0
  33. package/build/utils/index.d.ts.map +1 -0
  34. package/build/utils/index.js +3 -0
  35. package/build/utils/index.js.map +1 -0
  36. package/build/utils/server.d.ts +5 -0
  37. package/build/utils/server.d.ts.map +1 -0
  38. package/build/utils/server.js +15 -0
  39. package/build/utils/server.js.map +1 -0
  40. package/package.json +46 -0
  41. package/src/consts.ts +2 -0
  42. package/src/helper.ts +21 -0
  43. package/src/index.ts +6 -0
  44. package/src/middleware.ts +32 -0
  45. package/src/service.ts +133 -0
  46. package/src/types.ts +12 -0
  47. package/src/utils/connection.ts +124 -0
  48. package/src/utils/index.ts +3 -0
  49. package/src/utils/server.ts +21 -0
  50. package/tsconfig.json +16 -0
  51. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,124 @@
1
+ import type { WebSocket } from '@fastify/websocket'
2
+ import type { Connection, EventMessage } from '@owlmeans/socket'
3
+ import type { Config, Context } from '@owlmeans/server-api'
4
+ import {
5
+ SocketUnsupported, createBasicConnection, MessageType, SocketInitializationError, SocketUnauthorized
6
+ } from '@owlmeans/socket'
7
+ import { AbstractRequest } from '@owlmeans/module'
8
+ import { AuthenticationStage, AUTH_QUERY } from '@owlmeans/auth'
9
+ import type { Auth, AuthCredentials } from '@owlmeans/auth'
10
+ import { isAuth, isAuthCredentials, isAuthToken } from '@owlmeans/auth'
11
+ import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
12
+ import type { AuthServiceAppend } from '@owlmeans/server-auth'
13
+
14
+ export const makeConnection = <C extends Config, T extends Context<C> = Context<C>>(
15
+ request: AbstractRequest<WebSocket>, context: T
16
+ ): Connection => {
17
+ if (request.body == null) {
18
+ throw new SocketInitializationError('request')
19
+ }
20
+ let auth: Auth | AuthCredentials | null | undefined = undefined
21
+ const conn = request.body
22
+
23
+ const model = createBasicConnection()
24
+
25
+ model.close = async () => {
26
+ await conn.close()
27
+ // @TODO check if on close event is triggered
28
+ }
29
+
30
+ model.send = async message => {
31
+ if (typeof message !== 'string') {
32
+ model.prepare?.(message)
33
+ }
34
+ await conn.send(typeof message === 'string' ? message : JSON.stringify(message))
35
+ }
36
+
37
+ // @TODO This method is fully supported only by authentication services
38
+ model.authenticate = async (stage, payload) => {
39
+ if (AuthenticationStage.Authenticate === stage) {
40
+ if (isAuthToken(payload)) {
41
+ const ctx = context as AuthServiceAppend & T
42
+ const _auth = await ctx.auth().authenticate(payload)
43
+ if (_auth == null) {
44
+ throw new SocketUnauthorized(stage)
45
+ }
46
+ const model = makeEnvelopeModel<Auth>(_auth.token, EnvelopeKind.Token)
47
+ auth = model.message()
48
+
49
+ return [AuthenticationStage.Authenticated, _auth as any]
50
+ }
51
+ if (isAuth(payload)) {
52
+ auth = payload
53
+
54
+ return [stage, auth as any]
55
+ }
56
+ } else if (AuthenticationStage.Authenticated === stage) {
57
+ if (auth == null) {
58
+ throw new SocketUnauthorized(stage)
59
+ }
60
+
61
+ return [stage, auth as any]
62
+ }
63
+ throw new SocketUnsupported('auth-service')
64
+ }
65
+
66
+ model.prepare = (message, isRequest) => {
67
+ if (auth === undefined) {
68
+ auth = null
69
+ try {
70
+ let authorization = (request.query as any)?.[AUTH_QUERY]
71
+ if (authorization != null) {
72
+ const envelope = makeEnvelopeModel<Auth | AuthCredentials>(authorization, EnvelopeKind.Token)
73
+ const _auth = envelope.message()
74
+ if (isAuth(_auth) || isAuthCredentials(_auth)) {
75
+ auth = _auth
76
+ }
77
+ }
78
+ } catch {
79
+ auth = null
80
+ }
81
+ }
82
+ if (isRequest) {
83
+ message.dt = message.dt ?? Date.now()
84
+ if (auth != null && message.recipient == null) {
85
+ message.recipient = auth.profileId ?? auth.userId
86
+ }
87
+ if (message.sender == null) {
88
+ // @TODO think about using dids here
89
+ message.sender = context.cfg.service
90
+ }
91
+ } else {
92
+ if (auth != null && message.sender == null) {
93
+ message.sender = auth.profileId ?? auth.userId
94
+ }
95
+ }
96
+
97
+ return message
98
+ }
99
+
100
+ const messageHandler = async (_message: Buffer | Buffer[]) => {
101
+ _message = Array.isArray(_message) ? _message : [_message]
102
+ const message = _message.map(msg => msg.toString('utf8')).join('')
103
+ await model.receive(message)
104
+ }
105
+
106
+ const closeHandler = async (code: number) => {
107
+ const msg: EventMessage<{ code: number }> = {
108
+ type: MessageType.System,
109
+ event: 'close',
110
+ payload: { code }
111
+ }
112
+ if (model.prepare != null) {
113
+ model.prepare(msg)
114
+ }
115
+ await Promise.all(model.getListeners().map(async listener => listener(msg)))
116
+ conn.off('message', messageHandler)
117
+ conn.off('close', closeHandler)
118
+ }
119
+
120
+ conn.on('message', messageHandler)
121
+ conn.on('close', closeHandler)
122
+
123
+ return model
124
+ }
@@ -0,0 +1,3 @@
1
+
2
+ export * from './server.js'
3
+ export * from './connection.js'
@@ -0,0 +1,21 @@
1
+ import type { CommonModule } from '@owlmeans/module'
2
+ import type { ServerModule } from '@owlmeans/server-module'
3
+ import { RouteProtocols } from '@owlmeans/route'
4
+ import { AppType } from '@owlmeans/context'
5
+ import type { Context } from '@owlmeans/server-api'
6
+
7
+ export const canServerModule = (context: Context, module: CommonModule): module is ServerModule<unknown> => {
8
+ if (module.route.route.type !== AppType.Backend) {
9
+ return false
10
+ }
11
+
12
+ if (module.route.route.service != null && module.route.route.service !== context.cfg.service) {
13
+ return false
14
+ }
15
+
16
+ if (module.route.route.protocol !== RouteProtocols.SOCKET) {
17
+ return false
18
+ }
19
+
20
+ return 'isIntermediate' in module.route
21
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": [
3
+ "../tsconfig.default.json",
4
+ "../tsconfig.react.json",
5
+ ],
6
+ "compilerOptions": {
7
+ "rootDir": "./src/", /* Specify the root folder within your source files. */
8
+ "outDir": "./build/", /* Specify an output folder for all emitted files. */
9
+ "moduleResolution": "Bundler"
10
+ },
11
+ "exclude": [
12
+ "./dist/**/*",
13
+ "./build/**/*",
14
+ "./*.ts"
15
+ ]
16
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/consts.ts","./src/helper.ts","./src/index.ts","./src/middleware.ts","./src/service.ts","./src/types.ts","./src/utils/connection.ts","./src/utils/index.ts","./src/utils/server.ts"],"version":"5.6.3"}