proteum 1.0.0-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/.dockerignore +10 -0
- package/Rte.zip +0 -0
- package/cli/app/config.ts +54 -0
- package/cli/app/index.ts +195 -0
- package/cli/bin.js +11 -0
- package/cli/commands/build.ts +34 -0
- package/cli/commands/deploy/app.ts +29 -0
- package/cli/commands/deploy/web.ts +60 -0
- package/cli/commands/dev.ts +109 -0
- package/cli/commands/init.ts +85 -0
- package/cli/compiler/client/identite.ts +72 -0
- package/cli/compiler/client/index.ts +334 -0
- package/cli/compiler/common/babel/index.ts +170 -0
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +579 -0
- package/cli/compiler/common/babel/routes/imports.ts +127 -0
- package/cli/compiler/common/babel/routes/routes.ts +1130 -0
- package/cli/compiler/common/files/autres.ts +39 -0
- package/cli/compiler/common/files/images.ts +35 -0
- package/cli/compiler/common/files/style.ts +78 -0
- package/cli/compiler/common/index.ts +154 -0
- package/cli/compiler/index.ts +532 -0
- package/cli/compiler/server/index.ts +211 -0
- package/cli/index.ts +189 -0
- package/cli/paths.ts +165 -0
- package/cli/print.ts +12 -0
- package/cli/tsconfig.json +38 -0
- package/cli/utils/index.ts +22 -0
- package/cli/utils/keyboard.ts +78 -0
- package/client/app/component.tsx +54 -0
- package/client/app/index.ts +142 -0
- package/client/app/service.ts +34 -0
- package/client/app.tsconfig.json +28 -0
- package/client/components/Button.tsx +298 -0
- package/client/components/Dialog/Manager.tsx +309 -0
- package/client/components/Dialog/card.tsx +208 -0
- package/client/components/Dialog/index.less +151 -0
- package/client/components/Dialog/status.less +176 -0
- package/client/components/Dialog/status.tsx +48 -0
- package/client/components/index.ts +2 -0
- package/client/components/types.d.ts +3 -0
- package/client/data/input.ts +32 -0
- package/client/global.d.ts +5 -0
- package/client/hooks.ts +22 -0
- package/client/index.ts +6 -0
- package/client/pages/_layout/index.less +6 -0
- package/client/pages/_layout/index.tsx +43 -0
- package/client/pages/bug.tsx.old +60 -0
- package/client/pages/useHeader.tsx +50 -0
- package/client/services/captcha/index.ts +67 -0
- package/client/services/router/components/Link.tsx +46 -0
- package/client/services/router/components/Page.tsx +55 -0
- package/client/services/router/components/router.tsx +218 -0
- package/client/services/router/index.tsx +521 -0
- package/client/services/router/request/api.ts +267 -0
- package/client/services/router/request/history.ts +5 -0
- package/client/services/router/request/index.ts +53 -0
- package/client/services/router/request/multipart.ts +147 -0
- package/client/services/router/response/index.tsx +128 -0
- package/client/services/router/response/page.ts +86 -0
- package/client/services/socket/index.ts +147 -0
- package/client/utils/dom.ts +77 -0
- package/common/app/index.ts +9 -0
- package/common/data/chaines/index.ts +54 -0
- package/common/data/dates.ts +179 -0
- package/common/data/markdown.ts +73 -0
- package/common/data/rte/nodes.ts +83 -0
- package/common/data/stats.ts +90 -0
- package/common/errors/index.tsx +326 -0
- package/common/router/index.ts +213 -0
- package/common/router/layouts.ts +93 -0
- package/common/router/register.ts +55 -0
- package/common/router/request/api.ts +77 -0
- package/common/router/request/index.ts +35 -0
- package/common/router/response/index.ts +45 -0
- package/common/router/response/page.ts +128 -0
- package/common/utils/rte.ts +183 -0
- package/common/utils.ts +7 -0
- package/doc/TODO.md +71 -0
- package/doc/front/router.md +27 -0
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/package.json +171 -0
- package/server/app/commands.ts +141 -0
- package/server/app/container/config.ts +203 -0
- package/server/app/container/console/index.ts +550 -0
- package/server/app/container/index.ts +137 -0
- package/server/app/index.ts +273 -0
- package/server/app/service/container.ts +88 -0
- package/server/app/service/index.ts +235 -0
- package/server/app.tsconfig.json +28 -0
- package/server/context.ts +4 -0
- package/server/index.ts +4 -0
- package/server/services/auth/index.ts +250 -0
- package/server/services/auth/old.ts +277 -0
- package/server/services/auth/router/index.ts +95 -0
- package/server/services/auth/router/request.ts +54 -0
- package/server/services/auth/router/service.json +6 -0
- package/server/services/auth/service.json +6 -0
- package/server/services/cache/commands.ts +41 -0
- package/server/services/cache/index.ts +297 -0
- package/server/services/cache/service.json +6 -0
- package/server/services/cron/CronTask.ts +86 -0
- package/server/services/cron/index.ts +112 -0
- package/server/services/cron/service.json +6 -0
- package/server/services/disks/driver.ts +103 -0
- package/server/services/disks/drivers/local/index.ts +188 -0
- package/server/services/disks/drivers/local/service.json +6 -0
- package/server/services/disks/drivers/s3/index.ts +301 -0
- package/server/services/disks/drivers/s3/service.json +6 -0
- package/server/services/disks/index.ts +90 -0
- package/server/services/disks/service.json +6 -0
- package/server/services/email/index.ts +188 -0
- package/server/services/email/utils.ts +53 -0
- package/server/services/fetch/index.ts +201 -0
- package/server/services/fetch/service.json +7 -0
- package/server/services/models.7z +0 -0
- package/server/services/prisma/Facet.ts +142 -0
- package/server/services/prisma/index.ts +201 -0
- package/server/services/prisma/service.json +6 -0
- package/server/services/router/http/index.ts +217 -0
- package/server/services/router/http/multipart.ts +102 -0
- package/server/services/router/http/session.ts.old +40 -0
- package/server/services/router/index.ts +801 -0
- package/server/services/router/request/api.ts +87 -0
- package/server/services/router/request/index.ts +184 -0
- package/server/services/router/request/service.ts +21 -0
- package/server/services/router/request/validation/zod.ts +180 -0
- package/server/services/router/response/index.ts +338 -0
- package/server/services/router/response/mask/Filter.ts +323 -0
- package/server/services/router/response/mask/index.ts +60 -0
- package/server/services/router/response/mask/selecteurs.ts +92 -0
- package/server/services/router/response/page/document.tsx +160 -0
- package/server/services/router/response/page/index.tsx +196 -0
- package/server/services/router/service.json +6 -0
- package/server/services/router/service.ts +36 -0
- package/server/services/schema/index.ts +44 -0
- package/server/services/schema/request.ts +49 -0
- package/server/services/schema/router/index.ts +28 -0
- package/server/services/schema/router/service.json +6 -0
- package/server/services/schema/service.json +6 -0
- package/server/services/security/encrypt/aes/index.ts +85 -0
- package/server/services/security/encrypt/aes/service.json +6 -0
- package/server/services/socket/index.ts +162 -0
- package/server/services/socket/scope.ts +226 -0
- package/server/services/socket/service.json +6 -0
- package/server/services_old/SocketClient.ts +92 -0
- package/server/services_old/Token.old.ts +97 -0
- package/server/utils/slug.ts +79 -0
- package/tsconfig.common.json +45 -0
- package/tsconfig.json +3 -0
- package/types/aliases.d.ts +54 -0
- package/types/global/modules.d.ts +49 -0
- package/types/global/utils.d.ts +103 -0
- package/types/icons.d.ts +1 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import type { Application } from '@server/app';
|
|
7
|
+
import Service from '@server/app/service';
|
|
8
|
+
import markdown from '@common/data/markdown';
|
|
9
|
+
|
|
10
|
+
// Speciic
|
|
11
|
+
import { jsonToHtml } from './utils';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- SERVICE CONFIG
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
const LogPrefix = `[services][email]`
|
|
18
|
+
|
|
19
|
+
export type Config = {
|
|
20
|
+
debug: boolean,
|
|
21
|
+
simulateWhenLocal: boolean,
|
|
22
|
+
default: {
|
|
23
|
+
from: TPerson
|
|
24
|
+
},
|
|
25
|
+
bugReport: {
|
|
26
|
+
from: TPerson,
|
|
27
|
+
to: TPerson
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Hooks = {
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Services = {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*----------------------------------
|
|
40
|
+
- TYPES: EMAILS
|
|
41
|
+
----------------------------------*/
|
|
42
|
+
|
|
43
|
+
export type TEmail = THtmlEmail | TMarkdownEmail;
|
|
44
|
+
|
|
45
|
+
type TPerson = {
|
|
46
|
+
name?: string,
|
|
47
|
+
email: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type TBaseEmail = {
|
|
51
|
+
to: TPerson | TPerson[],
|
|
52
|
+
cc?: TPerson | TPerson[]
|
|
53
|
+
from?: TPerson,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type THtmlEmail = TBaseEmail & {
|
|
57
|
+
subject: string,
|
|
58
|
+
html: string | { [label: string]: any },
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type TMarkdownEmail = TBaseEmail & {
|
|
62
|
+
subject: string,
|
|
63
|
+
markdown: string,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type TCompleteEmail = With<THtmlEmail, {
|
|
67
|
+
to: TPerson[],
|
|
68
|
+
from: TPerson,
|
|
69
|
+
cc: TPerson[]
|
|
70
|
+
}>;
|
|
71
|
+
|
|
72
|
+
type TShortEmailSendArgs = [
|
|
73
|
+
to: string,
|
|
74
|
+
subject: string,
|
|
75
|
+
markdown: string,
|
|
76
|
+
options?: TOptions
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
type TCompleteEmailSendArgs = [
|
|
80
|
+
emails: TEmail | TEmail[],
|
|
81
|
+
options?: TOptions
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
type TEmailSendArgs = TShortEmailSendArgs | TCompleteEmailSendArgs;
|
|
85
|
+
|
|
86
|
+
/*----------------------------------
|
|
87
|
+
- TYPES: OPTIONS
|
|
88
|
+
----------------------------------*/
|
|
89
|
+
type TOptions = {
|
|
90
|
+
transporter?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*----------------------------------
|
|
94
|
+
- FONCTIONS
|
|
95
|
+
----------------------------------*/
|
|
96
|
+
export default abstract class Email<TConfig extends Config>
|
|
97
|
+
extends Service<TConfig, Hooks, Application, Application> {
|
|
98
|
+
|
|
99
|
+
/*----------------------------------
|
|
100
|
+
- ACTIONS
|
|
101
|
+
----------------------------------*/
|
|
102
|
+
|
|
103
|
+
protected abstract sendNow( emails: TCompleteEmail[] ): Promise<void>;
|
|
104
|
+
|
|
105
|
+
public async send( to: string, subject: string, markdown: string, options?: TOptions );
|
|
106
|
+
public async send( emails: TEmail | TEmail[], options?: TOptions ): Promise<void>;
|
|
107
|
+
public async send( ...args: TEmailSendArgs ): Promise<void> {
|
|
108
|
+
|
|
109
|
+
let emails: TEmail[] | TEmail;
|
|
110
|
+
let options: TOptions | undefined;
|
|
111
|
+
if (typeof args[0] === 'string') {
|
|
112
|
+
const [to, subject, markdown, opts] = args as TShortEmailSendArgs;
|
|
113
|
+
emails = [{
|
|
114
|
+
to: { email: to },
|
|
115
|
+
subject,
|
|
116
|
+
markdown
|
|
117
|
+
}]
|
|
118
|
+
options = opts;
|
|
119
|
+
} else {
|
|
120
|
+
|
|
121
|
+
([ emails, options ] = args as TCompleteEmailSendArgs);
|
|
122
|
+
if (!Array.isArray( emails ))
|
|
123
|
+
emails = [emails];
|
|
124
|
+
else if (emails.length === 0)
|
|
125
|
+
return console.warn(LogPrefix, `No email to send.`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
options = options || {}
|
|
129
|
+
|
|
130
|
+
this.config.debug && console.log(`Preparing to send ${emails.length} emails ...`);
|
|
131
|
+
|
|
132
|
+
const htmlWarning = this.app.env.profile === 'dev'
|
|
133
|
+
? `⚠️ This email has been sent for testing purposes. Please ignore it if you're not a developer.<br /><br />`
|
|
134
|
+
: '';
|
|
135
|
+
|
|
136
|
+
const emailsToSend: TCompleteEmail[] = emails.map(email => {
|
|
137
|
+
|
|
138
|
+
const from: TPerson = email.from === undefined
|
|
139
|
+
? this.config.default.from
|
|
140
|
+
: email.from;
|
|
141
|
+
|
|
142
|
+
const cc: TPerson[] = email.cc === undefined ? [] : Array.isArray(email.cc)
|
|
143
|
+
? email.cc
|
|
144
|
+
: [email.cc];
|
|
145
|
+
|
|
146
|
+
const to: TPerson[] = Array.isArray(email.to)
|
|
147
|
+
? email.to
|
|
148
|
+
: [email.to];
|
|
149
|
+
|
|
150
|
+
if ('markdown' in email) {
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
...email,
|
|
154
|
+
html: htmlWarning + markdown.render(email.markdown),
|
|
155
|
+
from,
|
|
156
|
+
to,
|
|
157
|
+
cc
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
} else {
|
|
161
|
+
return {
|
|
162
|
+
...email,
|
|
163
|
+
html: htmlWarning + (typeof email.html === "string"
|
|
164
|
+
? email.html
|
|
165
|
+
: jsonToHtml(email.html)),
|
|
166
|
+
from,
|
|
167
|
+
to,
|
|
168
|
+
cc
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
console.info(LogPrefix, `Sending ${emailsToSend.length} emails:`, emailsToSend[0].subject);
|
|
175
|
+
|
|
176
|
+
// Pas d'envoi d'email quand local
|
|
177
|
+
if (this.app.env.profile !== 'prod' && this.config.simulateWhenLocal === true) {
|
|
178
|
+
console.log(LogPrefix, `Simulate email sending:\n`, emailsToSend[0].html);
|
|
179
|
+
return;
|
|
180
|
+
} else if (emailsToSend.length === 0) {
|
|
181
|
+
console.warn(LogPrefix, `No email to send.`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await this.sendNow(emailsToSend);
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
// Npm
|
|
5
|
+
import dayjs from 'dayjs';
|
|
6
|
+
import escapehtml from 'escape-html';
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- FONCTIONS
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
export const espacesVersHtml = (txt: string) => txt
|
|
12
|
+
.replace(/(\n|\r)/g, '<br>') // Sauts de ligne
|
|
13
|
+
.replace(/(\t)/g, ' '.repeat(8)) // Tabulations
|
|
14
|
+
.replace(/ /g, ' ') // Espaces
|
|
15
|
+
/*.replace(/\"/g, '"') // Doubles quotes
|
|
16
|
+
.replace(/\'/g, "'") // Doubles quotes*/
|
|
17
|
+
|
|
18
|
+
export const jsonToHtml = (objet: {[cle: string]: any}, complet: boolean = false): string => {
|
|
19
|
+
|
|
20
|
+
let html: string[] = [];
|
|
21
|
+
|
|
22
|
+
for (const label in objet) {
|
|
23
|
+
|
|
24
|
+
let valeur = objet[label];
|
|
25
|
+
|
|
26
|
+
if (valeur === undefined || valeur === null) {
|
|
27
|
+
|
|
28
|
+
if (complet === true)
|
|
29
|
+
// quand undefined, JSON.stringify retourne aussi undefined
|
|
30
|
+
valeur = valeur === undefined
|
|
31
|
+
? 'undefined'
|
|
32
|
+
: JSON.stringify(valeur, null, 4);
|
|
33
|
+
else
|
|
34
|
+
continue;
|
|
35
|
+
|
|
36
|
+
} else if (typeof valeur === 'object') {
|
|
37
|
+
|
|
38
|
+
if (valeur instanceof Date)
|
|
39
|
+
valeur = dayjs(valeur).format('DD/MM/YYYY HH:mm:ss');
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!valeur || typeof valeur !== 'string')
|
|
44
|
+
valeur = JSON.stringify(valeur, null, 4);
|
|
45
|
+
|
|
46
|
+
html.push(
|
|
47
|
+
'<b>' + label + ':</b> ' + espacesVersHtml(escapehtml(valeur))
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return html.join('<br>');
|
|
52
|
+
|
|
53
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import type { default as sharp, Sharp } from 'sharp';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
import got, { Method, Options } from 'got';
|
|
9
|
+
|
|
10
|
+
// Node
|
|
11
|
+
import request from 'request';
|
|
12
|
+
|
|
13
|
+
// Core: general
|
|
14
|
+
import type { Application } from '@server/app';
|
|
15
|
+
import Service, { AnyService } from '@server/app/service';
|
|
16
|
+
import { viaHttpCode } from '@common/errors';
|
|
17
|
+
|
|
18
|
+
// Local
|
|
19
|
+
import type { TAnyRouter } from '../router';
|
|
20
|
+
import type DisksManager from '../disks';
|
|
21
|
+
import type FsDriver from '../disks/driver';
|
|
22
|
+
|
|
23
|
+
/*----------------------------------
|
|
24
|
+
- SERVICE TYPES
|
|
25
|
+
----------------------------------*/
|
|
26
|
+
|
|
27
|
+
export type Config = {
|
|
28
|
+
debug?: boolean,
|
|
29
|
+
disk?: string,
|
|
30
|
+
|
|
31
|
+
disks: DisksManager,
|
|
32
|
+
router?: TAnyRouter
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type Hooks = {
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*----------------------------------
|
|
40
|
+
- TYPES
|
|
41
|
+
----------------------------------*/
|
|
42
|
+
|
|
43
|
+
export type TImageConfig = {
|
|
44
|
+
sharp: typeof sharp,
|
|
45
|
+
width: number,
|
|
46
|
+
height: number,
|
|
47
|
+
fit: keyof sharp.FitEnum,
|
|
48
|
+
quality: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*----------------------------------
|
|
52
|
+
- CONST
|
|
53
|
+
----------------------------------*/
|
|
54
|
+
|
|
55
|
+
const LogPrefix = `[services][fetch]`
|
|
56
|
+
|
|
57
|
+
/*----------------------------------
|
|
58
|
+
- SERVICE
|
|
59
|
+
- Tools that helps to consume external resources (including apis, ..)
|
|
60
|
+
-----------------------------------*/
|
|
61
|
+
export default class FetchService extends Service<Config, Hooks, Application, Application> {
|
|
62
|
+
|
|
63
|
+
private disk?: FsDriver;
|
|
64
|
+
|
|
65
|
+
public async ready() {
|
|
66
|
+
|
|
67
|
+
if (this.config.disks)
|
|
68
|
+
this.disk = this.config.disks.get( this.config.disk );
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public async shutdown() {
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*----------------------------------
|
|
77
|
+
- EXTERNAL API REQUESTS
|
|
78
|
+
----------------------------------*/
|
|
79
|
+
|
|
80
|
+
public post(
|
|
81
|
+
url: string,
|
|
82
|
+
data: {[k: string]: any},
|
|
83
|
+
options: {} = {}
|
|
84
|
+
) {
|
|
85
|
+
|
|
86
|
+
return this.request('POST', url, data, options);
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async request(
|
|
91
|
+
method: Method,
|
|
92
|
+
url: string,
|
|
93
|
+
data: {[k: string]: any},
|
|
94
|
+
options: Options = {}
|
|
95
|
+
) {
|
|
96
|
+
|
|
97
|
+
// Parse url if router service is provided
|
|
98
|
+
if (this.config.router === undefined)
|
|
99
|
+
throw new Error(`Please bind the Router service to the Fetch service in order to contact APIs.`);
|
|
100
|
+
|
|
101
|
+
url = this.config.router.url(url);
|
|
102
|
+
|
|
103
|
+
// Send request
|
|
104
|
+
const res = await got(url, {
|
|
105
|
+
throwHttpErrors: false,
|
|
106
|
+
headers: {
|
|
107
|
+
'Accept': 'application/json',
|
|
108
|
+
},
|
|
109
|
+
method,
|
|
110
|
+
...(method === 'GET' ? {
|
|
111
|
+
searchParams: data
|
|
112
|
+
} : {
|
|
113
|
+
json: data
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// Handle errors
|
|
118
|
+
if (res.statusCode !== 200) {
|
|
119
|
+
|
|
120
|
+
// Instanciate error from HTTP code
|
|
121
|
+
const error = viaHttpCode( res.statusCode, res.body );
|
|
122
|
+
if (error)
|
|
123
|
+
throw error;
|
|
124
|
+
|
|
125
|
+
// Not catched via viaHttpCode
|
|
126
|
+
console.log("RESPONSE", res.body);
|
|
127
|
+
throw new Error("Error while contacting the API");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Format & return response
|
|
131
|
+
return JSON.parse( res.body );
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*----------------------------------
|
|
135
|
+
- IMAGES
|
|
136
|
+
----------------------------------*/
|
|
137
|
+
|
|
138
|
+
public toBuffer( uri: string ): Promise<Buffer> {
|
|
139
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
140
|
+
request(uri, { encoding: null }, (err, res, body) => {
|
|
141
|
+
|
|
142
|
+
if (err)
|
|
143
|
+
return reject(err);
|
|
144
|
+
|
|
145
|
+
if (!body)
|
|
146
|
+
return reject(`Body is empty for ${uri}.`);
|
|
147
|
+
|
|
148
|
+
resolve(body);
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public async image(
|
|
154
|
+
imageFileUrl: string,
|
|
155
|
+
imageMod: TImageConfig,
|
|
156
|
+
saveToBucket: string,
|
|
157
|
+
saveToPath?: string,
|
|
158
|
+
disk?: string
|
|
159
|
+
): Promise<Buffer | null> {
|
|
160
|
+
|
|
161
|
+
// Define target disk
|
|
162
|
+
if (this.disk === undefined)
|
|
163
|
+
throw new Error(`Please provide a Disks service in order to download files.`);
|
|
164
|
+
|
|
165
|
+
// Download
|
|
166
|
+
let imageBuffer: Buffer | null;
|
|
167
|
+
try {
|
|
168
|
+
imageBuffer = await this.toBuffer( imageFileUrl );
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error(LogPrefix, `Error while fetching image at ${imageFileUrl}:`, error);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (imageMod) {
|
|
175
|
+
|
|
176
|
+
const { sharp, width, height, fit, quality } = imageMod;
|
|
177
|
+
|
|
178
|
+
// Resize
|
|
179
|
+
const processing = sharp( imageBuffer )
|
|
180
|
+
// Max dimensions (save space)
|
|
181
|
+
.resize(width, height, { fit })
|
|
182
|
+
|
|
183
|
+
// Convert to webp and finalize
|
|
184
|
+
imageBuffer = await processing.webp({ quality }).toBuffer().catch(e => {
|
|
185
|
+
console.error(LogPrefix, `Error while processing image at ${imageFileUrl}:`, e);
|
|
186
|
+
return null;
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Save file
|
|
192
|
+
if (saveToPath !== undefined && imageBuffer !== null) {
|
|
193
|
+
console.log(LogPrefix, `Saving ${imageFileUrl} logo to ${saveToPath}`);
|
|
194
|
+
await this.disk.outputFile(saveToBucket, saveToPath, imageBuffer);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// We return the original, because Vibrant.js doesn't support webp
|
|
198
|
+
return imageBuffer;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { Prisma, PrismaClient } from '@models/types';
|
|
2
|
+
import * as runtime from '@/var/prisma/runtime/library.js';
|
|
3
|
+
|
|
4
|
+
/*export type TDelegate<R> = {
|
|
5
|
+
findMany(args?: any): Promise<R[]>
|
|
6
|
+
findFirst(args?: any): Promise<R | null>
|
|
7
|
+
}*/
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export type TDelegate<R> = PrismaClient[string];
|
|
15
|
+
|
|
16
|
+
/*export type TExtractPayload<D extends TDelegate<never>> =
|
|
17
|
+
D extends { [K in symbol]: { types: { payload: infer P } } } ? P : never;
|
|
18
|
+
|
|
19
|
+
export type TExtractPayload2<D> =
|
|
20
|
+
D extends { [K: symbol]: { types: Prisma.TypeMap<infer E>['model'][infer M] } }
|
|
21
|
+
? Prisma.TypeMap<E>['model'][M & keyof Prisma.TypeMap<E>['model']]['payload']
|
|
22
|
+
: never;*/
|
|
23
|
+
|
|
24
|
+
export type Transform<S extends TSubset, R, RT> = (
|
|
25
|
+
row: runtime.Types.Result.GetResult<
|
|
26
|
+
Prisma.$ProspectContactLeadPayload,
|
|
27
|
+
ReturnType<S>,
|
|
28
|
+
'findMany'
|
|
29
|
+
>[number]
|
|
30
|
+
) => RT
|
|
31
|
+
|
|
32
|
+
export type TWithStats = {
|
|
33
|
+
$table: string,
|
|
34
|
+
$key: string
|
|
35
|
+
} & {
|
|
36
|
+
[key: string]: string // key => SQL
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type TSubset = (...a: any[]) => Prisma.ProspectContactLeadFindFirstArgs & {
|
|
40
|
+
withStats?: TWithStats
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default class Facet<
|
|
44
|
+
D extends TDelegate<R>,
|
|
45
|
+
S extends TSubset,
|
|
46
|
+
R, // Result type
|
|
47
|
+
RT // Transformed result type
|
|
48
|
+
> {
|
|
49
|
+
constructor(
|
|
50
|
+
|
|
51
|
+
private readonly prisma: PrismaClient,
|
|
52
|
+
|
|
53
|
+
private readonly delegate: D,
|
|
54
|
+
private readonly subset: S,
|
|
55
|
+
|
|
56
|
+
/* the **ONLY** line that changed ↓↓↓ */
|
|
57
|
+
private readonly transform?: Transform<S, R, RT>,
|
|
58
|
+
) { }
|
|
59
|
+
|
|
60
|
+
public async findMany(
|
|
61
|
+
...args: Parameters<S>
|
|
62
|
+
): Promise<RT[]> {
|
|
63
|
+
|
|
64
|
+
const { withStats, ...subset } = this.subset(...args);
|
|
65
|
+
|
|
66
|
+
const results = await this.delegate.findMany(subset);
|
|
67
|
+
if (results.length === 0)
|
|
68
|
+
return [];
|
|
69
|
+
|
|
70
|
+
// Load stats
|
|
71
|
+
const stats = withStats
|
|
72
|
+
? await this.fetchStats( withStats, results )
|
|
73
|
+
: [];
|
|
74
|
+
|
|
75
|
+
return results.map(row => this.transformResult(row, stats, withStats));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async findFirst(
|
|
79
|
+
...args: Parameters<S>
|
|
80
|
+
): Promise<RT | null> {
|
|
81
|
+
|
|
82
|
+
const { withStats, ...subset } = this.subset(...args);
|
|
83
|
+
|
|
84
|
+
const result = await this.delegate.findFirst(subset);
|
|
85
|
+
if (!result)
|
|
86
|
+
return null;
|
|
87
|
+
|
|
88
|
+
const stats = withStats
|
|
89
|
+
? await this.fetchStats( withStats, [result] )
|
|
90
|
+
: [];
|
|
91
|
+
|
|
92
|
+
return this.transformResult(result, stats, withStats);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private async fetchStats(
|
|
96
|
+
{ $table, $key, ...withStats }: TWithStats,
|
|
97
|
+
results: any[]
|
|
98
|
+
): Promise<any[]> {
|
|
99
|
+
|
|
100
|
+
const select = Object.entries(withStats).map(([key, sql]) =>
|
|
101
|
+
`(COALESCE((
|
|
102
|
+
${sql}
|
|
103
|
+
), 0)) as ${key}`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const stats = await this.prisma.$queryRawUnsafe(`
|
|
107
|
+
SELECT ${$key}, ${select.join(', ')}
|
|
108
|
+
FROM ${$table}
|
|
109
|
+
WHERE ${$key} IN (
|
|
110
|
+
${results.map(r => "'" + r[ $key ] + "'").join(',')}
|
|
111
|
+
)
|
|
112
|
+
`);
|
|
113
|
+
|
|
114
|
+
for (const stat of stats) {
|
|
115
|
+
for (const key in stat) {
|
|
116
|
+
|
|
117
|
+
if (key === $key)
|
|
118
|
+
continue;
|
|
119
|
+
|
|
120
|
+
stat[key] = stat[key] ? parseInt(stat[key]) : 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return stats;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private transformResult( result: any, stats: any[], withStats?: TWithStats ) {
|
|
128
|
+
|
|
129
|
+
// Transform stats
|
|
130
|
+
const resultStats = withStats
|
|
131
|
+
? stats.find(stat => stat[withStats.$key] === result[withStats.$key]) || {}
|
|
132
|
+
: {};
|
|
133
|
+
|
|
134
|
+
if (this.transform)
|
|
135
|
+
result = this.transform(result);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
...resultStats
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|