5htp-core 0.3.0 → 0.3.1-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.
- package/package.json +2 -2
- package/src/client/components/Select/index.tsx +4 -2
- package/src/client/services/router/index.tsx +22 -1
- package/src/client/services/router/response/index.tsx +1 -1
- package/src/common/errors/index.ts +20 -0
- package/src/common/validation/validators.ts +12 -1
- package/src/server/app/commands.ts +21 -1
- package/src/server/app/container/config.ts +1 -0
- package/src/server/app/container/index.ts +6 -3
- package/src/server/app/index.ts +41 -17
- package/src/server/app/service/container.ts +13 -24
- package/src/server/app/service/index.ts +118 -54
- package/src/server/app.tsconfig.json +2 -0
- package/src/server/index.ts +2 -1
- package/src/server/services/cache/index.ts +6 -4
- package/src/server/services/console/index.ts +7 -24
- package/src/server/services/cron/index.ts +5 -1
- package/src/server/services/database/datatypes.ts +1 -0
- package/src/server/services/database/index.ts +67 -41
- package/src/server/services/disks/driver.ts +5 -1
- package/src/server/services/disks/drivers/s3/index.ts +1 -1
- package/src/server/services/disks/index.ts +16 -13
- package/src/server/services/email/index.ts +7 -6
- package/src/server/services/email/transporter.ts +19 -6
- package/src/server/services/fetch/index.ts +3 -5
- package/src/server/services/router/http/index.ts +1 -1
- package/src/server/services/router/index.ts +10 -11
- package/src/server/services/router/response/index.ts +2 -2
- package/src/server/services/router/service.ts +9 -3
- package/src/server/services/schema/router/index.ts +1 -2
- package/src/server/services/security/encrypt/aes/index.ts +9 -6
- package/src/server/services/socket/index.ts +16 -17
- package/src/server/services/users/index.ts +5 -1
- package/src/server/services/users/router/index.ts +2 -2
- package/src/server/app/instance.ts +0 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.1-2",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -97,6 +97,6 @@
|
|
|
97
97
|
"babel-plugin-glob-import": "^0.0.7"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
|
-
"5htp": "0.3.
|
|
100
|
+
"5htp": "0.3.1"
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
// Core
|
|
9
|
-
import
|
|
9
|
+
import { Props as DropdownProps } from '@client/components/dropdown';
|
|
10
10
|
import Input from '@client/components/inputv3';
|
|
11
11
|
|
|
12
12
|
// Specific
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
14
|
Props as SelectorProps,
|
|
15
15
|
Choice,
|
|
16
16
|
} from './ChoiceSelector';
|
|
@@ -24,6 +24,8 @@ export type Props = DropdownProps & SelectorProps & {
|
|
|
24
24
|
errors?: string[],
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export type { Choice } from './ChoiceSelector';
|
|
28
|
+
|
|
27
29
|
const ChoiceElement = ({ choice, currentList, onChange, multiple, includeCurrent }: {
|
|
28
30
|
choice: Choice,
|
|
29
31
|
currentList: Choice[],
|
|
@@ -40,7 +40,7 @@ import * as appRoutes from '@/client/pages/**/*.tsx';
|
|
|
40
40
|
- CONFIG
|
|
41
41
|
----------------------------------*/
|
|
42
42
|
|
|
43
|
-
const debug =
|
|
43
|
+
const debug = false;
|
|
44
44
|
const LogPrefix = '[router]'
|
|
45
45
|
|
|
46
46
|
/*----------------------------------
|
|
@@ -61,6 +61,8 @@ export type Response = ClientResponse<ClientRouter> | ServerResponse<ServerRoute
|
|
|
61
61
|
- TYPES: ROUTES LOADING
|
|
62
62
|
----------------------------------*/
|
|
63
63
|
|
|
64
|
+
// WARN: To be updated with the mplemenations list of Router.page
|
|
65
|
+
// (both server and client side)
|
|
64
66
|
export type TRegisterPageArgs<TProvidedData extends TFetcherList = {}, TRouter extends Router = Router> = ([
|
|
65
67
|
path: string,
|
|
66
68
|
controller: TDataProvider<TProvidedData> | null,
|
|
@@ -219,6 +221,25 @@ export default class ClientRouter<
|
|
|
219
221
|
return currentRoute;
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
public page(
|
|
225
|
+
path: string,
|
|
226
|
+
controller: TDataProvider<{}> | null,
|
|
227
|
+
renderer: TFrontRenderer<{}>
|
|
228
|
+
): TRoute;
|
|
229
|
+
|
|
230
|
+
public page(
|
|
231
|
+
path: string,
|
|
232
|
+
options: Partial<TRoute["options"]>,
|
|
233
|
+
renderer: TFrontRenderer<{}>
|
|
234
|
+
): TRoute;
|
|
235
|
+
|
|
236
|
+
public page(
|
|
237
|
+
path: string,
|
|
238
|
+
options: Partial<TRoute["options"]>,
|
|
239
|
+
controller: TDataProvider<{}> | null,
|
|
240
|
+
renderer: TFrontRenderer<{}>
|
|
241
|
+
): TRoute;
|
|
242
|
+
|
|
222
243
|
public page(...args: TRegisterPageArgs): TRoute {
|
|
223
244
|
|
|
224
245
|
const { path, options, controller, renderer, layout } = getRegisterPageArgs(...args);
|
|
@@ -16,6 +16,26 @@ type TDetailsErreur = {
|
|
|
16
16
|
urlRequete?: string,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- TYPES: BUG REPORT
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
|
|
23
|
+
export type ServerBug = {
|
|
24
|
+
// Context
|
|
25
|
+
hash: string,
|
|
26
|
+
date: Date, // Timestamp
|
|
27
|
+
channelType?: string,
|
|
28
|
+
channelId?: string,
|
|
29
|
+
|
|
30
|
+
user: string | null | undefined,
|
|
31
|
+
ip: string | null | undefined,
|
|
32
|
+
|
|
33
|
+
// Error
|
|
34
|
+
error: Error,
|
|
35
|
+
stacktrace: string,
|
|
36
|
+
logs: string,
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
/*----------------------------------
|
|
20
40
|
- ERREURS
|
|
21
41
|
----------------------------------*/
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
isURL
|
|
11
11
|
} from 'validator';
|
|
12
12
|
|
|
13
|
+
import normalizeUrl, { Options as NormalizeUrlOptions } from 'normalize-url';
|
|
14
|
+
|
|
13
15
|
// Core
|
|
14
16
|
import { InputError } from '@common/errors';
|
|
15
17
|
import FileToUpload from '@client/components/inputv3/file/FileToUpload';
|
|
@@ -157,16 +159,25 @@ export default class SchemaValidators {
|
|
|
157
159
|
|
|
158
160
|
}, opts)
|
|
159
161
|
|
|
160
|
-
public url = (opts: TValidator<string> & {
|
|
162
|
+
public url = (opts: TValidator<string> & {
|
|
163
|
+
normalize?: NormalizeUrlOptions
|
|
164
|
+
} = {}) =>
|
|
161
165
|
new Validator<string>('url', (inputVal, input, output, corriger?) => {
|
|
162
166
|
|
|
163
167
|
let val = this.string(opts).validate(inputVal, input, output, corriger);
|
|
164
168
|
|
|
169
|
+
// Check if URL
|
|
165
170
|
if (!isURL(val, {
|
|
166
171
|
// https://www.npmjs.com/package/validator
|
|
167
172
|
}))
|
|
168
173
|
throw new InputError(`Please provide a valid URL.`);
|
|
169
174
|
|
|
175
|
+
// Normalize
|
|
176
|
+
if (opts.normalize !== undefined)
|
|
177
|
+
val = normalizeUrl(val, opts.normalize);
|
|
178
|
+
|
|
179
|
+
console.log("@@@@@@@@@@@@@NORMALISZE URL", opts.normalize, val);
|
|
180
|
+
|
|
170
181
|
return val;
|
|
171
182
|
}, opts)
|
|
172
183
|
|
|
@@ -41,15 +41,35 @@ export type Hooks = {
|
|
|
41
41
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
export type Services = {
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
/*----------------------------------
|
|
45
49
|
- SERVICE
|
|
46
50
|
----------------------------------*/
|
|
47
|
-
export default class CommandsManager extends Service<Config, Hooks, Application> {
|
|
51
|
+
export default class CommandsManager extends Service<Config, Hooks, Application, Services> {
|
|
48
52
|
|
|
49
53
|
public priority = 2 as 2;
|
|
50
54
|
|
|
51
55
|
public commandsIndex: CommandsList = {}
|
|
52
56
|
|
|
57
|
+
/*----------------------------------
|
|
58
|
+
- LIFECYCLE
|
|
59
|
+
----------------------------------*/
|
|
60
|
+
|
|
61
|
+
protected async start() {
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected async ready() {
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected async shutdown() {
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
53
73
|
/*----------------------------------
|
|
54
74
|
- DEFINITIONS
|
|
55
75
|
----------------------------------*/
|
|
@@ -8,19 +8,22 @@ import './patch';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
|
|
10
10
|
// Core
|
|
11
|
-
import
|
|
11
|
+
import type { StartedServicesIndex } from '../service';
|
|
12
|
+
import Services, { ServicesContainer } from '../service/container';
|
|
12
13
|
import ConfigParser, { TEnvConfig } from './config';
|
|
13
14
|
|
|
14
15
|
/*----------------------------------
|
|
15
16
|
- CLASS
|
|
16
17
|
----------------------------------*/
|
|
17
|
-
export class ApplicationContainer
|
|
18
|
+
export class ApplicationContainer<
|
|
19
|
+
TServicesIndex extends StartedServicesIndex = StartedServicesIndex
|
|
20
|
+
> {
|
|
18
21
|
|
|
19
22
|
/*----------------------------------
|
|
20
23
|
- INIT
|
|
21
24
|
----------------------------------*/
|
|
22
25
|
|
|
23
|
-
public Services = Services
|
|
26
|
+
public Services = Services as ServicesContainer<TServicesIndex>;
|
|
24
27
|
public Environment: TEnvConfig;
|
|
25
28
|
public Identity: Config.Identity;
|
|
26
29
|
|
package/src/server/app/index.ts
CHANGED
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
// Core
|
|
6
6
|
import AppContainer from './container';
|
|
7
|
-
import ApplicationService, {
|
|
7
|
+
import ApplicationService, { StartedServicesIndex } from './service';
|
|
8
8
|
import CommandsManager from './commands';
|
|
9
|
-
import ServicesContainer, {
|
|
9
|
+
import ServicesContainer, {
|
|
10
|
+
ServicesContainer as ServicesContainerClass,
|
|
11
|
+
TRegisteredService,
|
|
12
|
+
TServiceMetas
|
|
13
|
+
} from './service/container';
|
|
14
|
+
import type { ServerBug } from '../services/console';
|
|
10
15
|
|
|
11
16
|
// Built-in
|
|
12
17
|
import type { default as Router, Request as ServerRequest } from '@server/services/router';
|
|
@@ -48,7 +53,12 @@ export const Service = ServicesContainer;
|
|
|
48
53
|
/*----------------------------------
|
|
49
54
|
- FUNCTIONS
|
|
50
55
|
----------------------------------*/
|
|
51
|
-
export class Application
|
|
56
|
+
export class Application<
|
|
57
|
+
TServicesContainer extends ServicesContainerClass = ServicesContainerClass
|
|
58
|
+
> extends ApplicationService<Config, Hooks, /* TODO: this ? */Application, {}> {
|
|
59
|
+
|
|
60
|
+
public app!: this;
|
|
61
|
+
public servicesContainer!: TServicesContainer;
|
|
52
62
|
|
|
53
63
|
/*----------------------------------
|
|
54
64
|
- PROPERTIES
|
|
@@ -72,8 +82,8 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
|
|
|
72
82
|
public debug: boolean = false;
|
|
73
83
|
public launched: boolean = false;
|
|
74
84
|
|
|
75
|
-
//
|
|
76
|
-
public
|
|
85
|
+
// Mandatory services
|
|
86
|
+
public Console = this.use('Core/Console');
|
|
77
87
|
|
|
78
88
|
/*----------------------------------
|
|
79
89
|
- INIT
|
|
@@ -81,10 +91,14 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
|
|
|
81
91
|
|
|
82
92
|
public constructor() {
|
|
83
93
|
|
|
94
|
+
const self = 'self' as unknown as Application;
|
|
95
|
+
|
|
84
96
|
// Application itself doesnt have configuration
|
|
85
97
|
// Configuration must be handled by application services
|
|
86
|
-
super(
|
|
98
|
+
super(self, {}, {}, self);
|
|
87
99
|
|
|
100
|
+
// We can't pass this in super so we assign here
|
|
101
|
+
this.parent = this;
|
|
88
102
|
this.app = this;
|
|
89
103
|
|
|
90
104
|
// Gestion crash
|
|
@@ -113,9 +127,9 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
|
|
|
113
127
|
console.log(`5HTP Core`, process.env.npm_package_version);
|
|
114
128
|
|
|
115
129
|
// Handle errors & crashs
|
|
116
|
-
this.on('error', this.
|
|
130
|
+
this.on('error', e => this.Console.createBugReport(e))
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
console.info(`[boot] Start services`);
|
|
119
133
|
await this.startServices();
|
|
120
134
|
|
|
121
135
|
this.debug && console.info(`[boot] App ready`);
|
|
@@ -132,8 +146,10 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
|
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
// Default error handler
|
|
135
|
-
public async
|
|
136
|
-
|
|
149
|
+
public async reportBug( bug: ServerBug ) {
|
|
150
|
+
|
|
151
|
+
console.error( bug.error );
|
|
152
|
+
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
public async shutdown() {
|
|
@@ -150,30 +166,38 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
|
|
|
150
166
|
|
|
151
167
|
// Don't check services prop as it will trigger an error (it's a proxy)
|
|
152
168
|
// TODO: exclude all properties coming from the Service class itself
|
|
153
|
-
if (propName === 'services' ||
|
|
169
|
+
if (propName === 'services' || this[ propName ] === undefined)
|
|
154
170
|
continue;
|
|
155
171
|
|
|
156
172
|
// Check if this property is a service registration
|
|
157
|
-
const
|
|
158
|
-
|
|
173
|
+
const service = this[ propName ] as TRegisteredService;
|
|
174
|
+
const isService = (
|
|
175
|
+
service instanceof ApplicationService
|
|
176
|
+
||
|
|
177
|
+
(
|
|
178
|
+
typeof service === 'function'
|
|
179
|
+
&&
|
|
180
|
+
service.serviceInstance instanceof ApplicationService
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
if (!isService)
|
|
159
184
|
continue;
|
|
160
185
|
|
|
161
186
|
// Instanciate the service
|
|
162
|
-
|
|
163
|
-
this[ propName ] = service;
|
|
187
|
+
this.bindService( propName, service );
|
|
164
188
|
|
|
165
189
|
// Register commands
|
|
166
190
|
if (service.commands)
|
|
167
191
|
this.commandsManager.fromList( service.commands );
|
|
168
192
|
|
|
169
193
|
// Start service
|
|
170
|
-
await this.startService( service );
|
|
194
|
+
await this.startService( propName, service );
|
|
171
195
|
}
|
|
172
196
|
|
|
173
197
|
// Check if any setup service has not been used
|
|
174
198
|
const unused: string[] = []
|
|
175
199
|
for (const serviceNS in ServicesContainer.registered)
|
|
176
|
-
if (
|
|
200
|
+
if (ServicesContainer.allServices[ serviceNS ] === undefined)
|
|
177
201
|
unused.push(serviceNS);
|
|
178
202
|
|
|
179
203
|
if (unused.length !== 0)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
// Specific
|
|
6
6
|
import type {
|
|
7
|
-
AnyService,
|
|
7
|
+
AnyService, StartedServicesIndex,
|
|
8
8
|
// Hooks
|
|
9
9
|
THookCallback, THooksIndex
|
|
10
10
|
} from ".";
|
|
@@ -46,13 +46,18 @@ const LogPrefix = '[service]';
|
|
|
46
46
|
/*----------------------------------
|
|
47
47
|
- CLASS
|
|
48
48
|
----------------------------------*/
|
|
49
|
-
export class ServicesContainer
|
|
49
|
+
export class ServicesContainer<
|
|
50
|
+
TServicesIndex extends StartedServicesIndex = StartedServicesIndex
|
|
51
|
+
> {
|
|
50
52
|
|
|
51
53
|
public registered: TRegisteredServicesIndex = {}
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// All service instances by service id
|
|
56
|
+
public allServices: TServicesIndex = {} as TServicesIndex
|
|
57
|
+
|
|
58
|
+
public setup<TServiceId extends keyof TServicesIndex>(
|
|
59
|
+
serviceId: keyof TServicesIndex,
|
|
60
|
+
serviceConfig: TServicesIndex[TServiceId]["config"]
|
|
56
61
|
): TRegisteredService {
|
|
57
62
|
|
|
58
63
|
// Check if the service is installed & has been indexed
|
|
@@ -86,25 +91,6 @@ export class ServicesContainer {
|
|
|
86
91
|
return service;
|
|
87
92
|
}
|
|
88
93
|
|
|
89
|
-
public use(
|
|
90
|
-
serviceId: string,
|
|
91
|
-
// TODO: Only subservices types supported by the parent service
|
|
92
|
-
subServices: TRegisteredServicesIndex = {}
|
|
93
|
-
): TRegisteredService {
|
|
94
|
-
|
|
95
|
-
// Check of the service has been configurated
|
|
96
|
-
const registered = this.registered[ serviceId ];
|
|
97
|
-
if (registered === undefined)
|
|
98
|
-
throw new Error(`Unable to use service "${serviceId}": This one hasn't been setup.`);
|
|
99
|
-
|
|
100
|
-
// Bind subservices
|
|
101
|
-
registered.subServices = subServices;
|
|
102
|
-
|
|
103
|
-
// Return the service metas
|
|
104
|
-
// The parent service will take care of instanciating & starting it
|
|
105
|
-
return registered;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
94
|
public callableInstance = <TInstance extends object, TCallableName extends keyof TInstance>(
|
|
109
95
|
instance: TInstance,
|
|
110
96
|
funcName: TCallableName
|
|
@@ -129,6 +115,9 @@ export class ServicesContainer {
|
|
|
129
115
|
? instance[ method ].bind( instance )
|
|
130
116
|
: instance[ method ];
|
|
131
117
|
|
|
118
|
+
// Allow us to recognize a callable as a service
|
|
119
|
+
callable.serviceInstance = instance;
|
|
120
|
+
|
|
132
121
|
return callable as TInstance[TCallableName] & TInstance;
|
|
133
122
|
}
|
|
134
123
|
}
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
import type { Application } from "..";
|
|
7
7
|
import type { Command } from "../commands";
|
|
8
8
|
import type { TServiceMetas, TRegisteredServicesIndex, TRegisteredService } from './container';
|
|
9
|
+
import ServicesContainer from './container';
|
|
9
10
|
|
|
10
11
|
/*----------------------------------
|
|
11
12
|
- TYPES: OPTIONS
|
|
12
13
|
----------------------------------*/
|
|
13
14
|
|
|
14
|
-
export type AnyService =
|
|
15
|
+
export type AnyService<TSubServices extends StartedServicesIndex = StartedServicesIndex> =
|
|
16
|
+
Service<{}, {}, Application, TSubServices>
|
|
15
17
|
|
|
16
18
|
type TServiceConfig = {
|
|
17
19
|
debug?: boolean
|
|
@@ -39,6 +41,12 @@ export type StartedServicesIndex = {
|
|
|
39
41
|
[serviceId: string]: AnyService
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
type ClassType<T = {}> = new (...args: any[]) => T;
|
|
45
|
+
|
|
46
|
+
type ServiceWithSubservices<TService extends ClassType<AnyService>, TSubservices extends StartedServicesIndex> = {
|
|
47
|
+
new (...args: any[]): TService & { services: TSubservices }
|
|
48
|
+
};
|
|
49
|
+
|
|
42
50
|
/*----------------------------------
|
|
43
51
|
- CONFIG
|
|
44
52
|
----------------------------------*/
|
|
@@ -52,7 +60,7 @@ export default abstract class Service<
|
|
|
52
60
|
TConfig extends TServiceConfig,
|
|
53
61
|
THooks extends THooksList,
|
|
54
62
|
TApplication extends Application,
|
|
55
|
-
TServicesIndex extends StartedServicesIndex
|
|
63
|
+
TServicesIndex extends StartedServicesIndex
|
|
56
64
|
> {
|
|
57
65
|
|
|
58
66
|
public started?: Promise<void>;
|
|
@@ -62,26 +70,34 @@ export default abstract class Service<
|
|
|
62
70
|
public metas!: TServiceMetas;
|
|
63
71
|
public bindings: string[] = []
|
|
64
72
|
|
|
65
|
-
public
|
|
66
|
-
parent: AnyService,
|
|
67
|
-
config: TServiceConfig,
|
|
68
|
-
services: TRegisteredServicesIndex,
|
|
69
|
-
app: Application
|
|
70
|
-
) => Service<TServiceConfig, THooksList, Application, StartedServicesIndex>;
|
|
73
|
+
public app: TApplication;
|
|
71
74
|
|
|
72
75
|
public constructor(
|
|
73
|
-
public parent: AnyService,
|
|
76
|
+
public parent: AnyService | 'self',
|
|
74
77
|
public config: TConfig,
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
// Make this argument appear as instanciated sercices index
|
|
79
|
+
// But actually, Setup.use returns a registered service, not yet launched
|
|
80
|
+
services: TServicesIndex,
|
|
81
|
+
app: TApplication | 'self'
|
|
77
82
|
) {
|
|
78
83
|
|
|
84
|
+
if (this.parent === 'self')
|
|
85
|
+
this.parent = this;
|
|
86
|
+
|
|
87
|
+
this.app = app === 'self'
|
|
88
|
+
? this as unknown as TApplication
|
|
89
|
+
: app
|
|
90
|
+
|
|
79
91
|
// Instanciate subservices
|
|
80
92
|
for (const localName in services)
|
|
81
|
-
this.
|
|
93
|
+
this.bindService( localName, services[localName] as unknown as TRegisteredService );
|
|
82
94
|
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
public getServiceInstance() {
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
85
101
|
/*----------------------------------
|
|
86
102
|
- LIFECYCLE
|
|
87
103
|
----------------------------------*/
|
|
@@ -90,7 +106,7 @@ export default abstract class Service<
|
|
|
90
106
|
|
|
91
107
|
// Instanciate subservices
|
|
92
108
|
for (const localName in this.services)
|
|
93
|
-
await this.startService( this.services[localName] );
|
|
109
|
+
await this.startService( localName, this.services[localName] );
|
|
94
110
|
|
|
95
111
|
// Start service
|
|
96
112
|
if (this.start)
|
|
@@ -112,66 +128,114 @@ export default abstract class Service<
|
|
|
112
128
|
- SUBSERVICES
|
|
113
129
|
----------------------------------*/
|
|
114
130
|
|
|
115
|
-
public
|
|
131
|
+
public registered: TRegisteredServicesIndex = {} as TRegisteredServicesIndex;
|
|
132
|
+
|
|
133
|
+
public services: TServicesIndex = {} as TServicesIndex;
|
|
134
|
+
|
|
135
|
+
/*new Proxy({}, {
|
|
116
136
|
get: (target, prop, recever) => {
|
|
117
137
|
if (!( prop in target )) {
|
|
118
138
|
|
|
119
|
-
throw new Error(`You made reference to the "${prop}" service, but this one hasn't been loaded
|
|
139
|
+
throw new Error(`You made reference to the "${prop}" service, but this one hasn't been loaded or started yet.
|
|
140
|
+
Registered services: ${Object.keys(this.services).join(', ')} ;
|
|
141
|
+
Loaded services: ${Object.keys(this.services).join(', ')}
|
|
142
|
+
`);
|
|
120
143
|
}
|
|
121
144
|
}
|
|
122
145
|
}) as TRegisteredServicesIndex*/
|
|
123
146
|
|
|
124
|
-
|
|
147
|
+
// this.use immediatly instanciate the subservice for few reasons:
|
|
148
|
+
// - The subservice instance can be accesses from another service in the constructor, no matter the order of loading of the services
|
|
149
|
+
// - Consistency: the subserviuce proprties shouldn't be assogned to two different values according depending on the app lifecycle
|
|
150
|
+
public use<
|
|
151
|
+
TInstalledServices extends this["app"]["servicesContainer"]["allServices"],
|
|
152
|
+
TServiceId extends keyof TInstalledServices,
|
|
153
|
+
TServiceClass extends TInstalledServices[TServiceId],
|
|
154
|
+
TSubServices extends TServiceClass["services"],
|
|
155
|
+
>(
|
|
156
|
+
serviceId: TServiceId,
|
|
157
|
+
subServices?: TSubServices
|
|
158
|
+
): (
|
|
159
|
+
{
|
|
160
|
+
new (...args: any[]): TServiceClass & { services: TSubServices }
|
|
161
|
+
}
|
|
162
|
+
/*Omit<TServiceClass, 'services'> & {
|
|
163
|
+
services: TSubServices
|
|
164
|
+
}*/
|
|
165
|
+
) {
|
|
125
166
|
|
|
126
|
-
//
|
|
127
|
-
|
|
167
|
+
// Check of the service has been configurated
|
|
168
|
+
const registered = ServicesContainer.registered[ serviceId ];
|
|
169
|
+
if (registered === undefined)
|
|
170
|
+
throw new Error(`Unable to use service "${serviceId}": This one hasn't been setup.`);
|
|
128
171
|
|
|
129
|
-
//
|
|
130
|
-
|
|
172
|
+
// Bind subservices
|
|
173
|
+
registered.subServices = subServices || {};
|
|
131
174
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
: new ServiceClass(this, registered.config, registered.subServices, this.app);
|
|
175
|
+
// Check if not already instanciated
|
|
176
|
+
const existing = ServicesContainer.allServices[ serviceId ];
|
|
177
|
+
if (existing !== undefined) {
|
|
178
|
+
console.info("Service", serviceId, "already instanciated through another service.");
|
|
179
|
+
return existing;
|
|
180
|
+
}
|
|
139
181
|
|
|
140
|
-
|
|
182
|
+
// Instanciate
|
|
183
|
+
console.log(`[app] Load service`, registered.metas.id);
|
|
184
|
+
const ServiceClass = registered.metas.class().default;
|
|
141
185
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
186
|
+
// Create class instance
|
|
187
|
+
const service = new ServiceClass(this, registered.config, registered.subServices, this.app || this)
|
|
188
|
+
.getServiceInstance()
|
|
145
189
|
|
|
146
|
-
// Bind
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.app.allServices[ registered.metas.id ] = service;
|
|
190
|
+
// Bind his own metas
|
|
191
|
+
service.metas = registered.metas;
|
|
192
|
+
ServicesContainer.allServices[ registered.metas.id ] = service;
|
|
150
193
|
|
|
151
194
|
return service;
|
|
152
|
-
|
|
153
195
|
}
|
|
154
196
|
|
|
155
|
-
|
|
197
|
+
public bindService( localName: string, service: AnyService ) {
|
|
156
198
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
service.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
process.exit();
|
|
169
|
-
else
|
|
170
|
-
throw e;
|
|
171
|
-
})
|
|
199
|
+
const serviceScope = this.constructor.name + '.' + localName;
|
|
200
|
+
|
|
201
|
+
// Fix the parent service (the app could be provided as parent because the dev called this.use() in the app class definition)
|
|
202
|
+
service.parent = this;
|
|
203
|
+
|
|
204
|
+
// Bind subservice to service
|
|
205
|
+
//console.log(`Binding service ${serviceScope}`);
|
|
206
|
+
service.bindings.push(serviceScope);
|
|
207
|
+
|
|
208
|
+
// Since now we have the localname, we can bind the service in this.service too
|
|
209
|
+
this.services[localName] = service;
|
|
172
210
|
|
|
173
|
-
|
|
174
|
-
|
|
211
|
+
// For serices that have been passed through this.use
|
|
212
|
+
if ((localName in this) && this[localName] === undefined)
|
|
213
|
+
this[ localName ] = service;
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public async startService( localName: string, service: AnyService ) {
|
|
218
|
+
// Service already started
|
|
219
|
+
if (!service.started) {
|
|
220
|
+
|
|
221
|
+
const serviceScope = this.constructor.name + '.' + localName;
|
|
222
|
+
|
|
223
|
+
// Start servuce & eventually his subservices
|
|
224
|
+
console.log(`[app] Start service`, serviceScope);
|
|
225
|
+
service.status = 'starting';
|
|
226
|
+
service.started = service.launch();
|
|
227
|
+
await service.started.catch(e => {
|
|
228
|
+
console.error("Catched error while starting service " + serviceScope, e);
|
|
229
|
+
if (this.app.env.profile === 'prod')
|
|
230
|
+
process.exit();
|
|
231
|
+
else
|
|
232
|
+
throw e;
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Bind to app
|
|
236
|
+
console.log(`[app] Service`, serviceScope, 'started (bound to:', service.bindings.join(', '),')');
|
|
237
|
+
service.status = 'running';
|
|
238
|
+
}
|
|
175
239
|
}
|
|
176
240
|
|
|
177
241
|
/*----------------------------------
|