5htp-core 0.6.2 → 0.6.3-2

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 (38) hide show
  1. package/client/app/index.ts +2 -1
  2. package/client/assets/css/components/table.less +1 -0
  3. package/client/components/Input.tsx +0 -2
  4. package/client/components/Rte/Editor.tsx +2 -0
  5. package/client/components/Rte/index.tsx +0 -1
  6. package/client/services/router/index.tsx +1 -1
  7. package/client/services/router/request/api.ts +26 -52
  8. package/common/data/dates.ts +3 -0
  9. package/common/router/request/api.ts +0 -8
  10. package/package.json +1 -1
  11. package/server/app/container/config.ts +43 -4
  12. package/server/app/container/console/index.ts +66 -49
  13. package/server/app/index.ts +19 -8
  14. package/server/app/service/index.ts +55 -15
  15. package/server/services/auth/router/index.ts +8 -4
  16. package/server/services/database/connection.ts +33 -19
  17. package/server/services/disks/driver.ts +5 -1
  18. package/server/services/disks/drivers/s3/index.ts +2 -2
  19. package/server/services/disks/index.ts +10 -5
  20. package/server/services/email/index.ts +1 -1
  21. package/server/services/prisma/Facet.ts +39 -15
  22. package/server/services/prisma/index.ts +5 -7
  23. package/server/services/router/http/multipart.ts +5 -0
  24. package/server/services/router/index.ts +50 -35
  25. package/server/services/router/request/api.ts +0 -12
  26. package/server/services/router/request/validation/zod.ts +180 -0
  27. package/server/services/router/response/index.ts +19 -10
  28. package/server/services/router/response/page/document.tsx +5 -3
  29. package/server/services/router/service.ts +7 -4
  30. package/server/services/schema/request.ts +21 -34
  31. package/server/services/schema/router/index.ts +3 -3
  32. package/types/global/utils.d.ts +22 -4
  33. package/types/icons.d.ts +1 -1
  34. package/common/data/input/validate.ts +0 -54
  35. package/server/services/router/request/validation/index.ts +0 -23
  36. package/server/services/router/request/validation/schema.ts +0 -211
  37. package/server/services/router/request/validation/validator.ts +0 -117
  38. package/server/services/router/request/validation/validators.ts +0 -485
@@ -7,11 +7,15 @@
7
7
  // Core
8
8
  import {
9
9
  default as Router, Request as ServerRequest, Response as ServerResponse, TAnyRoute,
10
- RouterService, TRouterServiceArgs
10
+ RouterService
11
11
  } from '@server/services/router';
12
12
 
13
+ import type { Application } from '@server/app';
14
+
15
+ import type { TRouterServiceArgs } from '@server/services/router/service';
16
+
13
17
  // Specific
14
- import type { default as UsersService, TUserRole } from '..';
18
+ import type { default as UsersService, TUserRole, TBasicUser } from '..';
15
19
  import UsersRequestService from './request';
16
20
 
17
21
  /*----------------------------------
@@ -29,7 +33,7 @@ const LogPrefix = '[router][auth]';
29
33
  - SERVICE
30
34
  ----------------------------------*/
31
35
  export default class AuthenticationRouterService<
32
- TUser extends {} = {},
36
+ TUser extends TBasicUser = TBasicUser,
33
37
  TRequest extends ServerRequest<Router> = ServerRequest<Router>,
34
38
  > extends RouterService {
35
39
 
@@ -37,7 +41,7 @@ export default class AuthenticationRouterService<
37
41
  - LIFECYCLE
38
42
  ----------------------------------*/
39
43
 
40
- public users: UsersService;
44
+ public users: UsersService<TUser, Application>;
41
45
 
42
46
  public constructor(...args: TRouterServiceArgs) {
43
47
  super(...args);
@@ -36,7 +36,6 @@ type ConnectionConfig = {
36
36
 
37
37
  export type DatabaseServiceConfig = {
38
38
  debug: boolean,
39
- connections: ConnectionConfig[],
40
39
  connectionsLimit: number
41
40
  }
42
41
 
@@ -66,6 +65,9 @@ export type TQueryResult = TSelectQueryResult;
66
65
  // TODO: specifiy return type of every mysql query type
67
66
  type TSelectQueryResult = any;
68
67
 
68
+ const DATABASE_URL = process.env.DATABASE_URL;
69
+ if (!DATABASE_URL) throw new Error("Missing env var: DATABASE_URL");
70
+
69
71
  /*----------------------------------
70
72
  - SERVICES
71
73
  ----------------------------------*/
@@ -99,14 +101,12 @@ export default class DatabaseManager {
99
101
 
100
102
  // Try to connect to one of the databases
101
103
  const connectionErrors: string[] = []
102
- for (const connectionConfig of this.config.connections){
103
- try {
104
- await this.connect(connectionConfig)
105
- break;
106
- } catch (error) {
107
- this.config.debug && console.warn(LogPrefix, `Failed to connect to ${connectionConfig.name}: ` + error);
108
- connectionErrors.push(connectionConfig.name + ': ' + error);
109
- }
104
+
105
+ try {
106
+ await this.connect()
107
+ } catch (error) {
108
+ this.config.debug && console.warn(LogPrefix, `Failed to connect to database: ` + error);
109
+ connectionErrors.push('database: ' + error);
110
110
  }
111
111
 
112
112
  // Coudnt connect to any database
@@ -127,17 +127,31 @@ export default class DatabaseManager {
127
127
  /*----------------------------------
128
128
  - INIT
129
129
  ----------------------------------*/
130
- public async connect(config: ConnectionConfig) {
130
+ public async connect() {
131
+
132
+
133
+ const u = new URL(DATABASE_URL as string);
134
+ const connectionConfig = {
135
+ name: u.hostname,
136
+ databases: [u.pathname.replace(/^\//, "")],
137
+ host: u.hostname,
138
+ port: u.port ? Number(u.port) : 3306,
139
+ login: decodeURIComponent(u.username),
140
+ password: decodeURIComponent(u.password),
141
+ }
142
+
143
+ console.log('connectionConfig', connectionConfig);
144
+
131
145
 
132
- this.config.debug && console.info(LogPrefix, `Trying to connect to ${config.name} ...`);
146
+ this.config.debug && console.info(LogPrefix, `Trying to connect to database...`, connectionConfig);
133
147
  this.connection = mysql.createPool({
134
148
 
135
149
  // Identification
136
- host: config.host,
137
- port: config.port,
138
- user: config.login,
139
- password: config.password,
140
- database: config.databases[0],
150
+ host: connectionConfig.host,
151
+ port: connectionConfig.port,
152
+ user: connectionConfig.login,
153
+ password: connectionConfig.password,
154
+ database: connectionConfig.databases[0],
141
155
 
142
156
  // Pool
143
157
  waitForConnections: true,
@@ -164,10 +178,10 @@ export default class DatabaseManager {
164
178
  }
165
179
  })
166
180
 
167
- this.tables = await this.metas.load( config.databases );
168
- this.connectionConfig = config; // Memorise configuration if connection succeed
181
+ this.tables = await this.metas.load(connectionConfig.databases);
182
+ this.connectionConfig = connectionConfig; // Memorise configuration if connection succeed
169
183
  this.status = 'connected';
170
- this.config.debug && console.info(LogPrefix, `Successfully connected to ${config.name}.`);
184
+ this.config.debug && console.info(LogPrefix, `Successfully connected to database.`);
171
185
  }
172
186
 
173
187
  private typeCast( field: mysql.Field, next: Function ) {
@@ -56,7 +56,11 @@ export type TReadFileOptions = {
56
56
  export default abstract class FsDriver<
57
57
  Config extends TDrivercnfig = TDrivercnfig,
58
58
  TBucketName = keyof Config["buckets"]
59
- > extends Service<Config, {}, Application> {
59
+ > extends Service<Config, {}, Application, Application> {
60
+
61
+ public constructor( config: Config, app: Application ) {
62
+ super(app, config, app);
63
+ }
60
64
 
61
65
  public abstract mount(): Promise<void>;
62
66
 
@@ -50,9 +50,9 @@ export default class S3Driver<
50
50
 
51
51
  public s3: AWS.S3;
52
52
 
53
- public constructor(...args: TServiceArgs) {
53
+ public constructor( config: TConfig, app: Application ) {
54
54
 
55
- super(...args);
55
+ super(config, app);
56
56
 
57
57
  AWS.config.update({
58
58
  accessKeyId: this.config.accessKeyId,
@@ -34,10 +34,10 @@ export type Services = {
34
34
  - SERVICE
35
35
  ----------------------------------*/
36
36
  export default class DisksManager<
37
- MountpointList extends Services = {},
38
- TConfig extends Config = Config,
39
- TApplication extends Application = Application
40
- > extends Service<TConfig, Hooks, TApplication> {
37
+ MountpointList extends Services,
38
+ TConfig extends Config,
39
+ TApplication extends Application
40
+ > extends Service<TConfig, Hooks, TApplication, TApplication> {
41
41
 
42
42
  public default!: Driver;
43
43
 
@@ -45,7 +45,7 @@ export default class DisksManager<
45
45
  - LIFECYCLE
46
46
  ----------------------------------*/
47
47
 
48
- public constructor( ...args: TServiceArgs<DisksManager>) {
48
+ public constructor( ...args: TServiceArgs<DisksManager<MountpointList, TConfig, TApplication>>) {
49
49
 
50
50
  super(...args);
51
51
 
@@ -54,6 +54,11 @@ export default class DisksManager<
54
54
  if (Object.keys( drivers ).length === 0)
55
55
  throw new Error("At least one disk driver should be mounted.");
56
56
 
57
+ // Bind current instance of the service as parent
58
+ /*for (const driverId in drivers) {
59
+ drivers[driverId].parent = this;
60
+ }*/
61
+
57
62
  const defaultDisk = drivers[ this.config.default ];
58
63
  if (defaultDisk === undefined)
59
64
  console.log(`Default disk "${this.config.default as string}" not mounted.`);
@@ -174,7 +174,7 @@ export default abstract class Email<TConfig extends Config>
174
174
  console.info(LogPrefix, `Sending ${emailsToSend.length} emails:`, emailsToSend[0].subject);
175
175
 
176
176
  // Pas d'envoi d'email quand local
177
- if (this.app.env.name === 'local' && this.config.simulateWhenLocal === true) {
177
+ if (this.app.env.profile !== 'prod' && this.config.simulateWhenLocal === true) {
178
178
  console.log(LogPrefix, `Simulate email sending:\n`, emailsToSend[0].html);
179
179
  return;
180
180
  } else if (emailsToSend.length === 0) {
@@ -1,6 +1,34 @@
1
1
  import type { Prisma, PrismaClient } from '@models/types';
2
2
  import * as runtime from '@/var/prisma/runtime/library.js';
3
3
 
4
+ /*export type TDelegate<R> = {
5
+ findMany(args?: any): Promise<R[]>
6
+ findFirst(args?: any): Promise<R | null>
7
+ }*/
8
+
9
+ /*
10
+
11
+ */
12
+
13
+
14
+ export type TDelegate<R> = PrismaClient[string];
15
+
16
+ /*export type TExtractPayload<D extends TDelegate<never>> =
17
+ D extends { [K in symbol]: { types: { payload: infer P } } } ? P : never;
18
+
19
+ export type TExtractPayload2<D> =
20
+ D extends { [K: symbol]: { types: Prisma.TypeMap<infer E>['model'][infer M] } }
21
+ ? Prisma.TypeMap<E>['model'][M & keyof Prisma.TypeMap<E>['model']]['payload']
22
+ : never;*/
23
+
24
+ export type Transform<S extends TSubset, R, RT> = (
25
+ row: runtime.Types.Result.GetResult<
26
+ Prisma.$ProspectContactLeadPayload,
27
+ ReturnType<S>,
28
+ 'findMany'
29
+ >[number]
30
+ ) => RT
31
+
4
32
  export type TWithStats = {
5
33
  $table: string,
6
34
  $key: string
@@ -13,12 +41,10 @@ export type TSubset = (...a: any[]) => Prisma.ProspectContactLeadFindFirstArgs &
13
41
  }
14
42
 
15
43
  export default class Facet<
16
- D extends {
17
- findMany(args?: any): Promise<any>
18
- findFirst(args?: any): Promise<any>
19
- },
44
+ D extends TDelegate<R>,
20
45
  S extends TSubset,
21
- R
46
+ R, // Result type
47
+ RT // Transformed result type
22
48
  > {
23
49
  constructor(
24
50
 
@@ -28,22 +54,18 @@ export default class Facet<
28
54
  private readonly subset: S,
29
55
 
30
56
  /* the **ONLY** line that changed ↓↓↓ */
31
- private readonly transform?: (
32
- row: runtime.Types.Result.GetResult<
33
- Prisma.$ProspectContactLeadPayload,
34
- ReturnType<S>,
35
- 'findMany'
36
- >[number]
37
- ) => R,
57
+ private readonly transform?: Transform<S, R, RT>,
38
58
  ) { }
39
59
 
40
60
  public async findMany(
41
61
  ...args: Parameters<S>
42
- ): Promise<R[]> {
62
+ ): Promise<RT[]> {
43
63
 
44
64
  const { withStats, ...subset } = this.subset(...args);
45
65
 
46
66
  const results = await this.delegate.findMany(subset);
67
+ if (results.length === 0)
68
+ return [];
47
69
 
48
70
  // Load stats
49
71
  const stats = withStats
@@ -55,17 +77,19 @@ export default class Facet<
55
77
 
56
78
  public async findFirst(
57
79
  ...args: Parameters<S>
58
- ): Promise<R | null> {
80
+ ): Promise<RT | null> {
59
81
 
60
82
  const { withStats, ...subset } = this.subset(...args);
61
83
 
62
84
  const result = await this.delegate.findFirst(subset);
85
+ if (!result)
86
+ return null;
63
87
 
64
88
  const stats = withStats
65
89
  ? await this.fetchStats( withStats, [result] )
66
90
  : [];
67
91
 
68
- return result ? this.transformResult(result, stats, withStats) : null;
92
+ return this.transformResult(result, stats, withStats);
69
93
  }
70
94
 
71
95
  private async fetchStats(
@@ -10,7 +10,7 @@ import type { Application } from '@server/app';
10
10
  import Service from '@server/app/service';
11
11
 
12
12
  // Specific
13
- import Facet, { TSubset } from './Facet';
13
+ import Facet, { TDelegate, TSubset, Transform } from './Facet';
14
14
 
15
15
  /*----------------------------------
16
16
  - TYPES
@@ -52,13 +52,11 @@ export default class ModelsManager extends Service<Config, Hooks, Application> {
52
52
  }
53
53
 
54
54
  public Facet<
55
- D extends {
56
- findMany(args?: any): Promise<any>
57
- findFirst(args?: any): Promise<any>
58
- },
55
+ D extends TDelegate<R>,
59
56
  S extends TSubset,
60
- R
61
- >(...args: [D, S, R]) {
57
+ R,
58
+ RT
59
+ >(...args: [D, S, Transform<S, R, RT>]) {
62
60
 
63
61
  return new Facet(
64
62
  this.client,
@@ -81,11 +81,16 @@ export const traiterMultipart = (...canaux: any[]) => {
81
81
  &&
82
82
  donnee.data instanceof Buffer
83
83
  ){
84
+ const md5 = donnee.md5;
85
+ const data = donnee.data;
84
86
  donnee = new File(donnee.data, donnee.name, {
85
87
  type: donnee.mimetype,
86
88
  lastModified: Date.now(),
87
89
  //size: donnee.size,
88
90
  });
91
+
92
+ donnee.md5 = md5;
93
+ donnee.data = data;
89
94
  }
90
95
 
91
96
  brancheA[ cle ] = donnee;
@@ -55,7 +55,7 @@ export type { default as Request, UploadedFile } from "./request";
55
55
  export type { default as Response, TRouterContext } from "./response";
56
56
  export type { TRoute, TAnyRoute } from '@common/router';
57
57
 
58
- export type TApiRegisterArgs<TRouter extends ServerRouter> = ([
58
+ export type TApiRegisterArgs<TRouter extends TServerRouter> = ([
59
59
  path: string,
60
60
  controller: TServerController<TRouter>
61
61
  ] | [
@@ -64,7 +64,7 @@ export type TApiRegisterArgs<TRouter extends ServerRouter> = ([
64
64
  controller: TServerController<TRouter>
65
65
  ])
66
66
 
67
- export type TServerController<TRouter extends ServerRouter> = (context: TRouterContext<TRouter>) => any;
67
+ export type TServerController<TRouter extends TServerRouter> = (context: TRouterContext<TRouter>) => any;
68
68
 
69
69
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
70
70
  export type TRouteHttpMethod = HttpMethod | '*';
@@ -83,8 +83,7 @@ export type HttpHeaders = { [cle: string]: string }
83
83
  const LogPrefix = '[router]';
84
84
 
85
85
  export type Config<
86
- TServiceList extends TRouterServicesList = TRouterServicesList,
87
- TAdditionnalSsrData extends {} = {}
86
+ TServices extends TRouterServicesList
88
87
  > = {
89
88
 
90
89
  debug: boolean,
@@ -96,18 +95,16 @@ export type Config<
96
95
  http: HttpServiceConfig,
97
96
 
98
97
  context: (
99
- request: ServerRequest<ServerRouter>,
98
+ request: ServerRequest<TServerRouter>,
100
99
  app: Application
101
- ) => TAdditionnalSsrData,
100
+ ) => {},
102
101
 
103
- plugins: {
104
- [routerServiceId: string]: RouterService
105
- }
102
+ plugins: TServices
106
103
  }
107
104
 
108
105
  // Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
109
106
  type TRouterServicesList = {
110
- [serviceName: string]: RouterService<ServerRouter>
107
+ [serviceName: string]: RouterService
111
108
  }
112
109
 
113
110
  export type Hooks = {
@@ -117,14 +114,20 @@ export type Hooks = {
117
114
  export type TControllerDefinition = {
118
115
  path?: string,
119
116
  schema?: zod.ZodSchema,
120
- controller: TServerController<ServerRouter>,
117
+ controller: TServerController<TServerRouter>,
121
118
  }
122
119
 
120
+ export type TServerRouter = ServerRouter<Application, TRouterServicesList, Config<TRouterServicesList>>;
121
+
123
122
  /*----------------------------------
124
123
  - CLASSE
125
124
  ----------------------------------*/
126
- export default class ServerRouter
127
- extends Service<Config, Hooks, Application> implements BaseRouter {
125
+ export default class ServerRouter<
126
+ TApplication extends Application,
127
+ TServices extends TRouterServicesList,
128
+ TConfig extends Config<TServices>,
129
+ >
130
+ extends Service<TConfig, Hooks, TApplication, TApplication> implements BaseRouter {
128
131
 
129
132
  public disks = this.use<DisksManager>('Core/Disks', { optional: true });
130
133
 
@@ -151,7 +154,7 @@ export default class ServerRouter
151
154
  - SERVICE
152
155
  ----------------------------------*/
153
156
 
154
- public constructor( ...args: TServiceArgs<ServerRouter>) {
157
+ public constructor( ...args: TServiceArgs< ServerRouter<TApplication, TServices, TConfig> >) {
155
158
 
156
159
  super(...args);
157
160
 
@@ -165,14 +168,11 @@ export default class ServerRouter
165
168
 
166
169
  public async ready() {
167
170
 
168
- // Every hours
169
- setInterval(() => {
170
- this.refreshStaticPages();
171
- }, 1000 * 60 * 60);
172
-
173
171
  // Detect router services
174
172
  for (const serviceName in this.config.plugins) {
175
- this.app.register( this.config.plugins[serviceName] )
173
+ const service = this.config.plugins[serviceName];
174
+ service.parent = this;
175
+ this.app.register( service )
176
176
  }
177
177
 
178
178
  // Use require to avoid circular references
@@ -211,6 +211,12 @@ export default class ServerRouter
211
211
  );
212
212
  };
213
213
 
214
+
215
+ // When all the services are ready, initialize static routes
216
+ this.app.on('ready', () => {
217
+ this.initStaticRoutes();
218
+ });
219
+
214
220
  }
215
221
 
216
222
  public async shutdown() {
@@ -233,6 +239,8 @@ export default class ServerRouter
233
239
 
234
240
  if (!rendered) {
235
241
 
242
+ console.log('[router] renderStatic: url', url);
243
+
236
244
  const fullUrl = this.url(url, {}, true);
237
245
  const response = await got( fullUrl, {
238
246
  method: 'GET',
@@ -261,6 +269,26 @@ export default class ServerRouter
261
269
 
262
270
  }
263
271
 
272
+ private initStaticRoutes() {
273
+
274
+ for (const route of this.routes) {
275
+
276
+ if (!route.options.static)
277
+ continue;
278
+
279
+ // Add to static pages
280
+ // Should be a GET oage that don't take any parameter
281
+ for (const url of route.options.static.urls) {
282
+ this.renderStatic(url, route.options.static);
283
+ }
284
+ }
285
+
286
+ // Every hours, refresh static pages
287
+ setInterval(() => {
288
+ this.refreshStaticPages();
289
+ }, 1000 * 60 * 60);
290
+ }
291
+
264
292
  private refreshStaticPages() {
265
293
 
266
294
  console.log('[router] refreshStaticPages');
@@ -270,15 +298,10 @@ export default class ServerRouter
270
298
  if (page.expire && page.expire < Date.now()) {
271
299
 
272
300
  this.renderStatic(pageUrl, page.options);
273
-
274
301
  }
275
302
  }
276
303
  }
277
304
 
278
-
279
-
280
-
281
-
282
305
  private registerRoutes(defModules: GlobImportedWithMetas<TRouteModule>) {
283
306
  for (const routeModule of defModules) {
284
307
 
@@ -327,14 +350,6 @@ export default class ServerRouter
327
350
 
328
351
  this.routes.push(route);
329
352
 
330
- // Add to static pages
331
- // Should be a GET oage that don't take any parameter
332
- if (options.static) {
333
- for (const url of options.static.urls) {
334
- this.renderStatic(url, options.static);
335
- }
336
- }
337
-
338
353
  return this;
339
354
 
340
355
  }
@@ -370,10 +385,10 @@ export default class ServerRouter
370
385
  req: Request,
371
386
  res: Response,
372
387
  next: NextFunction,
373
- requestContext: TRouterContext
388
+ requestContext: TRouterContext<this>
374
389
  ) => void
375
390
  ) {
376
- return (context: TRouterContext) => new Promise((resolve) => {
391
+ return (context: TRouterContext<this>) => new Promise((resolve) => {
377
392
 
378
393
  context.request.res.on('finish', function() {
379
394
  //console.log('the response has been sent', request.res.statusCode);
@@ -31,18 +31,6 @@ export default class ApiClientRequest extends RequestService implements ApiClien
31
31
  throw new Error("api.fetch shouldn't be called here.");
32
32
  }
33
33
 
34
- public get = <TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions) =>
35
- this.createFetcher<TData>('GET', path, data, opts);
36
-
37
- public post = <TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions) =>
38
- this.createFetcher<TData>('POST', path, data, opts);
39
-
40
- public put = <TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions) =>
41
- this.createFetcher<TData>('PUT', path, data, opts);
42
-
43
- public delete = <TData extends unknown = unknown>(path: string, data?: TObjetDonnees, opts?: TApiFetchOptions) =>
44
- this.createFetcher<TData>('DELETE', path, data, opts);
45
-
46
34
  /*----------------------------------
47
35
  - PLACEHOLDERS
48
36
  ----------------------------------*/