5htp-core 0.1.1 → 0.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 +4 -7
- package/src/client/assets/css/components/components.less +8 -4
- package/src/client/context/index.ts +3 -4
- package/src/client/index.tsx +1 -2
- package/src/client/router/Link.tsx +36 -0
- package/src/client/router/{index.tsx → index.ts} +69 -84
- package/src/client/router/route.ts +75 -0
- package/src/common/router/index.ts +6 -4
- package/src/server/services/auth/base.ts +9 -1
- package/src/server/services/console/bugReporter.ts +14 -1
- package/src/server/services/email/index.ts +13 -25
- package/src/server/services/router/index.ts +0 -0
- package/src/server/services/router/request/services/auth.ts +1 -3
- package/src/types/aliases.d.ts +0 -11
- package/src/common/models/index.ts +0 -43
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "5-HTP, scientifically called 5-Hydroxytryptophan, is the precursor of happiness neurotransmitter.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.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",
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
"nodemailer": "^6.6.3",
|
|
59
59
|
"path-to-regexp": "^6.2.0",
|
|
60
60
|
"picomatch": "^2.3.1",
|
|
61
|
+
"preact": "^10.5.15",
|
|
62
|
+
"preact-render-to-string": "^5.1.19",
|
|
61
63
|
"react-scrollbars-custom": "^4.0.27",
|
|
62
64
|
"react-slider": "^2.0.1",
|
|
63
65
|
"react-textarea-autosize": "^8.3.3",
|
|
@@ -66,7 +68,6 @@
|
|
|
66
68
|
"request": "^2.88.2",
|
|
67
69
|
"sharp": "^0.29.1",
|
|
68
70
|
"sql-formatter": "^4.0.2",
|
|
69
|
-
"ts-alias": "^0.0.3-1",
|
|
70
71
|
"tslog": "^3.2.2",
|
|
71
72
|
"uuid": "^8.3.2",
|
|
72
73
|
"uuid-by-string": "^3.0.4",
|
|
@@ -83,9 +84,5 @@
|
|
|
83
84
|
"@types/universal-analytics": "^0.4.5",
|
|
84
85
|
"@types/webpack-env": "^1.16.2",
|
|
85
86
|
"@types/ws": "^7.4.7"
|
|
86
|
-
}
|
|
87
|
-
"peerDependencies": {
|
|
88
|
-
"preact": "^10.5.15",
|
|
89
|
-
"preact-render-to-string": "^5.1.19"
|
|
90
|
-
}
|
|
87
|
+
}
|
|
91
88
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
.white-card() {
|
|
2
|
+
background: white;
|
|
3
|
+
box-shadow: 0 3px 2px fade(#000, 10%);
|
|
4
|
+
border: solid 1px fade(#000, 10%);
|
|
5
|
+
border-radius: @radius;
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
.card,
|
|
2
9
|
.input.text,
|
|
3
10
|
.btn,
|
|
@@ -7,10 +14,7 @@ i.solid {
|
|
|
7
14
|
.build-theme-bg( #fff, #8E8E8E);
|
|
8
15
|
|
|
9
16
|
&:not(.bg) {
|
|
10
|
-
|
|
11
|
-
box-shadow: 0 3px 2px fade(#000, 10%);
|
|
12
|
-
border: solid 1px fade(#000, 10%);
|
|
13
|
-
border-radius: @radius;
|
|
17
|
+
.white-card();
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
.bg & {
|
|
@@ -19,7 +19,6 @@ import SocketClient from './socket';
|
|
|
19
19
|
/*----------------------------------
|
|
20
20
|
- TYPES
|
|
21
21
|
----------------------------------*/
|
|
22
|
-
import { User, GuestUser } from '@common/models';
|
|
23
22
|
|
|
24
23
|
import type { Layout } from '@common/router';
|
|
25
24
|
|
|
@@ -63,7 +62,7 @@ export const useState = <TData extends TObjetDonnees>(initial: TData): [
|
|
|
63
62
|
export class ClientContext {
|
|
64
63
|
|
|
65
64
|
public context = this; // To access to the full nstance within a destructuration
|
|
66
|
-
public user: User |
|
|
65
|
+
public user: User | null;
|
|
67
66
|
|
|
68
67
|
public id = Date.now();
|
|
69
68
|
public bridges: { [name: string]: Function } = {};
|
|
@@ -109,7 +108,7 @@ export class ClientContext {
|
|
|
109
108
|
) {
|
|
110
109
|
|
|
111
110
|
this.request = request;
|
|
112
|
-
this.user = this.request.user ||
|
|
111
|
+
this.user = this.request.user || null;
|
|
113
112
|
|
|
114
113
|
}
|
|
115
114
|
|
|
@@ -120,7 +119,7 @@ export class ClientContext {
|
|
|
120
119
|
// Actions on the native app
|
|
121
120
|
reloadDaemon: () => { },
|
|
122
121
|
reloadGui: () => {
|
|
123
|
-
this.
|
|
122
|
+
this.page?.go('/');
|
|
124
123
|
},
|
|
125
124
|
optimize: () => {},
|
|
126
125
|
|
package/src/client/index.tsx
CHANGED
|
@@ -14,7 +14,6 @@ import router from '@client/router';
|
|
|
14
14
|
import { location } from '@client/router/request/history';
|
|
15
15
|
import coreRoutes from '@client/pages/**/*.tsx';
|
|
16
16
|
import appRoutes from '@/client/pages/**/*.tsx';
|
|
17
|
-
import { GuestUser } from '@common/models';
|
|
18
17
|
|
|
19
18
|
import ClientResponse from './router/response';
|
|
20
19
|
import ClientRequest from './router/request';
|
|
@@ -99,7 +98,7 @@ try {
|
|
|
99
98
|
if (!route)
|
|
100
99
|
throw new Error(`Route ${ssrResponse.page.id} was not found in ssr routes list.`);
|
|
101
100
|
|
|
102
|
-
context.user = request.user = ssrResponse.user ||
|
|
101
|
+
context.user = request.user = ssrResponse.user || null;
|
|
103
102
|
|
|
104
103
|
request.data = ssrResponse.request.data;
|
|
105
104
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { ComponentChild } from 'preact';
|
|
8
|
+
import { history } from './request/history';
|
|
9
|
+
|
|
10
|
+
/*----------------------------------
|
|
11
|
+
- COMPONENT
|
|
12
|
+
----------------------------------*/
|
|
13
|
+
// Simple link
|
|
14
|
+
export const Link = ({ to, ...props }: {
|
|
15
|
+
to: string,
|
|
16
|
+
children?: ComponentChild,
|
|
17
|
+
class?: string,
|
|
18
|
+
className?: string
|
|
19
|
+
} & React.HTMLProps<HTMLAnchorElement>) => {
|
|
20
|
+
|
|
21
|
+
// External = open in new tab by default
|
|
22
|
+
if (to[0] !== '/' || to.startsWith('//'))
|
|
23
|
+
props.target = '_blank';
|
|
24
|
+
// Otherwise, propagate to the router
|
|
25
|
+
else
|
|
26
|
+
props.onClick = (e) => {
|
|
27
|
+
history?.push(to);
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<a {...props} href={to} />
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -4,119 +4,102 @@
|
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import type { ComponentChild } from 'preact';
|
|
8
|
-
import type { Location } from 'history';
|
|
9
7
|
|
|
10
8
|
// Core: libs
|
|
11
9
|
import ClientRequest from './request';
|
|
12
10
|
import ClientResponse from './response';
|
|
13
|
-
import { history } from './request/history';
|
|
14
11
|
|
|
15
12
|
// Core: types
|
|
16
|
-
import BaseRouter, {
|
|
13
|
+
import BaseRouter, { defaultOptions, } from '@common/router'
|
|
17
14
|
import { PageResponse } from '@common/router/response';
|
|
18
|
-
import {
|
|
19
|
-
import type { TSsrData, default as ServerResponse } from '@server/services/router/response';
|
|
15
|
+
import type { TSsrData } from '@server/services/router/response';
|
|
20
16
|
import type { ClientContext } from '@client/context';
|
|
21
17
|
|
|
22
18
|
// Type xports
|
|
23
19
|
export type { default as ClientResponse } from "./response";
|
|
20
|
+
export { Link } from './Link';
|
|
21
|
+
import type {
|
|
22
|
+
TClientRoute,
|
|
23
|
+
TUnresolvedRoute,
|
|
24
|
+
TSsrUnresolvedRoute,
|
|
25
|
+
TRoutesLoaders,
|
|
26
|
+
TRouteCallback,
|
|
27
|
+
TFetchedRoute,
|
|
28
|
+
TRegisterPageArgs
|
|
29
|
+
} from './route';
|
|
30
|
+
|
|
31
|
+
// Temporary
|
|
32
|
+
// TODO: Import these types directly from router/routes
|
|
33
|
+
export type {
|
|
34
|
+
TClientRoute,
|
|
35
|
+
TUnresolvedRoute,
|
|
36
|
+
TSsrUnresolvedRoute,
|
|
37
|
+
TRoutesLoaders,
|
|
38
|
+
TRouteCallback,
|
|
39
|
+
TFetchedRoute,
|
|
40
|
+
TRegisterPageArgs,
|
|
41
|
+
TFrontController
|
|
42
|
+
} from './route';
|
|
24
43
|
|
|
25
44
|
/*----------------------------------
|
|
26
|
-
-
|
|
45
|
+
- CONFIG
|
|
27
46
|
----------------------------------*/
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
chunk: string,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type TUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
|
|
35
|
-
regex: RegExp,
|
|
36
|
-
chunk: string,
|
|
37
|
-
load: TRouteLoader,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
type TFetchedRoute = Pick<TClientRoute, 'path' | 'options' | 'controller' | 'renderer' | 'method'>
|
|
48
|
+
const debug = true;
|
|
49
|
+
const LogPrefix = '[router]'
|
|
41
50
|
|
|
42
51
|
/*----------------------------------
|
|
43
|
-
- TYPES
|
|
52
|
+
- TYPES
|
|
44
53
|
----------------------------------*/
|
|
45
54
|
|
|
46
|
-
type
|
|
47
|
-
|
|
48
|
-
export type TRoutesLoaders = {
|
|
49
|
-
[chunkId: string]: /* Preloaded via require() */TFetchedRoute | /* Loader via import() */TRouteLoader/* | undefined*/
|
|
50
|
-
}
|
|
55
|
+
export type THookCallback = (request: ClientRequest) => void;
|
|
51
56
|
|
|
52
|
-
type
|
|
53
|
-
|
|
54
|
-
export type TRegisterPageArgs<TControllerData extends TFetcherList = {}> = [
|
|
55
|
-
path: string,
|
|
56
|
-
options: Partial<TRouteOptions>,
|
|
57
|
-
controller: TFrontController<TControllerData> | null,
|
|
58
|
-
renderer: TFrontRenderer<TControllerData>
|
|
59
|
-
];
|
|
57
|
+
type THookName = 'locationChange'
|
|
60
58
|
|
|
61
59
|
/*----------------------------------
|
|
62
|
-
-
|
|
60
|
+
- ROUTER
|
|
63
61
|
----------------------------------*/
|
|
62
|
+
class Router extends BaseRouter {
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
64
|
+
/*----------------------------------
|
|
65
|
+
- HOOKS
|
|
66
|
+
----------------------------------*/
|
|
67
|
+
private hooks: {
|
|
68
|
+
[hookname in THookName]?: (THookCallback | null)[]
|
|
69
|
+
} = {}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
type TypeWithGeneric<T> = TFetcher<T>
|
|
74
|
-
type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never
|
|
75
|
-
|
|
76
|
-
export type TFrontController<TControllerData extends TFetcherList = {}> =
|
|
77
|
-
(urlParams: TObjetDonnees, context: ClientContext) => TControllerData
|
|
78
|
-
|
|
79
|
-
export type TFrontRenderer<TControllerData extends TFetcherList = {}> = (
|
|
80
|
-
data: {
|
|
81
|
-
[Property in keyof TControllerData]: undefined | (extractGeneric<TControllerData[Property]> extends ((...args: any[]) => any)
|
|
82
|
-
? ThenArg<ReturnType< extractGeneric<TControllerData[Property]> >>
|
|
83
|
-
: extractGeneric<TControllerData[Property]>
|
|
84
|
-
)
|
|
85
|
-
},
|
|
86
|
-
context: ClientContext
|
|
87
|
-
) => ComponentChild
|
|
88
|
-
|
|
89
|
-
// Simple link
|
|
90
|
-
export const Link = ({ to, ...props }: {
|
|
91
|
-
to: string,
|
|
92
|
-
children?: ComponentChild,
|
|
93
|
-
class?: string,
|
|
94
|
-
className?: string
|
|
95
|
-
}) => {
|
|
96
|
-
|
|
97
|
-
// External = open in new tab by default
|
|
98
|
-
if (to[0] !== '/' || to.startsWith('//'))
|
|
99
|
-
props.target = '_blank';
|
|
100
|
-
// Otherwise, propagate to the router
|
|
101
|
-
else
|
|
102
|
-
props.onClick = (e) => {
|
|
103
|
-
history?.push(to);
|
|
104
|
-
e.preventDefault();
|
|
105
|
-
return false
|
|
106
|
-
}
|
|
71
|
+
public on( hookName: THookName, callback: THookCallback ) {
|
|
107
72
|
|
|
108
|
-
|
|
109
|
-
<a {...props} href={to} />
|
|
110
|
-
)
|
|
73
|
+
debug && console.info(LogPrefix, `Register hook ${hookName}`);
|
|
111
74
|
|
|
112
|
-
|
|
75
|
+
let cbIndex: number;
|
|
76
|
+
let callbacks = this.hooks[ hookName ];
|
|
77
|
+
if (!callbacks) {
|
|
78
|
+
cbIndex = 0;
|
|
79
|
+
callbacks = this.hooks[ hookName ] = [callback]
|
|
80
|
+
} else {
|
|
81
|
+
cbIndex = callbacks.length;
|
|
82
|
+
callbacks.push(callback);
|
|
83
|
+
}
|
|
113
84
|
|
|
114
|
-
|
|
85
|
+
// Listener remover
|
|
86
|
+
return () => {
|
|
87
|
+
debug && console.info(LogPrefix, `De-register hook ${hookName} (index ${cbIndex})`);
|
|
88
|
+
delete (callbacks as THookCallback[])[ cbIndex ];
|
|
89
|
+
}
|
|
115
90
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
91
|
+
}
|
|
92
|
+
private runHook( hookName: THookName, request: ClientRequest ) {
|
|
93
|
+
const callbacks = this.hooks[hookName];
|
|
94
|
+
if (callbacks)
|
|
95
|
+
for (const callback of callbacks)
|
|
96
|
+
// callback can be null since we use delete to unregister
|
|
97
|
+
callback && callback(request);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/*----------------------------------
|
|
101
|
+
- ROUTES MANAGEMENT
|
|
102
|
+
----------------------------------*/
|
|
120
103
|
|
|
121
104
|
public disableResolver = false;
|
|
122
105
|
|
|
@@ -171,6 +154,8 @@ class Router extends BaseRouter {
|
|
|
171
154
|
public async resolve( request: ClientRequest, context: ClientContext ): Promise<PageResponse | undefined | null> {
|
|
172
155
|
debug && console.log('Resolving request', request.path, Object.keys(request.data));
|
|
173
156
|
|
|
157
|
+
this.runHook('locationChange', request);
|
|
158
|
+
|
|
174
159
|
for (let iRoute = 0; iRoute < this.routes.length; iRoute++) {
|
|
175
160
|
|
|
176
161
|
let route = this.routes[iRoute];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import type { ComponentChild } from 'preact';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
import type { ClientContext } from '@client/context';
|
|
10
|
+
import type { TBaseRoute, TRouteOptions, } from '@common/router'
|
|
11
|
+
import type { TFetcher, TFetcherList } from '@common/router/request';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES: PARTIAL ROUTES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
export type TSsrUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
|
|
18
|
+
regex: string,
|
|
19
|
+
chunk: string,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type TUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
|
|
23
|
+
regex: RegExp | null,
|
|
24
|
+
chunk: string,
|
|
25
|
+
load: TRouteLoader,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type TFetchedRoute = Pick<TClientRoute, 'path' | 'options' | 'controller' | 'renderer' | 'method'>
|
|
29
|
+
|
|
30
|
+
/*----------------------------------
|
|
31
|
+
- TYPES: REGISTER
|
|
32
|
+
----------------------------------*/
|
|
33
|
+
|
|
34
|
+
type TRouteLoader = () => Promise<{ default: TFetchedRoute }>;
|
|
35
|
+
|
|
36
|
+
export type TRoutesLoaders = {
|
|
37
|
+
[chunkId: string]: /* Preloaded via require() */TFetchedRoute | /* Loader via import() */TRouteLoader/* | undefined*/
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type TRouteCallback = (route?: TClientRoute) => void;
|
|
41
|
+
|
|
42
|
+
export type TRegisterPageArgs<TControllerData extends TFetcherList = {}> = [
|
|
43
|
+
path: string,
|
|
44
|
+
options: Partial<TRouteOptions>,
|
|
45
|
+
controller: TFrontController<TControllerData> | null,
|
|
46
|
+
renderer: TFrontRenderer<TControllerData>
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/*----------------------------------
|
|
50
|
+
- TYPES: COMPLETE ROUTES
|
|
51
|
+
----------------------------------*/
|
|
52
|
+
|
|
53
|
+
export type TClientRoute = TBaseRoute & {
|
|
54
|
+
type: 'PAGE',
|
|
55
|
+
method: 'GET',
|
|
56
|
+
controller: TFrontController | null,
|
|
57
|
+
renderer: TFrontRenderer
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// https://stackoverflow.com/questions/44851268/typescript-how-to-extract-the-generic-parameter-from-a-type
|
|
61
|
+
type TypeWithGeneric<T> = TFetcher<T>
|
|
62
|
+
type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never
|
|
63
|
+
|
|
64
|
+
export type TFrontController<TControllerData extends TFetcherList = {}> =
|
|
65
|
+
(urlParams: TObjetDonnees, context: ClientContext) => TControllerData
|
|
66
|
+
|
|
67
|
+
export type TFrontRenderer<TControllerData extends TFetcherList = {}> = (
|
|
68
|
+
data: {
|
|
69
|
+
[Property in keyof TControllerData]: undefined | (extractGeneric<TControllerData[Property]> extends ((...args: any[]) => any)
|
|
70
|
+
? ThenArg<ReturnType< extractGeneric<TControllerData[Property]> >>
|
|
71
|
+
: extractGeneric<TControllerData[Property]>
|
|
72
|
+
)
|
|
73
|
+
},
|
|
74
|
+
context: ClientContext
|
|
75
|
+
) => ComponentChild
|
|
@@ -13,11 +13,13 @@ import type {
|
|
|
13
13
|
TFrontRenderer
|
|
14
14
|
} from '@client/router';
|
|
15
15
|
|
|
16
|
-
import type {
|
|
16
|
+
import type { ClientContext } from '@client/context';
|
|
17
17
|
|
|
18
18
|
import type { TSchema } from '@common/data/input/validate';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import type { TApiServerRoute } from '@server/services/router';
|
|
21
|
+
|
|
22
|
+
import type { TUserRole } from '@server/services/auth/base';
|
|
21
23
|
|
|
22
24
|
/*----------------------------------
|
|
23
25
|
- TYPES: layouts
|
|
@@ -25,7 +27,7 @@ import { TUserRole } from '@common/models';
|
|
|
25
27
|
|
|
26
28
|
import layouts from '@/client/pages/**/_layout/index.tsx';
|
|
27
29
|
|
|
28
|
-
type LayoutComponent = ({ context: ClientContext }) => ComponentChild;
|
|
30
|
+
type LayoutComponent = (attributes: { context: ClientContext }) => ComponentChild;
|
|
29
31
|
export type Layout = { path: string, Component: LayoutComponent }
|
|
30
32
|
const getLayout = (routePath: string | undefined): Layout | undefined => {
|
|
31
33
|
|
|
@@ -84,7 +86,7 @@ export type TRouteOptions = {
|
|
|
84
86
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
export const defaultOptions
|
|
89
|
+
export const defaultOptions = {
|
|
88
90
|
priority: 0,
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -10,7 +10,7 @@ import { OAuth2Client, LoginTicket } from 'google-auth-library';
|
|
|
10
10
|
import { Forbidden } from '@common/errors';
|
|
11
11
|
|
|
12
12
|
// App Libs
|
|
13
|
-
import { IP } from '@models';
|
|
13
|
+
import { IP } from '@server/models';
|
|
14
14
|
import app, { $ } from '@server/app';
|
|
15
15
|
|
|
16
16
|
// Serbices
|
|
@@ -22,6 +22,8 @@ import '@server/services/database';
|
|
|
22
22
|
|
|
23
23
|
import type ServerRequest from '@server/services/router/request';
|
|
24
24
|
|
|
25
|
+
export type TUserRole = typeof UserRoles[number]
|
|
26
|
+
|
|
25
27
|
type AuthResponse = {
|
|
26
28
|
token: string,
|
|
27
29
|
redirect: string,
|
|
@@ -36,6 +38,12 @@ const config = app.config.auth;
|
|
|
36
38
|
|
|
37
39
|
const LogPrefix = '[auth]'
|
|
38
40
|
|
|
41
|
+
export const UserRoles = ['USER', 'ADMIN', 'TEST', 'DEV'] as const
|
|
42
|
+
|
|
43
|
+
/*----------------------------------
|
|
44
|
+
- SERVICE CONVIG
|
|
45
|
+
----------------------------------*/
|
|
46
|
+
|
|
39
47
|
export type AuthConfig = {
|
|
40
48
|
debug: boolean,
|
|
41
49
|
logoutUrl: string,
|
|
@@ -32,6 +32,9 @@ type AppBugInfos = {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type ServerBug = {
|
|
35
|
+
|
|
36
|
+
type: 'server',
|
|
37
|
+
|
|
35
38
|
// Context
|
|
36
39
|
hash: string,
|
|
37
40
|
date: Date, // Timestamp
|
|
@@ -42,11 +45,15 @@ export type ServerBug = {
|
|
|
42
45
|
ip: string | null | undefined,
|
|
43
46
|
|
|
44
47
|
// Error
|
|
48
|
+
error: Error,
|
|
45
49
|
stacktrace: string,
|
|
46
|
-
logs: string
|
|
50
|
+
logs: string,
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
export type ApplicationBug = {
|
|
54
|
+
|
|
55
|
+
type: 'application',
|
|
56
|
+
|
|
50
57
|
// Context
|
|
51
58
|
hash: string,
|
|
52
59
|
date: Date,
|
|
@@ -146,6 +153,9 @@ export default class BugReporter {
|
|
|
146
153
|
);
|
|
147
154
|
|
|
148
155
|
const bugReport: ServerBug = {
|
|
156
|
+
|
|
157
|
+
type: 'server',
|
|
158
|
+
|
|
149
159
|
// Context
|
|
150
160
|
hash: hash,
|
|
151
161
|
date: now,
|
|
@@ -155,6 +165,7 @@ export default class BugReporter {
|
|
|
155
165
|
user: request?.user?.name,
|
|
156
166
|
ip: request?.ip,
|
|
157
167
|
// Error
|
|
168
|
+
error,
|
|
158
169
|
stacktrace: error.stack || error.message,
|
|
159
170
|
logs: logsHtml
|
|
160
171
|
}
|
|
@@ -193,6 +204,8 @@ export default class BugReporter {
|
|
|
193
204
|
|
|
194
205
|
const bugReport: ApplicationBug = {
|
|
195
206
|
|
|
207
|
+
type: 'application',
|
|
208
|
+
|
|
196
209
|
// Context
|
|
197
210
|
hash: hash,
|
|
198
211
|
date: now,
|
|
@@ -11,7 +11,6 @@ import app, { $ } from '@server/app';
|
|
|
11
11
|
//import templates from './templates';
|
|
12
12
|
const templates = {} as {[template: string]: (data: any) => string}
|
|
13
13
|
import { jsonToHtml } from './utils';
|
|
14
|
-
import greetings from '@common/data/chaines/greetings';
|
|
15
14
|
|
|
16
15
|
/*----------------------------------
|
|
17
16
|
- SERVICE CONFIG
|
|
@@ -20,10 +19,10 @@ import greetings from '@common/data/chaines/greetings';
|
|
|
20
19
|
export type EmailServiceConfig = {
|
|
21
20
|
debug: boolean,
|
|
22
21
|
default: {
|
|
23
|
-
transporter:
|
|
22
|
+
transporter: string,
|
|
24
23
|
from: string
|
|
25
24
|
},
|
|
26
|
-
transporters: Core.
|
|
25
|
+
transporters: Core.EmailTransporters
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
declare global {
|
|
@@ -67,32 +66,15 @@ export type TCompleteEmail = With<THtmlEmail, {
|
|
|
67
66
|
- TYPES: OPTIONS
|
|
68
67
|
----------------------------------*/
|
|
69
68
|
|
|
70
|
-
type TransporterName = keyof Core.Config.EmailTransporters;
|
|
71
|
-
|
|
72
69
|
export abstract class Transporter {
|
|
73
70
|
public abstract send( emails: TCompleteEmail[] ): Promise<void>;
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
type TOptions = {
|
|
77
|
-
transporter?:
|
|
74
|
+
transporter?: string,
|
|
78
75
|
testing?: boolean
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
// TODO: to normalize
|
|
82
|
-
export const userMail = (username: string, content: string) => `
|
|
83
|
-
${greetings(username)}<br/><br/>
|
|
84
|
-
|
|
85
|
-
${content}
|
|
86
|
-
|
|
87
|
-
<br/><br/>
|
|
88
|
-
|
|
89
|
-
If you need any help, send me an email at <a href="mailto:contact@gaetan-legac.fr">contact@gaetan-legac.fr</a>, and I will reply to as soon as I can.
|
|
90
|
-
|
|
91
|
-
<br/><br/>
|
|
92
|
-
Peace,<br/>
|
|
93
|
-
<a href="https://www.linkedin.com/in/ga%C3%ABtanlegac/">Gaëtan Le Gac</a>
|
|
94
|
-
`
|
|
95
|
-
|
|
96
78
|
const config = app.config.email;
|
|
97
79
|
|
|
98
80
|
/*----------------------------------
|
|
@@ -100,16 +82,16 @@ const config = app.config.email;
|
|
|
100
82
|
----------------------------------*/
|
|
101
83
|
export default class Email {
|
|
102
84
|
|
|
103
|
-
private transporters = {} as {[name
|
|
104
|
-
public register( name:
|
|
85
|
+
private transporters = {} as {[name: string]: Transporter};
|
|
86
|
+
public register( name: string, transporter: (new () => Transporter) ) {
|
|
105
87
|
console.log(`[email] registering email transporter: ${name}`);
|
|
106
88
|
this.transporters[ name ] = new transporter();
|
|
107
89
|
}
|
|
108
90
|
|
|
109
91
|
public load() {
|
|
110
|
-
$.console.bugReport.addTransporter('email', (report
|
|
92
|
+
$.console.bugReport.addTransporter('email', (report) => this.send(report.type === 'server' ? {
|
|
111
93
|
to: app.identity.author.email,
|
|
112
|
-
subject: "
|
|
94
|
+
subject: "Bug on server: " + (report.error.message),
|
|
113
95
|
html: `
|
|
114
96
|
<a href="${app.env.url}/admin/activity/requests/${report.channelId}">
|
|
115
97
|
View Request details & console
|
|
@@ -117,6 +99,12 @@ export default class Email {
|
|
|
117
99
|
<br/>
|
|
118
100
|
${report.logs}
|
|
119
101
|
`
|
|
102
|
+
} : {
|
|
103
|
+
to: app.identity.author.email,
|
|
104
|
+
subject: "Bug on application " + (report.action),
|
|
105
|
+
html: {
|
|
106
|
+
...report
|
|
107
|
+
}
|
|
120
108
|
}));
|
|
121
109
|
|
|
122
110
|
}
|
|
File without changes
|
|
@@ -10,14 +10,12 @@ import jwt from 'jsonwebtoken';
|
|
|
10
10
|
// Cre
|
|
11
11
|
import app, { $ } from '@server/app';
|
|
12
12
|
import { InputError, AuthRequired, Forbidden } from '@common/errors';
|
|
13
|
-
import { TUserRole } from '@
|
|
13
|
+
import type { TUserRole } from '@server/services/auth/base';
|
|
14
14
|
|
|
15
15
|
/*----------------------------------
|
|
16
16
|
- TYPES
|
|
17
17
|
----------------------------------*/
|
|
18
18
|
|
|
19
|
-
import { User } from '@models';
|
|
20
|
-
|
|
21
19
|
import type ServerRequest from '@server/services/router/request'
|
|
22
20
|
|
|
23
21
|
type TJwtSession = { email: string }
|
package/src/types/aliases.d.ts
CHANGED
|
@@ -7,17 +7,6 @@ declare module "@client/pages/\*.tsx" {
|
|
|
7
7
|
export = value;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
// Basic Models
|
|
11
|
-
declare module "@/server/models/User" {
|
|
12
|
-
const User: import("../common/models").User;
|
|
13
|
-
export = User;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
declare module "@/server/models/IP" {
|
|
17
|
-
const IP: import("../common/models").IP;
|
|
18
|
-
export = IP;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
10
|
declare module "@/server/services/auth" {
|
|
22
11
|
const UserAuthService: import("../server/services/auth/base").default;
|
|
23
12
|
export = UserAuthService;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const UserRoles = ['USER', 'ADMIN', 'TEST', 'DEV'] as const
|
|
4
|
-
export type TUserRole = typeof UserRoles[number]
|
|
5
|
-
|
|
6
|
-
export interface User {
|
|
7
|
-
name: string,
|
|
8
|
-
email: string,
|
|
9
|
-
emailHash: string,
|
|
10
|
-
roles: TUserRole[],
|
|
11
|
-
|
|
12
|
-
balance: number,
|
|
13
|
-
banned?: Date,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const GuestUser = {
|
|
17
|
-
name: "Guest",
|
|
18
|
-
|
|
19
|
-
balance: 10,
|
|
20
|
-
multiplier: 1,
|
|
21
|
-
level: 1,
|
|
22
|
-
|
|
23
|
-
isGuest: true
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface IP {
|
|
27
|
-
address: string,
|
|
28
|
-
|
|
29
|
-
country: string,
|
|
30
|
-
isp?: string,
|
|
31
|
-
user_name?: string,
|
|
32
|
-
|
|
33
|
-
meet: Date,
|
|
34
|
-
activity: Date,
|
|
35
|
-
updated?: Date,
|
|
36
|
-
|
|
37
|
-
iphub?: number,
|
|
38
|
-
getipintel?: number,
|
|
39
|
-
ipinfo?: number,
|
|
40
|
-
|
|
41
|
-
banned?: Date,
|
|
42
|
-
banReason?: string,
|
|
43
|
-
}
|