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,90 @@
|
|
|
1
|
+
import hInterval from 'human-interval';
|
|
2
|
+
|
|
3
|
+
type TObjDonneesStats = { [cheminStats: string]: number }
|
|
4
|
+
export type TStat<TDonnees extends TObjDonneesStats> = { date: string } & TDonnees
|
|
5
|
+
export type TTimeStat<TDonnees extends TObjDonneesStats> = { time: number } & TDonnees
|
|
6
|
+
|
|
7
|
+
/*----------------------------------
|
|
8
|
+
- OUTILS DE TRAITEMENT
|
|
9
|
+
----------------------------------*/
|
|
10
|
+
type TRetourStats<TDonnees extends TObjDonneesStats> = {
|
|
11
|
+
graph: TTimeStat<TDonnees>[],
|
|
12
|
+
total: TDonnees,
|
|
13
|
+
|
|
14
|
+
start: Date,
|
|
15
|
+
end: Date,
|
|
16
|
+
interval: number,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const buildStats = <TDonnees extends TObjDonneesStats>(
|
|
20
|
+
periodStr: string,
|
|
21
|
+
intervalStr: string,
|
|
22
|
+
data: TStat<TDonnees>[]
|
|
23
|
+
): TRetourStats<TDonnees> => {
|
|
24
|
+
|
|
25
|
+
// NOTE: On ne génère pas le timestamp via la bdd pour éviter les incohérences de timezone
|
|
26
|
+
|
|
27
|
+
if (!Array.isArray(data)) {
|
|
28
|
+
console.log('data =', data);
|
|
29
|
+
throw new Error(`Stats data must be an array (received ${typeof data}). See console for full provided data.`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const periodTime = hInterval(periodStr);
|
|
33
|
+
if (periodTime === undefined) throw new Error(`Invalid period string`);
|
|
34
|
+
const intervalTime = hInterval(intervalStr);
|
|
35
|
+
if (intervalTime === undefined) throw new Error(`Invalid interval string`);
|
|
36
|
+
|
|
37
|
+
let start = Date.now() - periodTime
|
|
38
|
+
start -= start % intervalTime; // Round start date to the specified interval
|
|
39
|
+
const end = Date.now()
|
|
40
|
+
|
|
41
|
+
const total: TDonnees = {} as TDonnees
|
|
42
|
+
let graph: TTimeStat<TDonnees>[] = []
|
|
43
|
+
if (data.length > 0) {
|
|
44
|
+
|
|
45
|
+
// Group data by time
|
|
46
|
+
const groups: { [timestamp: number]: TTimeStat<TDonnees> } = {};
|
|
47
|
+
for (const { date, ...stats } of data) {
|
|
48
|
+
|
|
49
|
+
if (date === undefined)
|
|
50
|
+
throw new Error(`La date est absente des données statistiques. Est-elle bien spécifiée dans le SELECT ?`);
|
|
51
|
+
|
|
52
|
+
const timeA = new Date(date).getTime();
|
|
53
|
+
groups[timeA] = { time: timeA, ...stats };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Completion
|
|
57
|
+
for (let timeA = start; timeA <= end; timeA += intervalTime) {
|
|
58
|
+
|
|
59
|
+
const stats = { time: timeA, ...total }
|
|
60
|
+
|
|
61
|
+
if (groups[timeA] !== undefined)
|
|
62
|
+
for (const nom in groups[timeA]) {
|
|
63
|
+
|
|
64
|
+
// numeric value only
|
|
65
|
+
if (typeof groups[timeA][nom] !== 'number')
|
|
66
|
+
continue;
|
|
67
|
+
|
|
68
|
+
// Add cumulated value
|
|
69
|
+
stats[nom] = stats[nom] === undefined
|
|
70
|
+
? groups[timeA][nom]
|
|
71
|
+
: stats[nom] + groups[timeA][nom];
|
|
72
|
+
|
|
73
|
+
total[nom] = stats[nom];
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
graph.push(stats);
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
graph,
|
|
84
|
+
total,
|
|
85
|
+
start: new Date(start),
|
|
86
|
+
end: new Date(end),
|
|
87
|
+
interval: intervalTime
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ComponentChild } from 'preact';
|
|
3
|
+
|
|
4
|
+
import type { TJsonLog } from '@server/app/container/console';
|
|
5
|
+
import type ServerRequest from '@server/services/router/request';
|
|
6
|
+
import type { TBasicUser } from '@server/services/auth';
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- TYPES
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
|
|
12
|
+
export type TListeErreursSaisie<TClesDonnees extends string = string> = {[champ in TClesDonnees]: string[]}
|
|
13
|
+
|
|
14
|
+
export type TJsonError = {
|
|
15
|
+
code: number,
|
|
16
|
+
origin?: string,
|
|
17
|
+
message: string,
|
|
18
|
+
// Form fields
|
|
19
|
+
errors?: TListeErreursSaisie
|
|
20
|
+
} & TErrorDetails
|
|
21
|
+
|
|
22
|
+
type TErrorDetails = {
|
|
23
|
+
|
|
24
|
+
// Allow to identify the error catched (ex: displaying custop content, running custom actions, ...)
|
|
25
|
+
id?: string,
|
|
26
|
+
data?: {},
|
|
27
|
+
|
|
28
|
+
cta?: {
|
|
29
|
+
label: string,
|
|
30
|
+
link: string,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// For debugging
|
|
34
|
+
stack?: string,
|
|
35
|
+
origin?: string,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*----------------------------------
|
|
39
|
+
- TYPES: AUTH REQUIRED FEATURE
|
|
40
|
+
----------------------------------*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Global, augmentable feature catalog used to constrain the `feature` argument
|
|
44
|
+
* of `AuthRequired`.
|
|
45
|
+
*
|
|
46
|
+
* Default behavior (no augmentation): `feature` stays a free-form string.
|
|
47
|
+
* App behavior (augmentation provided by the host app): `feature` becomes a
|
|
48
|
+
* curated union of feature keys.
|
|
49
|
+
*/
|
|
50
|
+
declare global {
|
|
51
|
+
interface TAuthRequiredFeatureCatalog {}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type TAuthRequiredFeatureKey = Extract<keyof TAuthRequiredFeatureCatalog, string>;
|
|
55
|
+
type TAuthRequiredFeature = [TAuthRequiredFeatureKey] extends [never]
|
|
56
|
+
? string
|
|
57
|
+
: TAuthRequiredFeatureKey;
|
|
58
|
+
|
|
59
|
+
/*----------------------------------
|
|
60
|
+
- TYPES: BUG REPORT
|
|
61
|
+
----------------------------------*/
|
|
62
|
+
|
|
63
|
+
export type ServerBug = {
|
|
64
|
+
|
|
65
|
+
// Context
|
|
66
|
+
hash: string,
|
|
67
|
+
isDuplicate: boolean,
|
|
68
|
+
date: Date, // Timestamp
|
|
69
|
+
channelType?: string,
|
|
70
|
+
channelId?: string,
|
|
71
|
+
|
|
72
|
+
// User
|
|
73
|
+
user?: TBasicUser | null,
|
|
74
|
+
ip?: string | null,
|
|
75
|
+
|
|
76
|
+
// Request
|
|
77
|
+
request?: {
|
|
78
|
+
method: ServerRequest["method"],
|
|
79
|
+
url: ServerRequest["url"],
|
|
80
|
+
data: ServerRequest["data"],
|
|
81
|
+
validatedData: ServerRequest["validatedData"],
|
|
82
|
+
headers: ServerRequest["headers"],
|
|
83
|
+
cookies: ServerRequest["cookies"],
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Error
|
|
87
|
+
title?: string,
|
|
88
|
+
stacktraces: string[],
|
|
89
|
+
context: object[],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type TCatchedError = Error | CoreError | Anomaly;
|
|
93
|
+
|
|
94
|
+
/*----------------------------------
|
|
95
|
+
- ERREURS
|
|
96
|
+
----------------------------------*/
|
|
97
|
+
export abstract class CoreError extends Error {
|
|
98
|
+
|
|
99
|
+
public static msgDefaut: string;
|
|
100
|
+
|
|
101
|
+
public abstract http: number;
|
|
102
|
+
public title: string = "Uh Oh ...";
|
|
103
|
+
public message: string;
|
|
104
|
+
public details: TErrorDetails = {};
|
|
105
|
+
|
|
106
|
+
// Note: On ne le redéfini pas ici, car déjà présent dans Error
|
|
107
|
+
// La redéfinition reset la valeur du stacktrace
|
|
108
|
+
//public stack?: string;
|
|
109
|
+
|
|
110
|
+
public constructor(message?: string, details?: TErrorDetails) {
|
|
111
|
+
|
|
112
|
+
super(message);
|
|
113
|
+
|
|
114
|
+
this.message = message || (this.constructor as typeof CoreError).msgDefaut;
|
|
115
|
+
this.details = details || {};
|
|
116
|
+
|
|
117
|
+
// Inject stack
|
|
118
|
+
if (details !== undefined)
|
|
119
|
+
this.stack = details.stack;
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public json(): TJsonError {
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
code: this.http,
|
|
127
|
+
message: this.message,
|
|
128
|
+
...this.details
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public toString() {
|
|
133
|
+
return this.message;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public render?(): ComponentChild;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export class InputError extends CoreError {
|
|
140
|
+
public http = 400;
|
|
141
|
+
public title = "Bad Request";
|
|
142
|
+
public static msgDefaut = "Bad Request.";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export class InputErrorSchema extends CoreError {
|
|
146
|
+
|
|
147
|
+
public http = 400;
|
|
148
|
+
public title = "Bad Request";
|
|
149
|
+
public static msgDefaut = "Bad Request.";
|
|
150
|
+
|
|
151
|
+
private static listeToString(liste: TListeErreursSaisie) {
|
|
152
|
+
let chaines: string[] = []
|
|
153
|
+
for (const champ in liste)
|
|
154
|
+
chaines.push(champ + ': ' + liste[champ].join('. '));
|
|
155
|
+
return chaines.join('; ');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public constructor( public errors: TListeErreursSaisie, details?: TErrorDetails) {
|
|
159
|
+
|
|
160
|
+
super( InputErrorSchema.listeToString(errors), details );
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public json(): TJsonError {
|
|
165
|
+
return {
|
|
166
|
+
...super.json(),
|
|
167
|
+
errors: this.errors,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public render(): ComponentChild {
|
|
172
|
+
return (
|
|
173
|
+
<ul class="col al-left">
|
|
174
|
+
{Object.keys(this.errors).map( champ => (
|
|
175
|
+
<li>{champ}: {this.errors[champ].join('. ')}</li>
|
|
176
|
+
))}
|
|
177
|
+
</ul>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export class AuthRequired extends CoreError {
|
|
183
|
+
public http = 401;
|
|
184
|
+
public title = "Authentication Required";
|
|
185
|
+
public static msgDefaut = "Please Login to Continue.";
|
|
186
|
+
|
|
187
|
+
public constructor(message: string, feature?: TAuthRequiredFeature);
|
|
188
|
+
public constructor(message: string, motivation: string | undefined, details: TErrorDetails | undefined);
|
|
189
|
+
public constructor(
|
|
190
|
+
message: string,
|
|
191
|
+
public feature?: string,
|
|
192
|
+
details?: TErrorDetails
|
|
193
|
+
) {
|
|
194
|
+
super(message, details);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public json(): TJsonError & { feature?: string } {
|
|
198
|
+
return {
|
|
199
|
+
...super.json(),
|
|
200
|
+
feature: this.feature,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export class UpgradeRequired extends CoreError {
|
|
206
|
+
public http = 402;
|
|
207
|
+
public title = "Upgrade Required";
|
|
208
|
+
public static msgDefaut = "Please Upgrade to Continue.";
|
|
209
|
+
|
|
210
|
+
public constructor(
|
|
211
|
+
message: string,
|
|
212
|
+
public feature: string,
|
|
213
|
+
details?: TErrorDetails
|
|
214
|
+
) {
|
|
215
|
+
super(message, details);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
public json(): TJsonError & { feature: string } {
|
|
219
|
+
return {
|
|
220
|
+
...super.json(),
|
|
221
|
+
feature: this.feature,
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export class Forbidden extends CoreError {
|
|
227
|
+
public http = 403;
|
|
228
|
+
public title = "Access Denied";
|
|
229
|
+
public static msgDefaut = "You do not have sufficient permissions to access this content.";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export class NotFound extends CoreError {
|
|
233
|
+
public http = 404;
|
|
234
|
+
public title = "Not Found";
|
|
235
|
+
public static msgDefaut = "The resource you asked for was not found.";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export class Gone extends CoreError {
|
|
239
|
+
public http = 410;
|
|
240
|
+
public title = "Gone";
|
|
241
|
+
public static msgDefaut = "The resource you asked for has been removed.";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export class RateLimit extends CoreError {
|
|
245
|
+
public http = 429;
|
|
246
|
+
public title = "You're going too fast";
|
|
247
|
+
public static msgDefaut = "Please slow down a bit and retry again later.";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export class Anomaly extends CoreError {
|
|
251
|
+
|
|
252
|
+
public http = 500;
|
|
253
|
+
public title = "Technical Error";
|
|
254
|
+
public static msgDefaut = "A technical error has occurred. A notification has just been sent to the admin.";
|
|
255
|
+
|
|
256
|
+
public constructor(
|
|
257
|
+
message: string,
|
|
258
|
+
public dataForDebugging?: object,
|
|
259
|
+
public originalError?: Error,
|
|
260
|
+
) {
|
|
261
|
+
super(message);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export class NotAvailable extends CoreError {
|
|
266
|
+
|
|
267
|
+
// TODO: page erreur pour code 503
|
|
268
|
+
public http = 404;
|
|
269
|
+
public title = "Not Available";
|
|
270
|
+
public static msgDefaut = "Sorry, the service is currently not available.";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export class NetworkError extends Error {
|
|
274
|
+
public title = "Network Error";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
export const viaHttpCode = (
|
|
279
|
+
code: number,
|
|
280
|
+
message: string,
|
|
281
|
+
details?: TErrorDetails
|
|
282
|
+
): CoreError => {
|
|
283
|
+
return fromJson({
|
|
284
|
+
code,
|
|
285
|
+
message,
|
|
286
|
+
...details
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export const toJson = (e: Error | CoreError): TJsonError => {
|
|
291
|
+
|
|
292
|
+
if (('json' in e) && typeof e.json === 'function')
|
|
293
|
+
return e.json();
|
|
294
|
+
|
|
295
|
+
const details = ('details' in e)
|
|
296
|
+
? e.details
|
|
297
|
+
: { stack: e.stack };
|
|
298
|
+
|
|
299
|
+
return { code: 500, message: e.message, ...details }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export const fromJson = ({ code, message, ...details }: TJsonError) => {
|
|
303
|
+
|
|
304
|
+
switch (code) {
|
|
305
|
+
case 400:
|
|
306
|
+
if (details.errors)
|
|
307
|
+
return new InputErrorSchema( details.errors, details );
|
|
308
|
+
else
|
|
309
|
+
return new InputError( message, details );
|
|
310
|
+
|
|
311
|
+
case 401: return new AuthRequired( message, (details as any).feature, details );
|
|
312
|
+
|
|
313
|
+
case 402: return new UpgradeRequired( message, details.feature, details );
|
|
314
|
+
|
|
315
|
+
case 403: return new Forbidden( message, details );
|
|
316
|
+
|
|
317
|
+
case 404: return new NotFound( message, details );
|
|
318
|
+
|
|
319
|
+
case 429: return new RateLimit( message, details );
|
|
320
|
+
|
|
321
|
+
default: return new Anomaly( message, details );
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export default CoreError;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import type zod from 'zod';
|
|
7
|
+
|
|
8
|
+
// types
|
|
9
|
+
import type {
|
|
10
|
+
default as ClientRouter,
|
|
11
|
+
TRouterContext as ClientRouterContext,
|
|
12
|
+
TRegisterPageArgs
|
|
13
|
+
} from '@client/services/router';
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
TAnyRouter,
|
|
17
|
+
TRouterContext as ServerRouterContext,
|
|
18
|
+
TRouteHttpMethod
|
|
19
|
+
} from '@server/services/router';
|
|
20
|
+
|
|
21
|
+
import type RouterRequest from './request';
|
|
22
|
+
|
|
23
|
+
import type { TUserRole } from '@server/services/auth';
|
|
24
|
+
|
|
25
|
+
import type { TAppArrowFunction } from '@common/app';
|
|
26
|
+
|
|
27
|
+
// Specfic
|
|
28
|
+
import type { default as Page, TFrontRenderer, TDataProvider } from './response/page';
|
|
29
|
+
|
|
30
|
+
/*----------------------------------
|
|
31
|
+
- TYPES: ROUTES
|
|
32
|
+
----------------------------------*/
|
|
33
|
+
|
|
34
|
+
export type { Layout } from './layouts';
|
|
35
|
+
|
|
36
|
+
export type { default as Request } from './request';
|
|
37
|
+
export type { default as Response } from './response';
|
|
38
|
+
|
|
39
|
+
export type ClientOrServerRouter = ClientRouter | TAnyRouter;
|
|
40
|
+
|
|
41
|
+
export type TRoute<RouterContext extends TClientOrServerContextForPage = TClientOrServerContextForPage> = {
|
|
42
|
+
|
|
43
|
+
// Match
|
|
44
|
+
method: TRouteHttpMethod,
|
|
45
|
+
path: string,
|
|
46
|
+
|
|
47
|
+
// Execute
|
|
48
|
+
schema?: zod.ZodSchema,
|
|
49
|
+
controller: TRouteController<RouterContext>,
|
|
50
|
+
options: TRouteOptions
|
|
51
|
+
} & (
|
|
52
|
+
// With regex
|
|
53
|
+
{
|
|
54
|
+
regex: RegExp,
|
|
55
|
+
keys: (number | string)[],
|
|
56
|
+
}
|
|
57
|
+
// Without regex (for ex: controllers)
|
|
58
|
+
|
|
|
59
|
+
{
|
|
60
|
+
regex?: undefined,
|
|
61
|
+
keys?: undefined,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
export type TErrorRoute<RouterContext extends TClientOrServerContextForPage = TClientOrServerContextForPage> = {
|
|
66
|
+
code,
|
|
67
|
+
controller: TRouteController<RouterContext>,
|
|
68
|
+
options: TRouteOptions
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type TAnyRoute<RouterContext extends TClientOrServerContextForPage = TClientOrServerContextForPage> =
|
|
72
|
+
TRoute<RouterContext> | TErrorRoute<RouterContext>
|
|
73
|
+
|
|
74
|
+
// ClientRouterContext already includes server context
|
|
75
|
+
export type TClientOrServerContext = ClientRouterContext;// | ServerRouterContext;
|
|
76
|
+
|
|
77
|
+
export type TClientOrServerContextForPage = With<TClientOrServerContext, 'page'>
|
|
78
|
+
|
|
79
|
+
export type TRouteController<RouterContext extends TClientOrServerContextForPage = TClientOrServerContextForPage> =
|
|
80
|
+
(context: RouterContext) => /* Page to render */Page | /* Any data (html, json) */Promise<any>
|
|
81
|
+
|
|
82
|
+
export type TRouteOptions = {
|
|
83
|
+
|
|
84
|
+
// Injected by the page plugin
|
|
85
|
+
filepath?: string,
|
|
86
|
+
data?: TDataProvider
|
|
87
|
+
|
|
88
|
+
// Indexing
|
|
89
|
+
bodyId?: string,
|
|
90
|
+
priority: number,
|
|
91
|
+
preload?: boolean,
|
|
92
|
+
|
|
93
|
+
// Resolving
|
|
94
|
+
domain?: string,
|
|
95
|
+
accept?: string,
|
|
96
|
+
raw?: boolean, // true to return raw data
|
|
97
|
+
auth?: TUserRole | boolean,
|
|
98
|
+
redirectLogged?: string, // Redirect to this route if auth: false and user is logged
|
|
99
|
+
|
|
100
|
+
// Rendering
|
|
101
|
+
static?: {
|
|
102
|
+
refresh?: string,
|
|
103
|
+
urls: string[]
|
|
104
|
+
},
|
|
105
|
+
whenStatic?: boolean, // If true, the route is only executed even if the page is cached
|
|
106
|
+
canonicalParams?: string[], // For SEO + unique ID for static cache
|
|
107
|
+
layout?: false | string, // The nale of the layout
|
|
108
|
+
|
|
109
|
+
// To cleanup
|
|
110
|
+
TESTING?: boolean,
|
|
111
|
+
logging?: boolean,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type TRouteModule<TRegisteredRoute = any> = {
|
|
115
|
+
// exporing __register is a way to know we axport a TAppArrowFunction
|
|
116
|
+
__register?: TAppArrowFunction<TRegisteredRoute>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type TDomainsList = {
|
|
120
|
+
[endpointId: string]: string
|
|
121
|
+
} & {
|
|
122
|
+
current: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export const defaultOptions = {
|
|
126
|
+
priority: 0,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/*----------------------------------
|
|
130
|
+
- FUNCTIONS
|
|
131
|
+
----------------------------------*/
|
|
132
|
+
export const buildUrl = (
|
|
133
|
+
path: string,
|
|
134
|
+
params: {[key: string]: any},
|
|
135
|
+
domains: {[alias: string]: string},
|
|
136
|
+
absolute: boolean
|
|
137
|
+
) => {
|
|
138
|
+
|
|
139
|
+
let prefix: string = '';
|
|
140
|
+
|
|
141
|
+
// Relative to domain
|
|
142
|
+
if (path[0] === '/' && absolute)
|
|
143
|
+
prefix = domains.current;
|
|
144
|
+
// Other domains of the project
|
|
145
|
+
else if (path[0] === '@') {
|
|
146
|
+
|
|
147
|
+
// Extract domain ID from path
|
|
148
|
+
let domainId: string;
|
|
149
|
+
let slackPos = path.indexOf('/');
|
|
150
|
+
if (slackPos === -1)
|
|
151
|
+
slackPos = path.length;
|
|
152
|
+
domainId = path.substring(1, slackPos);
|
|
153
|
+
path = path.substring(slackPos);
|
|
154
|
+
|
|
155
|
+
// Get domain
|
|
156
|
+
const domain = domains[ domainId ];
|
|
157
|
+
if (domain === undefined)
|
|
158
|
+
throw new Error("Unknown API endpoint ID: " + domainId);
|
|
159
|
+
|
|
160
|
+
// Return full url
|
|
161
|
+
prefix = domain;
|
|
162
|
+
|
|
163
|
+
// Absolute URL
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Path parapeters
|
|
167
|
+
const searchParams = new URLSearchParams();
|
|
168
|
+
for (const key in params) {
|
|
169
|
+
|
|
170
|
+
// Exclude undefined of empty
|
|
171
|
+
if (!params[key])
|
|
172
|
+
continue;
|
|
173
|
+
// Path placeholder
|
|
174
|
+
else if (path.includes(':' + key))
|
|
175
|
+
path = path.replace(':' + key, params[key]);
|
|
176
|
+
// Query string
|
|
177
|
+
else
|
|
178
|
+
searchParams.set(key, params[key]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Return final url
|
|
182
|
+
return prefix + path + (searchParams.toString() ? '?' + searchParams.toString() : '');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export const matchRoute = (route: TRoute, request: RouterRequest) => {
|
|
186
|
+
|
|
187
|
+
// Match Path
|
|
188
|
+
const match = route.regex.exec(request.path);
|
|
189
|
+
if (!match)
|
|
190
|
+
return false;
|
|
191
|
+
|
|
192
|
+
// Extract URL params
|
|
193
|
+
for (let iKey = 0; iKey < route.keys.length; iKey++) {
|
|
194
|
+
const key = route.keys[iKey];
|
|
195
|
+
const value = match[iKey + 1];
|
|
196
|
+
if (typeof key === 'string' && value) // number = sans nom
|
|
197
|
+
request.data[key] = decodeURIComponent( value.replaceAll('+', '%20') );
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/*----------------------------------
|
|
204
|
+
- BASE ROUTER
|
|
205
|
+
----------------------------------*/
|
|
206
|
+
|
|
207
|
+
export default abstract class RouterInterface {
|
|
208
|
+
|
|
209
|
+
public abstract page<TControllerData extends TObjetDonnees = {}>(...args: TRegisterPageArgs<TControllerData>);
|
|
210
|
+
|
|
211
|
+
public abstract error(code: number, options, renderer: TFrontRenderer<{}, { message: string }>);
|
|
212
|
+
|
|
213
|
+
}
|