@axium/storage 0.19.2 → 0.20.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/db.json +10 -0
- package/dist/client/cli.js +1 -1
- package/dist/client/local.d.ts +1 -1
- package/dist/common.d.ts +27 -27
- package/dist/common.js +4 -4
- package/dist/server/batch.js +5 -4
- package/dist/server/cli.js +2 -2
- package/dist/server/config.js +1 -1
- package/dist/server/db.js +5 -3
- package/dist/server/hooks.js +1 -1
- package/dist/server/item.d.ts +1 -1
- package/dist/server/item.js +3 -3
- package/dist/server/raw.js +25 -16
- package/lib/Add.svelte +0 -1
- package/lib/Preview.svelte +14 -3
- package/lib/Usage.svelte +2 -2
- package/locales/en.json +1 -2
- package/package.json +3 -3
- package/routes/files/usage/+page.svelte +2 -2
package/db.json
CHANGED
package/dist/client/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ cli.command('usage')
|
|
|
26
26
|
.action(async () => {
|
|
27
27
|
const { limits, itemCount, usedBytes } = await api.getUserStats(session().userId);
|
|
28
28
|
console.log(`Items: ${itemCount} ${limits.user_items ? ' / ' + limits.user_items : ''}`);
|
|
29
|
-
console.log(`Space: ${formatBytes(usedBytes)} ${limits.user_size ? ' / ' + formatBytes(limits.user_size *
|
|
29
|
+
console.log(`Space: ${formatBytes(usedBytes)} ${limits.user_size ? ' / ' + formatBytes(limits.user_size * 1000000n) : ''}`);
|
|
30
30
|
});
|
|
31
31
|
cli.command('ls')
|
|
32
32
|
.alias('list')
|
package/dist/client/local.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ declare const StorageCache: z.ZodObject<{
|
|
|
14
14
|
name: z.ZodString;
|
|
15
15
|
userId: z.ZodUUID;
|
|
16
16
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
17
|
-
size: z.
|
|
17
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
18
18
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
19
19
|
type: z.ZodString;
|
|
20
20
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
package/dist/common.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare const StorageItemMetadata: z.ZodObject<{
|
|
|
23
23
|
name: z.ZodString;
|
|
24
24
|
userId: z.ZodUUID;
|
|
25
25
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
26
|
-
size: z.
|
|
26
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
27
27
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
28
28
|
type: z.ZodString;
|
|
29
29
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -67,12 +67,12 @@ export declare const syncProtocolVersion = 0;
|
|
|
67
67
|
export declare const StorageLimits: z.ZodObject<{
|
|
68
68
|
item_size: z.ZodInt;
|
|
69
69
|
user_items: z.ZodInt;
|
|
70
|
-
user_size: z.
|
|
70
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
71
71
|
}, z.core.$strip>;
|
|
72
72
|
export interface StorageLimits extends z.infer<typeof StorageLimits> {
|
|
73
73
|
}
|
|
74
74
|
export declare const StorageStats: z.ZodObject<{
|
|
75
|
-
usedBytes: z.
|
|
75
|
+
usedBytes: z.ZodCoercedBigInt<unknown>;
|
|
76
76
|
itemCount: z.ZodInt;
|
|
77
77
|
lastModified: z.ZodCoercedDate<unknown>;
|
|
78
78
|
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
@@ -83,9 +83,9 @@ export declare const UserStorageInfo: z.ZodObject<{
|
|
|
83
83
|
limits: z.ZodObject<{
|
|
84
84
|
item_size: z.ZodInt;
|
|
85
85
|
user_items: z.ZodInt;
|
|
86
|
-
user_size: z.
|
|
86
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
87
87
|
}, z.core.$strip>;
|
|
88
|
-
usedBytes: z.
|
|
88
|
+
usedBytes: z.ZodCoercedBigInt<unknown>;
|
|
89
89
|
itemCount: z.ZodInt;
|
|
90
90
|
lastModified: z.ZodCoercedDate<unknown>;
|
|
91
91
|
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
@@ -103,7 +103,7 @@ export declare const UserStorage: z.ZodObject<{
|
|
|
103
103
|
name: z.ZodString;
|
|
104
104
|
userId: z.ZodUUID;
|
|
105
105
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
106
|
-
size: z.
|
|
106
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
107
107
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
108
108
|
type: z.ZodString;
|
|
109
109
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -137,9 +137,9 @@ export declare const UserStorage: z.ZodObject<{
|
|
|
137
137
|
limits: z.ZodObject<{
|
|
138
138
|
item_size: z.ZodInt;
|
|
139
139
|
user_items: z.ZodInt;
|
|
140
|
-
user_size: z.
|
|
140
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
141
141
|
}, z.core.$strip>;
|
|
142
|
-
usedBytes: z.
|
|
142
|
+
usedBytes: z.ZodCoercedBigInt<unknown>;
|
|
143
143
|
itemCount: z.ZodInt;
|
|
144
144
|
lastModified: z.ZodCoercedDate<unknown>;
|
|
145
145
|
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
@@ -208,7 +208,7 @@ export declare const StorageConfig: z.ZodObject<{
|
|
|
208
208
|
limits: z.ZodObject<{
|
|
209
209
|
item_size: z.ZodInt;
|
|
210
210
|
user_items: z.ZodInt;
|
|
211
|
-
user_size: z.
|
|
211
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
212
212
|
}, z.core.$strip>;
|
|
213
213
|
trash_duration: z.ZodNumber;
|
|
214
214
|
temp_dir: z.ZodString;
|
|
@@ -221,7 +221,7 @@ declare module '@axium/core/plugins' {
|
|
|
221
221
|
}
|
|
222
222
|
export declare const StorageItemInit: z.ZodObject<{
|
|
223
223
|
name: z.ZodString;
|
|
224
|
-
size: z.
|
|
224
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
225
225
|
type: z.ZodString;
|
|
226
226
|
parentId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
227
227
|
hash: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
@@ -249,7 +249,7 @@ export declare const UploadInitResult: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
249
249
|
name: z.ZodString;
|
|
250
250
|
userId: z.ZodUUID;
|
|
251
251
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
252
|
-
size: z.
|
|
252
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
253
253
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
254
254
|
type: z.ZodString;
|
|
255
255
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -288,9 +288,9 @@ declare const StorageAPI: {
|
|
|
288
288
|
limits: z.ZodObject<{
|
|
289
289
|
item_size: z.ZodInt;
|
|
290
290
|
user_items: z.ZodInt;
|
|
291
|
-
user_size: z.
|
|
291
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
292
292
|
}, z.core.$strip>;
|
|
293
|
-
usedBytes: z.
|
|
293
|
+
usedBytes: z.ZodCoercedBigInt<unknown>;
|
|
294
294
|
itemCount: z.ZodInt;
|
|
295
295
|
lastModified: z.ZodCoercedDate<unknown>;
|
|
296
296
|
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
@@ -311,7 +311,7 @@ declare const StorageAPI: {
|
|
|
311
311
|
name: z.ZodString;
|
|
312
312
|
userId: z.ZodUUID;
|
|
313
313
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
314
|
-
size: z.
|
|
314
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
315
315
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
316
316
|
type: z.ZodString;
|
|
317
317
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -345,9 +345,9 @@ declare const StorageAPI: {
|
|
|
345
345
|
limits: z.ZodObject<{
|
|
346
346
|
item_size: z.ZodInt;
|
|
347
347
|
user_items: z.ZodInt;
|
|
348
|
-
user_size: z.
|
|
348
|
+
user_size: z.ZodCoercedBigInt<unknown>;
|
|
349
349
|
}, z.core.$strip>;
|
|
350
|
-
usedBytes: z.
|
|
350
|
+
usedBytes: z.ZodCoercedBigInt<unknown>;
|
|
351
351
|
itemCount: z.ZodInt;
|
|
352
352
|
lastModified: z.ZodCoercedDate<unknown>;
|
|
353
353
|
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
@@ -364,7 +364,7 @@ declare const StorageAPI: {
|
|
|
364
364
|
name: z.ZodString;
|
|
365
365
|
userId: z.ZodUUID;
|
|
366
366
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
367
|
-
size: z.
|
|
367
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
368
368
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
369
369
|
type: z.ZodString;
|
|
370
370
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -407,7 +407,7 @@ declare const StorageAPI: {
|
|
|
407
407
|
name: z.ZodString;
|
|
408
408
|
userId: z.ZodUUID;
|
|
409
409
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
410
|
-
size: z.
|
|
410
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
411
411
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
412
412
|
type: z.ZodString;
|
|
413
413
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -450,7 +450,7 @@ declare const StorageAPI: {
|
|
|
450
450
|
name: z.ZodString;
|
|
451
451
|
userId: z.ZodUUID;
|
|
452
452
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
453
|
-
size: z.
|
|
453
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
454
454
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
455
455
|
type: z.ZodString;
|
|
456
456
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -495,7 +495,7 @@ declare const StorageAPI: {
|
|
|
495
495
|
}, z.core.$strip>;
|
|
496
496
|
readonly PUT: readonly [z.ZodObject<{
|
|
497
497
|
name: z.ZodString;
|
|
498
|
-
size: z.
|
|
498
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
499
499
|
type: z.ZodString;
|
|
500
500
|
parentId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
501
501
|
hash: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
@@ -520,7 +520,7 @@ declare const StorageAPI: {
|
|
|
520
520
|
name: z.ZodString;
|
|
521
521
|
userId: z.ZodUUID;
|
|
522
522
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
523
|
-
size: z.
|
|
523
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
524
524
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
525
525
|
type: z.ZodString;
|
|
526
526
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -575,7 +575,7 @@ declare const StorageAPI: {
|
|
|
575
575
|
name: z.ZodString;
|
|
576
576
|
userId: z.ZodUUID;
|
|
577
577
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
578
|
-
size: z.
|
|
578
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
579
579
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
580
580
|
type: z.ZodString;
|
|
581
581
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -620,7 +620,7 @@ declare const StorageAPI: {
|
|
|
620
620
|
name: z.ZodString;
|
|
621
621
|
userId: z.ZodUUID;
|
|
622
622
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
623
|
-
size: z.
|
|
623
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
624
624
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
625
625
|
type: z.ZodString;
|
|
626
626
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -661,7 +661,7 @@ declare const StorageAPI: {
|
|
|
661
661
|
name: z.ZodString;
|
|
662
662
|
userId: z.ZodUUID;
|
|
663
663
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
664
|
-
size: z.
|
|
664
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
665
665
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
666
666
|
type: z.ZodString;
|
|
667
667
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -706,7 +706,7 @@ declare const StorageAPI: {
|
|
|
706
706
|
name: z.ZodString;
|
|
707
707
|
userId: z.ZodUUID;
|
|
708
708
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
709
|
-
size: z.
|
|
709
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
710
710
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
711
711
|
type: z.ZodString;
|
|
712
712
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -749,7 +749,7 @@ declare const StorageAPI: {
|
|
|
749
749
|
name: z.ZodString;
|
|
750
750
|
userId: z.ZodUUID;
|
|
751
751
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
752
|
-
size: z.
|
|
752
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
753
753
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
754
754
|
type: z.ZodString;
|
|
755
755
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -792,7 +792,7 @@ declare const StorageAPI: {
|
|
|
792
792
|
name: z.ZodString;
|
|
793
793
|
userId: z.ZodUUID;
|
|
794
794
|
parentId: z.ZodNullable<z.ZodUUID>;
|
|
795
|
-
size: z.
|
|
795
|
+
size: z.ZodCoercedBigInt<unknown>;
|
|
796
796
|
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
797
797
|
type: z.ZodString;
|
|
798
798
|
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
package/dist/common.js
CHANGED
|
@@ -26,7 +26,7 @@ export const StorageItemMetadata = z.object({
|
|
|
26
26
|
name: z.string(),
|
|
27
27
|
userId: z.uuid(),
|
|
28
28
|
parentId: z.uuid().nullable(),
|
|
29
|
-
size: z.
|
|
29
|
+
size: z.coerce.bigint().nonnegative(),
|
|
30
30
|
trashedAt: z.coerce.date().nullable(),
|
|
31
31
|
type: z.string(),
|
|
32
32
|
metadata: z.record(z.string(), z.unknown()),
|
|
@@ -44,10 +44,10 @@ export const StorageLimits = z.object({
|
|
|
44
44
|
/** Maximum number of items per user */
|
|
45
45
|
user_items: z.int().nonnegative(),
|
|
46
46
|
/** The maximum storage size per user in MB */
|
|
47
|
-
user_size: z.
|
|
47
|
+
user_size: z.coerce.bigint().nonnegative(),
|
|
48
48
|
});
|
|
49
49
|
export const StorageStats = z.object({
|
|
50
|
-
usedBytes: z.
|
|
50
|
+
usedBytes: z.coerce.bigint().nonnegative(),
|
|
51
51
|
itemCount: z.int().nonnegative(),
|
|
52
52
|
lastModified: z.coerce.date(),
|
|
53
53
|
lastTrashed: z.coerce.date().nullable(),
|
|
@@ -126,7 +126,7 @@ export const StorageConfig = StoragePublicConfig.safeExtend({
|
|
|
126
126
|
setServerConfig('@axium/storage', StorageConfig);
|
|
127
127
|
export const StorageItemInit = z.object({
|
|
128
128
|
name: z.string(),
|
|
129
|
-
size: z.
|
|
129
|
+
size: z.coerce.bigint().nonnegative(),
|
|
130
130
|
type: z.string(),
|
|
131
131
|
parentId: z.uuid().nullish(),
|
|
132
132
|
hash: z.hex().nullish(),
|
package/dist/server/batch.js
CHANGED
|
@@ -25,8 +25,8 @@ addRoute({
|
|
|
25
25
|
const batchHeaderSize = Number(req.headers.get('x-batch-header-size'));
|
|
26
26
|
if (!Number.isSafeInteger(batchHeaderSize) || batchHeaderSize < 2)
|
|
27
27
|
error(400, 'Invalid or missing header, X-Batch-Header-Size');
|
|
28
|
-
const size =
|
|
29
|
-
if (
|
|
28
|
+
const size = BigInt(req.headers.get('content-length') || '-1');
|
|
29
|
+
if (size < 0n)
|
|
30
30
|
error(411, 'Missing or invalid content length header');
|
|
31
31
|
const raw = await req.bytes();
|
|
32
32
|
if (raw.byteLength - batchHeaderSize > size) {
|
|
@@ -77,7 +77,8 @@ addRoute({
|
|
|
77
77
|
acl.check(item.acl, changedIds.has(item.id) ? { write: true } : { manage: true });
|
|
78
78
|
error(403, 'Missing permission for item: ' + item.id);
|
|
79
79
|
}
|
|
80
|
-
if (limits.user_size &&
|
|
80
|
+
if (limits.user_size &&
|
|
81
|
+
(usage.usedBytes + size - items.reduce((sum, item) => sum + item.size, 0n)) / 1000000n >= limits.user_size)
|
|
81
82
|
error(413, 'Not enough space');
|
|
82
83
|
const tx = await database.startTransaction().execute();
|
|
83
84
|
const results = new Map();
|
|
@@ -88,7 +89,7 @@ addRoute({
|
|
|
88
89
|
const result = await tx
|
|
89
90
|
.updateTable('storage')
|
|
90
91
|
.where('id', '=', itemId)
|
|
91
|
-
.set({ size, modifiedAt: new Date(), hash })
|
|
92
|
+
.set({ size: BigInt(size), modifiedAt: new Date(), hash })
|
|
92
93
|
.returningAll()
|
|
93
94
|
.executeTakeFirstOrThrow();
|
|
94
95
|
writeFileSync(join(getConfig('@axium/storage').data, result.id), content);
|
package/dist/server/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ cli.command('usage')
|
|
|
15
15
|
.selectFrom('storage')
|
|
16
16
|
.select(eb => eb.fn.sum('size').as('size'))
|
|
17
17
|
.executeTakeFirstOrThrow();
|
|
18
|
-
console.log(`${items} items totaling ${formatBytes(
|
|
18
|
+
console.log(`${items} items totaling ${formatBytes(BigInt(size))}`);
|
|
19
19
|
});
|
|
20
20
|
const _byteSize = (msg) => (v) => parseByteSize(v) ?? io.exit(msg);
|
|
21
21
|
cli.command('query')
|
|
@@ -48,7 +48,7 @@ cli.command('query')
|
|
|
48
48
|
}
|
|
49
49
|
let validMinSize = false;
|
|
50
50
|
if (opt.minSize !== undefined) {
|
|
51
|
-
if (opt.minSize ==
|
|
51
|
+
if (opt.minSize == 0n)
|
|
52
52
|
io.warn('Minimum size of 0 has no effect, ignoring.');
|
|
53
53
|
else {
|
|
54
54
|
query = query.where('size', '>=', opt.minSize);
|
package/dist/server/config.js
CHANGED
package/dist/server/db.js
CHANGED
|
@@ -25,9 +25,11 @@ export async function getUserStats(userId) {
|
|
|
25
25
|
eb.fn.max('trashedAt').as('lastTrashed'),
|
|
26
26
|
])
|
|
27
27
|
.executeTakeFirstOrThrow();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
return {
|
|
29
|
+
...result,
|
|
30
|
+
usedBytes: BigInt(result.usedBytes || 0n),
|
|
31
|
+
itemCount: Number(result.itemCount),
|
|
32
|
+
};
|
|
31
33
|
}
|
|
32
34
|
export async function get(itemId) {
|
|
33
35
|
const result = await database
|
package/dist/server/hooks.js
CHANGED
|
@@ -12,7 +12,7 @@ export function load() {
|
|
|
12
12
|
export async function statusText() {
|
|
13
13
|
const { storage: items } = await count('storage');
|
|
14
14
|
const size = await getTotalUse();
|
|
15
|
-
return `${items} items totaling ${formatBytes(
|
|
15
|
+
return `${items} items totaling ${formatBytes(size)}`;
|
|
16
16
|
}
|
|
17
17
|
export async function clean(opt) {
|
|
18
18
|
start('Removing expired trash items');
|
package/dist/server/item.d.ts
CHANGED
package/dist/server/item.js
CHANGED
|
@@ -31,11 +31,11 @@ export async function checkNewItem(init, session) {
|
|
|
31
31
|
: null;
|
|
32
32
|
if (parentId)
|
|
33
33
|
await authSessionForItem('storage', parentId, { write: true }, session);
|
|
34
|
-
if (
|
|
34
|
+
if (BigInt(size) < 0n)
|
|
35
35
|
error(411, 'Missing or invalid content length');
|
|
36
36
|
if (limits.user_items && usage.itemCount >= limits.user_items)
|
|
37
37
|
error(409, 'Too many items');
|
|
38
|
-
if (limits.user_size && (usage.usedBytes + size) /
|
|
38
|
+
if (limits.user_size && (usage.usedBytes + size) / 1000000n >= limits.user_size)
|
|
39
39
|
error(413, 'Not enough space');
|
|
40
40
|
if (limits.item_size && size > limits.item_size * 1_000_000)
|
|
41
41
|
error(413, 'File size exceeds maximum size');
|
|
@@ -120,7 +120,7 @@ export function startUpload(init, session) {
|
|
|
120
120
|
hash: createHash('BLAKE2b512'),
|
|
121
121
|
file,
|
|
122
122
|
fd,
|
|
123
|
-
uploadedBytes:
|
|
123
|
+
uploadedBytes: 0n,
|
|
124
124
|
sessionId: session.id,
|
|
125
125
|
userId: session.userId,
|
|
126
126
|
init,
|
package/dist/server/raw.js
CHANGED
|
@@ -57,13 +57,22 @@ import { database } from '@axium/server/database';
|
|
|
57
57
|
import { error, withError } from '@axium/server/requests';
|
|
58
58
|
import { addRoute } from '@axium/server/routes';
|
|
59
59
|
import { createHash } from 'node:crypto';
|
|
60
|
-
import { closeSync,
|
|
60
|
+
import { closeSync, copyFileSync, openSync, readSync, renameSync, unlinkSync, writeFileSync, writeSync } from 'node:fs';
|
|
61
61
|
import { join } from 'node:path/posix';
|
|
62
62
|
import * as z from 'zod';
|
|
63
63
|
import '../polyfills.js';
|
|
64
64
|
import { getLimits } from './config.js';
|
|
65
65
|
import { getUserStats, parseItem } from './db.js';
|
|
66
66
|
import { checkNewItem, createNewItem, requireUpload } from './item.js';
|
|
67
|
+
function contentDispositionFor(name) {
|
|
68
|
+
const fallback = name
|
|
69
|
+
.replace(/[\r\n]/g, '')
|
|
70
|
+
.replace(/[^\x20-\x7E]/g, '_')
|
|
71
|
+
.trim()
|
|
72
|
+
.replace(/[\\"]/g, '\\$&') || 'download';
|
|
73
|
+
const encoded = encodeURIComponent(name.replace(/[\r\n]/g, '')).replace(/['()*]/g, char => '%' + char.charCodeAt(0).toString(16).toUpperCase());
|
|
74
|
+
return `attachment; filename="${fallback}"; filename*=UTF-8''${encoded}`;
|
|
75
|
+
}
|
|
67
76
|
addRoute({
|
|
68
77
|
path: '/raw/storage',
|
|
69
78
|
async PUT(request) {
|
|
@@ -73,7 +82,7 @@ addRoute({
|
|
|
73
82
|
const { userId } = session;
|
|
74
83
|
const name = request.headers.get('x-name'); // checked in `checkNewItem`
|
|
75
84
|
const parentId = request.headers.get('x-parent');
|
|
76
|
-
const size =
|
|
85
|
+
const size = BigInt(request.headers.get('x-size') || -1);
|
|
77
86
|
const type = request.headers.get('content-type') || 'application/octet-stream';
|
|
78
87
|
const content = await request.bytes();
|
|
79
88
|
if (content.byteLength > size) {
|
|
@@ -92,22 +101,22 @@ addRoute({
|
|
|
92
101
|
if (!getConfig('@axium/storage').enabled)
|
|
93
102
|
error(503, 'User storage is disabled');
|
|
94
103
|
const upload = await requireUpload(request);
|
|
95
|
-
const size =
|
|
96
|
-
if (
|
|
104
|
+
const size = BigInt(request.headers.get('content-length') || -1);
|
|
105
|
+
if (size < 0n)
|
|
97
106
|
error(411, 'Missing or invalid content length');
|
|
98
107
|
if (upload.uploadedBytes + size > upload.init.size)
|
|
99
108
|
error(413, 'Upload exceeds allowed size');
|
|
100
109
|
const content = await request.bytes();
|
|
101
|
-
if (content.byteLength != size) {
|
|
110
|
+
if (content.byteLength != Number(size)) {
|
|
102
111
|
await audit('storage_size_mismatch', upload.userId, { item: null });
|
|
103
112
|
error(400, `Content length mismatch: expected ${size}, got ${content.byteLength}`);
|
|
104
113
|
}
|
|
105
|
-
const offset =
|
|
114
|
+
const offset = BigInt(request.headers.get('x-offset') || -1);
|
|
106
115
|
if (offset != upload.uploadedBytes)
|
|
107
116
|
error(400, `Expected offset ${upload.uploadedBytes} but got ${offset}`);
|
|
108
117
|
writeSync(upload.fd, content); // opened with 'a', this appends
|
|
109
118
|
upload.hash.update(content);
|
|
110
|
-
upload.uploadedBytes += size;
|
|
119
|
+
upload.uploadedBytes += BigInt(size);
|
|
111
120
|
if (upload.uploadedBytes != upload.init.size)
|
|
112
121
|
return new Response(null, { status: 204 });
|
|
113
122
|
const hash = upload.hash.digest();
|
|
@@ -122,7 +131,7 @@ addRoute({
|
|
|
122
131
|
catch (e) {
|
|
123
132
|
if (e.code != 'EXDEV')
|
|
124
133
|
throw e;
|
|
125
|
-
|
|
134
|
+
copyFileSync(upload.file, path);
|
|
126
135
|
}
|
|
127
136
|
});
|
|
128
137
|
try {
|
|
@@ -149,14 +158,14 @@ addRoute({
|
|
|
149
158
|
const range = request.headers.get('range');
|
|
150
159
|
const fd = openSync(path, 'r');
|
|
151
160
|
const _ = __addDisposableResource(env_1, { [Symbol.dispose]: () => closeSync(fd) }, false);
|
|
152
|
-
let start = 0, end = item.size -
|
|
161
|
+
let start = 0, end = Number(item.size - 1n), length = Number(item.size);
|
|
153
162
|
if (range) {
|
|
154
|
-
const [_start, _end =
|
|
163
|
+
const [_start, _end = end] = range
|
|
155
164
|
.replace(/bytes=/, '')
|
|
156
165
|
.split('-')
|
|
157
166
|
.map(val => (val && Number.isSafeInteger(parseInt(val)) ? parseInt(val) : undefined));
|
|
158
|
-
start = typeof _start == 'number' ? _start : item.size - _end;
|
|
159
|
-
end = typeof _start == 'number' ? _end :
|
|
167
|
+
start = typeof _start == 'number' ? _start : Number(item.size) - _end;
|
|
168
|
+
end = typeof _start == 'number' ? _end : end;
|
|
160
169
|
length = end - start + 1;
|
|
161
170
|
}
|
|
162
171
|
if (start >= item.size || end >= item.size || start > end || start < 0) {
|
|
@@ -168,13 +177,13 @@ addRoute({
|
|
|
168
177
|
const content = new Uint8Array(length);
|
|
169
178
|
readSync(fd, content, 0, length, start);
|
|
170
179
|
return new Response(content, {
|
|
171
|
-
status: length == item.size ? 200 : 206,
|
|
180
|
+
status: BigInt(length) == item.size ? 200 : 206,
|
|
172
181
|
headers: {
|
|
173
182
|
'Content-Range': `bytes ${start}-${end}/${item.size}`,
|
|
174
183
|
'Accept-Ranges': 'bytes',
|
|
175
184
|
'Content-Length': String(length),
|
|
176
185
|
'Content-Type': item.type,
|
|
177
|
-
'Content-Disposition':
|
|
186
|
+
'Content-Disposition': contentDispositionFor(item.name),
|
|
178
187
|
},
|
|
179
188
|
});
|
|
180
189
|
}
|
|
@@ -205,7 +214,7 @@ addRoute({
|
|
|
205
214
|
if (Number.isNaN(size))
|
|
206
215
|
error(411, 'Missing or invalid content length header');
|
|
207
216
|
const [usage, limits] = await Promise.all([getUserStats(item.userId), getLimits(item.userId)]).catch(withError('Could not fetch usage and/or limits'));
|
|
208
|
-
if (limits.user_size && (usage.usedBytes + size - item.size) /
|
|
217
|
+
if (limits.user_size && (usage.usedBytes + BigInt(size) - item.size) / 1000000n >= limits.user_size)
|
|
209
218
|
error(413, 'Not enough space');
|
|
210
219
|
if (limits.item_size && size > limits.item_size * 1_000_000)
|
|
211
220
|
error(413, 'File size exceeds maximum size');
|
|
@@ -220,7 +229,7 @@ addRoute({
|
|
|
220
229
|
const result = await tx
|
|
221
230
|
.updateTable('storage')
|
|
222
231
|
.where('id', '=', itemId)
|
|
223
|
-
.set({ size, modifiedAt: new Date(), hash })
|
|
232
|
+
.set({ size: BigInt(size), modifiedAt: new Date(), hash })
|
|
224
233
|
.returningAll()
|
|
225
234
|
.executeTakeFirstOrThrow();
|
|
226
235
|
writeFileSync(join(getConfig('@axium/storage').data, result.id), content);
|
package/lib/Add.svelte
CHANGED
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
<span class="menu-item" onclick={() => uploadDialog.showModal()}><Icon i="upload" />{text('storage.Add.upload')}</span>
|
|
39
39
|
{@render _item('inode/directory', text('storage.Add.new_folder'))}
|
|
40
40
|
{@render _item('text/plain', text('storage.Add.plain_text'))}
|
|
41
|
-
{@render _item('text/x-uri', text('storage.Add.url'), true)}
|
|
42
41
|
</Popover>
|
|
43
42
|
|
|
44
43
|
<FormDialog
|
package/lib/Preview.svelte
CHANGED
|
@@ -70,11 +70,11 @@
|
|
|
70
70
|
</div>
|
|
71
71
|
<div class="preview-content">
|
|
72
72
|
{#if item.type.startsWith('image/')}
|
|
73
|
-
<img src={item.dataURL} alt={item.name}
|
|
73
|
+
<img src={item.dataURL} alt={item.name} />
|
|
74
74
|
{:else if item.type.startsWith('audio/')}
|
|
75
75
|
<audio src={item.dataURL} controls></audio>
|
|
76
76
|
{:else if item.type.startsWith('video/')}
|
|
77
|
-
<video src={item.dataURL} controls
|
|
77
|
+
<video src={item.dataURL} controls>
|
|
78
78
|
<track kind="captions" />
|
|
79
79
|
</video>
|
|
80
80
|
{:else if item.type == 'application/pdf'}
|
|
@@ -195,7 +195,12 @@
|
|
|
195
195
|
|
|
196
196
|
.preview-content {
|
|
197
197
|
position: absolute;
|
|
198
|
-
inset: 3em 10em
|
|
198
|
+
inset: 3em 10em 1em;
|
|
199
|
+
display: flex;
|
|
200
|
+
text-align: center;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
flex-direction: column;
|
|
199
204
|
|
|
200
205
|
.full-fill {
|
|
201
206
|
position: absolute;
|
|
@@ -211,6 +216,12 @@
|
|
|
211
216
|
background-color: var(--bg-menu);
|
|
212
217
|
font-family: monospace;
|
|
213
218
|
}
|
|
219
|
+
|
|
220
|
+
img,
|
|
221
|
+
video {
|
|
222
|
+
max-width: 100%;
|
|
223
|
+
max-height: 100%;
|
|
224
|
+
}
|
|
214
225
|
}
|
|
215
226
|
|
|
216
227
|
.no-preview {
|
package/lib/Usage.svelte
CHANGED
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
<p>
|
|
16
16
|
<a href="/files/usage">
|
|
17
17
|
<NumberBar
|
|
18
|
-
max={info.limits.user_size && info.limits.user_size *
|
|
18
|
+
max={info.limits.user_size && info.limits.user_size * 1_000_000n}
|
|
19
19
|
value={info.usedBytes}
|
|
20
20
|
text="{formatBytes(info.usedBytes)} {!info.limits.user_size
|
|
21
21
|
? ''
|
|
22
|
-
: '/ ' + formatBytes(info.limits.user_size *
|
|
22
|
+
: '/ ' + formatBytes(info.limits.user_size * 1_000_000n)}"
|
|
23
23
|
/>
|
|
24
24
|
</a>
|
|
25
25
|
</p>
|
package/locales/en.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"description": "User file storage for Axium",
|
|
6
6
|
"funding": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"build": "tsc"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@axium/client": ">=0.19.
|
|
44
|
-
"@axium/core": ">=0.
|
|
43
|
+
"@axium/client": ">=0.19.1",
|
|
44
|
+
"@axium/core": ">=0.22.0",
|
|
45
45
|
"@axium/server": ">=0.39.0",
|
|
46
46
|
"@sveltejs/kit": "^2.27.3",
|
|
47
47
|
"utilium": "^2.6.3"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
let barText = $derived(
|
|
14
14
|
limits.user_size
|
|
15
|
-
? text('page.files.usage.bar_text', { used: formatBytes(usedBytes), total: formatBytes(limits.user_size *
|
|
15
|
+
? text('page.files.usage.bar_text', { used: formatBytes(usedBytes), total: formatBytes(limits.user_size * 1_000_000n) })
|
|
16
16
|
: text('page.files.usage.bar_text_unlimited', { used: formatBytes(usedBytes) })
|
|
17
17
|
);
|
|
18
18
|
</script>
|
|
@@ -23,6 +23,6 @@
|
|
|
23
23
|
|
|
24
24
|
<h2>{text('page.files.usage.heading')}</h2>
|
|
25
25
|
|
|
26
|
-
<p><NumberBar max={limits.user_size *
|
|
26
|
+
<p><NumberBar max={limits.user_size * 1_000_000n} value={usedBytes} text={barText} /></p>
|
|
27
27
|
|
|
28
28
|
<List bind:items emptyText={text('page.files.usage.empty')} user={data.session?.user} />
|