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.
Files changed (156) hide show
  1. package/.dockerignore +10 -0
  2. package/Rte.zip +0 -0
  3. package/cli/app/config.ts +54 -0
  4. package/cli/app/index.ts +195 -0
  5. package/cli/bin.js +11 -0
  6. package/cli/commands/build.ts +34 -0
  7. package/cli/commands/deploy/app.ts +29 -0
  8. package/cli/commands/deploy/web.ts +60 -0
  9. package/cli/commands/dev.ts +109 -0
  10. package/cli/commands/init.ts +85 -0
  11. package/cli/compiler/client/identite.ts +72 -0
  12. package/cli/compiler/client/index.ts +334 -0
  13. package/cli/compiler/common/babel/index.ts +170 -0
  14. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  15. package/cli/compiler/common/babel/plugins/services.ts +579 -0
  16. package/cli/compiler/common/babel/routes/imports.ts +127 -0
  17. package/cli/compiler/common/babel/routes/routes.ts +1130 -0
  18. package/cli/compiler/common/files/autres.ts +39 -0
  19. package/cli/compiler/common/files/images.ts +35 -0
  20. package/cli/compiler/common/files/style.ts +78 -0
  21. package/cli/compiler/common/index.ts +154 -0
  22. package/cli/compiler/index.ts +532 -0
  23. package/cli/compiler/server/index.ts +211 -0
  24. package/cli/index.ts +189 -0
  25. package/cli/paths.ts +165 -0
  26. package/cli/print.ts +12 -0
  27. package/cli/tsconfig.json +38 -0
  28. package/cli/utils/index.ts +22 -0
  29. package/cli/utils/keyboard.ts +78 -0
  30. package/client/app/component.tsx +54 -0
  31. package/client/app/index.ts +142 -0
  32. package/client/app/service.ts +34 -0
  33. package/client/app.tsconfig.json +28 -0
  34. package/client/components/Button.tsx +298 -0
  35. package/client/components/Dialog/Manager.tsx +309 -0
  36. package/client/components/Dialog/card.tsx +208 -0
  37. package/client/components/Dialog/index.less +151 -0
  38. package/client/components/Dialog/status.less +176 -0
  39. package/client/components/Dialog/status.tsx +48 -0
  40. package/client/components/index.ts +2 -0
  41. package/client/components/types.d.ts +3 -0
  42. package/client/data/input.ts +32 -0
  43. package/client/global.d.ts +5 -0
  44. package/client/hooks.ts +22 -0
  45. package/client/index.ts +6 -0
  46. package/client/pages/_layout/index.less +6 -0
  47. package/client/pages/_layout/index.tsx +43 -0
  48. package/client/pages/bug.tsx.old +60 -0
  49. package/client/pages/useHeader.tsx +50 -0
  50. package/client/services/captcha/index.ts +67 -0
  51. package/client/services/router/components/Link.tsx +46 -0
  52. package/client/services/router/components/Page.tsx +55 -0
  53. package/client/services/router/components/router.tsx +218 -0
  54. package/client/services/router/index.tsx +521 -0
  55. package/client/services/router/request/api.ts +267 -0
  56. package/client/services/router/request/history.ts +5 -0
  57. package/client/services/router/request/index.ts +53 -0
  58. package/client/services/router/request/multipart.ts +147 -0
  59. package/client/services/router/response/index.tsx +128 -0
  60. package/client/services/router/response/page.ts +86 -0
  61. package/client/services/socket/index.ts +147 -0
  62. package/client/utils/dom.ts +77 -0
  63. package/common/app/index.ts +9 -0
  64. package/common/data/chaines/index.ts +54 -0
  65. package/common/data/dates.ts +179 -0
  66. package/common/data/markdown.ts +73 -0
  67. package/common/data/rte/nodes.ts +83 -0
  68. package/common/data/stats.ts +90 -0
  69. package/common/errors/index.tsx +326 -0
  70. package/common/router/index.ts +213 -0
  71. package/common/router/layouts.ts +93 -0
  72. package/common/router/register.ts +55 -0
  73. package/common/router/request/api.ts +77 -0
  74. package/common/router/request/index.ts +35 -0
  75. package/common/router/response/index.ts +45 -0
  76. package/common/router/response/page.ts +128 -0
  77. package/common/utils/rte.ts +183 -0
  78. package/common/utils.ts +7 -0
  79. package/doc/TODO.md +71 -0
  80. package/doc/front/router.md +27 -0
  81. package/doc/workspace/workspace.png +0 -0
  82. package/doc/workspace/workspace2.png +0 -0
  83. package/doc/workspace/workspace_26.01.22.png +0 -0
  84. package/package.json +171 -0
  85. package/server/app/commands.ts +141 -0
  86. package/server/app/container/config.ts +203 -0
  87. package/server/app/container/console/index.ts +550 -0
  88. package/server/app/container/index.ts +137 -0
  89. package/server/app/index.ts +273 -0
  90. package/server/app/service/container.ts +88 -0
  91. package/server/app/service/index.ts +235 -0
  92. package/server/app.tsconfig.json +28 -0
  93. package/server/context.ts +4 -0
  94. package/server/index.ts +4 -0
  95. package/server/services/auth/index.ts +250 -0
  96. package/server/services/auth/old.ts +277 -0
  97. package/server/services/auth/router/index.ts +95 -0
  98. package/server/services/auth/router/request.ts +54 -0
  99. package/server/services/auth/router/service.json +6 -0
  100. package/server/services/auth/service.json +6 -0
  101. package/server/services/cache/commands.ts +41 -0
  102. package/server/services/cache/index.ts +297 -0
  103. package/server/services/cache/service.json +6 -0
  104. package/server/services/cron/CronTask.ts +86 -0
  105. package/server/services/cron/index.ts +112 -0
  106. package/server/services/cron/service.json +6 -0
  107. package/server/services/disks/driver.ts +103 -0
  108. package/server/services/disks/drivers/local/index.ts +188 -0
  109. package/server/services/disks/drivers/local/service.json +6 -0
  110. package/server/services/disks/drivers/s3/index.ts +301 -0
  111. package/server/services/disks/drivers/s3/service.json +6 -0
  112. package/server/services/disks/index.ts +90 -0
  113. package/server/services/disks/service.json +6 -0
  114. package/server/services/email/index.ts +188 -0
  115. package/server/services/email/utils.ts +53 -0
  116. package/server/services/fetch/index.ts +201 -0
  117. package/server/services/fetch/service.json +7 -0
  118. package/server/services/models.7z +0 -0
  119. package/server/services/prisma/Facet.ts +142 -0
  120. package/server/services/prisma/index.ts +201 -0
  121. package/server/services/prisma/service.json +6 -0
  122. package/server/services/router/http/index.ts +217 -0
  123. package/server/services/router/http/multipart.ts +102 -0
  124. package/server/services/router/http/session.ts.old +40 -0
  125. package/server/services/router/index.ts +801 -0
  126. package/server/services/router/request/api.ts +87 -0
  127. package/server/services/router/request/index.ts +184 -0
  128. package/server/services/router/request/service.ts +21 -0
  129. package/server/services/router/request/validation/zod.ts +180 -0
  130. package/server/services/router/response/index.ts +338 -0
  131. package/server/services/router/response/mask/Filter.ts +323 -0
  132. package/server/services/router/response/mask/index.ts +60 -0
  133. package/server/services/router/response/mask/selecteurs.ts +92 -0
  134. package/server/services/router/response/page/document.tsx +160 -0
  135. package/server/services/router/response/page/index.tsx +196 -0
  136. package/server/services/router/service.json +6 -0
  137. package/server/services/router/service.ts +36 -0
  138. package/server/services/schema/index.ts +44 -0
  139. package/server/services/schema/request.ts +49 -0
  140. package/server/services/schema/router/index.ts +28 -0
  141. package/server/services/schema/router/service.json +6 -0
  142. package/server/services/schema/service.json +6 -0
  143. package/server/services/security/encrypt/aes/index.ts +85 -0
  144. package/server/services/security/encrypt/aes/service.json +6 -0
  145. package/server/services/socket/index.ts +162 -0
  146. package/server/services/socket/scope.ts +226 -0
  147. package/server/services/socket/service.json +6 -0
  148. package/server/services_old/SocketClient.ts +92 -0
  149. package/server/services_old/Token.old.ts +97 -0
  150. package/server/utils/slug.ts +79 -0
  151. package/tsconfig.common.json +45 -0
  152. package/tsconfig.json +3 -0
  153. package/types/aliases.d.ts +54 -0
  154. package/types/global/modules.d.ts +49 -0
  155. package/types/global/utils.d.ts +103 -0
  156. package/types/icons.d.ts +1 -0
@@ -0,0 +1,250 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import jwt from 'jsonwebtoken';
7
+ import type express from 'express';
8
+ import type http from 'http';
9
+
10
+ // Core
11
+ import { Application } from '@server/app';
12
+ import Service from '@server/app/service';
13
+ import type { TAnyRouter } from '@server/services/router';
14
+ import {
15
+ default as Router, Request as ServerRequest,
16
+ } from '@server/services/router';
17
+ import { InputError, AuthRequired, Forbidden } from '@common/errors';
18
+
19
+ /*----------------------------------
20
+ - TYPES
21
+ ----------------------------------*/
22
+
23
+ export type TUserRole = typeof UserRoles[number]
24
+
25
+ export type THttpRequest = express.Request | http.IncomingMessage;
26
+
27
+ /*----------------------------------
28
+ - CONFIG
29
+ ----------------------------------*/
30
+
31
+ const LogPrefix = '[auth]'
32
+
33
+ export const UserRoles = ['USER', 'ADMIN', 'TEST', 'DEV'] as const
34
+
35
+ /*----------------------------------
36
+ - SERVICE CONVIG
37
+ ----------------------------------*/
38
+
39
+ export type TConfig = {
40
+ debug: boolean,
41
+ logoutUrl: string,
42
+ jwt: {
43
+ // 2048 bits
44
+ key: string,
45
+ expiration: number,
46
+ },
47
+ }
48
+
49
+ export type THooks = {
50
+
51
+ }
52
+
53
+ export type TBasicUser = {
54
+ type: string,
55
+ name: string | null,
56
+ email: string,
57
+ roles: string[]
58
+ }
59
+
60
+ export type TBasicJwtSession = (
61
+ { apiKey: string }
62
+ |
63
+ { email: string }
64
+ )
65
+
66
+ /*----------------------------------
67
+ - SERVICE
68
+ ----------------------------------*/
69
+ export default abstract class AuthService<
70
+ TUser extends TBasicUser,
71
+ TApplication extends Application,
72
+ TJwtSession extends TBasicJwtSession = TBasicJwtSession,
73
+ TRequest extends ServerRequest<TAnyRouter> = ServerRequest<TAnyRouter>,
74
+ > extends Service<TConfig, THooks, TApplication, TApplication> {
75
+
76
+ //public abstract login( ...args: any[] ): Promise<{ user: TUser, token: string }>;
77
+ public abstract decodeSession( jwt: TJwtSession, req: THttpRequest ): Promise<TUser | null>;
78
+
79
+ // https://beeceptor.com/docs/concepts/authorization-header/#examples
80
+ public async decode( req: THttpRequest, withData: true ): Promise<TUser | null>;
81
+ public async decode( req: THttpRequest, withData?: false ): Promise<TJwtSession | null>;
82
+ public async decode( req: THttpRequest, withData: boolean = false ): Promise<TJwtSession | TUser | null> {
83
+
84
+ this.config.debug && console.log(LogPrefix, 'Decode:', { cookie: req.cookies['authorization'] });
85
+
86
+ // Get auth token
87
+ const authMethod = this.getAuthMethod(req);
88
+ if (authMethod === null)
89
+ return null;
90
+ const { tokenType, token } = authMethod;
91
+
92
+ // Get auth session
93
+ const session = this.getAuthSession(tokenType, token);
94
+ if (session === null)
95
+ return null;
96
+
97
+ // Return email only
98
+ if (!withData) {
99
+ this.config.debug && console.log(LogPrefix, `Auth user successfull. Return email only`);
100
+ return session;
101
+ }
102
+
103
+ // Deserialize full user data
104
+ this.config.debug && console.log(LogPrefix, `Deserialize user`, session);
105
+ const user = await this.decodeSession(session, req);
106
+ if (user === null)
107
+ return null;
108
+
109
+ this.config.debug && console.log(LogPrefix, `Deserialized user:`, user.name);
110
+
111
+ return {
112
+ ...user,
113
+ _token: token
114
+ };
115
+ }
116
+
117
+ private getAuthMethod( req: THttpRequest ): null | { token: string, tokenType?: string } {
118
+
119
+ let token: string | undefined;
120
+ let tokenType: string | undefined;
121
+ if (typeof req.headers['authorization'] === 'string') {
122
+
123
+ ([ tokenType, token ] = req.headers['authorization'].split(' '));
124
+
125
+ } else if (('cookies' in req) && typeof req.cookies['authorization'] === 'string') {
126
+
127
+ token = req.cookies['authorization'];
128
+ tokenType = 'Bearer';
129
+
130
+ } else
131
+ return null;
132
+
133
+ if (token === undefined)
134
+ return null;
135
+
136
+ return { tokenType, token };
137
+ }
138
+
139
+ private getAuthSession( tokenType: string | undefined, token: string ): TJwtSession | null {
140
+
141
+ let session: TJwtSession;
142
+
143
+ // API Key
144
+ if (tokenType === 'Apikey') {
145
+
146
+ const [accountType] = token.split('-');
147
+
148
+ this.config.debug && console.log(LogPrefix, `Auth via API Key`, token);
149
+ session = { accountType, apiKey: token } as TJwtSession;
150
+
151
+ // JWT
152
+ } else if (tokenType === 'Bearer') {
153
+ this.config.debug && console.log(LogPrefix, `Auth via JWT token`, token);
154
+ try {
155
+ session = jwt.verify(token, this.config.jwt.key, {
156
+ maxAge: this.config.jwt.expiration
157
+ });
158
+ } catch (error) {
159
+ console.warn(LogPrefix, "Failed to decode jwt token:", token);
160
+ return null;
161
+ //throw new Forbidden(`The JWT token provided in the Authorization header is invalid`);
162
+ }
163
+ } else
164
+ return null;
165
+ //throw new InputError(`The authorization scheme provided in the Authorization header is unsupported.`);
166
+
167
+ return session;
168
+ }
169
+
170
+ public createSession( session: TJwtSession, request: TRequest ): string {
171
+
172
+ this.config.debug && console.info(LogPrefix, `Creating new session:`, session);
173
+
174
+ const token = jwt.sign(session, this.config.jwt.key);
175
+
176
+ this.config.debug && console.info(LogPrefix, `Generated JWT token for session:` + token);
177
+
178
+ request.res.cookie('authorization', token, {
179
+ maxAge: this.config.jwt.expiration,
180
+ });
181
+
182
+ return token;
183
+ }
184
+
185
+ public logout( request: TRequest ) {
186
+
187
+ const user = request.user;
188
+ if (!user) return;
189
+
190
+ this.config.debug && console.info(LogPrefix, `Logout ${user.name}`);
191
+ request.res.clearCookie('authorization');
192
+ }
193
+
194
+ public check(
195
+ request: TRequest,
196
+ role: TUserRole,
197
+ motivation?: string,
198
+ dataForDebug?: { [key: string]: any }
199
+ ): TUser;
200
+
201
+ public check(
202
+ request: TRequest,
203
+ role: false,
204
+ motivation?: string,
205
+ dataForDebug?: { [key: string]: any }
206
+ ): null;
207
+
208
+ public check(
209
+ request: TRequest,
210
+ role: TUserRole | false = 'USER',
211
+ motivation?: string,
212
+ dataForDebug?: { [key: string]: any }
213
+ ): TUser | null {
214
+
215
+ const user = request.user;
216
+
217
+ this.config.debug && console.warn(LogPrefix, `Check auth, role = ${role}. Current user =`, user?.name, motivation);
218
+
219
+ if (user === undefined) {
220
+
221
+ throw new Error(`request.user has not been decoded.`);
222
+
223
+ // Shoudln't be logged
224
+ } else if (role === false) {
225
+
226
+ return user;
227
+
228
+ // Not connected
229
+ } else if (user === null) {
230
+
231
+ console.warn(LogPrefix, "Refusé pour anonyme (" + request.ip + ")");
232
+ throw new AuthRequired('Please login to continue', motivation, dataForDebug);
233
+
234
+ // Insufficient permissions
235
+ } else if (!user.roles.includes(role)) {
236
+
237
+ console.warn(LogPrefix, "Refusé: " + role + " pour " + user.name + " (" + (user.roles || 'role inconnu') + ")");
238
+
239
+ throw new Forbidden("You do not have sufficient permissions to access this resource.");
240
+
241
+ } else {
242
+
243
+ this.config.debug && console.warn(LogPrefix, "Autorisé " + role + " pour " + user.name + " (" + user.roles + ")");
244
+
245
+ }
246
+
247
+ return user;
248
+ }
249
+
250
+ }
@@ -0,0 +1,277 @@
1
+
2
+ import md5 from 'md5';
3
+ import { OAuth2Client, LoginTicket } from 'google-auth-library';
4
+
5
+
6
+
7
+ type AuthResponse = {
8
+ token: string,
9
+ redirect: string,
10
+ user: User
11
+ }
12
+
13
+ export default class {
14
+
15
+
16
+ protected async ready() {
17
+
18
+ // Google auth client
19
+ if (this.config.google) {
20
+
21
+ const httpConfig = this.app.http.publicUrl;
22
+
23
+ this.googleClient = new OAuth2Client(
24
+ this.config.google.web.clientId, // Google Client ID
25
+ this.config.google.web.secret, // Private key
26
+ httpConfig + "/auth/google/response" // Redirect url
27
+ );
28
+ }
29
+ }
30
+
31
+
32
+ private googleClient: OAuth2Client | undefined;
33
+
34
+ public async FromGoogle(request: ServerRequest): Promise<string> {
35
+
36
+ if (!this.googleClient)
37
+ throw new Forbidden(`Authentication method disabled.`);
38
+
39
+ // Register start time, so we can determine the signup time to display
40
+ request.response?.cookie('signupstart', Date.now());
41
+
42
+ // Google auth doesn't support local env
43
+ // So we simulate that google sent a response with the user email
44
+ if (app.env.name === 'local') {
45
+ const { redirect } = await this.Auth("tbsoftwares23@gmail.com", request, true);
46
+ return redirect;
47
+ }
48
+
49
+ return this.googleClient.generateAuthUrl({
50
+ access_type: 'offline',
51
+ scope: [
52
+ "email", "profile"
53
+ ]
54
+ });
55
+
56
+ }
57
+
58
+ public async GoogleResponse(
59
+ type: 'code' | 'token',
60
+ codeOrToken: string | undefined,
61
+ request: ServerRequest,
62
+ ): Promise<AuthResponse> {
63
+
64
+ const googleConfig = this.config.google;
65
+ if (!this.googleClient || !googleConfig)
66
+ throw new Forbidden(`Authentication method disabled.`);
67
+
68
+ if (codeOrToken === undefined)
69
+ throw new Forbidden("Bad code / token");
70
+
71
+ if (type === 'code') {
72
+ const r = await this.googleClient.getToken(codeOrToken);
73
+ return this.GoogleResponse('token', r.tokens.id_token, request);
74
+ }
75
+
76
+ this.config.debug && console.log(LogPrefix, "Auth via google", googleConfig);
77
+
78
+ let ticket: LoginTicket;
79
+ try {
80
+ ticket = await this.googleClient.verifyIdToken({
81
+ idToken: codeOrToken,
82
+ audience: [
83
+ googleConfig.web.clientId,
84
+ googleConfig.android.clientId,
85
+ ]
86
+ });
87
+ } catch (error) {
88
+ throw new Forbidden(`Google denied your login attempt: ` + error.message + `. If you don't think it's normal, please contact us.`);
89
+ }
90
+
91
+ const payload = ticket.getPayload();
92
+ if (payload === undefined)
93
+ throw new Forbidden("Invalid payload");
94
+ const { email, sub: google_id } = payload;
95
+
96
+ if (email === undefined)
97
+ throw new Forbidden("Unable to get your email address from the Google sign-in.");
98
+
99
+ return await this.Auth(email, request, true);
100
+
101
+ }
102
+
103
+ /**
104
+ * Login u
105
+ * @param email Email that identifies the account to log in
106
+ * @param request Used to call security-related features
107
+ * @param canPass true when from 3rd party auth, we're sure the email is owned bu the current user
108
+ * @param userInfo User field to update (eg: lastLogin)
109
+ * @returns
110
+ */
111
+ public async Auth(
112
+ email: string,
113
+ request: ServerRequest,
114
+ canPass: boolean = false,
115
+ userInfo: Partial<User> = {}
116
+ ): Promise<AuthResponse> {
117
+
118
+ let user = await this.getData('email = ' + this.sql.esc(email));
119
+ let ip: IP;
120
+ let redirect: string;
121
+ if (!user) { // Signup
122
+
123
+ ip = await request.detect.botsAndMultiaccount();
124
+
125
+ // Create user
126
+ ({ user, redirect } = await this.Signup(email, request));
127
+
128
+ } else if (!canPass) { // Send login email
129
+
130
+ throw new Forbidden("This option is not available");
131
+
132
+ // If the current IP was used to connect to another account that the current
133
+ ip = await request.detect.botsAndMultiaccount(user.email);
134
+
135
+ // Send email with link to /auth/token (expiration 5 min)
136
+
137
+ // Route /auth/:token: check si même ip, même device
138
+
139
+ } else { // Login
140
+
141
+ redirect = config.logoutUrl;
142
+
143
+ }
144
+
145
+ /*await this.sql`
146
+ INSERT INTO UserLogin SET
147
+ user = ${user.email},
148
+ date = NOW(),
149
+ ip = ${request.ip},
150
+ device = ${request.deviceString()}
151
+ `.run();*/
152
+
153
+ const token = request.auth.login(user.email);
154
+
155
+ return { token, user, redirect };
156
+
157
+ }
158
+
159
+ public async Signup(email: string, request: ServerRequest, moreInfos: Partial<User> = {}) {
160
+
161
+ let username = email.split('@')[0];
162
+
163
+ // Prefix username if alreasy existing
164
+ const duplicates = await this.sql`
165
+ FROM User
166
+ WHERE name REGEXP CONCAT('^', ${username}, '[0-9]*$');
167
+ `.count();
168
+
169
+ if (duplicates !== 0)
170
+ username += duplicates;
171
+
172
+ const user: Partial<User> = {
173
+ email,
174
+ emailHash: md5(email),
175
+ name: username,
176
+ referrer: request.cookies.r,
177
+ utm: request.cookies.utm,
178
+ ...moreInfos
179
+ }
180
+
181
+ // Hook
182
+ if (this.beforeSignup !== undefined)
183
+ await this.beforeSignup( user );
184
+
185
+ // Referrer
186
+ if (user.referrer !== undefined) {
187
+
188
+ const refExists = await this.sql`FROM User WHERE name = ${user.referrer}`.exists();
189
+ if (!refExists)
190
+ user.referrer = undefined;
191
+ else {
192
+
193
+ await this.sql.upsert('UserStats', {
194
+ user: user.referrer,
195
+ date: new Date,
196
+ refSignups: 1
197
+ }, ['refSignups'], {
198
+ upsertMode: 'increment'
199
+ });
200
+ }
201
+ }
202
+
203
+ // Create user
204
+ await this.sql.insert('User', user);
205
+ await this.sql.update('logs.IP', { user_name: username }, { address: request.ip });
206
+
207
+ // Hook
208
+ let redirect: string = '/';
209
+ if (this.afterSignup !== undefined)
210
+ ({ redirect } = await this.afterSignup(user));
211
+
212
+ // TODO: download user avatar from gravatar
213
+ // remove user.emailHash
214
+
215
+ // Notif email
216
+ await this.email.send({
217
+ to: app.identity.author.email,
218
+ subject: app.identity.name + ": New User",
219
+ html: JSON.stringify(user)
220
+ });
221
+
222
+ // TODO
223
+ // this.hook.signup(user);
224
+ //Achievements.activity(user, 'just signed up', null);
225
+
226
+ // Instanciation
227
+ return { user, redirect };
228
+
229
+ }
230
+
231
+ public async setReferrer( referrer: unknown, { user, response, cookies, req, detect, request }: ServerRequest) {
232
+
233
+ if (!response || user)
234
+ return;
235
+
236
+ // Source tracking
237
+ if (!cookies.utm) {
238
+
239
+ const data = req.query;
240
+
241
+ const utm = [data.utm_medium, data.utm_source, data.utm_campaign]
242
+ .filter(a => a !== undefined);
243
+
244
+ response.cookie('utm', utm.join(','));
245
+ }
246
+
247
+ // Referral program
248
+ if (cookies.r === undefined && typeof referrer === "string") {
249
+
250
+ const conflict = await detect.conflictOfInterest( referrer, request );
251
+ if (conflict)
252
+ return;
253
+
254
+ // Check if user exists
255
+ const referrerExists = await this.sql`FROM User WHERE name = ${referrer}`.count();
256
+ if (referrerExists === 0)
257
+ return;
258
+
259
+ // await secutity.detectCheatAttempts();
260
+ // => Check IP (reprendre code existant)
261
+ // => Check si IP pas déjà utilisée par un autre membre (1 compte par IP)
262
+
263
+ // Tracking cookie for signup
264
+ response.cookie('r', referrer);
265
+
266
+ // Count the clic
267
+ await this.sql.upsert('UserStats', {
268
+ user: referrer,
269
+ date: new Date,
270
+ refClics: 1
271
+ }, ['refClics'], {
272
+ upsertMode: 'increment'
273
+ });
274
+ }
275
+
276
+ }
277
+ }
@@ -0,0 +1,95 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+
7
+ // Core
8
+ import {
9
+ default as Router, Request as ServerRequest, Response as ServerResponse, TAnyRoute,
10
+ RouterService, TAnyRouter
11
+ } from '@server/services/router';
12
+
13
+ import type { Application } from '@server/app';
14
+
15
+ import type { TRouterServiceArgs } from '@server/services/router/service';
16
+
17
+ // Specific
18
+ import type { default as UsersService, TUserRole, TBasicUser } from '..';
19
+ import UsersRequestService from './request';
20
+
21
+ /*----------------------------------
22
+ - TYPES
23
+ ----------------------------------*/
24
+
25
+
26
+ /*----------------------------------
27
+ - CONFIG
28
+ ----------------------------------*/
29
+
30
+ const LogPrefix = '[router][auth]';
31
+
32
+ /*----------------------------------
33
+ - SERVICE
34
+ ----------------------------------*/
35
+ export default class AuthenticationRouterService<
36
+ TApplication extends Application = Application,
37
+ TUser extends TBasicUser = TApplication["app"]["userType"],
38
+ TRouter extends TAnyRouter = TAnyRouter,
39
+ TRequest extends ServerRequest<TRouter> = ServerRequest<TRouter>,
40
+ > extends RouterService<{}, TRouter> {
41
+
42
+ /*----------------------------------
43
+ - LIFECYCLE
44
+ ----------------------------------*/
45
+
46
+ public users: UsersService<TUser, Application>;
47
+
48
+ public constructor( getConfig: TRouterServiceArgs[0], app: TApplication ) {
49
+ super( getConfig, app );
50
+
51
+ this.users = this.config.users;
52
+ }
53
+
54
+ protected async ready() {
55
+
56
+ // Decode current user
57
+ this.parent.on('request', async (request: TRequest) => {
58
+
59
+ // TODO: Typings. (context.user ?)
60
+ const decoded = await this.users.decode( request.req, true);
61
+
62
+ request.user = decoded || null;
63
+ })
64
+
65
+ // Check route permissions
66
+ this.parent.on('resolved', async (
67
+ route: TAnyRoute,
68
+ request: TRequest,
69
+ response: ServerResponse<TRouter>
70
+ ) => {
71
+
72
+ if (route.options.auth !== undefined) {
73
+
74
+ // Basic auth check
75
+ this.users.check(request, route.options.auth);
76
+
77
+ // Redirect to logged page
78
+ if (route.options.auth === false && request.user && route.options.redirectLogged)
79
+ response.redirect(route.options.redirectLogged);
80
+ }
81
+ })
82
+ }
83
+
84
+ protected async shutdown() {
85
+
86
+ }
87
+
88
+ /*----------------------------------
89
+ - ROUTER SERVICE LIFECYCLE
90
+ ----------------------------------*/
91
+
92
+ public requestService( request: TRequest ): UsersRequestService<TRouter, TUser> {
93
+ return new UsersRequestService( request, this );
94
+ }
95
+ }
@@ -0,0 +1,54 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import jwt from 'jsonwebtoken';
7
+
8
+ // Core
9
+ import type { default as Router, Request as ServerRequest, TAnyRouter } from '@server/services/router';
10
+ import RequestService from '@server/services/router/request/service';
11
+ import { InputError, AuthRequired, Forbidden } from '@common/errors';
12
+
13
+ // Specific
14
+ import type AuthenticationRouterService from '.';
15
+ import type { default as UsersManagementService, TUserRole } from '..';
16
+
17
+ // Types
18
+ import type { TBasicUser } from '@server/services/auth';
19
+
20
+ /*----------------------------------
21
+ - TYPES
22
+ ----------------------------------*/
23
+
24
+ /*----------------------------------
25
+ - MODULE
26
+ ----------------------------------*/
27
+ export default class UsersRequestService<
28
+ TRouter extends TAnyRouter,
29
+ TUser extends TBasicUser
30
+ > extends RequestService {
31
+
32
+ public constructor(
33
+ request: ServerRequest<TRouter>,
34
+ public auth: AuthenticationRouterService,
35
+ public users = auth.users,
36
+ ) {
37
+ super(request);
38
+ }
39
+
40
+ public login( email: string ) {
41
+ return this.users.login( this.request, email );
42
+ }
43
+
44
+ public logout() {
45
+ return this.users.logout( this.request );
46
+ }
47
+
48
+ // TODO: return user type according to entity
49
+ public check(role: TUserRole, motivation?: string, dataForDebug?: {}): TUser;
50
+ public check(role: false, motivation?: string, dataForDebug?: {}): null;
51
+ public check(role: TUserRole | boolean = 'USER', motivation?: string, dataForDebug?: {}): TUser | null {
52
+ return this.users.check( this.request, role, motivation, dataForDebug );
53
+ }
54
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "Core/Users/Router",
3
+ "name": "RouteUser",
4
+ "parent": "router",
5
+ "dependences": []
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "Core/Users",
3
+ "name": "Users",
4
+ "parent": "app",
5
+ "dependences": []
6
+ }