5htp-core 0.5.1-4 → 0.5.1-6
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 +1 -1
- package/src/client/components/Table/index.tsx +2 -2
- package/src/client/components/inputv3/file/index.less +10 -8
- package/src/client/components/inputv3/file/index.tsx +48 -24
- package/src/server/services/database/index.ts +4 -3
- package/src/server/services/disks/driver.ts +1 -1
- package/src/server/services/disks/drivers/local/index.ts +4 -2
- package/src/server/services/router/index.ts +2 -2
- package/src/server/services/router/request/index.ts +15 -1
- package/src/server/services/router/response/index.ts +8 -1
- package/src/server/utils/rte.ts +1 -1
- package/src/server/utils/slug.ts +32 -20
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.5.1-
|
|
4
|
+
"version": "0.5.1-6",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -170,11 +170,11 @@ export default function Liste<TRow extends TDonneeInconnue>({
|
|
|
170
170
|
|
|
171
171
|
render = (
|
|
172
172
|
<div class="row sp-05">
|
|
173
|
-
{cell.map((item, i) => (
|
|
173
|
+
{cell.map((item, i) => typeof item === 'string' ? (
|
|
174
174
|
<span class={"badge bg light" + ((i % 7) + 1)}>
|
|
175
175
|
{item}
|
|
176
176
|
</span>
|
|
177
|
-
))}
|
|
177
|
+
) : item)}
|
|
178
178
|
</div>
|
|
179
179
|
)
|
|
180
180
|
|
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
position: relative;
|
|
6
6
|
overflow: hidden;
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
&.box {
|
|
9
|
+
border-radius: @radius;
|
|
10
|
+
border: dashed 3px var(--cLine);
|
|
11
|
+
background: @cBgPage + #090909;
|
|
12
|
+
|
|
13
|
+
padding: @spacing;
|
|
14
|
+
width: 100%;
|
|
15
|
+
min-height: 200px;
|
|
16
|
+
height: 14.15rem;
|
|
17
|
+
}
|
|
16
18
|
|
|
17
19
|
.preview,
|
|
18
20
|
.indication,
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import { ComponentChild } from 'preact';
|
|
7
7
|
|
|
8
8
|
// Composants généraux
|
|
9
|
-
import
|
|
9
|
+
import Button, { Props as BtnProps } from '@client/components/button';
|
|
10
10
|
|
|
11
11
|
// Core libs
|
|
12
12
|
import { InputWrapper } from '../base';
|
|
@@ -67,6 +67,7 @@ export type Props = {
|
|
|
67
67
|
emptyText?: ComponentChild,
|
|
68
68
|
className?: string,
|
|
69
69
|
previewUrl?: string,
|
|
70
|
+
button?: boolean | BtnProps,
|
|
70
71
|
|
|
71
72
|
// Actions
|
|
72
73
|
onChange: (file: FileToUpload | undefined) => void
|
|
@@ -85,11 +86,12 @@ export default (props: Props) => {
|
|
|
85
86
|
let {
|
|
86
87
|
// Input
|
|
87
88
|
value: file,
|
|
88
|
-
className,
|
|
89
|
+
className: customClassName,
|
|
89
90
|
|
|
90
91
|
// Display
|
|
91
92
|
emptyText = 'Click here to select a File',
|
|
92
93
|
previewUrl: previewUrlInit,
|
|
94
|
+
button,
|
|
93
95
|
|
|
94
96
|
// Actions
|
|
95
97
|
onChange
|
|
@@ -97,7 +99,13 @@ export default (props: Props) => {
|
|
|
97
99
|
|
|
98
100
|
const [previewUrl, setPreviewUrl] = React.useState<string | undefined>(previewUrlInit);
|
|
99
101
|
|
|
100
|
-
className = 'input upload
|
|
102
|
+
let className = 'input upload';
|
|
103
|
+
|
|
104
|
+
if (!button)
|
|
105
|
+
className += ' box';
|
|
106
|
+
|
|
107
|
+
if (customClassName !== undefined)
|
|
108
|
+
className += ' ' + customClassName;
|
|
101
109
|
|
|
102
110
|
/*----------------------------------
|
|
103
111
|
- ACTIONS
|
|
@@ -131,33 +139,49 @@ export default (props: Props) => {
|
|
|
131
139
|
|
|
132
140
|
<div class={className}>
|
|
133
141
|
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
{button ? <>
|
|
143
|
+
|
|
144
|
+
<Button type="secondary"
|
|
145
|
+
{...button === true ? {} : button}
|
|
146
|
+
>
|
|
147
|
+
{emptyText}
|
|
148
|
+
</Button>
|
|
149
|
+
|
|
150
|
+
</> : <>
|
|
151
|
+
|
|
152
|
+
{file && <>
|
|
153
|
+
<div class="preview">
|
|
154
|
+
|
|
155
|
+
{previewUrl ? (
|
|
156
|
+
<img src={previewUrl} />
|
|
157
|
+
) : typeof file === 'string' ? <>
|
|
158
|
+
<strong>A file has been selected</strong>
|
|
159
|
+
</> : file ? <>
|
|
160
|
+
<strong>{file.name}</strong>
|
|
161
|
+
</> : null}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div class="row actions sp-05">
|
|
165
|
+
|
|
166
|
+
{typeof file === 'string' && <>
|
|
167
|
+
<Button type="secondary" icon="eye" shape="pill" size="s" link={file} />
|
|
168
|
+
</>}
|
|
169
|
+
|
|
170
|
+
<Button class='bg error' icon="trash" shape="pill" size="s"
|
|
171
|
+
async onClick={() => onChange(undefined)} />
|
|
172
|
+
</div>
|
|
173
|
+
</>}
|
|
174
|
+
|
|
175
|
+
<div class="indication col al-center">
|
|
176
|
+
{typeof file === 'string' ? <>
|
|
140
177
|
<strong>A file has been selected</strong>
|
|
141
178
|
</> : file ? <>
|
|
142
179
|
<strong>{file.name}</strong>
|
|
143
|
-
</> :
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
<div class="row actions sp-05">
|
|
147
|
-
|
|
148
|
-
{typeof file === 'string' && <>
|
|
149
|
-
<Bouton type="secondary" icon="eye" shape="pill" size="s" link={file} />
|
|
150
|
-
</>}
|
|
151
|
-
|
|
152
|
-
<Bouton class='bg error' icon="trash" shape="pill" size="s"
|
|
153
|
-
async onClick={() => onChange(undefined)} />
|
|
180
|
+
</> : emptyText}
|
|
154
181
|
</div>
|
|
182
|
+
|
|
155
183
|
</>}
|
|
156
184
|
|
|
157
|
-
<div class="indication col al-center">
|
|
158
|
-
{emptyText}
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
185
|
<input type="file" onChange={selectFile} />
|
|
162
186
|
</div>
|
|
163
187
|
</InputWrapper>
|
|
@@ -255,8 +255,9 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
|
|
|
255
255
|
}).join(' ').trim();
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
public equalities = (data: TObjetDonnees,
|
|
259
|
-
keys.map(k => '' + k + ' = ' + this.esc( data[k] ))
|
|
258
|
+
public equalities = (data: TObjetDonnees, forStorage: boolean = false) => {
|
|
259
|
+
return Object.keys(data).map(k => '' + k + ' = ' + this.esc( data[k], forStorage ))
|
|
260
|
+
}
|
|
260
261
|
|
|
261
262
|
/*----------------------------------
|
|
262
263
|
- OPERATIONS: LOW LEVELf
|
|
@@ -380,7 +381,7 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
|
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
// Create equalities
|
|
383
|
-
const egalitesData = this.equalities(data).join(', ')
|
|
384
|
+
const egalitesData = this.equalities(data, true).join(', ')
|
|
384
385
|
const egalitesWhere = this.equalities(where).join(' AND ')
|
|
385
386
|
|
|
386
387
|
// Build query
|
|
@@ -76,7 +76,9 @@ export default class LocalFS<
|
|
|
76
76
|
bucketName: TBucketName,
|
|
77
77
|
filename: string
|
|
78
78
|
) {
|
|
79
|
-
|
|
79
|
+
const bucketDir = this.config.buckets[bucketName];
|
|
80
|
+
const fullPath = path.join( this.rootDir, bucketDir, filename || '.' );
|
|
81
|
+
return fullPath;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
public async readDir( bucketName: TBucketName, dirname?: string ) {
|
|
@@ -135,7 +137,7 @@ export default class LocalFS<
|
|
|
135
137
|
const fullPath = path.join( this.rootDir, bucketDir, filename );
|
|
136
138
|
|
|
137
139
|
this.config.debug && console.log(`readFile ${fullPath}`);
|
|
138
|
-
return fs.readFileSync( fullPath )
|
|
140
|
+
return fs.readFileSync( fullPath );
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
public createReadStream( bucketName: TBucketName, filename: string ) {
|
|
@@ -605,8 +605,8 @@ declare type Routes = {
|
|
|
605
605
|
} else {
|
|
606
606
|
|
|
607
607
|
// For debugging HTTP errors
|
|
608
|
-
if (this.app.env.profile === "dev")
|
|
609
|
-
console.warn(e)
|
|
608
|
+
/*if (this.app.env.profile === "dev")
|
|
609
|
+
console.warn(e);*/
|
|
610
610
|
|
|
611
611
|
await this.app.runHook('error.' + code, e, request);
|
|
612
612
|
}
|
|
@@ -24,7 +24,21 @@ import ServerResponse from '../response';
|
|
|
24
24
|
- TYPES
|
|
25
25
|
----------------------------------*/
|
|
26
26
|
|
|
27
|
-
const localeFilter = (input: any) =>
|
|
27
|
+
const localeFilter = (input: any) => {
|
|
28
|
+
|
|
29
|
+
// Data type
|
|
30
|
+
if (typeof input !== 'string')
|
|
31
|
+
return;
|
|
32
|
+
|
|
33
|
+
// Extract ISO code
|
|
34
|
+
let lang = input.trim().split(/[-_]/)[0].toLowerCase();
|
|
35
|
+
|
|
36
|
+
// Check size
|
|
37
|
+
if (!ISO6391.validate(lang))
|
|
38
|
+
return;
|
|
39
|
+
|
|
40
|
+
return lang.toUpperCase();
|
|
41
|
+
}
|
|
28
42
|
|
|
29
43
|
export type UploadedFile = With<FileToUpload, 'md5'|'ext'>
|
|
30
44
|
|
|
@@ -310,8 +310,15 @@ export default class ServerResponse<
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
// envoi filename
|
|
313
|
-
const file = await disk.readFile('data', filename, {
|
|
313
|
+
const file = await disk.readFile('data', filename, {
|
|
314
|
+
encoding: 'buffer'
|
|
315
|
+
});
|
|
314
316
|
this.data = file;
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
// Mimetype
|
|
320
|
+
this.headers['Content-Type'] = 'image/jpeg';
|
|
321
|
+
|
|
315
322
|
return this.end();
|
|
316
323
|
}
|
|
317
324
|
|
package/src/server/utils/rte.ts
CHANGED
package/src/server/utils/slug.ts
CHANGED
|
@@ -37,31 +37,43 @@ export class Slug {
|
|
|
37
37
|
|
|
38
38
|
// Check if already existing
|
|
39
39
|
if (SQL !== undefined) {
|
|
40
|
-
|
|
41
|
-
const escapedSlug = escapeStringRegexp(slug);
|
|
42
|
-
|
|
43
|
-
const duplicates = await SQL.selectVal<number>(`
|
|
44
|
-
SELECT
|
|
45
|
-
IF( ${column} LIKE ${SQL.esc(slug)},
|
|
46
|
-
1,
|
|
47
|
-
CAST(SUBSTRING_INDEX(slug, '-', -1) AS UNSIGNED)
|
|
48
|
-
) AS duplicates
|
|
49
|
-
FROM ${table}
|
|
50
|
-
WHERE
|
|
51
|
-
${column} LIKE ${SQL.esc(slug)}
|
|
52
|
-
OR
|
|
53
|
-
${column} REGEXP '^${escapedSlug}-[0-9]+$'
|
|
54
|
-
ORDER BY duplicates DESC
|
|
55
|
-
LIMIT 1
|
|
56
|
-
`);
|
|
57
|
-
|
|
58
|
-
if (duplicates && duplicates > 0)
|
|
59
|
-
slug += `-${duplicates + 1}`;
|
|
40
|
+
slug = await this.Correct(slug, SQL, table, column);
|
|
60
41
|
}
|
|
61
42
|
|
|
62
43
|
return slug;
|
|
63
44
|
}
|
|
64
45
|
|
|
46
|
+
public async Correct(
|
|
47
|
+
slug: string,
|
|
48
|
+
SQL: SQL,
|
|
49
|
+
table: string,
|
|
50
|
+
column: string
|
|
51
|
+
) {
|
|
52
|
+
|
|
53
|
+
const escapedSlug = escapeStringRegexp(slug);
|
|
54
|
+
|
|
55
|
+
const duplicates = await SQL.selectVal<number>(`
|
|
56
|
+
SELECT
|
|
57
|
+
IF( ${column} LIKE ${SQL.esc(slug)},
|
|
58
|
+
1,
|
|
59
|
+
CAST(SUBSTRING_INDEX(slug, '-', -1) AS UNSIGNED)
|
|
60
|
+
) AS duplicates
|
|
61
|
+
FROM ${table}
|
|
62
|
+
WHERE
|
|
63
|
+
${column} LIKE ${SQL.esc(slug)}
|
|
64
|
+
OR
|
|
65
|
+
${column} REGEXP '^${escapedSlug}-[0-9]+$'
|
|
66
|
+
ORDER BY duplicates DESC
|
|
67
|
+
LIMIT 1
|
|
68
|
+
`);
|
|
69
|
+
|
|
70
|
+
if (duplicates && duplicates > 0)
|
|
71
|
+
slug += `-${duplicates + 1}`;
|
|
72
|
+
|
|
73
|
+
return slug;
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
export default new Slug;
|