5htp-core 0.1.2 → 0.2.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/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -11
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/assets/css/components.less +52 -0
- package/src/client/assets/css/core.less +7 -28
- package/src/client/assets/css/theme.less +1 -1
- package/src/client/assets/css/{borders.less → utils/borders.less} +0 -0
- package/src/client/assets/css/{layouts.less → utils/layouts.less} +0 -0
- package/src/client/assets/css/{medias.less → utils/medias.less} +14 -1
- package/src/client/assets/css/{sizing.less → utils/sizing.less} +0 -0
- package/src/client/assets/css/{spacing.less → utils/spacing.less} +0 -0
- package/src/client/components/Card/index.tsx +13 -7
- package/src/client/components/Dialog/Manager.tsx +41 -14
- package/src/client/components/Dialog/index.less +2 -4
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/Row/index.less +0 -2
- package/src/client/components/Table/index.tsx +3 -2
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/containers/champs.less +0 -2
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +23 -0
- package/src/client/components/input/BaseV2/index.less +0 -2
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/Date/index.less +0 -2
- package/src/client/components/input/Periode/index.less +0 -2
- package/src/client/components/input/Radio/index.less +0 -2
- package/src/client/components/input/UploadImage/index.less +0 -2
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +52 -74
- package/src/client/services/router/index.tsx +453 -0
- package/src/client/services/router/request/api.ts +227 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +90 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +46 -54
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +185 -0
- package/src/common/validation/validator.ts +95 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +9 -27
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +59 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +213 -80
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/debug.ts +20 -0
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +30 -62
- package/src/server/services/email/transporter.ts +38 -0
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -202
- package/src/server/services/router/request/api.ts +76 -0
- package/src/server/services/router/request/index.ts +16 -97
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +131 -65
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/server/{data → services_old}/SocketClient.ts +0 -0
- package/src/server/{data/Token.olg.ts → services_old/Token.old.ts} +0 -0
- package/src/server/{data → services_old}/aes.ts +0 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/assets/css/components/components.less +0 -31
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- package/src/server/services/router/request/services/auth.ts +0 -177
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
- OUTILS DE TRAITEMENT
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Npm
|
|
6
6
|
import hInterval from 'human-interval';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
import
|
|
10
|
-
import
|
|
8
|
+
// Core
|
|
9
|
+
import Service from '@server/app/service';
|
|
10
|
+
import type CacheService from '../cache';
|
|
11
|
+
import type SQL from '../database';
|
|
11
12
|
|
|
12
13
|
/*----------------------------------
|
|
13
14
|
- CONST
|
|
@@ -41,6 +42,11 @@ const debug = false;
|
|
|
41
42
|
/*----------------------------------
|
|
42
43
|
- TYPES
|
|
43
44
|
----------------------------------*/
|
|
45
|
+
|
|
46
|
+
type TStatsServiceConfig = {
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
type TObjDonneesStats = { [cheminStats: string]: number }
|
|
45
51
|
export type TStat<TDonnees extends TObjDonneesStats> = { date: string } & TDonnees
|
|
46
52
|
export type TTimeStat<TDonnees extends TObjDonneesStats> = { time: number } & TDonnees
|
|
@@ -55,141 +61,160 @@ type TRetourStats<TDonnees extends TObjDonneesStats> = {
|
|
|
55
61
|
interval: number,
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Query
|
|
63
|
-
where,
|
|
64
|
-
// time
|
|
65
|
-
period, interval,
|
|
66
|
-
// Options
|
|
67
|
-
relative, cache
|
|
68
|
-
|
|
69
|
-
}: {
|
|
70
|
-
// Database
|
|
71
|
-
where?: string,
|
|
72
|
-
// Time
|
|
73
|
-
period: string,
|
|
74
|
-
interval: string,
|
|
75
|
-
// Options
|
|
76
|
-
relative?: boolean,
|
|
77
|
-
cache?: { id: string, duration?: string },
|
|
78
|
-
}): Promise< TRetourStats<TDonnees> > {
|
|
79
|
-
|
|
80
|
-
if (!debug && cache !== undefined) {
|
|
81
|
-
const fromCache = await Cache.getVal<TRetourStats<TDonnees>>(cache.id);
|
|
82
|
-
if (fromCache !== null) {
|
|
83
|
-
console.log("Using value from cache " + cache.id);
|
|
84
|
-
return fromCache;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
64
|
+
/*----------------------------------
|
|
65
|
+
- SERVICE
|
|
66
|
+
----------------------------------*/
|
|
67
|
+
export default class StatsService extends Service<TStatsServiceConfig> {
|
|
87
68
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const periodSec = Math.floor(periodTime / 1000);
|
|
94
|
-
|
|
95
|
-
const startTime = scaleTime(Date.now() - periodTime, intervalTime); // Round start date to the specified interval
|
|
96
|
-
const endTime = scaleTime(Date.now(), intervalTime);
|
|
97
|
-
const stats: TRetourStats<TDonnees> = {
|
|
98
|
-
graph: [],
|
|
99
|
-
stats: {} as TDonnees,
|
|
100
|
-
start: new Date(startTime),
|
|
101
|
-
end: new Date(endTime),
|
|
102
|
-
interval: intervalTime
|
|
103
|
-
}
|
|
69
|
+
public constructor(
|
|
70
|
+
config: TStatsServiceConfig,
|
|
71
|
+
private cache: CacheService,
|
|
72
|
+
private sql: SQL
|
|
73
|
+
) {
|
|
104
74
|
|
|
105
|
-
|
|
106
|
-
let previousRow: TObjetDonnees = {};
|
|
107
|
-
|
|
108
|
-
const selector = statsCols(columns, relative ? 'SUM' : '');
|
|
109
|
-
|
|
110
|
-
// Fetch data (initial point + variations)
|
|
111
|
-
// Since values are relative, we will use stats.total as initialValues,
|
|
112
|
-
// and then increment it to get the latest tota value
|
|
113
|
-
([[previousRow], rows] = await $.sql.query(`
|
|
114
|
-
|
|
115
|
-
# Latest values before start time
|
|
116
|
-
SELECT ${selector}
|
|
117
|
-
FROM ${table}
|
|
118
|
-
WHERE date < (NOW() - INTERVAL ${periodSec} SECOND) ${where ? 'AND ' + where : ''}
|
|
119
|
-
ORDER BY date DESC
|
|
120
|
-
LIMIT 1;
|
|
121
|
-
|
|
122
|
-
# First value for each peiod of time
|
|
123
|
-
SELECT ${selector},
|
|
124
|
-
FLOOR( UNIX_TIMESTAMP(date) * 1000 / ${intervalTime}) * ${intervalTime} as periodTime
|
|
125
|
-
FROM ${table}
|
|
126
|
-
WHERE (date BETWEEN (NOW() - INTERVAL ${periodSec} SECOND) AND NOW())
|
|
127
|
-
${where ? 'AND ' + where : ''}
|
|
128
|
-
|
|
129
|
-
# In addition of the variations between start and end date, we take the latest values
|
|
130
|
-
GROUP BY (date = (SELECT MAX(date) FROM core.NetworkStats)), periodTime
|
|
131
|
-
ORDER BY date DESC;
|
|
132
|
-
|
|
133
|
-
`) as [[TDonnees | undefined], TDonnees[]]);
|
|
134
|
-
|
|
135
|
-
// Process
|
|
136
|
-
if (rows.length === 0)
|
|
137
|
-
return stats;
|
|
138
|
-
if (previousRow === undefined)
|
|
139
|
-
previousRow = {};
|
|
75
|
+
super(config);
|
|
140
76
|
|
|
141
|
-
|
|
77
|
+
}
|
|
142
78
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for (const { periodTime, ...row } of rows)
|
|
146
|
-
periods[periodTime] = row;
|
|
79
|
+
public async register() {}
|
|
80
|
+
public async start() {}
|
|
147
81
|
|
|
148
|
-
|
|
149
|
-
|
|
82
|
+
public async fetchStats<TDonnees extends TObjDonneesStats>(
|
|
83
|
+
|
|
84
|
+
table: string, columns: (string | [string, string])[], {
|
|
85
|
+
|
|
86
|
+
// Query
|
|
87
|
+
where,
|
|
88
|
+
// time
|
|
89
|
+
period, interval,
|
|
90
|
+
// Options
|
|
91
|
+
relative, cache
|
|
92
|
+
|
|
93
|
+
}: {
|
|
94
|
+
// Database
|
|
95
|
+
where?: string,
|
|
96
|
+
// Time
|
|
97
|
+
period: string,
|
|
98
|
+
interval: string,
|
|
99
|
+
// Options
|
|
100
|
+
relative?: boolean,
|
|
101
|
+
cache?: { id: string, duration?: string },
|
|
102
|
+
}): Promise< TRetourStats<TDonnees> > {
|
|
103
|
+
|
|
104
|
+
if (!debug && cache !== undefined) {
|
|
105
|
+
const fromCache = await this.cache.get<TRetourStats<TDonnees>>(cache.id);
|
|
106
|
+
if (fromCache !== null) {
|
|
107
|
+
console.log("Using value from cache " + cache.id);
|
|
108
|
+
return fromCache.value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
150
111
|
|
|
151
|
-
|
|
152
|
-
|
|
112
|
+
// NOTE: On ne génère pas le timestamp via la bdd pour éviter les incohérences de timezone
|
|
113
|
+
const periodTime = hInterval(period);
|
|
114
|
+
if (periodTime === undefined) throw new Error(`Invalid period string: ` + period);
|
|
115
|
+
const intervalTime = hInterval(interval);
|
|
116
|
+
if (intervalTime === undefined) throw new Error(`Invalid interval string: ` + interval);
|
|
117
|
+
const periodSec = Math.floor(periodTime / 1000);
|
|
118
|
+
|
|
119
|
+
const startTime = scaleTime(Date.now() - periodTime, intervalTime); // Round start date to the specified interval
|
|
120
|
+
const endTime = scaleTime(Date.now(), intervalTime);
|
|
121
|
+
const stats: TRetourStats<TDonnees> = {
|
|
122
|
+
graph: [],
|
|
123
|
+
stats: {} as TDonnees,
|
|
124
|
+
start: new Date(startTime),
|
|
125
|
+
end: new Date(endTime),
|
|
126
|
+
interval: intervalTime
|
|
127
|
+
}
|
|
153
128
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
continue;
|
|
129
|
+
let rows: TDonnees[];
|
|
130
|
+
let previousRow: TObjetDonnees = {};
|
|
157
131
|
|
|
158
|
-
|
|
132
|
+
const selector = statsCols(columns, relative ? 'SUM' : '');
|
|
133
|
+
|
|
134
|
+
// Fetch data (initial point + variations)
|
|
135
|
+
// Since values are relative, we will use stats.total as initialValues,
|
|
136
|
+
// and then increment it to get the latest tota value
|
|
137
|
+
([[previousRow], rows] = await this.sql.query(`
|
|
138
|
+
|
|
139
|
+
# Latest values before start time
|
|
140
|
+
SELECT ${selector}
|
|
141
|
+
FROM ${table}
|
|
142
|
+
WHERE date < (NOW() - INTERVAL ${periodSec} SECOND) ${where ? 'AND ' + where : ''}
|
|
143
|
+
ORDER BY date DESC
|
|
144
|
+
LIMIT 1;
|
|
145
|
+
|
|
146
|
+
# First value for each peiod of time
|
|
147
|
+
SELECT ${selector},
|
|
148
|
+
FLOOR( UNIX_TIMESTAMP(date) * 1000 / ${intervalTime}) * ${intervalTime} as periodTime
|
|
149
|
+
FROM ${table}
|
|
150
|
+
WHERE (date BETWEEN (NOW() - INTERVAL ${periodSec} SECOND) AND NOW())
|
|
151
|
+
${where ? 'AND ' + where : ''}
|
|
152
|
+
|
|
153
|
+
# In addition of the variations between start and end date, we take the latest values
|
|
154
|
+
GROUP BY (date = (SELECT MAX(date) FROM core.NetworkStats)), periodTime
|
|
155
|
+
ORDER BY date DESC;
|
|
156
|
+
|
|
157
|
+
`) as [[TDonnees | undefined], TDonnees[]]);
|
|
158
|
+
|
|
159
|
+
// Process
|
|
160
|
+
if (rows.length === 0)
|
|
161
|
+
return stats;
|
|
162
|
+
if (previousRow === undefined)
|
|
163
|
+
previousRow = {};
|
|
164
|
+
|
|
165
|
+
rows.reverse();
|
|
166
|
+
|
|
167
|
+
// Index entries by time
|
|
168
|
+
const periods: { [periodTime: number]: TTimeStat<TDonnees> } = {};
|
|
169
|
+
for (const { periodTime, ...row } of rows)
|
|
170
|
+
periods[periodTime] = row;
|
|
171
|
+
|
|
172
|
+
// Completion
|
|
173
|
+
for (let time = startTime; time <= endTime; time += intervalTime) {
|
|
174
|
+
|
|
175
|
+
const row = relative ? { ...previousRow, time } : { time };
|
|
176
|
+
stats.graph.push(row);
|
|
177
|
+
|
|
178
|
+
// No data for this period
|
|
179
|
+
if (periods[time] === undefined)
|
|
180
|
+
continue;
|
|
181
|
+
|
|
182
|
+
for (const nom in periods[time]) {
|
|
159
183
|
|
|
160
|
-
|
|
184
|
+
const value = periods[time][nom];
|
|
161
185
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
row[nom] = (relative && typeof row[nom] === 'number')
|
|
187
|
+
? row[nom] + value
|
|
188
|
+
: value;
|
|
165
189
|
|
|
166
|
-
|
|
190
|
+
}
|
|
167
191
|
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
previousRow = { ...row };
|
|
193
|
+
}
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
// Total = latest known values
|
|
196
|
+
stats.stats = previousRow;
|
|
197
|
+
|
|
198
|
+
// Check if the times selected from the database matches with the times iéterated from the completion
|
|
199
|
+
if (debug) {
|
|
200
|
+
for (const period in periods)
|
|
201
|
+
if (!stats.graph.find( g => g.time.toString() === period )) {
|
|
202
|
+
console.warn(
|
|
203
|
+
`The timetamps selected from the database do not match with the one generated by the completion function`,
|
|
204
|
+
'\nStart date:', startTime, stats.start.toISOString(), ' End date:', endTime, stats.end.toISOString(),
|
|
205
|
+
'\nFrom database', Object.keys(periods).map(time => time + ' (' + new Date( parseInt(time) ).toISOString() + ')'),
|
|
206
|
+
'\nFrom completion (now - period ; now):', stats.graph.map(g => g.time + ' (' + new Date(g.time).toISOString() + ')')
|
|
207
|
+
);
|
|
208
|
+
throw new Error("Integrity check failed.");
|
|
209
|
+
|
|
210
|
+
}
|
|
185
211
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
console.log(cache?.id, "Cumulative stats: total =", stats.stats, "rows =", rows, "periods =", periods, "stats =", stats);
|
|
189
|
-
}
|
|
212
|
+
console.log(cache?.id, "Cumulative stats: total =", stats.stats, "rows =", rows, "periods =", periods, "stats =", stats);
|
|
213
|
+
}
|
|
190
214
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
if (cache !== undefined)
|
|
216
|
+
await this.cache.set( cache.id, stats, cache.duration || interval );
|
|
217
|
+
|
|
218
|
+
return stats;
|
|
219
|
+
}
|
|
195
220
|
}
|
|
@@ -7,39 +7,41 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// Core
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import Application, { Service } from '@server/app';
|
|
11
|
+
|
|
12
|
+
// Speciic
|
|
13
13
|
import { jsonToHtml } from './utils';
|
|
14
|
+
import type { Transporter } from './transporter';
|
|
14
15
|
|
|
15
16
|
/*----------------------------------
|
|
16
17
|
- SERVICE CONFIG
|
|
17
18
|
----------------------------------*/
|
|
18
19
|
|
|
19
|
-
export type
|
|
20
|
+
export type Config = {
|
|
20
21
|
debug: boolean,
|
|
21
22
|
default: {
|
|
22
23
|
transporter: string,
|
|
23
24
|
from: string
|
|
24
25
|
},
|
|
25
|
-
transporters:
|
|
26
|
+
transporters: {
|
|
27
|
+
[transporterName: string]: Transporter
|
|
28
|
+
},
|
|
29
|
+
bugReport: {
|
|
30
|
+
from: string,
|
|
31
|
+
to: string
|
|
32
|
+
}
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
interface EmailTransporters { }
|
|
31
|
-
namespace Config {
|
|
32
|
-
interface Services {
|
|
33
|
-
email: EmailServiceConfig
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
35
|
+
export type Hooks = {
|
|
36
|
+
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
/*----------------------------------
|
|
40
40
|
- TYPES: EMAILS
|
|
41
41
|
----------------------------------*/
|
|
42
42
|
|
|
43
|
+
export { Transporter } from './transporter';
|
|
44
|
+
|
|
43
45
|
export type TEmail = THtmlEmail | TTemplateEmail;
|
|
44
46
|
|
|
45
47
|
type TBaseEmail = {
|
|
@@ -65,50 +67,27 @@ export type TCompleteEmail = With<THtmlEmail, {
|
|
|
65
67
|
/*----------------------------------
|
|
66
68
|
- TYPES: OPTIONS
|
|
67
69
|
----------------------------------*/
|
|
68
|
-
|
|
69
|
-
export abstract class Transporter {
|
|
70
|
-
public abstract send( emails: TCompleteEmail[] ): Promise<void>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
70
|
type TOptions = {
|
|
74
71
|
transporter?: string,
|
|
75
72
|
testing?: boolean
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
const config = app.config.email;
|
|
79
|
-
|
|
80
75
|
/*----------------------------------
|
|
81
76
|
- FONCTIONS
|
|
82
77
|
----------------------------------*/
|
|
83
|
-
export default class Email {
|
|
78
|
+
export default class Email extends Service<Config, Hooks, Application> {
|
|
79
|
+
|
|
80
|
+
private transporters = this.config.transporters;
|
|
81
|
+
|
|
82
|
+
public async register() {
|
|
84
83
|
|
|
85
|
-
private transporters = {} as {[name: string]: Transporter};
|
|
86
|
-
public register( name: string, transporter: (new () => Transporter) ) {
|
|
87
|
-
console.log(`[email] registering email transporter: ${name}`);
|
|
88
|
-
this.transporters[ name ] = new transporter();
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
public
|
|
92
|
-
$.console.bugReport.addTransporter('email', (report) => this.send(report.type === 'server' ? {
|
|
93
|
-
to: app.identity.author.email,
|
|
94
|
-
subject: "Bug on server: " + (report.error.message),
|
|
95
|
-
html: `
|
|
96
|
-
<a href="${app.env.url}/admin/activity/requests/${report.channelId}">
|
|
97
|
-
View Request details & console
|
|
98
|
-
</a>
|
|
99
|
-
<br/>
|
|
100
|
-
${report.logs}
|
|
101
|
-
`
|
|
102
|
-
} : {
|
|
103
|
-
to: app.identity.author.email,
|
|
104
|
-
subject: "Bug on application " + (report.action),
|
|
105
|
-
html: {
|
|
106
|
-
...report
|
|
107
|
-
}
|
|
108
|
-
}));
|
|
86
|
+
public async start() {
|
|
109
87
|
|
|
110
88
|
}
|
|
111
89
|
|
|
90
|
+
|
|
112
91
|
public async send(
|
|
113
92
|
emails: TEmail | TEmail[],
|
|
114
93
|
options: TOptions = {}
|
|
@@ -118,12 +97,12 @@ export default class Email {
|
|
|
118
97
|
if (!Array.isArray( emails ))
|
|
119
98
|
emails = [emails];
|
|
120
99
|
|
|
121
|
-
config.debug && console.log(`Preparing to send ${emails.length} emails ...`);
|
|
100
|
+
this.config.debug && console.log(`Preparing to send ${emails.length} emails ...`);
|
|
122
101
|
|
|
123
102
|
const emailsToSend: TCompleteEmail[] = emails.map(email => {
|
|
124
103
|
|
|
125
104
|
const from = email.from === undefined
|
|
126
|
-
? config.default.from
|
|
105
|
+
? this.config.default.from
|
|
127
106
|
: email.from;
|
|
128
107
|
|
|
129
108
|
const to = typeof email.to === 'string'
|
|
@@ -131,6 +110,7 @@ export default class Email {
|
|
|
131
110
|
: email.to;
|
|
132
111
|
|
|
133
112
|
// Via template
|
|
113
|
+
// TODO: Restore templates feature
|
|
134
114
|
if ('template' in email) {
|
|
135
115
|
|
|
136
116
|
const template = templates[email.template];
|
|
@@ -164,7 +144,7 @@ export default class Email {
|
|
|
164
144
|
|
|
165
145
|
});
|
|
166
146
|
|
|
167
|
-
const transporterName = options.transporter ||
|
|
147
|
+
const transporterName = options.transporter || this.config.default.transporter;
|
|
168
148
|
if (transporterName === undefined)
|
|
169
149
|
throw new Error(`Please define at least one mail transporter.`);
|
|
170
150
|
|
|
@@ -182,20 +162,8 @@ export default class Email {
|
|
|
182
162
|
return;
|
|
183
163
|
}
|
|
184
164
|
|
|
185
|
-
const
|
|
186
|
-
await
|
|
165
|
+
const transporter = this.transporters[ transporterName ];
|
|
166
|
+
await transporter.send(emailsToSend);
|
|
187
167
|
|
|
188
168
|
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/*----------------------------------
|
|
192
|
-
- REGISTER SERVICE
|
|
193
|
-
----------------------------------*/
|
|
194
|
-
app.register('email', Email);
|
|
195
|
-
declare global {
|
|
196
|
-
namespace Core {
|
|
197
|
-
interface Services {
|
|
198
|
-
email: Email;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
169
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
|
|
7
|
+
// Core
|
|
8
|
+
import type Application from "@server/app";
|
|
9
|
+
import type EmailService from '@server/services/email';
|
|
10
|
+
|
|
11
|
+
// Specific
|
|
12
|
+
import type { TCompleteEmail } from ".";
|
|
13
|
+
|
|
14
|
+
/*----------------------------------
|
|
15
|
+
- TYPES
|
|
16
|
+
----------------------------------*/
|
|
17
|
+
|
|
18
|
+
export type TBasicConfig = {
|
|
19
|
+
api: string,
|
|
20
|
+
debug: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*----------------------------------
|
|
24
|
+
- CLASS
|
|
25
|
+
----------------------------------*/
|
|
26
|
+
export abstract class Transporter<TConfig extends {} = {}> {
|
|
27
|
+
|
|
28
|
+
public constructor(
|
|
29
|
+
protected app: Application & { email: EmailService },
|
|
30
|
+
protected config: TBasicConfig & TConfig,
|
|
31
|
+
|
|
32
|
+
protected email = app.email
|
|
33
|
+
) {
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public abstract send( emails: TCompleteEmail[] ): Promise<void>;
|
|
38
|
+
}
|
|
@@ -3,17 +3,14 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
|
-
import
|
|
6
|
+
import got from 'got';
|
|
7
7
|
|
|
8
8
|
// Core
|
|
9
9
|
import { Forbidden } from '@common/errors';
|
|
10
|
-
import
|
|
11
|
-
import ServerRequest from '..';
|
|
12
|
-
import TrackerService from './tracking';
|
|
10
|
+
import ServerRequest from '../router/request';
|
|
13
11
|
|
|
14
12
|
// App
|
|
15
13
|
import app from '@server/app';
|
|
16
|
-
import { IP } from '@models';
|
|
17
14
|
|
|
18
15
|
/*----------------------------------
|
|
19
16
|
- TYPES
|
|
@@ -80,6 +77,8 @@ export default class ProtectService {
|
|
|
80
77
|
|
|
81
78
|
return conflict;*/
|
|
82
79
|
|
|
80
|
+
return false;
|
|
81
|
+
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
public async captcha(token?: string) {
|
|
@@ -92,14 +91,13 @@ export default class ProtectService {
|
|
|
92
91
|
if (!token)
|
|
93
92
|
throw new Forbidden("Le captcha n'a pas été complété.");
|
|
94
93
|
|
|
95
|
-
const res = await
|
|
96
|
-
|
|
94
|
+
const res = await got.post('https://www.google.com/recaptcha/api/siteverify', {
|
|
95
|
+
body: JSON.stringify({
|
|
97
96
|
secret: app.config.http.security.recaptcha.prv,
|
|
98
97
|
response: token,
|
|
99
98
|
remoteip: null
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
}).then(res => res.data)
|
|
99
|
+
})
|
|
100
|
+
}).json()
|
|
103
101
|
|
|
104
102
|
console.info(`Réponse captcha`, res);
|
|
105
103
|
|