5htp-core 0.2.6 → 0.2.7-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.
Files changed (34) hide show
  1. package/package.json +5 -4
  2. package/src/client/app/index.ts +2 -2
  3. package/src/client/assets/css/text/icons.less +8 -2
  4. package/src/client/assets/css/utils/layouts.less +3 -0
  5. package/src/client/components/Form.ts +2 -2
  6. package/src/client/components/index.ts +2 -0
  7. package/src/client/components/input/Checkbox/index.less +0 -0
  8. package/src/client/components/input/Checkbox/index.tsx +58 -50
  9. package/src/client/components/input/Checkbox/old.tsx +74 -0
  10. package/src/client/components/inputv3/base.tsx +13 -1
  11. package/src/client/components/inputv3/date/index.tsx +49 -0
  12. package/src/client/components/inputv3/date/react-calendar.less +143 -0
  13. package/src/client/components/inputv3/date/react-daterange-picker.less +112 -0
  14. package/src/client/pages/useHeader.tsx +3 -2
  15. package/src/client/services/router/components/router.tsx +3 -2
  16. package/src/client/services/router/request/api.ts +0 -5
  17. package/src/client/services/router/request/index.ts +10 -0
  18. package/src/client/services/router/request/multipart.ts +120 -9
  19. package/src/server/app/config.ts +2 -0
  20. package/src/server/app/index.ts +4 -2
  21. package/src/server/services/console/index.ts +4 -4
  22. package/src/server/services/fetch/index.ts +1 -1
  23. package/src/server/services/router/http/index.ts +7 -3
  24. package/src/server/services/router/index.ts +10 -2
  25. package/src/server/services/router/response/index.ts +3 -1
  26. package/src/server/services/router/response/page/document.tsx +7 -19
  27. package/src/server/services/router/response/page/index.tsx +17 -1
  28. package/src/server/services/router/service.ts +1 -1
  29. package/src/types/global/modules.d.ts +1 -1
  30. package/src/client/components/input/Date/index.less +0 -167
  31. package/src/client/components/input/Date/index.tsx +0 -90
  32. package/src/client/services/metrics/index.ts +0 -37
  33. package/src/server/services/metrics/detect.ts +0 -109
  34. package/src/server/services/metrics/index.ts +0 -272
@@ -1,90 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
- import React from 'react';
5
- import Champ from '../Base';
6
- import dayjs from 'dayjs';
7
- import Popover from '@client/components/Conteneurs/Popover';
8
- import Calendar from 'react-calendar';
9
-
10
- /*----------------------------------
11
- - TYPES
12
- ----------------------------------*/
13
- type TValeur = string | Date;
14
- type TValeurDefaut = Date;
15
- type TValeurOut = Date;
16
- const valeurDefaut = undefined;
17
-
18
- export type Props = {
19
- valeur: TValeur,
20
- placeholder?: string,
21
- min?: string,
22
- max?: string
23
- }
24
-
25
- /*----------------------------------
26
- - COMPOSANT
27
- ----------------------------------*/
28
- import './index.less';
29
- export default Champ<Props, TValeurDefaut, TValeurOut>('date', { valeurDefaut }, ({
30
- // Spread TOUTES les props dont on a besoin pour éviter les problèmes de référence avec props
31
- prefixe, className, min, max
32
- }, { state, valeur, setState }, rendre) => {
33
-
34
- const maintenant = new Date;
35
-
36
- /* TODO: onchange saisie input = pas de validation
37
- onchange click calendrier = validation immédiate
38
- */
39
-
40
- const [affCalendrier, setAffCalendrier] = React.useState<boolean>(false);
41
-
42
- const saisieValide = valeur !== undefined && (
43
- typeof valeur !== 'string' || !isNaN( Date.parse( valeur ) )
44
- );
45
-
46
- /*----------------------------------
47
- - CONSTRUCTION CHAMP
48
- ----------------------------------*/
49
- if (prefixe === undefined)
50
- prefixe = <i src="calendar-alt" />;
51
-
52
- /*----------------------------------
53
- - RENDU DU CHAMP
54
- ----------------------------------*/
55
- return rendre(<>
56
-
57
- <Popover
58
- afficher={affCalendrier}
59
- fermer={() => setAffCalendrier(false)}
60
- interactions
61
- //{...props}
62
- className={"bloc input-date" + (className ? ' ' + className : '')}
63
- width={300}
64
- content={(
65
- <div>
66
- <Calendar
67
- minDate={min ? new Date(min) : maintenant}
68
- maxDate={max ? new Date(max) : undefined}
69
- value={saisieValide ? new Date(valeur) : maintenant}
70
- showDoubleView={false}
71
- onChange={(val: Date) => {
72
- setAffCalendrier(false)
73
- setState({ valeur: val });
74
- }}
75
- />
76
- </div>
77
- )}
78
- >
79
- <input
80
- className="champ"
81
- value={saisieValide ? dayjs(valeur).format('DD/MM/YYYY') : valeur}
82
- onChange={(e) => setState({ valeur: e.target.value })}
83
- onFocus={() => setAffCalendrier(true)}
84
- placeholder='DD/MM/YYYY'
85
- readOnly
86
- />
87
- </Popover>
88
-
89
- </>, { prefixe }); // Les propétés modifiées sont passées ici
90
- })
@@ -1,37 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
-
7
- // Core
8
- import type ClientApplication from '@client/app';
9
-
10
- /*----------------------------------
11
- - TYPES
12
- ----------------------------------*/
13
-
14
-
15
-
16
- /*----------------------------------
17
- - SERVICE
18
- ----------------------------------*/
19
- export default class ClientMetrics {
20
-
21
- public constructor( public app: ClientApplication ) {
22
-
23
- }
24
-
25
- public start() {
26
-
27
- }
28
-
29
- // Tracking
30
- public event( name: string, params?: object ) {
31
- if (!window.gtag) return;
32
- if (name === 'pageview')
33
- window.gtag('send', name);
34
- else
35
- window.gtag('event', name, params);
36
- }
37
- }
@@ -1,109 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import got from 'got';
7
-
8
- // Core
9
- import { Forbidden } from '@common/errors';
10
- import ServerRequest from '../router/request';
11
-
12
- // App
13
- import app from '@server/app';
14
-
15
- /*----------------------------------
16
- - TYPES
17
- ----------------------------------*/
18
-
19
- /*----------------------------------
20
- - MODULE
21
- ----------------------------------*/
22
- export default class ProtectService {
23
-
24
- private tracking: TrackerService;
25
-
26
- public constructor( private request: ServerRequest ) {
27
-
28
- this.tracking = request.tracking;
29
-
30
- }
31
-
32
- public async bots(): Promise<IP> {
33
-
34
- // Récupération et varifications informations ip
35
- const ip = await this.tracking.checkIP();
36
-
37
- // Captcha
38
- /*if (this.request.method === 'POST')
39
- await this.captcha(this.request.data.captcha);*/
40
-
41
- return ip;
42
- }
43
-
44
- public async multiaccount(ip: IP, whitelist?: string) {
45
-
46
- // DON'T USE IT
47
- // People can't try the app when another user recommands it IRL
48
- // TODO: find another way to detect / avoid multi account
49
- // Ex: Force login with Google only
50
-
51
- // If IP Address already used by another account
52
- /*const username = whitelist || this.request.user?.name;
53
- if (ip.user_name !== undefined && ip.user_name !== null && (username === undefined || username !== ip.user_name))
54
- throw new Forbidden(`
55
- I noticed you're trying to use multiple accounts.
56
- Only one account is allowed per IP address.
57
- If you think I'm wrong, please contact me at contact@gaetan-legac.fr and I will solve the problem.
58
- `);*/
59
- }
60
-
61
- public async botsAndMultiaccount( usernameWhitelist?: string ) {
62
- const ip = await this.bots();
63
- await this.multiaccount(ip, usernameWhitelist);
64
- return ip;
65
- }
66
-
67
- public async conflictOfInterest( username: string, request: ServerRequest ) {
68
-
69
- // NOTE: Don't base conflict of interest detection on IP
70
-
71
- // If the current ip have never been used by username
72
-
73
- /*const conflict = await sql`
74
- FROM UserLogin
75
- WHERE ip = ${request.ip} AND user = ${username}
76
- `.exists();
77
-
78
- return conflict;*/
79
-
80
- return false;
81
-
82
- }
83
-
84
- public async captcha(token?: string) {
85
-
86
- console.info(`Validation du captcha`, {
87
- secret: app.config.http.security.recaptcha.prv,
88
- response: token
89
- });
90
-
91
- if (!token)
92
- throw new Forbidden("Le captcha n'a pas été complété.");
93
-
94
- const res = await got.post('https://www.google.com/recaptcha/api/siteverify', {
95
- body: JSON.stringify({
96
- secret: app.config.http.security.recaptcha.prv,
97
- response: token,
98
- remoteip: null
99
- })
100
- }).json()
101
-
102
- console.info(`Réponse captcha`, res);
103
-
104
- const ok = res.success || false;
105
-
106
- if (!ok) throw new Forbidden("Le captcha est incorrect");
107
- }
108
-
109
- }
@@ -1,272 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import locale from 'locale'
7
- import dayjs from 'dayjs';
8
- import got from 'got';
9
-
10
- // Core
11
- import Application, { Service } from '@server/app';
12
- import type Router from '@server/services/router'
13
- import type ServerRequest from '@server/services/router/request'
14
- import type SQL from '@server/services/database';
15
- import { arrayToObj } from '@common/data/tableaux';
16
- import { Forbidden } from '@common/errors';
17
-
18
- /*----------------------------------
19
- - TYPES
20
- ----------------------------------*/
21
-
22
- type IP = {
23
- // Identity
24
- ip: string,
25
- address: string,
26
- isp: string,
27
- country: string,
28
- iphub: number,
29
- // Status
30
- banned: Date,
31
- banReason?: string,
32
- updated?: Date
33
- }
34
-
35
- export type TrackingInfos = {
36
- user: User | null,
37
- ip: IP,
38
- country: string,
39
- langue: string
40
- }
41
-
42
- type TAssoChaines = { [id: string]: string }
43
-
44
- /*----------------------------------
45
- - SERVICE
46
- ----------------------------------*/
47
-
48
- export type Config = {
49
- ga: {
50
- pub: string,
51
- prv: string,
52
- secret: string,
53
- }
54
- }
55
-
56
- export type Hooks = {
57
-
58
- }
59
-
60
- export default class TrackerService extends Service<Config, Hooks, Application> {
61
-
62
- public countries!: TAssoChaines;
63
- public langues!: TAssoChaines;
64
- public locales!: locale.Locales;
65
-
66
- public async register() {
67
-
68
- }
69
-
70
- public async start() {
71
-
72
- await this.indexData();
73
-
74
-
75
-
76
- }
77
-
78
- private async indexData() {
79
-
80
- // On n'oublie pas le tri alphabétique pour les listings
81
- /*const [countries, langues] = await this.app.sql`
82
- SELECT id, name FROM Countries ORDER BY name ASC;
83
- SELECT id, name FROM Locales ORDER BY name ASC;
84
- `().then(([listePays, listeLangues]) => [
85
- arrayToObj(listePays, { index: 'id', val: 'name' }),
86
- arrayToObj(listeLangues, { index: 'id', val: 'name' }),
87
- ]);
88
-
89
- this.countries = countries as TAssoChaines;
90
- this.langues = langues as TAssoChaines;
91
- this.locales = new locale.Locales(Object.keys(this.langues));*/
92
- }
93
-
94
- public async request( request: ServerRequest ) {
95
- return new TrackingRequestService(request, this);
96
- }
97
- }
98
-
99
- /*----------------------------------
100
- - REQUEST SERVICE
101
- ----------------------------------*/
102
- export class TrackingRequestService {
103
-
104
- // Services
105
- protected sql: SQL;
106
-
107
- // Caches
108
- private langue?: string;
109
- private ip?: IP;
110
-
111
- public constructor(
112
- private request: ServerRequest,
113
- private tracker: TrackerService,
114
- ) {
115
-
116
- this.sql = tracker.sql;
117
-
118
- }
119
-
120
- public event( event: 'pageview' ) {
121
-
122
- // Ne compte pas les events lorsque mode dev ou admin
123
- if (this.app.env.profile == 'dev' ||
124
- (this.request.user && this.request.user.roles.includes('ADMIN'))
125
- )
126
- return;
127
-
128
- console.log(`[router][request] Send GA event ${event}`);
129
- /*got.post(`https://www.google-analytics.com/mp/collect`
130
- + `?measurement_id=${app.config.tracking.ga.pub}`
131
- + `&api_secret=${app.config.tracking.ga.secret}`, {
132
-
133
- body: JSON.stringify({
134
- client_id: app.config.tracking.ga.prv,
135
- events: [{
136
- name: 'pageview',
137
- params: {},
138
- }]
139
- })
140
- })*/
141
-
142
- }
143
-
144
- public async infos(): Promise<TrackingInfos> {
145
-
146
- if (this.langue === undefined)
147
- this.langue = this.getLangue();
148
-
149
- if (this.ip === undefined)
150
- this.ip = await this.checkIP();
151
-
152
- return {
153
- user: this.request.user || null,
154
- langue: this.langue,
155
- country: this.ip.country,
156
- ip: this.ip
157
- }
158
- }
159
-
160
- public static values({ user, ip, langue, country }: TrackingInfos) {
161
- return {
162
- user: user ? user.email : null,
163
- ip: ip.address,
164
- country,
165
- langue
166
- }
167
- }
168
-
169
- private getLangue() {
170
-
171
- const locales = new locale.Locales( this.request.headers["accept-language"] )
172
- const langue = locales.best( this.tracker.locales ).language.toUpperCase();
173
-
174
- return (langue in this.tracker.langues) ? langue : 'EN';
175
-
176
- }
177
-
178
- public async checkIP(): Promise<IP> {
179
-
180
- let address: string = this.request.ip;
181
-
182
- // Détection multicompte: 1 Compte max / IP
183
- console.log('Checking IP ...', address);
184
- const now: Date = new Date;
185
-
186
- let ip = await this.sql`SELECT * FROM logs.IP WHERE address = ${address}`.first();
187
- if (!ip) {
188
-
189
- console.log(`New IP`);
190
-
191
- ip = {
192
- address,
193
- meet: now,
194
- activity: now,
195
- user_name: this.request.user?.name,
196
- }
197
-
198
- await this.retrieveScore(ip);
199
-
200
- this.sql.insert("logs.IP", ip);
201
-
202
- // Nouvelle IP
203
- } else {
204
-
205
- console.log(`Existing IP`, address, ip.banned);
206
-
207
- // Déjà banni
208
- if (ip.banned)
209
- throw new Forbidden(`Banned for the following reason: ` + ip.banReason);
210
-
211
- // Données expirées
212
- const tempsDepuisDerniereMaj = dayjs().diff(ip.dateMaj, 'day');
213
- if (tempsDepuisDerniereMaj >= 7) {
214
- console.log(`Dernière màj il y a ` + tempsDepuisDerniereMaj + ' jours');
215
- await this.retrieveScore(ip);
216
- }
217
-
218
- ip.activity = now;
219
-
220
- this.sql.update("logs.IP", ip, { address });
221
-
222
- }
223
-
224
- return ip;
225
- }
226
-
227
- private async retrieveScore(ip: IP) {
228
-
229
- console.log(ip.address, `Computing score ...`);
230
-
231
- // Retourner true pour autoriser
232
- const [iphubOk] = await Promise.all([
233
-
234
- // IPhub = le plus fiable en premier
235
- got.get('http://v2.api.iphub.info/ip/' + ip.address, {
236
- headers: {
237
- 'X-Key': app.config.http.security.iphub
238
- }
239
- }).json().then((iphub: any) => {
240
-
241
- ip.iphub = iphub.block;
242
- ip.country = iphub.countryCode;
243
- ip.isp = iphub.isp;
244
-
245
- console.log(ip.address, "IpHub:", iphub);
246
-
247
- /*
248
- block: 0 - Residential or business IP (i.e. safe IP)
249
- block: 1 - Non-residential IP (hosting provider, proxy, etc.)
250
- block: 2 - Non-residential & residential IP (warning, may flag innocent people)
251
- */
252
- return ip.iphub !== 1;
253
-
254
- })
255
-
256
- ]);
257
-
258
- // Impossible d'avoir le country = douteux
259
- // NOTE: Pour Iphub, ZZ = inconnu
260
- if (!(ip.country in this.tracker.countries)) {
261
- ip.banned = new Date;
262
- ip.banReason = "Invalid location: " + ip.country;
263
- // Considéré comme suspect par iphub etou getipintel
264
- } else if (!(iphubOk)) {
265
- ip.banned = new Date;
266
- ip.banReason = "Suspicious activity on your network";
267
- }
268
-
269
- ip.updated = new Date;
270
- }
271
-
272
- }