proteum 1.0.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +10 -0
- package/Rte.zip +0 -0
- package/cli/app/config.ts +54 -0
- package/cli/app/index.ts +195 -0
- package/cli/bin.js +11 -0
- package/cli/commands/build.ts +34 -0
- package/cli/commands/deploy/app.ts +29 -0
- package/cli/commands/deploy/web.ts +60 -0
- package/cli/commands/dev.ts +109 -0
- package/cli/commands/init.ts +85 -0
- package/cli/compiler/client/identite.ts +72 -0
- package/cli/compiler/client/index.ts +334 -0
- package/cli/compiler/common/babel/index.ts +170 -0
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +579 -0
- package/cli/compiler/common/babel/routes/imports.ts +127 -0
- package/cli/compiler/common/babel/routes/routes.ts +1130 -0
- package/cli/compiler/common/files/autres.ts +39 -0
- package/cli/compiler/common/files/images.ts +35 -0
- package/cli/compiler/common/files/style.ts +78 -0
- package/cli/compiler/common/index.ts +154 -0
- package/cli/compiler/index.ts +532 -0
- package/cli/compiler/server/index.ts +211 -0
- package/cli/index.ts +189 -0
- package/cli/paths.ts +165 -0
- package/cli/print.ts +12 -0
- package/cli/tsconfig.json +38 -0
- package/cli/utils/index.ts +22 -0
- package/cli/utils/keyboard.ts +78 -0
- package/client/app/component.tsx +54 -0
- package/client/app/index.ts +142 -0
- package/client/app/service.ts +34 -0
- package/client/app.tsconfig.json +28 -0
- package/client/components/Button.tsx +298 -0
- package/client/components/Dialog/Manager.tsx +309 -0
- package/client/components/Dialog/card.tsx +208 -0
- package/client/components/Dialog/index.less +151 -0
- package/client/components/Dialog/status.less +176 -0
- package/client/components/Dialog/status.tsx +48 -0
- package/client/components/index.ts +2 -0
- package/client/components/types.d.ts +3 -0
- package/client/data/input.ts +32 -0
- package/client/global.d.ts +5 -0
- package/client/hooks.ts +22 -0
- package/client/index.ts +6 -0
- package/client/pages/_layout/index.less +6 -0
- package/client/pages/_layout/index.tsx +43 -0
- package/client/pages/bug.tsx.old +60 -0
- package/client/pages/useHeader.tsx +50 -0
- package/client/services/captcha/index.ts +67 -0
- package/client/services/router/components/Link.tsx +46 -0
- package/client/services/router/components/Page.tsx +55 -0
- package/client/services/router/components/router.tsx +218 -0
- package/client/services/router/index.tsx +521 -0
- package/client/services/router/request/api.ts +267 -0
- package/client/services/router/request/history.ts +5 -0
- package/client/services/router/request/index.ts +53 -0
- package/client/services/router/request/multipart.ts +147 -0
- package/client/services/router/response/index.tsx +128 -0
- package/client/services/router/response/page.ts +86 -0
- package/client/services/socket/index.ts +147 -0
- package/client/utils/dom.ts +77 -0
- package/common/app/index.ts +9 -0
- package/common/data/chaines/index.ts +54 -0
- package/common/data/dates.ts +179 -0
- package/common/data/markdown.ts +73 -0
- package/common/data/rte/nodes.ts +83 -0
- package/common/data/stats.ts +90 -0
- package/common/errors/index.tsx +326 -0
- package/common/router/index.ts +213 -0
- package/common/router/layouts.ts +93 -0
- package/common/router/register.ts +55 -0
- package/common/router/request/api.ts +77 -0
- package/common/router/request/index.ts +35 -0
- package/common/router/response/index.ts +45 -0
- package/common/router/response/page.ts +128 -0
- package/common/utils/rte.ts +183 -0
- package/common/utils.ts +7 -0
- package/doc/TODO.md +71 -0
- package/doc/front/router.md +27 -0
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/package.json +171 -0
- package/server/app/commands.ts +141 -0
- package/server/app/container/config.ts +203 -0
- package/server/app/container/console/index.ts +550 -0
- package/server/app/container/index.ts +137 -0
- package/server/app/index.ts +273 -0
- package/server/app/service/container.ts +88 -0
- package/server/app/service/index.ts +235 -0
- package/server/app.tsconfig.json +28 -0
- package/server/context.ts +4 -0
- package/server/index.ts +4 -0
- package/server/services/auth/index.ts +250 -0
- package/server/services/auth/old.ts +277 -0
- package/server/services/auth/router/index.ts +95 -0
- package/server/services/auth/router/request.ts +54 -0
- package/server/services/auth/router/service.json +6 -0
- package/server/services/auth/service.json +6 -0
- package/server/services/cache/commands.ts +41 -0
- package/server/services/cache/index.ts +297 -0
- package/server/services/cache/service.json +6 -0
- package/server/services/cron/CronTask.ts +86 -0
- package/server/services/cron/index.ts +112 -0
- package/server/services/cron/service.json +6 -0
- package/server/services/disks/driver.ts +103 -0
- package/server/services/disks/drivers/local/index.ts +188 -0
- package/server/services/disks/drivers/local/service.json +6 -0
- package/server/services/disks/drivers/s3/index.ts +301 -0
- package/server/services/disks/drivers/s3/service.json +6 -0
- package/server/services/disks/index.ts +90 -0
- package/server/services/disks/service.json +6 -0
- package/server/services/email/index.ts +188 -0
- package/server/services/email/utils.ts +53 -0
- package/server/services/fetch/index.ts +201 -0
- package/server/services/fetch/service.json +7 -0
- package/server/services/models.7z +0 -0
- package/server/services/prisma/Facet.ts +142 -0
- package/server/services/prisma/index.ts +201 -0
- package/server/services/prisma/service.json +6 -0
- package/server/services/router/http/index.ts +217 -0
- package/server/services/router/http/multipart.ts +102 -0
- package/server/services/router/http/session.ts.old +40 -0
- package/server/services/router/index.ts +801 -0
- package/server/services/router/request/api.ts +87 -0
- package/server/services/router/request/index.ts +184 -0
- package/server/services/router/request/service.ts +21 -0
- package/server/services/router/request/validation/zod.ts +180 -0
- package/server/services/router/response/index.ts +338 -0
- package/server/services/router/response/mask/Filter.ts +323 -0
- package/server/services/router/response/mask/index.ts +60 -0
- package/server/services/router/response/mask/selecteurs.ts +92 -0
- package/server/services/router/response/page/document.tsx +160 -0
- package/server/services/router/response/page/index.tsx +196 -0
- package/server/services/router/service.json +6 -0
- package/server/services/router/service.ts +36 -0
- package/server/services/schema/index.ts +44 -0
- package/server/services/schema/request.ts +49 -0
- package/server/services/schema/router/index.ts +28 -0
- package/server/services/schema/router/service.json +6 -0
- package/server/services/schema/service.json +6 -0
- package/server/services/security/encrypt/aes/index.ts +85 -0
- package/server/services/security/encrypt/aes/service.json +6 -0
- package/server/services/socket/index.ts +162 -0
- package/server/services/socket/scope.ts +226 -0
- package/server/services/socket/service.json +6 -0
- package/server/services_old/SocketClient.ts +92 -0
- package/server/services_old/Token.old.ts +97 -0
- package/server/utils/slug.ts +79 -0
- package/tsconfig.common.json +45 -0
- package/tsconfig.json +3 -0
- package/types/aliases.d.ts +54 -0
- package/types/global/modules.d.ts +49 -0
- package/types/global/utils.d.ts +103 -0
- package/types/icons.d.ts +1 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPS
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Nodejs
|
|
6
|
+
import crypto, { Encoding } from 'crypto';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
import type { Application } from '@server/app';
|
|
10
|
+
import Service from '@server/app/service';
|
|
11
|
+
import { Forbidden } from '@common/errors';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- SERVICE CONFIG
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
export type Config = {
|
|
18
|
+
debug?: boolean,
|
|
19
|
+
// Initialisation vector
|
|
20
|
+
// Generate one here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx
|
|
21
|
+
iv: string,
|
|
22
|
+
// Define usage-specific keys
|
|
23
|
+
// You can also generate one via the link upper
|
|
24
|
+
keys: {[keyName: string]: string}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type Hooks = {
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Services = {
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*----------------------------------
|
|
36
|
+
- TYPES
|
|
37
|
+
----------------------------------*/
|
|
38
|
+
|
|
39
|
+
type TEncryptOptions = {
|
|
40
|
+
encoding: Encoding
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type TDecryptOptions = {
|
|
44
|
+
encoding: Encoding
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*----------------------------------
|
|
48
|
+
- SERVICE
|
|
49
|
+
----------------------------------*/
|
|
50
|
+
export default class AES<TConfig extends Config = Config> extends Service<TConfig, Hooks, Application, Application> {
|
|
51
|
+
|
|
52
|
+
public encrypt( keyName: keyof TConfig["keys"], data: any, options: TEncryptOptions = {
|
|
53
|
+
encoding: 'base64url'
|
|
54
|
+
}) {
|
|
55
|
+
|
|
56
|
+
const encKey = this.config.keys[ keyName as keyof typeof this.config.keys ];
|
|
57
|
+
|
|
58
|
+
data = JSON.stringify(data);
|
|
59
|
+
|
|
60
|
+
let cipher = crypto.createCipheriv('aes-256-cbc', encKey, this.config.iv);
|
|
61
|
+
let encrypted = cipher.update(data, 'utf8', options.encoding);
|
|
62
|
+
encrypted += cipher.final(options.encoding);
|
|
63
|
+
return encrypted;
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public decrypt( keyName: keyof TConfig["keys"], data: string, options: TDecryptOptions = {
|
|
68
|
+
encoding: 'base64url'
|
|
69
|
+
}) {
|
|
70
|
+
|
|
71
|
+
const encKey = this.config.keys[ keyName as keyof typeof this.config.keys ];
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
|
|
75
|
+
let decipher = crypto.createDecipheriv('aes-256-cbc', encKey, this.config.iv);
|
|
76
|
+
let decrypted = decipher.update(data, options.encoding, 'utf8');
|
|
77
|
+
return JSON.parse(decrypted + decipher.final('utf8'));
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
|
|
81
|
+
throw new Forbidden("Invalid token.");
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import { Server as WebSocketServer, ServerOptions } from 'ws';
|
|
7
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
+
import { IncomingMessage } from 'http';
|
|
9
|
+
import cookie from 'cookie';
|
|
10
|
+
|
|
11
|
+
// Core
|
|
12
|
+
import type { Application } from '@server/app';
|
|
13
|
+
import Service, { AnyService, TRegisteredService } from '@server/app/service';
|
|
14
|
+
import SocketScope, { WebSocket } from './scope';
|
|
15
|
+
import type Router from '@server/services/router';
|
|
16
|
+
export type { WebSocket, default as SocketScope } from './scope';
|
|
17
|
+
import type UsersManagementService from '../auth';
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- TYPES
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export type Config<TUser extends {}> = {
|
|
25
|
+
debug?: boolean,
|
|
26
|
+
//server: ServerOptions["server"],
|
|
27
|
+
//users: UsersManagementService<TUser>,
|
|
28
|
+
port: number,
|
|
29
|
+
|
|
30
|
+
users: UsersManagementService<TUser, Application>,
|
|
31
|
+
router: Router
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type Hooks = {
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*----------------------------------
|
|
39
|
+
- MANAGER
|
|
40
|
+
----------------------------------*/
|
|
41
|
+
export default class WebSocketCommander<
|
|
42
|
+
TUser extends {},
|
|
43
|
+
TConfig extends Config<TUser>= Config<TUser>
|
|
44
|
+
> extends Service<TConfig, Hooks, Application> {
|
|
45
|
+
|
|
46
|
+
// Services
|
|
47
|
+
public ws!: WebSocketServer;
|
|
48
|
+
public users!: TConfig["users"];
|
|
49
|
+
public router!: TConfig["router"];
|
|
50
|
+
|
|
51
|
+
// Context
|
|
52
|
+
public scopes: {[path: string]: SocketScope<TUser>} = {}
|
|
53
|
+
|
|
54
|
+
public constructor(
|
|
55
|
+
parent: AnyService,
|
|
56
|
+
config: TConfig,
|
|
57
|
+
app: TApplication,
|
|
58
|
+
) {
|
|
59
|
+
super(parent, config, app);
|
|
60
|
+
|
|
61
|
+
this.users = this.config.users;
|
|
62
|
+
this.router = this.config.router;
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/*----------------------------------
|
|
67
|
+
- LIFECYCLE
|
|
68
|
+
----------------------------------*/
|
|
69
|
+
|
|
70
|
+
public loading: Promise<void> | undefined = undefined;
|
|
71
|
+
protected async ready() {
|
|
72
|
+
|
|
73
|
+
this.users.on('disconnect', async (userId: string) => {
|
|
74
|
+
this.disconnect(userId, 'Logout');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
console.info(`Loading socket commander`);
|
|
78
|
+
this.ws = new WebSocketServer({ server: this.router.http.http })
|
|
79
|
+
.on('connection', (socket: WebSocket, req: IncomingMessage) => {
|
|
80
|
+
|
|
81
|
+
// Resolve scope
|
|
82
|
+
const path = req.url;
|
|
83
|
+
let scope: SocketScope<TUser> | undefined;
|
|
84
|
+
for (const scopePath in this.scopes)
|
|
85
|
+
if (path === scopePath) {
|
|
86
|
+
scope = this.scopes[path];
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (scope === undefined) {
|
|
91
|
+
console.warn("Unknown scope path:", path);
|
|
92
|
+
socket.close();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
socket.id = uuidv4();
|
|
97
|
+
|
|
98
|
+
// req.headers['x-forwarded-for'] = IP réelle du client quand on passe par un porxy apache
|
|
99
|
+
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
|
100
|
+
if (typeof ip !== 'string') {
|
|
101
|
+
console.warn("Invalid IP address", ip);
|
|
102
|
+
socket.close();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
socket.ip = ip;
|
|
106
|
+
|
|
107
|
+
// Cookies
|
|
108
|
+
if (req.headers.cookie) {
|
|
109
|
+
req.cookies = cookie.parse(req.headers.cookie);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
scope.newClient(socket, req);
|
|
113
|
+
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
console.info(`Socket commander bound to http server.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public async shutdown() {
|
|
120
|
+
this.closeAll();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/*----------------------------------
|
|
124
|
+
- ACTIONS
|
|
125
|
+
----------------------------------*/
|
|
126
|
+
|
|
127
|
+
public open(path: string) {
|
|
128
|
+
|
|
129
|
+
if (!(path in this.scopes)) {
|
|
130
|
+
|
|
131
|
+
console.info("Registering socket scope:", path);
|
|
132
|
+
this.scopes[path] = new SocketScope(path, this);
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return this.scopes[path];
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
public send(scopename: string, usernames: string | string[], command: string, data?: any) {
|
|
142
|
+
|
|
143
|
+
const scope = this.scopes[scopename];
|
|
144
|
+
if (scope === undefined)
|
|
145
|
+
return console.warn("No scope with name", scopename);
|
|
146
|
+
|
|
147
|
+
scope.send(usernames, command, data);
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public disconnect( usernames: string | string[], reason: string, data?: any ) {
|
|
152
|
+
console.log(`Disconnecting ${usernames} from all scopes`);
|
|
153
|
+
for (const path in this.scopes)
|
|
154
|
+
this.scopes[path].disconnect( usernames, reason );
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public closeAll() {
|
|
158
|
+
console.log("Closing All connections");
|
|
159
|
+
for (const path in this.scopes)
|
|
160
|
+
this.scopes[path].close();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import ws from 'ws';
|
|
7
|
+
import type { IncomingMessage } from 'http';
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import type SocketService from '.';
|
|
11
|
+
import context from '@server/context';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
export type WebSocket = ws & {
|
|
18
|
+
ip: string,
|
|
19
|
+
id: string,
|
|
20
|
+
username: string,
|
|
21
|
+
activity: number | false,
|
|
22
|
+
disconnect: (reason: string) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type TConnectCallback = (client: WebSocket, req: IncomingMessage) => Promise<void | false>;
|
|
26
|
+
|
|
27
|
+
type TEventCallback = (data: any, socket: WebSocket) => Promise<void>
|
|
28
|
+
|
|
29
|
+
type TCommands = { [command: string]: TEventCallback }
|
|
30
|
+
|
|
31
|
+
const activityDelay = 10 * 1000; // Clearn broken connections veery 10s
|
|
32
|
+
|
|
33
|
+
/*----------------------------------
|
|
34
|
+
- SCOPE
|
|
35
|
+
----------------------------------*/
|
|
36
|
+
export default class SocketScope<TUser extends {}> {
|
|
37
|
+
|
|
38
|
+
private connectEvent?: TConnectCallback;
|
|
39
|
+
private disconnectEvent?: TConnectCallback;
|
|
40
|
+
public commands: TCommands = {};
|
|
41
|
+
public users: { [username: string]: WebSocket[] } = {}
|
|
42
|
+
|
|
43
|
+
public constructor(
|
|
44
|
+
public path: string,
|
|
45
|
+
private socket: SocketService<TUser>,
|
|
46
|
+
private app = socket.app
|
|
47
|
+
) {
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private cleaner = setInterval(() => {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
for (const username in this.users) {
|
|
54
|
+
for (const socket of this.users[username]) {
|
|
55
|
+
|
|
56
|
+
// If the client did not sent any rsponse to the ping
|
|
57
|
+
if (socket.activity === false) {
|
|
58
|
+
|
|
59
|
+
// We consider it as a dead connection (discnnected, but no terminate packet was sent. Ex: internet disabled)
|
|
60
|
+
console.log(this.path + ':', username, socket.ip, socket.id, "is dead");
|
|
61
|
+
socket.terminate();
|
|
62
|
+
|
|
63
|
+
// If user has not been active since x seconds
|
|
64
|
+
} else if (socket.activity < now - activityDelay) {
|
|
65
|
+
|
|
66
|
+
// We consider hil a dead, until he responds to a ping
|
|
67
|
+
socket.activity = false;
|
|
68
|
+
socket.ping();
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, activityDelay);
|
|
74
|
+
|
|
75
|
+
public newClient(socket: WebSocket, req: IncomingMessage) {
|
|
76
|
+
context.run({ channelType: 'socket', channelId: this.path }, async () => {
|
|
77
|
+
|
|
78
|
+
// Auth
|
|
79
|
+
const username = await this.socket.config.users.decode(req);
|
|
80
|
+
if (!username) {
|
|
81
|
+
console.log(`Rejecting connection on ${this.path} for client ${socket.ip} (${socket.id})}: Not authenticated`);
|
|
82
|
+
socket.close(4004, "auth");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
socket.username = username;
|
|
87
|
+
socket.disconnect = (reason: string) => socket.close(4004, reason);
|
|
88
|
+
|
|
89
|
+
// On connect event
|
|
90
|
+
if (this.connectEvent && await this.connectEvent(socket, req) === false) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Indexage
|
|
95
|
+
if (this.users[username] === undefined) {
|
|
96
|
+
this.users[username] = [socket];
|
|
97
|
+
} else {
|
|
98
|
+
this.users[username].push(socket);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(`Client ${socket.username} (${socket.ip}) connected on ${this.path}. Connections number for this user: ` + this.users[username].length);
|
|
102
|
+
// Indique au cient que sa connexion a bien été acceptée
|
|
103
|
+
socket.send("ok>ok")
|
|
104
|
+
|
|
105
|
+
// Détection déconnexion
|
|
106
|
+
socket.activity = Date.now();
|
|
107
|
+
socket.on('pong', () => {
|
|
108
|
+
socket.activity = Date.now();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Ecoute résolution des challenges
|
|
112
|
+
socket.on('message', (m: ws.Data) => {
|
|
113
|
+
|
|
114
|
+
const response = m.toString();
|
|
115
|
+
var i = response.indexOf('>');
|
|
116
|
+
if (i === -1) {
|
|
117
|
+
return console.warn(`Bad data structure:`, response.substring(0, 100) + '...');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const command = response.slice(0, i);
|
|
121
|
+
const strData = response.slice(i + 1);
|
|
122
|
+
|
|
123
|
+
let data;
|
|
124
|
+
try {
|
|
125
|
+
data = JSON.parse(strData);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.warn(`Error decoding data`, error);
|
|
128
|
+
this.send(socket.username, 'log', `Invalid data format.`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const handler = this.commands[command];
|
|
133
|
+
if (handler === undefined)
|
|
134
|
+
return console.error('Command « ' + command + ' » does not exists.');
|
|
135
|
+
|
|
136
|
+
socket.activity = Date.now();
|
|
137
|
+
|
|
138
|
+
handler(data, socket);
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
socket.on('close', () => {
|
|
144
|
+
|
|
145
|
+
if (this.users[username].length > 1)
|
|
146
|
+
this.users[username] = this.users[username].filter(s => s.id !== socket.id);
|
|
147
|
+
else
|
|
148
|
+
this.users[username] = [];
|
|
149
|
+
|
|
150
|
+
console.log(`Client ${socket.ip} (${socket.id}) disconnected from ${this.path}. Connections number for this user: ` + this.users[username].length);
|
|
151
|
+
|
|
152
|
+
if (this.disconnectEvent !== undefined)
|
|
153
|
+
this.disconnectEvent(socket, req);
|
|
154
|
+
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public isConnected(username: string) {
|
|
160
|
+
return this.users[username] !== undefined && this.users[username].length !== 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public send(usernames: string | string[], command: string, data: any = true) {
|
|
164
|
+
|
|
165
|
+
if (usernames === '*')
|
|
166
|
+
usernames = Object.keys(this.users);
|
|
167
|
+
else if (!Array.isArray(usernames))
|
|
168
|
+
usernames = [usernames];
|
|
169
|
+
|
|
170
|
+
for (const username of usernames) {
|
|
171
|
+
|
|
172
|
+
const user = this.users[username];
|
|
173
|
+
if (user === undefined)
|
|
174
|
+
return console.warn("User", username, "is not connected to", this.path);
|
|
175
|
+
|
|
176
|
+
for (const client of user) {
|
|
177
|
+
client.send(command + '>' + JSON.stringify(data));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log("Sent event " + command + " to " + username + " on " + this.path)
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public disconnect(usernames: string | string[], reason: string, data?: any) {
|
|
186
|
+
|
|
187
|
+
if (usernames === '*')
|
|
188
|
+
usernames = Object.keys(this.users);
|
|
189
|
+
else if (!Array.isArray(usernames))
|
|
190
|
+
usernames = [usernames];
|
|
191
|
+
|
|
192
|
+
for (const username of usernames) {
|
|
193
|
+
|
|
194
|
+
const user = this.users[username];
|
|
195
|
+
if (user === undefined)
|
|
196
|
+
return console.warn("User", username, "is not connected to", this.path);
|
|
197
|
+
|
|
198
|
+
for (const client of user) {
|
|
199
|
+
client.disconnect(reason);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log("Disconnected " + username + " from " + this.path + " fo the following reason: " + reason)
|
|
203
|
+
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public onConnect(cb: TConnectCallback) {
|
|
208
|
+
this.connectEvent = cb;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public on(event: string, cb: TEventCallback) {
|
|
213
|
+
this.commands[event] = cb;
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public onDisconnect(cb: TConnectCallback) {
|
|
218
|
+
this.disconnectEvent = cb;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public close() {
|
|
223
|
+
clearInterval(this.cleaner);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Source: https://github.com/binance-exchange/binance-websocket-examples/blob/master/src/lib/socketClient.js
|
|
2
|
+
// Exmeple utilisation: https://github.com/binance-exchange/binance-websocket-examples/blob/master/src/monitor-spot.js
|
|
3
|
+
|
|
4
|
+
import WebSocket from 'ws';
|
|
5
|
+
|
|
6
|
+
type THandler = (message: any) => void;
|
|
7
|
+
|
|
8
|
+
class SocketClient {
|
|
9
|
+
|
|
10
|
+
private url: string;
|
|
11
|
+
|
|
12
|
+
private ws!: WebSocket;
|
|
13
|
+
private handlers: THandler[] = [];
|
|
14
|
+
|
|
15
|
+
constructor(url: string) {
|
|
16
|
+
this.url = url;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private log = (...args: any[]) => console.log('[socket][client]['+ this.url +']', ...args);
|
|
20
|
+
|
|
21
|
+
public connecter(): Promise<this> {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
|
|
24
|
+
this.log('Connexion');
|
|
25
|
+
|
|
26
|
+
this.ws = new WebSocket(this.url);
|
|
27
|
+
|
|
28
|
+
this.ws.onopen = () => {
|
|
29
|
+
this.log('Connecté.');
|
|
30
|
+
|
|
31
|
+
resolve(this);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.ws.onmessage = (msg) => {
|
|
35
|
+
try {
|
|
36
|
+
const message = JSON.parse(msg.data);
|
|
37
|
+
for (const handler of this.handlers)
|
|
38
|
+
handler(message);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
this.log('Parse message failed', e);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.ws.onerror = (err) => {
|
|
45
|
+
this.log('Erreur', err);
|
|
46
|
+
|
|
47
|
+
reject(err);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
this.ws.onclose = (e) => {
|
|
51
|
+
this.log('Fermé', e.reason);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.ws.on('pong', () => {
|
|
55
|
+
this.log('Le serveur a envoyé pong');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this.ws.on('ping', () => {
|
|
59
|
+
if (this.ws.readyState === WebSocket.OPEN)
|
|
60
|
+
this.ws.pong();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
//this.heartBeat();
|
|
64
|
+
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*public heartBeat() {
|
|
69
|
+
setInterval(() => {
|
|
70
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
71
|
+
this.ws.ping();
|
|
72
|
+
this.log("Ping serveur");
|
|
73
|
+
}
|
|
74
|
+
}, 5000);
|
|
75
|
+
}*/
|
|
76
|
+
|
|
77
|
+
public send(data: object) {
|
|
78
|
+
this.ws.send( JSON.stringify(data) );
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public handle(handler: THandler) {
|
|
83
|
+
this.handlers.push(handler);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public close() {
|
|
88
|
+
this.ws.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default SocketClient;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
NOTE: Replaced by AES
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- DEPS
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
|
|
12
|
+
// NPM
|
|
13
|
+
import { v4 as uuid } from 'uuid';
|
|
14
|
+
import hInterval from 'human-interval';
|
|
15
|
+
|
|
16
|
+
// Core
|
|
17
|
+
import Cron, { CronTask } from '@server/services/cron';
|
|
18
|
+
import { Forbidden } from '@common/errors';
|
|
19
|
+
|
|
20
|
+
const debug = true;
|
|
21
|
+
|
|
22
|
+
/*----------------------------------
|
|
23
|
+
- TYPES
|
|
24
|
+
----------------------------------*/
|
|
25
|
+
|
|
26
|
+
type TokenOptions = {
|
|
27
|
+
expires?: number, // Timestamp
|
|
28
|
+
data?: any
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/*----------------------------------
|
|
32
|
+
- SERVICE
|
|
33
|
+
----------------------------------*/
|
|
34
|
+
class Tokens {
|
|
35
|
+
|
|
36
|
+
private tokens: {[token: string]: TokenOptions} = {};
|
|
37
|
+
|
|
38
|
+
public constructor() {
|
|
39
|
+
Cron.task("tokens.expiration", '*/5 * * * *', async () => {
|
|
40
|
+
this.cleanExpired()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private cleanExpired() {
|
|
45
|
+
debug && console.log("Cleaning expired tokens ...");
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
for (const token in this.tokens) {
|
|
48
|
+
const expires = this.tokens[token].expires;
|
|
49
|
+
if (expires !== undefined && expires < now) {
|
|
50
|
+
debug && console.log("Expired: " + token);
|
|
51
|
+
delete this.tokens[token];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public create( duration?: string, data?: any ) {
|
|
57
|
+
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
const token = uuid();
|
|
60
|
+
const options: TokenOptions = {
|
|
61
|
+
data
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (duration !== undefined) {
|
|
65
|
+
|
|
66
|
+
const interval = hInterval(duration);
|
|
67
|
+
if (!interval)
|
|
68
|
+
throw new Error(`Invalid interval expression: ${duration}`);
|
|
69
|
+
|
|
70
|
+
options.expires = now + interval;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.tokens[ token ] = options;
|
|
74
|
+
|
|
75
|
+
return token;
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public get<TData extends any>( token: string, critical: boolean = true ): TData | undefined {
|
|
80
|
+
|
|
81
|
+
const options = this.tokens[token];
|
|
82
|
+
debug && console.log("Get token", token, options);
|
|
83
|
+
if (options === undefined) {
|
|
84
|
+
if (critical)
|
|
85
|
+
throw new Forbidden(`Invalid token.`);
|
|
86
|
+
else
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
delete this.tokens[token];
|
|
91
|
+
|
|
92
|
+
return options.data;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default new Tokens;
|