5htp-core 0.2.1 → 0.2.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 +10 -3
- package/src/client/app/index.ts +2 -2
- package/src/client/assets/css/components/card.less +0 -3
- package/src/client/assets/css/components/other.less +2 -4
- package/src/client/assets/css/components/table.less +1 -2
- package/src/client/assets/css/components.less +4 -0
- package/src/client/assets/css/core.less +0 -1
- package/src/client/assets/css/theme.less +2 -2
- package/src/client/assets/css/utils/medias.less +21 -0
- package/src/client/components/Card/index.tsx +8 -5
- package/src/client/components/Dialog/index.less +3 -3
- package/src/client/components/Row/index.less +2 -0
- package/src/client/components/Row/index.tsx +44 -10
- package/src/client/components/Video/index.less +39 -0
- package/src/client/components/Video/index.tsx +69 -0
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/data/Time.tsx +1 -1
- package/src/client/components/data/progressbar/circular/index.tsx +1 -1
- package/src/client/components/index.ts +24 -8
- package/src/client/components/input/BaseV2/index.tsx +0 -1
- package/src/client/components/{input/BaseV2/index.less → inputv3/base.less} +1 -1
- package/src/client/components/inputv3/base.tsx +73 -0
- package/src/client/components/{input/UploadImage → inputv3/file}/Bouton.tsx +0 -0
- package/src/client/components/inputv3/file/FileToUpload.ts +34 -0
- package/src/client/components/inputv3/file/index.less +59 -0
- package/src/client/components/inputv3/file/index.tsx +157 -0
- package/src/client/components/{input → inputv3/string}/index.tsx +41 -27
- package/src/client/pages/bug.tsx +3 -4
- package/src/client/services/router/index.tsx +0 -1
- package/src/client/services/router/request/api.ts +20 -12
- package/src/client/services/router/request/multipart.ts +27 -0
- package/src/common/data/chaines/greetings.ts +1 -1
- package/src/common/data/dates.ts +1 -1
- package/src/common/data/input/validate.ts +0 -9
- package/src/common/data/markdown.ts +1 -1
- package/src/common/errors/index.ts +16 -12
- package/src/common/router/request/api.ts +11 -3
- package/src/common/validation/schema.ts +21 -20
- package/src/common/validation/validators.ts +3 -6
- package/src/server/app/commands.ts +149 -0
- package/src/server/app/index.ts +23 -4
- package/src/server/app/service.ts +4 -0
- package/src/server/services/cache/commands.ts +41 -0
- package/src/server/services/cache/index.ts +102 -34
- package/src/server/services/console/index.ts +1 -1
- package/src/server/services/database/connection.ts +38 -22
- package/src/server/services/database/datatypes.ts +51 -12
- package/src/server/services/database/index.ts +133 -40
- package/src/server/services/database/metas.ts +63 -37
- package/src/server/services/database/repository.ts +26 -0
- package/src/server/services/email/index.ts +102 -42
- package/src/server/services/fetch/index.ts +110 -0
- package/src/server/services/router/http/multipart.ts +70 -41
- package/src/server/services/router/index.ts +35 -4
- package/src/server/services/router/request/index.ts +8 -6
- package/src/server/services/schema/index.ts +4 -11
- package/src/server/services/schema/request.ts +16 -7
- package/src/server/services/schema/router.ts +6 -2
- package/src/server/{services_old → services/security/encrypt}/aes.ts +33 -14
- package/src/server/services/users/index.ts +3 -3
- package/src/server/services/users/router/index.ts +0 -2
- package/src/types/global/utils.d.ts +11 -1
- package/tsconfig.common.json +3 -0
- package/src/client/components/input/Textarea.tsx +0 -57
- package/src/client/components/input/Upload.tsx +0 -5
- package/src/client/components/input/UploadImage/index.less +0 -93
- package/src/client/components/input/UploadImage/index.tsx +0 -220
- package/src/common/data/file.ts +0 -25
|
@@ -8,6 +8,7 @@ import type mysql from 'mysql2/promise';
|
|
|
8
8
|
|
|
9
9
|
// Core
|
|
10
10
|
import Application from '@server/app';
|
|
11
|
+
import { ucfirst } from '@common/data/chaines';
|
|
11
12
|
|
|
12
13
|
// Database
|
|
13
14
|
import type DatabaseConnection from './connection';
|
|
@@ -66,12 +67,14 @@ export type TMetasColonne = {
|
|
|
66
67
|
// Column
|
|
67
68
|
table: TMetasTable,
|
|
68
69
|
nom: string,
|
|
70
|
+
pathname: string,
|
|
69
71
|
attached: boolean, // Si les métadonnées ont bien été enrichies avec celles provenant de MySQL
|
|
70
72
|
primary: boolean,
|
|
71
73
|
|
|
72
74
|
// Type
|
|
73
75
|
type: TColumnTypes,
|
|
74
76
|
nullable: boolean,
|
|
77
|
+
optional: boolean,
|
|
75
78
|
|
|
76
79
|
// Value
|
|
77
80
|
defaut?: any, // ATTENTION: Valeur déjà sérialisée
|
|
@@ -88,12 +91,14 @@ export type TColumnTypes = {
|
|
|
88
91
|
|
|
89
92
|
export type TMySQLType = {
|
|
90
93
|
name: TMySQLTypeName,
|
|
91
|
-
params
|
|
94
|
+
params?: string[],
|
|
95
|
+
raw: string
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
export type TJsType = {
|
|
95
99
|
name: TJsTypeName,
|
|
96
|
-
params
|
|
100
|
+
params?: string[],
|
|
101
|
+
raw: string
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
/*----------------------------------
|
|
@@ -103,7 +108,7 @@ export type TJsType = {
|
|
|
103
108
|
const LogPrefix = '[database][meta]';
|
|
104
109
|
|
|
105
110
|
const sqlTypeParamsReg = /\'([^\']+)\'\,?/gi;
|
|
106
|
-
const typeViaCommentReg =
|
|
111
|
+
const typeViaCommentReg = /\[type=([a-z]+)\]/g;
|
|
107
112
|
|
|
108
113
|
const modelsTypesPath = process.cwd() + '/src/server/models.ts';
|
|
109
114
|
|
|
@@ -196,7 +201,10 @@ export default class MySQLMetasParser {
|
|
|
196
201
|
|
|
197
202
|
// Extrct tablesIndex
|
|
198
203
|
const table = tablesIndex[database][nomTable];
|
|
199
|
-
const
|
|
204
|
+
const isNullable = nullable === 'YES';
|
|
205
|
+
const defaultValue = defaut === null ? undefined : defaut;
|
|
206
|
+
const isOptional = isNullable || defaultValue !== undefined;
|
|
207
|
+
const parsedTypes = this.parseColType(nomColonne, type, comment, isOptional);
|
|
200
208
|
|
|
201
209
|
// Reference primary keys
|
|
202
210
|
const isPrimary = cle === 'PRI';
|
|
@@ -209,15 +217,17 @@ export default class MySQLMetasParser {
|
|
|
209
217
|
// Column
|
|
210
218
|
table: tablesIndex[database][nomTable],
|
|
211
219
|
nom: nomColonne,
|
|
220
|
+
pathname: database + '.' + nomTable + '.' + nomColonne,
|
|
212
221
|
attached: true,
|
|
213
222
|
primary: isPrimary,
|
|
214
223
|
|
|
215
224
|
// Type
|
|
216
225
|
type: parsedTypes,
|
|
217
|
-
nullable:
|
|
226
|
+
nullable: isNullable,
|
|
227
|
+
optional: isOptional,
|
|
218
228
|
|
|
219
229
|
// Value
|
|
220
|
-
defaut:
|
|
230
|
+
defaut: defaultValue,
|
|
221
231
|
autoIncrement: extra.includes('auto_increment'),
|
|
222
232
|
|
|
223
233
|
// Extra
|
|
@@ -241,76 +251,92 @@ export default class MySQLMetasParser {
|
|
|
241
251
|
|
|
242
252
|
}
|
|
243
253
|
|
|
244
|
-
private parseColType( rawType: string, comment: string | null ): TColumnTypes {
|
|
254
|
+
private parseColType( name: string, rawType: string, comment: string | null, isOptional?: boolean ): TColumnTypes {
|
|
245
255
|
|
|
246
256
|
const mysqlType = this.parseMySQLType(rawType);
|
|
247
257
|
|
|
248
|
-
const jsType = this.parseJsType(mysqlType, comment);
|
|
258
|
+
const jsType = this.parseJsType(name, mysqlType, comment, isOptional);
|
|
249
259
|
|
|
250
260
|
return {
|
|
251
261
|
sql: mysqlType,
|
|
252
262
|
js: jsType
|
|
253
263
|
}
|
|
254
|
-
|
|
255
264
|
}
|
|
256
265
|
|
|
257
266
|
private parseMySQLType( rawType: string ): TMySQLType {
|
|
258
267
|
|
|
259
268
|
let name: TMySQLType["name"];
|
|
260
|
-
let params: TMySQLType["params"]
|
|
269
|
+
let params: TMySQLType["params"];
|
|
261
270
|
|
|
262
271
|
// Via native MySQL type: parse type expression + params
|
|
263
272
|
const posParenthese = rawType.indexOf('(');
|
|
264
|
-
if (posParenthese === -1)
|
|
273
|
+
if (posParenthese === -1) {
|
|
274
|
+
|
|
265
275
|
name = rawType.toUpperCase() as TMySQLType["name"];
|
|
266
|
-
|
|
276
|
+
|
|
277
|
+
} else {
|
|
278
|
+
|
|
267
279
|
name = rawType.substring(0, posParenthese).toUpperCase() as TMySQLType["name"];
|
|
280
|
+
params = []
|
|
268
281
|
|
|
269
282
|
// Extraction paramètres du type entre les parentheses
|
|
270
283
|
const paramsStr = rawType.substring(posParenthese + 1, rawType.length - 1)
|
|
271
284
|
let m;
|
|
272
285
|
do {
|
|
286
|
+
|
|
273
287
|
m = sqlTypeParamsReg.exec(paramsStr);
|
|
274
288
|
if (m)
|
|
275
289
|
params.push(m[1]);
|
|
290
|
+
|
|
276
291
|
} while (m);
|
|
292
|
+
|
|
293
|
+
if (params.length === 0)
|
|
294
|
+
params = undefined;
|
|
277
295
|
}
|
|
278
296
|
|
|
279
|
-
return { name, params }
|
|
297
|
+
return { name, params, raw: rawType }
|
|
280
298
|
|
|
281
299
|
}
|
|
282
300
|
|
|
283
|
-
private parseJsType( mysqlType: TMySQLType, comment: string | null ): TJsType {
|
|
301
|
+
private parseJsType( name: string, mysqlType: TMySQLType, comment: string | null, isOptional?: boolean ): TJsType {
|
|
302
|
+
|
|
303
|
+
let typeName: TJsType["name"] | undefined;
|
|
304
|
+
let params: TJsType["params"];
|
|
284
305
|
|
|
285
|
-
//
|
|
306
|
+
// Find type info via comment
|
|
286
307
|
if (comment) {
|
|
287
308
|
// Exract via comments: [type=array]
|
|
288
|
-
const foundTypeExpression =
|
|
289
|
-
if (foundTypeExpression !==
|
|
309
|
+
const foundTypeExpression = [...comment.matchAll( typeViaCommentReg )][0];
|
|
310
|
+
if (foundTypeExpression !== undefined) {
|
|
290
311
|
|
|
291
312
|
const typeNameViaComment = foundTypeExpression[1];
|
|
292
313
|
if (!(typeNameViaComment in jsTypes))
|
|
293
314
|
console.warn(`Invalid type "${typeNameViaComment}" found in column comments.`);
|
|
294
315
|
else
|
|
295
|
-
|
|
296
|
-
name: typeNameViaComment as TJsTypeName,
|
|
297
|
-
params: []
|
|
298
|
-
}
|
|
316
|
+
typeName = typeNameViaComment as TJsTypeName;
|
|
299
317
|
}
|
|
300
318
|
}
|
|
301
319
|
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
jsTypeName = mysqlToJs['UNKNOWN'];
|
|
307
|
-
}
|
|
320
|
+
// Find type info via mysql Type
|
|
321
|
+
if (typeName === undefined) {
|
|
322
|
+
|
|
323
|
+
typeName = mysqlToJs[ mysqlType.name ];
|
|
308
324
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
// Equivalent not found
|
|
326
|
+
if (typeName === undefined) {
|
|
327
|
+
console.warn(`The mySQL data type « ${mysqlType.name} » has not been associated with a JS equivalent in mysqlToJs. Using any instead.`);
|
|
328
|
+
typeName = mysqlToJs['UNKNOWN'];
|
|
329
|
+
}
|
|
312
330
|
}
|
|
313
331
|
|
|
332
|
+
// Get utils from name
|
|
333
|
+
const jsTypeUtils = jsTypes[ typeName ];
|
|
334
|
+
if (!jsTypeUtils)
|
|
335
|
+
throw new Error(`Unable to find the typescript print funvction for js type "${typeName}"`);
|
|
336
|
+
|
|
337
|
+
const raw = name + (isOptional ? '?' : '') + ': ' + jsTypeUtils.print( mysqlType.params );
|
|
338
|
+
|
|
339
|
+
return { name: typeName, params, raw }
|
|
314
340
|
}
|
|
315
341
|
|
|
316
342
|
private genTypesDef( databases: TDatabasesList ) {
|
|
@@ -325,18 +351,18 @@ export default class MySQLMetasParser {
|
|
|
325
351
|
const colsDecl: string[] = [];
|
|
326
352
|
for (const colName in table.colonnes) {
|
|
327
353
|
|
|
354
|
+
// Get column metdata
|
|
328
355
|
const col = table.colonnes[colName];
|
|
329
|
-
const jsTypeUtils = jsTypes[ col.type.js.name ];
|
|
330
|
-
if (!jsTypeUtils) {
|
|
331
|
-
console.warn(`Unable to find the typescript print funvction for js type "${col.type.js.name}"`);
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
356
|
|
|
335
|
-
|
|
357
|
+
// Generate typescript typedef
|
|
358
|
+
colsDecl.push('\t' + col.type.js.raw);
|
|
359
|
+
|
|
360
|
+
// Generate enum
|
|
361
|
+
if (['ENUM', 'SET'].includes( col.type.sql.name ) && col.type.sql.params !== undefined)
|
|
362
|
+
types.push('export const ' + table.nom + ucfirst( colName ) + ' = [' + col.type.sql.params.map( p => "'" + p + "'") + '] as const;');
|
|
336
363
|
}
|
|
337
364
|
|
|
338
365
|
types.push('export type ' + table.nom + ' = {\n' + colsDecl.join(',\n') + '\n}');
|
|
339
|
-
|
|
340
366
|
}
|
|
341
367
|
}
|
|
342
368
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Specific
|
|
6
|
+
import type Database from '.';
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- TYPES
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- CLASS
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
export default class QueriesRepository {
|
|
16
|
+
|
|
17
|
+
public constructor(
|
|
18
|
+
protected database: Database
|
|
19
|
+
) {
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected sql( strings: TemplateStringsArray, ...data: any[] ) {
|
|
24
|
+
return this.database.sql(strings, ...data);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
// Core
|
|
10
10
|
import Application, { Service } from '@server/app';
|
|
11
|
+
import markdown from '@common/data/markdown';
|
|
11
12
|
|
|
12
13
|
// Speciic
|
|
13
14
|
import { jsonToHtml } from './utils';
|
|
@@ -17,18 +18,21 @@ import type { Transporter } from './transporter';
|
|
|
17
18
|
- SERVICE CONFIG
|
|
18
19
|
----------------------------------*/
|
|
19
20
|
|
|
21
|
+
const LogPrefix = `[services][email]`
|
|
22
|
+
|
|
20
23
|
export type Config = {
|
|
21
24
|
debug: boolean,
|
|
25
|
+
simulateWhenLocal: boolean,
|
|
22
26
|
default: {
|
|
23
27
|
transporter: string,
|
|
24
|
-
from:
|
|
28
|
+
from: TPerson
|
|
25
29
|
},
|
|
26
30
|
transporters: {
|
|
27
31
|
[transporterName: string]: Transporter
|
|
28
32
|
},
|
|
29
33
|
bugReport: {
|
|
30
|
-
from:
|
|
31
|
-
to:
|
|
34
|
+
from: TPerson,
|
|
35
|
+
to: TPerson
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -42,11 +46,17 @@ export type Hooks = {
|
|
|
42
46
|
|
|
43
47
|
export { Transporter } from './transporter';
|
|
44
48
|
|
|
45
|
-
export type TEmail = THtmlEmail | TTemplateEmail;
|
|
49
|
+
export type TEmail = THtmlEmail | TMarkdownEmail// | TTemplateEmail;
|
|
50
|
+
|
|
51
|
+
type TPerson = {
|
|
52
|
+
name?: string,
|
|
53
|
+
email: string
|
|
54
|
+
}
|
|
46
55
|
|
|
47
56
|
type TBaseEmail = {
|
|
48
|
-
to:
|
|
49
|
-
|
|
57
|
+
to: TPerson | TPerson[],
|
|
58
|
+
cc?: TPerson | TPerson[]
|
|
59
|
+
from?: TPerson,
|
|
50
60
|
};
|
|
51
61
|
|
|
52
62
|
export type THtmlEmail = TBaseEmail & {
|
|
@@ -54,22 +64,41 @@ export type THtmlEmail = TBaseEmail & {
|
|
|
54
64
|
html: string | { [label: string]: any },
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
export type
|
|
67
|
+
export type TMarkdownEmail = TBaseEmail & {
|
|
68
|
+
subject: string,
|
|
69
|
+
markdown: string,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*export type TTemplateEmail = TBaseEmail & {
|
|
58
73
|
template: keyof typeof templates,
|
|
59
74
|
data?: TObjetDonnees
|
|
60
|
-
}
|
|
75
|
+
}*/
|
|
61
76
|
|
|
62
77
|
export type TCompleteEmail = With<THtmlEmail, {
|
|
63
|
-
to:
|
|
64
|
-
from:
|
|
78
|
+
to: TPerson[],
|
|
79
|
+
from: TPerson,
|
|
80
|
+
cc: TPerson[]
|
|
65
81
|
}>;
|
|
66
82
|
|
|
83
|
+
type TShortEmailSendArgs = [
|
|
84
|
+
to: string,
|
|
85
|
+
subject: string,
|
|
86
|
+
markdown: string,
|
|
87
|
+
options?: TOptions
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
type TCompleteEmailSendArgs = [
|
|
91
|
+
emails: TEmail | TEmail[],
|
|
92
|
+
options?: TOptions
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
type TEmailSendArgs = TShortEmailSendArgs | TCompleteEmailSendArgs;
|
|
96
|
+
|
|
67
97
|
/*----------------------------------
|
|
68
98
|
- TYPES: OPTIONS
|
|
69
99
|
----------------------------------*/
|
|
70
100
|
type TOptions = {
|
|
71
|
-
transporter?: string
|
|
72
|
-
testing?: boolean
|
|
101
|
+
transporter?: string
|
|
73
102
|
}
|
|
74
103
|
|
|
75
104
|
/*----------------------------------
|
|
@@ -88,30 +117,52 @@ export default class Email extends Service<Config, Hooks, Application> {
|
|
|
88
117
|
}
|
|
89
118
|
|
|
90
119
|
|
|
91
|
-
public async send(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
public async send( to: string, subject: string, markdown: string, options?: TOptions );
|
|
121
|
+
public async send( emails: TEmail | TEmail[], options?: TOptions ): Promise<void>;
|
|
122
|
+
public async send( ...args: TEmailSendArgs ): Promise<void> {
|
|
123
|
+
|
|
124
|
+
let emails: TEmail[] | TEmail;
|
|
125
|
+
let options: TOptions | undefined;
|
|
126
|
+
if (typeof args[0] === 'string') {
|
|
127
|
+
const [to, subject, markdown, opts] = args as TShortEmailSendArgs;
|
|
128
|
+
emails = [{
|
|
129
|
+
to: { email: to },
|
|
130
|
+
subject,
|
|
131
|
+
markdown
|
|
132
|
+
}]
|
|
133
|
+
options = opts;
|
|
134
|
+
} else {
|
|
135
|
+
|
|
136
|
+
([ emails, options ] = args as TCompleteEmailSendArgs);
|
|
137
|
+
if (!Array.isArray( emails ))
|
|
138
|
+
emails = [emails];
|
|
139
|
+
}
|
|
95
140
|
|
|
96
|
-
|
|
97
|
-
if (!Array.isArray( emails ))
|
|
98
|
-
emails = [emails];
|
|
141
|
+
options = options || {}
|
|
99
142
|
|
|
100
143
|
this.config.debug && console.log(`Preparing to send ${emails.length} emails ...`);
|
|
101
144
|
|
|
145
|
+
const htmlWarning = this.app.env.profile === 'dev'
|
|
146
|
+
? `⚠️ This email has been sent for testing purposes. Please ignore it if you're not a developer.<br /><br />`
|
|
147
|
+
: '';
|
|
148
|
+
|
|
102
149
|
const emailsToSend: TCompleteEmail[] = emails.map(email => {
|
|
103
150
|
|
|
104
|
-
const from = email.from === undefined
|
|
151
|
+
const from: TPerson = email.from === undefined
|
|
105
152
|
? this.config.default.from
|
|
106
153
|
: email.from;
|
|
107
154
|
|
|
108
|
-
const
|
|
109
|
-
?
|
|
110
|
-
: email.
|
|
155
|
+
const cc: TPerson[] = email.cc === undefined ? [] : Array.isArray(email.cc)
|
|
156
|
+
? email.cc
|
|
157
|
+
: [email.cc];
|
|
158
|
+
|
|
159
|
+
const to: TPerson[] = Array.isArray(email.to)
|
|
160
|
+
? email.to
|
|
161
|
+
: [email.to];
|
|
111
162
|
|
|
112
163
|
// Via template
|
|
113
164
|
// TODO: Restore templates feature
|
|
114
|
-
if ('template' in email) {
|
|
165
|
+
/*if ('template' in email) {
|
|
115
166
|
|
|
116
167
|
const template = templates[email.template];
|
|
117
168
|
|
|
@@ -126,20 +177,32 @@ export default class Email extends Service<Config, Hooks, Application> {
|
|
|
126
177
|
...email,
|
|
127
178
|
// Vire le "> " au début
|
|
128
179
|
subject: txt.substring(2, delimTitre),
|
|
129
|
-
html: txt.substring(delimTitre + 2),
|
|
180
|
+
html: htmlWarning + txt.substring(delimTitre + 2),
|
|
130
181
|
from,
|
|
131
|
-
to
|
|
182
|
+
to,
|
|
183
|
+
cc
|
|
132
184
|
}
|
|
133
185
|
|
|
134
|
-
}
|
|
186
|
+
} else */if ('markdown' in email) {
|
|
135
187
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
188
|
+
return {
|
|
189
|
+
...email,
|
|
190
|
+
html: htmlWarning + markdown.render(email.markdown),
|
|
191
|
+
from,
|
|
192
|
+
to,
|
|
193
|
+
cc
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
} else {
|
|
197
|
+
return {
|
|
198
|
+
...email,
|
|
199
|
+
html: htmlWarning + (typeof email.html === "string"
|
|
200
|
+
? email.html
|
|
201
|
+
: jsonToHtml(email.html)),
|
|
202
|
+
from,
|
|
203
|
+
to,
|
|
204
|
+
cc
|
|
205
|
+
}
|
|
143
206
|
}
|
|
144
207
|
|
|
145
208
|
});
|
|
@@ -148,17 +211,14 @@ export default class Email extends Service<Config, Hooks, Application> {
|
|
|
148
211
|
if (transporterName === undefined)
|
|
149
212
|
throw new Error(`Please define at least one mail transporter.`);
|
|
150
213
|
|
|
151
|
-
console.info(`Sending ${emailsToSend.length} emails via transporter "${transporterName}"`, emailsToSend[0].subject);
|
|
214
|
+
console.info(LogPrefix, `Sending ${emailsToSend.length} emails via transporter "${transporterName}"`, emailsToSend[0].subject);
|
|
152
215
|
|
|
153
216
|
// Pas d'envoi d'email quand local
|
|
154
|
-
|
|
155
|
-
console.log(
|
|
156
|
-
'options.testing': options.testing,
|
|
157
|
-
'app.env.name': app.env.name,
|
|
158
|
-
});
|
|
217
|
+
if (this.app.env.name === 'local' && this.config.simulateWhenLocal === true) {
|
|
218
|
+
console.log(LogPrefix, `Simulate email sending:\n`, emailsToSend[0].html);
|
|
159
219
|
return;
|
|
160
|
-
} else
|
|
161
|
-
console.
|
|
220
|
+
} else if (emailsToSend.length === 0) {
|
|
221
|
+
console.warn(LogPrefix, `No email to send.`);
|
|
162
222
|
return;
|
|
163
223
|
}
|
|
164
224
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import sharp from 'sharp';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
|
|
9
|
+
// Node
|
|
10
|
+
import request from 'request';
|
|
11
|
+
|
|
12
|
+
// Core: general
|
|
13
|
+
import type Application from '@server/app';
|
|
14
|
+
import Service from '@server/app/service';
|
|
15
|
+
|
|
16
|
+
/*----------------------------------
|
|
17
|
+
- SERVICE TYPES
|
|
18
|
+
----------------------------------*/
|
|
19
|
+
|
|
20
|
+
export type Config = {
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type Hooks = {
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/*----------------------------------
|
|
29
|
+
- TYPES
|
|
30
|
+
----------------------------------*/
|
|
31
|
+
|
|
32
|
+
export type TImageConfig = {
|
|
33
|
+
width: number,
|
|
34
|
+
height: number,
|
|
35
|
+
fit: keyof sharp.FitEnum,
|
|
36
|
+
quality: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*----------------------------------
|
|
40
|
+
- CONST
|
|
41
|
+
----------------------------------*/
|
|
42
|
+
|
|
43
|
+
const LogPrefix = `[services][fetch]`
|
|
44
|
+
|
|
45
|
+
/*----------------------------------
|
|
46
|
+
- SERVICE
|
|
47
|
+
- Tools that helps to consume external resources (including apis, ..)
|
|
48
|
+
-----------------------------------*/
|
|
49
|
+
export default class FetchService extends Service<Config, Hooks, Application> {
|
|
50
|
+
|
|
51
|
+
public async register() {
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async start() {
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public toBuffer( uri: string ): Promise<Buffer> {
|
|
60
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
61
|
+
request(uri, { encoding: null }, (err, res, body) => {
|
|
62
|
+
|
|
63
|
+
if (err)
|
|
64
|
+
return reject(err);
|
|
65
|
+
|
|
66
|
+
if (!body)
|
|
67
|
+
return reject(`Body is empty for ${uri}.`);
|
|
68
|
+
|
|
69
|
+
resolve(body);
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public async image(
|
|
75
|
+
imageFileUrl: string,
|
|
76
|
+
{ width, height, fit, quality }: TImageConfig,
|
|
77
|
+
saveToPath?: string
|
|
78
|
+
): Promise<Buffer | null> {
|
|
79
|
+
|
|
80
|
+
// Download
|
|
81
|
+
let imageBuffer: Buffer;
|
|
82
|
+
try {
|
|
83
|
+
imageBuffer = await this.toBuffer( imageFileUrl );
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error(LogPrefix, `Error while fetching image at ${imageFileUrl}:`, error);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Resize
|
|
90
|
+
const processing = sharp( imageBuffer )
|
|
91
|
+
// Max dimensions (save space)
|
|
92
|
+
.resize(width, height, { fit })
|
|
93
|
+
|
|
94
|
+
// Convert to webp and finalize
|
|
95
|
+
const processedBuffer = await processing.webp({ quality }).toBuffer().catch(e => {
|
|
96
|
+
console.error(LogPrefix, `Error while processing image at ${imageBuffer}:`, e);
|
|
97
|
+
return null;
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Save file
|
|
101
|
+
if (saveToPath !== undefined && processedBuffer !== null) {
|
|
102
|
+
console.log(LogPrefix, `Saving ${imageFileUrl} logo to ${saveToPath}`);
|
|
103
|
+
fs.outputFileSync(saveToPath, processedBuffer);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// We return the original, because Vibrant.js doesn't support webp
|
|
107
|
+
return imageBuffer;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|