@axium/storage 0.1.4 → 0.2.0
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/dist/client.d.ts +1 -0
- package/dist/client.js +8 -8
- package/dist/common.d.ts +4 -5
- package/dist/common.js +1 -1
- package/dist/plugin.js +4 -3
- package/dist/server.d.ts +11 -4
- package/dist/server.js +35 -7
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StorageItemMetadata, StorageItemUpdate, UserFilesInfo } from './common.js';
|
|
2
2
|
import type { ItemSelection } from './selection.js';
|
|
3
|
+
export declare function parseItem(result: StorageItemMetadata): StorageItemMetadata;
|
|
3
4
|
export declare function uploadItem(file: File): Promise<StorageItemMetadata>;
|
|
4
5
|
export declare function updateItem(fileId: string, data: Blob): Promise<StorageItemMetadata>;
|
|
5
6
|
export declare function getItemMetadata(fileId: string): Promise<StorageItemMetadata>;
|
package/dist/client.js
CHANGED
|
@@ -22,7 +22,7 @@ async function _upload(method, url, data) {
|
|
|
22
22
|
json.modifiedAt = new Date(json.modifiedAt);
|
|
23
23
|
return json;
|
|
24
24
|
}
|
|
25
|
-
function
|
|
25
|
+
export function parseItem(result) {
|
|
26
26
|
result.createdAt = new Date(result.createdAt);
|
|
27
27
|
result.modifiedAt = new Date(result.modifiedAt);
|
|
28
28
|
if (result.trashedAt)
|
|
@@ -30,19 +30,19 @@ function _parse(result) {
|
|
|
30
30
|
return result;
|
|
31
31
|
}
|
|
32
32
|
export async function uploadItem(file) {
|
|
33
|
-
return
|
|
33
|
+
return parseItem(await _upload('PUT', '/raw/storage', file));
|
|
34
34
|
}
|
|
35
35
|
export async function updateItem(fileId, data) {
|
|
36
|
-
return
|
|
36
|
+
return parseItem(await _upload('POST', '/raw/storage/' + fileId, data));
|
|
37
37
|
}
|
|
38
38
|
export async function getItemMetadata(fileId) {
|
|
39
39
|
const result = await fetchAPI('GET', 'storage/item/:id', undefined, fileId);
|
|
40
|
-
return
|
|
40
|
+
return parseItem(result);
|
|
41
41
|
}
|
|
42
42
|
export async function getDirectoryMetadata(parentId) {
|
|
43
43
|
const result = await fetchAPI('GET', 'storage/directory/:id', undefined, parentId);
|
|
44
44
|
for (const item of result)
|
|
45
|
-
|
|
45
|
+
parseItem(item);
|
|
46
46
|
return result;
|
|
47
47
|
}
|
|
48
48
|
export async function downloadItem(fileId) {
|
|
@@ -55,15 +55,15 @@ export async function downloadItem(fileId) {
|
|
|
55
55
|
}
|
|
56
56
|
export async function updateItemMetadata(fileId, metadata) {
|
|
57
57
|
const result = await fetchAPI('PATCH', 'storage/item/:id', metadata, fileId);
|
|
58
|
-
return
|
|
58
|
+
return parseItem(result);
|
|
59
59
|
}
|
|
60
60
|
export async function deleteItem(fileId) {
|
|
61
61
|
const result = await fetchAPI('DELETE', 'storage/item/:id', undefined, fileId);
|
|
62
|
-
return
|
|
62
|
+
return parseItem(result);
|
|
63
63
|
}
|
|
64
64
|
export async function getUserFiles(userId) {
|
|
65
65
|
const result = await fetchAPI('GET', 'users/:id/storage', undefined, userId);
|
|
66
66
|
for (const item of result.items)
|
|
67
|
-
|
|
67
|
+
parseItem(item);
|
|
68
68
|
return result;
|
|
69
69
|
}
|
package/dist/common.d.ts
CHANGED
|
@@ -43,10 +43,10 @@ export declare const StorageItemUpdate: z.ZodObject<{
|
|
|
43
43
|
owner: z.ZodOptional<z.ZodUUID>;
|
|
44
44
|
trash: z.ZodOptional<z.ZodBoolean>;
|
|
45
45
|
restrict: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
-
|
|
46
|
+
publicPermission: z.ZodOptional<z.ZodNumber>;
|
|
47
47
|
}, z.core.$strip>;
|
|
48
48
|
export type StorageItemUpdate = z.infer<typeof StorageItemUpdate>;
|
|
49
|
-
export interface StorageItemMetadata {
|
|
49
|
+
export interface StorageItemMetadata<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
50
50
|
createdAt: Date;
|
|
51
51
|
dataURL?: string;
|
|
52
52
|
hash: string;
|
|
@@ -56,10 +56,9 @@ export interface StorageItemMetadata {
|
|
|
56
56
|
name: string;
|
|
57
57
|
userId: string;
|
|
58
58
|
parentId: string | null;
|
|
59
|
-
|
|
60
|
-
restricted: boolean;
|
|
59
|
+
publicPermission: number;
|
|
61
60
|
size: number;
|
|
62
61
|
trashedAt: Date | null;
|
|
63
62
|
type: string;
|
|
64
|
-
|
|
63
|
+
metadata?: T;
|
|
65
64
|
}
|
package/dist/common.js
CHANGED
package/dist/plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatBytes } from '@axium/core/format';
|
|
1
2
|
import config from '@axium/server/config';
|
|
2
3
|
import { count, database, warnExists } from '@axium/server/database';
|
|
3
4
|
import { done, start } from '@axium/server/io';
|
|
@@ -5,9 +6,8 @@ import { sql } from 'kysely';
|
|
|
5
6
|
import pkg from '../package.json' with { type: 'json' };
|
|
6
7
|
import './common.js';
|
|
7
8
|
import './server.js';
|
|
8
|
-
import { formatBytes } from '@axium/core/format';
|
|
9
9
|
async function statusText() {
|
|
10
|
-
const items = await count('storage');
|
|
10
|
+
const { storage: items } = await count('storage');
|
|
11
11
|
const { size } = await database
|
|
12
12
|
.selectFrom('storage')
|
|
13
13
|
.select(eb => eb.fn.sum('size').as('size'))
|
|
@@ -30,7 +30,8 @@ async function db_init(opt, db) {
|
|
|
30
30
|
.addColumn('name', 'text', col => col.defaultTo(null))
|
|
31
31
|
.addColumn('type', 'text', col => col.notNull())
|
|
32
32
|
.addColumn('immutable', 'boolean', col => col.notNull())
|
|
33
|
-
.addColumn('
|
|
33
|
+
.addColumn('publicPermission', 'integer', col => col.notNull().defaultTo(0))
|
|
34
|
+
.addColumn('metadata', 'jsonb', col => col.defaultTo('{}'))
|
|
34
35
|
.execute()
|
|
35
36
|
.then(done)
|
|
36
37
|
.catch(warnExists);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Schema } from '@axium/server/database';
|
|
2
|
-
import type { Generated, Selectable } from 'kysely';
|
|
2
|
+
import type { ExpressionBuilder, Generated, Selectable } from 'kysely';
|
|
3
3
|
import type { StorageItemMetadata, StorageLimits, StorageUsage } from './common.js';
|
|
4
4
|
import './polyfills.js';
|
|
5
5
|
declare module '@axium/server/database' {
|
|
@@ -17,9 +17,13 @@ declare module '@axium/server/database' {
|
|
|
17
17
|
trashedAt: Date | null;
|
|
18
18
|
type: string;
|
|
19
19
|
userId: string;
|
|
20
|
-
|
|
20
|
+
publicPermission: Generated<number>;
|
|
21
|
+
metadata: Generated<Record<string, unknown>>;
|
|
21
22
|
};
|
|
22
23
|
}
|
|
24
|
+
interface ExpectedSchema {
|
|
25
|
+
storage: ColumnTypes<Schema['storage']>;
|
|
26
|
+
}
|
|
23
27
|
}
|
|
24
28
|
declare module '@axium/server/config' {
|
|
25
29
|
interface Config {
|
|
@@ -49,12 +53,15 @@ declare module '@axium/server/config' {
|
|
|
49
53
|
export interface StorageItem extends StorageItemMetadata {
|
|
50
54
|
data: Uint8Array<ArrayBufferLike>;
|
|
51
55
|
}
|
|
52
|
-
export declare function parseItem(item: Selectable<Schema['storage']>
|
|
56
|
+
export declare function parseItem<T extends Record<string, unknown> = Record<string, unknown>>(item: Selectable<Schema['storage']> & {
|
|
57
|
+
hash: string;
|
|
58
|
+
}): StorageItemMetadata<T>;
|
|
53
59
|
/**
|
|
54
60
|
* Returns the current usage of the storage for a user in bytes.
|
|
55
61
|
*/
|
|
56
62
|
export declare function currentUsage(userId: string): Promise<StorageUsage>;
|
|
57
|
-
export declare function get(itemId: string): Promise<StorageItemMetadata
|
|
63
|
+
export declare function get<T extends Record<string, unknown> = Record<string, unknown>>(itemId: string): Promise<StorageItemMetadata<T>>;
|
|
64
|
+
export declare function withEncodedHash(eb: ExpressionBuilder<Schema, 'storage'>): import("kysely").AliasedExpression<string, "hash">;
|
|
58
65
|
export type ExternalLimitHandler = (userId?: string) => StorageLimits | Promise<StorageLimits>;
|
|
59
66
|
/**
|
|
60
67
|
* Define the handler to get limits for a user externally.
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getSessionAndUser } from '@axium/server/auth';
|
|
2
2
|
import { addConfigDefaults, config } from '@axium/server/config';
|
|
3
|
-
import { connect, database } from '@axium/server/database';
|
|
3
|
+
import { connect, database, expectedTypes } from '@axium/server/database';
|
|
4
4
|
import { dirs } from '@axium/server/io';
|
|
5
5
|
import { checkAuth, getToken, parseBody, withError } from '@axium/server/requests';
|
|
6
6
|
import { addRoute } from '@axium/server/routes';
|
|
@@ -12,6 +12,22 @@ import { join } from 'node:path/posix';
|
|
|
12
12
|
import * as z from 'zod';
|
|
13
13
|
import { StorageItemUpdate } from './common.js';
|
|
14
14
|
import './polyfills.js';
|
|
15
|
+
expectedTypes.storage = {
|
|
16
|
+
createdAt: { type: 'timestamptz', required: true, hasDefault: true },
|
|
17
|
+
hash: { type: 'bytea', required: true },
|
|
18
|
+
id: { type: 'uuid', required: true, hasDefault: true },
|
|
19
|
+
immutable: { type: 'bool', required: true, hasDefault: true },
|
|
20
|
+
modifiedAt: { type: 'timestamptz', required: true, hasDefault: true },
|
|
21
|
+
name: { type: 'text' },
|
|
22
|
+
parentId: { type: 'uuid' },
|
|
23
|
+
restricted: { type: 'bool', required: true, hasDefault: true },
|
|
24
|
+
size: { type: 'int4', required: true },
|
|
25
|
+
trashedAt: { type: 'timestampz' },
|
|
26
|
+
type: { type: 'text', required: true },
|
|
27
|
+
userId: { type: 'uuid', required: true, hasDefault: true },
|
|
28
|
+
publicPermission: { type: 'int4', required: true, hasDefault: true },
|
|
29
|
+
metadata: { type: 'jsonb', required: true, hasDefault: true },
|
|
30
|
+
};
|
|
15
31
|
const defaultCASMime = [/video\/.*/, /audio\/.*/];
|
|
16
32
|
addConfigDefaults({
|
|
17
33
|
storage: {
|
|
@@ -34,7 +50,7 @@ addConfigDefaults({
|
|
|
34
50
|
export function parseItem(item) {
|
|
35
51
|
return {
|
|
36
52
|
...item,
|
|
37
|
-
|
|
53
|
+
metadata: item.metadata,
|
|
38
54
|
dataURL: `/raw/storage/${item.id}`,
|
|
39
55
|
};
|
|
40
56
|
}
|
|
@@ -53,9 +69,17 @@ export async function currentUsage(userId) {
|
|
|
53
69
|
}
|
|
54
70
|
export async function get(itemId) {
|
|
55
71
|
connect();
|
|
56
|
-
const result = await database
|
|
72
|
+
const result = await database
|
|
73
|
+
.selectFrom('storage')
|
|
74
|
+
.where('id', '=', itemId)
|
|
75
|
+
.selectAll()
|
|
76
|
+
.select(withEncodedHash)
|
|
77
|
+
.executeTakeFirstOrThrow();
|
|
57
78
|
return parseItem(result);
|
|
58
79
|
}
|
|
80
|
+
export function withEncodedHash(eb) {
|
|
81
|
+
return eb.fn('encode', ['hash', eb.val('hex')]).as('hash');
|
|
82
|
+
}
|
|
59
83
|
let _getLimits = null;
|
|
60
84
|
/**
|
|
61
85
|
* Define the handler to get limits for a user externally.
|
|
@@ -94,8 +118,8 @@ addRoute({
|
|
|
94
118
|
error(404, 'Item not found');
|
|
95
119
|
await checkAuth(event, item.userId);
|
|
96
120
|
const values = {};
|
|
97
|
-
if ('
|
|
98
|
-
values.
|
|
121
|
+
if ('publicPermission' in body)
|
|
122
|
+
values.publicPermission = body.publicPermission;
|
|
99
123
|
if ('trash' in body)
|
|
100
124
|
values.trashedAt = body.trash ? new Date() : null;
|
|
101
125
|
if ('owner' in body)
|
|
@@ -109,6 +133,7 @@ addRoute({
|
|
|
109
133
|
.where('id', '=', itemId)
|
|
110
134
|
.set(values)
|
|
111
135
|
.returningAll()
|
|
136
|
+
.returning(withEncodedHash)
|
|
112
137
|
.executeTakeFirstOrThrow()
|
|
113
138
|
.catch(withError('Could not update item')));
|
|
114
139
|
},
|
|
@@ -154,6 +179,7 @@ addRoute({
|
|
|
154
179
|
.where('parentId', '=', itemId)
|
|
155
180
|
.where('trashedAt', '!=', null)
|
|
156
181
|
.selectAll()
|
|
182
|
+
.select(withEncodedHash)
|
|
157
183
|
.execute();
|
|
158
184
|
return items.map(parseItem);
|
|
159
185
|
},
|
|
@@ -202,6 +228,7 @@ addRoute({
|
|
|
202
228
|
.insertInto('storage')
|
|
203
229
|
.values({ userId: userId, hash, name, size, type, immutable: useCAS, parentId })
|
|
204
230
|
.returningAll()
|
|
231
|
+
.returning(withEncodedHash)
|
|
205
232
|
.executeTakeFirstOrThrow()
|
|
206
233
|
.catch(withError('Could not create item'));
|
|
207
234
|
const path = join(config.storage.data, result.id);
|
|
@@ -256,7 +283,7 @@ addRoute({
|
|
|
256
283
|
error(403, 'Item is immutable');
|
|
257
284
|
if (item.trashedAt)
|
|
258
285
|
error(410, 'Trashed items can not be changed');
|
|
259
|
-
if (item.
|
|
286
|
+
if (item.userId != accessor.id)
|
|
260
287
|
error(403, 'Item editing is restricted to the owner');
|
|
261
288
|
const type = event.request.headers.get('content-type') || 'application/octet-stream';
|
|
262
289
|
// @todo: add this to the audit log
|
|
@@ -281,6 +308,7 @@ addRoute({
|
|
|
281
308
|
.where('id', '=', itemId)
|
|
282
309
|
.set({ size, modifiedAt: new Date(), hash })
|
|
283
310
|
.returningAll()
|
|
311
|
+
.returning(withEncodedHash)
|
|
284
312
|
.executeTakeFirstOrThrow()
|
|
285
313
|
.catch(withError('Could not update item'));
|
|
286
314
|
await writeFile(join(config.storage.data, result.id), content).catch(withError('Could not write'));
|
|
@@ -304,7 +332,7 @@ addRoute({
|
|
|
304
332
|
const userId = event.params.id;
|
|
305
333
|
await checkAuth(event, userId);
|
|
306
334
|
const [items, usage, limits] = await Promise.all([
|
|
307
|
-
database.selectFrom('storage').where('userId', '=', userId).selectAll().execute(),
|
|
335
|
+
database.selectFrom('storage').where('userId', '=', userId).selectAll().select(withEncodedHash).execute(),
|
|
308
336
|
currentUsage(userId),
|
|
309
337
|
getLimits(userId),
|
|
310
338
|
]).catch(withError('Could not fetch data'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev> (https://jamespre.dev)",
|
|
5
5
|
"description": "User file storage for Axium",
|
|
6
6
|
"funding": {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@axium/client": ">=0.1.0",
|
|
35
|
-
"@axium/core": ">=0.
|
|
36
|
-
"@axium/server": ">=0.
|
|
35
|
+
"@axium/core": ">=0.5.0",
|
|
36
|
+
"@axium/server": ">=0.17.0",
|
|
37
37
|
"@sveltejs/kit": "^2.23.0",
|
|
38
38
|
"utilium": "^2.3.8"
|
|
39
39
|
},
|