@axium/storage 0.12.0 → 0.13.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/dist/client/api.d.ts +1 -4
- package/dist/client/api.js +12 -47
- package/dist/client/local.d.ts +6 -4
- package/dist/common.d.ts +324 -71
- package/dist/common.js +101 -26
- package/dist/server/api.js +12 -12
- package/dist/server/batch.js +5 -5
- package/dist/server/config.d.ts +1 -32
- package/dist/server/config.js +3 -57
- package/dist/server/db.js +2 -2
- package/dist/server/hooks.d.ts +1 -0
- package/dist/server/hooks.js +5 -3
- package/dist/server/raw.js +10 -10
- package/package.json +30 -5
- package/routes/+layout.ts +1 -0
package/dist/client/api.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { StorageItemMetadata, StorageItemUpdate, UserStorage, UserStorageInfo } from '../common.js';
|
|
2
|
-
export declare function parseItem(result: StorageItemMetadata): StorageItemMetadata;
|
|
3
2
|
export interface UploadOptions {
|
|
4
3
|
parentId?: string;
|
|
5
4
|
name?: string;
|
|
@@ -7,9 +6,7 @@ export interface UploadOptions {
|
|
|
7
6
|
export declare function uploadItem(file: Blob | File, opt?: UploadOptions): Promise<StorageItemMetadata>;
|
|
8
7
|
export declare function updateItem(fileId: string, data: Blob): Promise<StorageItemMetadata>;
|
|
9
8
|
export declare function getItemMetadata(fileId: string): Promise<StorageItemMetadata>;
|
|
10
|
-
/**
|
|
11
|
-
* Gets the metadata for all items in a directory.
|
|
12
|
-
*/
|
|
9
|
+
/** Gets the metadata for all items in a directory. */
|
|
13
10
|
export declare function getDirectoryMetadata(parentId: string): Promise<StorageItemMetadata[]>;
|
|
14
11
|
export declare function downloadItem(fileId: string): Promise<Blob>;
|
|
15
12
|
export declare function updateItemMetadata(fileId: string, metadata: StorageItemUpdate): Promise<StorageItemMetadata>;
|
package/dist/client/api.js
CHANGED
|
@@ -20,16 +20,8 @@ async function _upload(method, url, data, extraHeaders = {}) {
|
|
|
20
20
|
const json = await response.json().catch(() => ({ message: 'Unknown server error (invalid JSON response)' }));
|
|
21
21
|
if (!response.ok)
|
|
22
22
|
throw new Error(json.message);
|
|
23
|
-
json.modifiedAt = new Date(json.modifiedAt);
|
|
24
23
|
return json;
|
|
25
24
|
}
|
|
26
|
-
export function parseItem(result) {
|
|
27
|
-
result.createdAt = new Date(result.createdAt);
|
|
28
|
-
result.modifiedAt = new Date(result.modifiedAt);
|
|
29
|
-
if (result.trashedAt)
|
|
30
|
-
result.trashedAt = new Date(result.trashedAt);
|
|
31
|
-
return result;
|
|
32
|
-
}
|
|
33
25
|
function rawStorage(fileId) {
|
|
34
26
|
const raw = '/raw/storage' + (fileId ? '/' + fileId : '');
|
|
35
27
|
if (prefix[0] == '/')
|
|
@@ -44,23 +36,17 @@ export async function uploadItem(file, opt = {}) {
|
|
|
44
36
|
headers['x-parent'] = opt.parentId;
|
|
45
37
|
if (opt.name)
|
|
46
38
|
headers['x-name'] = opt.name;
|
|
47
|
-
return
|
|
39
|
+
return await _upload('PUT', rawStorage(), file, headers);
|
|
48
40
|
}
|
|
49
41
|
export async function updateItem(fileId, data) {
|
|
50
|
-
return
|
|
42
|
+
return await _upload('POST', rawStorage(fileId), data);
|
|
51
43
|
}
|
|
52
44
|
export async function getItemMetadata(fileId) {
|
|
53
|
-
|
|
54
|
-
return parseItem(result);
|
|
45
|
+
return await fetchAPI('GET', 'storage/item/:id', undefined, fileId);
|
|
55
46
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Gets the metadata for all items in a directory.
|
|
58
|
-
*/
|
|
47
|
+
/** Gets the metadata for all items in a directory. */
|
|
59
48
|
export async function getDirectoryMetadata(parentId) {
|
|
60
|
-
|
|
61
|
-
for (const item of result)
|
|
62
|
-
parseItem(item);
|
|
63
|
-
return result;
|
|
49
|
+
return await fetchAPI('GET', 'storage/directory/:id', undefined, parentId);
|
|
64
50
|
}
|
|
65
51
|
export async function downloadItem(fileId) {
|
|
66
52
|
const response = await fetch(rawStorage(fileId), {
|
|
@@ -71,44 +57,23 @@ export async function downloadItem(fileId) {
|
|
|
71
57
|
return await response.blob();
|
|
72
58
|
}
|
|
73
59
|
export async function updateItemMetadata(fileId, metadata) {
|
|
74
|
-
|
|
75
|
-
return parseItem(result);
|
|
60
|
+
return await fetchAPI('PATCH', 'storage/item/:id', metadata, fileId);
|
|
76
61
|
}
|
|
77
62
|
export async function deleteItem(fileId) {
|
|
78
|
-
|
|
79
|
-
return parseItem(result);
|
|
63
|
+
return await fetchAPI('DELETE', 'storage/item/:id', undefined, fileId);
|
|
80
64
|
}
|
|
81
65
|
export async function getUserStorage(userId) {
|
|
82
|
-
|
|
83
|
-
result.lastModified = new Date(result.lastModified);
|
|
84
|
-
if (result.lastTrashed)
|
|
85
|
-
result.lastTrashed = new Date(result.lastTrashed);
|
|
86
|
-
for (const item of result.items)
|
|
87
|
-
parseItem(item);
|
|
88
|
-
return result;
|
|
66
|
+
return await fetchAPI('GET', 'users/:id/storage', undefined, userId);
|
|
89
67
|
}
|
|
90
68
|
export async function getUserStats(userId) {
|
|
91
|
-
|
|
92
|
-
result.lastModified = new Date(result.lastModified);
|
|
93
|
-
if (result.lastTrashed)
|
|
94
|
-
result.lastTrashed = new Date(result.lastTrashed);
|
|
95
|
-
return result;
|
|
69
|
+
return await fetchAPI('OPTIONS', 'users/:id/storage', undefined, userId);
|
|
96
70
|
}
|
|
97
71
|
export async function getUserTrash(userId) {
|
|
98
|
-
|
|
99
|
-
for (const item of result)
|
|
100
|
-
parseItem(item);
|
|
101
|
-
return result;
|
|
72
|
+
return await fetchAPI('GET', 'users/:id/storage/trash', undefined, userId);
|
|
102
73
|
}
|
|
103
74
|
export async function itemsSharedWith(userId) {
|
|
104
|
-
|
|
105
|
-
for (const item of result)
|
|
106
|
-
parseItem(item);
|
|
107
|
-
return result;
|
|
75
|
+
return await fetchAPI('GET', 'users/:id/storage/shared', undefined, userId);
|
|
108
76
|
}
|
|
109
77
|
export async function getUserStorageRoot(userId) {
|
|
110
|
-
|
|
111
|
-
for (const item of result)
|
|
112
|
-
parseItem(item);
|
|
113
|
-
return result;
|
|
78
|
+
return await fetchAPI('GET', 'users/:id/storage/root', undefined, userId);
|
|
114
79
|
}
|
package/dist/client/local.d.ts
CHANGED
|
@@ -23,14 +23,16 @@ declare const StorageCache: z.ZodObject<{
|
|
|
23
23
|
id: z.ZodUUID;
|
|
24
24
|
name: z.ZodString;
|
|
25
25
|
email: z.ZodOptional<z.ZodEmail>;
|
|
26
|
-
emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.
|
|
26
|
+
emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
|
|
27
27
|
image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
|
|
28
|
-
preferences: z.ZodOptional<z.ZodObject<{
|
|
29
|
-
debug: z.ZodBoolean
|
|
30
|
-
}, z.core.$strip
|
|
28
|
+
preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
|
|
29
|
+
debug: z.ZodDefault<z.ZodBoolean>;
|
|
30
|
+
}, z.core.$strip>>>;
|
|
31
31
|
roles: z.ZodArray<z.ZodString>;
|
|
32
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
32
33
|
registeredAt: z.ZodCoercedDate<unknown>;
|
|
33
34
|
isAdmin: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
isSuspended: z.ZodOptional<z.ZodBoolean>;
|
|
34
36
|
}, z.core.$strip>>;
|
|
35
37
|
}, z.core.$strip>;
|
|
36
38
|
interface StorageCache extends z.infer<typeof StorageCache> {
|
package/dist/common.d.ts
CHANGED
|
@@ -1,75 +1,4 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
|
-
declare module '@axium/core/api' {
|
|
3
|
-
interface $API {
|
|
4
|
-
'users/:id/storage': {
|
|
5
|
-
OPTIONS: UserStorageInfo;
|
|
6
|
-
GET: UserStorage;
|
|
7
|
-
};
|
|
8
|
-
'users/:id/storage/root': {
|
|
9
|
-
GET: StorageItemMetadata[];
|
|
10
|
-
};
|
|
11
|
-
'users/:id/storage/trash': {
|
|
12
|
-
GET: StorageItemMetadata[];
|
|
13
|
-
};
|
|
14
|
-
'users/:id/storage/shared': {
|
|
15
|
-
GET: StorageItemMetadata[];
|
|
16
|
-
};
|
|
17
|
-
storage: {
|
|
18
|
-
OPTIONS: StoragePublicConfig & {
|
|
19
|
-
syncProtocolVersion: number;
|
|
20
|
-
batchFormatVersion: number;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
'storage/batch': {
|
|
24
|
-
POST: [StorageBatchUpdate[], StorageItemMetadata[]];
|
|
25
|
-
};
|
|
26
|
-
'storage/item/:id': {
|
|
27
|
-
GET: StorageItemMetadata;
|
|
28
|
-
DELETE: StorageItemMetadata;
|
|
29
|
-
PATCH: [z.input<typeof StorageItemUpdate>, StorageItemMetadata];
|
|
30
|
-
};
|
|
31
|
-
'storage/directory/:id': {
|
|
32
|
-
GET: StorageItemMetadata[];
|
|
33
|
-
};
|
|
34
|
-
'storage/directory/:id/recursive': {
|
|
35
|
-
GET: (StorageItemMetadata & {
|
|
36
|
-
path: string;
|
|
37
|
-
})[];
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
export declare const StoragePublicConfig: z.ZodObject<{
|
|
42
|
-
batch: z.ZodObject<{
|
|
43
|
-
enabled: z.ZodBoolean;
|
|
44
|
-
max_items: z.ZodNumber;
|
|
45
|
-
max_item_size: z.ZodNumber;
|
|
46
|
-
}, z.core.$strip>;
|
|
47
|
-
chunk: z.ZodBoolean;
|
|
48
|
-
max_transfer_size: z.ZodNumber;
|
|
49
|
-
max_chunks: z.ZodNumber;
|
|
50
|
-
}, z.core.$strip>;
|
|
51
|
-
export interface StoragePublicConfig extends z.infer<typeof StoragePublicConfig> {
|
|
52
|
-
}
|
|
53
|
-
export declare const syncProtocolVersion = 0;
|
|
54
|
-
export declare const StorageLimits: z.ZodObject<{
|
|
55
|
-
item_size: z.ZodNumber;
|
|
56
|
-
user_items: z.ZodNumber;
|
|
57
|
-
user_size: z.ZodNumber;
|
|
58
|
-
}, z.core.$strip>;
|
|
59
|
-
export interface StorageLimits extends z.infer<typeof StorageLimits> {
|
|
60
|
-
}
|
|
61
|
-
export interface StorageStats {
|
|
62
|
-
usedBytes: number;
|
|
63
|
-
itemCount: number;
|
|
64
|
-
lastModified: Date;
|
|
65
|
-
lastTrashed: Date | null;
|
|
66
|
-
}
|
|
67
|
-
export interface UserStorageInfo extends StorageStats {
|
|
68
|
-
limits: StorageLimits;
|
|
69
|
-
}
|
|
70
|
-
export interface UserStorage extends UserStorageInfo {
|
|
71
|
-
items: StorageItemMetadata[];
|
|
72
|
-
}
|
|
73
2
|
/**
|
|
74
3
|
* An update to file metadata.
|
|
75
4
|
*/
|
|
@@ -97,6 +26,63 @@ export declare const StorageItemMetadata: z.ZodObject<{
|
|
|
97
26
|
export interface StorageItemMetadata<T extends Record<string, unknown> = Record<string, unknown>> extends z.infer<typeof StorageItemMetadata> {
|
|
98
27
|
metadata: T;
|
|
99
28
|
}
|
|
29
|
+
export declare const syncProtocolVersion = 0;
|
|
30
|
+
export declare const StorageLimits: z.ZodObject<{
|
|
31
|
+
item_size: z.ZodNumber;
|
|
32
|
+
user_items: z.ZodNumber;
|
|
33
|
+
user_size: z.ZodNumber;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
export interface StorageLimits extends z.infer<typeof StorageLimits> {
|
|
36
|
+
}
|
|
37
|
+
export declare const StorageStats: z.ZodObject<{
|
|
38
|
+
usedBytes: z.ZodNumber;
|
|
39
|
+
itemCount: z.ZodNumber;
|
|
40
|
+
lastModified: z.ZodCoercedDate<unknown>;
|
|
41
|
+
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export interface StorageStats extends z.infer<typeof StorageStats> {
|
|
44
|
+
}
|
|
45
|
+
export declare const UserStorageInfo: z.ZodObject<{
|
|
46
|
+
limits: z.ZodObject<{
|
|
47
|
+
item_size: z.ZodNumber;
|
|
48
|
+
user_items: z.ZodNumber;
|
|
49
|
+
user_size: z.ZodNumber;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
usedBytes: z.ZodNumber;
|
|
52
|
+
itemCount: z.ZodNumber;
|
|
53
|
+
lastModified: z.ZodCoercedDate<unknown>;
|
|
54
|
+
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export interface UserStorageInfo extends z.infer<typeof UserStorageInfo> {
|
|
57
|
+
}
|
|
58
|
+
export declare const UserStorage: z.ZodObject<{
|
|
59
|
+
items: z.ZodArray<z.ZodObject<{
|
|
60
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
61
|
+
dataURL: z.ZodString;
|
|
62
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
|
+
id: z.ZodUUID;
|
|
64
|
+
immutable: z.ZodBoolean;
|
|
65
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
66
|
+
name: z.ZodString;
|
|
67
|
+
userId: z.ZodUUID;
|
|
68
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
69
|
+
size: z.ZodInt;
|
|
70
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
71
|
+
type: z.ZodString;
|
|
72
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
73
|
+
}, z.core.$strip>>;
|
|
74
|
+
limits: z.ZodObject<{
|
|
75
|
+
item_size: z.ZodNumber;
|
|
76
|
+
user_items: z.ZodNumber;
|
|
77
|
+
user_size: z.ZodNumber;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
usedBytes: z.ZodNumber;
|
|
80
|
+
itemCount: z.ZodNumber;
|
|
81
|
+
lastModified: z.ZodCoercedDate<unknown>;
|
|
82
|
+
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
83
|
+
}, z.core.$strip>;
|
|
84
|
+
export interface UserStorage extends z.infer<typeof UserStorage> {
|
|
85
|
+
}
|
|
100
86
|
/**
|
|
101
87
|
* Formats:
|
|
102
88
|
*
|
|
@@ -124,3 +110,270 @@ export declare const StorageBatchUpdate: z.ZodObject<{
|
|
|
124
110
|
}, z.core.$strip>;
|
|
125
111
|
export interface StorageBatchUpdate extends z.infer<typeof StorageBatchUpdate> {
|
|
126
112
|
}
|
|
113
|
+
export declare const StoragePublicConfig: z.ZodObject<{
|
|
114
|
+
batch: z.ZodObject<{
|
|
115
|
+
enabled: z.ZodBoolean;
|
|
116
|
+
max_items: z.ZodInt;
|
|
117
|
+
max_item_size: z.ZodInt;
|
|
118
|
+
}, z.core.$strip>;
|
|
119
|
+
chunk: z.ZodBoolean;
|
|
120
|
+
max_transfer_size: z.ZodInt;
|
|
121
|
+
max_chunks: z.ZodInt;
|
|
122
|
+
}, z.core.$strip>;
|
|
123
|
+
export interface StoragePublicConfig extends z.infer<typeof StoragePublicConfig> {
|
|
124
|
+
}
|
|
125
|
+
export declare const StorageConfig: z.ZodObject<{
|
|
126
|
+
batch: z.ZodObject<{
|
|
127
|
+
enabled: z.ZodBoolean;
|
|
128
|
+
max_items: z.ZodInt;
|
|
129
|
+
max_item_size: z.ZodInt;
|
|
130
|
+
}, z.core.$strip>;
|
|
131
|
+
chunk: z.ZodBoolean;
|
|
132
|
+
max_transfer_size: z.ZodInt;
|
|
133
|
+
max_chunks: z.ZodInt;
|
|
134
|
+
app_enabled: z.ZodBoolean;
|
|
135
|
+
cas: z.ZodObject<{
|
|
136
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
137
|
+
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
138
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
139
|
+
}, z.core.$strip>;
|
|
140
|
+
data: z.ZodString;
|
|
141
|
+
enabled: z.ZodBoolean;
|
|
142
|
+
limits: z.ZodObject<{
|
|
143
|
+
item_size: z.ZodNumber;
|
|
144
|
+
user_items: z.ZodNumber;
|
|
145
|
+
user_size: z.ZodNumber;
|
|
146
|
+
}, z.core.$strip>;
|
|
147
|
+
trash_duration: z.ZodNumber;
|
|
148
|
+
}, z.core.$strip>;
|
|
149
|
+
declare module '@axium/core/plugins' {
|
|
150
|
+
interface $PluginConfigs {
|
|
151
|
+
'@axium/storage': z.infer<typeof StorageConfig>;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
declare const StorageAPI: {
|
|
155
|
+
readonly 'users/:id/storage': {
|
|
156
|
+
readonly OPTIONS: z.ZodObject<{
|
|
157
|
+
limits: z.ZodObject<{
|
|
158
|
+
item_size: z.ZodNumber;
|
|
159
|
+
user_items: z.ZodNumber;
|
|
160
|
+
user_size: z.ZodNumber;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
usedBytes: z.ZodNumber;
|
|
163
|
+
itemCount: z.ZodNumber;
|
|
164
|
+
lastModified: z.ZodCoercedDate<unknown>;
|
|
165
|
+
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
166
|
+
}, z.core.$strip>;
|
|
167
|
+
readonly GET: z.ZodObject<{
|
|
168
|
+
items: z.ZodArray<z.ZodObject<{
|
|
169
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
170
|
+
dataURL: z.ZodString;
|
|
171
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
172
|
+
id: z.ZodUUID;
|
|
173
|
+
immutable: z.ZodBoolean;
|
|
174
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
175
|
+
name: z.ZodString;
|
|
176
|
+
userId: z.ZodUUID;
|
|
177
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
178
|
+
size: z.ZodInt;
|
|
179
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
180
|
+
type: z.ZodString;
|
|
181
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
182
|
+
}, z.core.$strip>>;
|
|
183
|
+
limits: z.ZodObject<{
|
|
184
|
+
item_size: z.ZodNumber;
|
|
185
|
+
user_items: z.ZodNumber;
|
|
186
|
+
user_size: z.ZodNumber;
|
|
187
|
+
}, z.core.$strip>;
|
|
188
|
+
usedBytes: z.ZodNumber;
|
|
189
|
+
itemCount: z.ZodNumber;
|
|
190
|
+
lastModified: z.ZodCoercedDate<unknown>;
|
|
191
|
+
lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
192
|
+
}, z.core.$strip>;
|
|
193
|
+
};
|
|
194
|
+
readonly 'users/:id/storage/root': {
|
|
195
|
+
readonly GET: z.ZodArray<z.ZodObject<{
|
|
196
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
197
|
+
dataURL: z.ZodString;
|
|
198
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
199
|
+
id: z.ZodUUID;
|
|
200
|
+
immutable: z.ZodBoolean;
|
|
201
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
202
|
+
name: z.ZodString;
|
|
203
|
+
userId: z.ZodUUID;
|
|
204
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
205
|
+
size: z.ZodInt;
|
|
206
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
207
|
+
type: z.ZodString;
|
|
208
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
209
|
+
}, z.core.$strip>>;
|
|
210
|
+
};
|
|
211
|
+
readonly 'users/:id/storage/trash': {
|
|
212
|
+
readonly GET: z.ZodArray<z.ZodObject<{
|
|
213
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
214
|
+
dataURL: z.ZodString;
|
|
215
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
216
|
+
id: z.ZodUUID;
|
|
217
|
+
immutable: z.ZodBoolean;
|
|
218
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
219
|
+
name: z.ZodString;
|
|
220
|
+
userId: z.ZodUUID;
|
|
221
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
222
|
+
size: z.ZodInt;
|
|
223
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
224
|
+
type: z.ZodString;
|
|
225
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
226
|
+
}, z.core.$strip>>;
|
|
227
|
+
};
|
|
228
|
+
readonly 'users/:id/storage/shared': {
|
|
229
|
+
readonly GET: z.ZodArray<z.ZodObject<{
|
|
230
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
231
|
+
dataURL: z.ZodString;
|
|
232
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
233
|
+
id: z.ZodUUID;
|
|
234
|
+
immutable: z.ZodBoolean;
|
|
235
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
236
|
+
name: z.ZodString;
|
|
237
|
+
userId: z.ZodUUID;
|
|
238
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
239
|
+
size: z.ZodInt;
|
|
240
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
241
|
+
type: z.ZodString;
|
|
242
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
243
|
+
}, z.core.$strip>>;
|
|
244
|
+
};
|
|
245
|
+
readonly storage: {
|
|
246
|
+
readonly OPTIONS: z.ZodObject<{
|
|
247
|
+
batch: z.ZodObject<{
|
|
248
|
+
enabled: z.ZodBoolean;
|
|
249
|
+
max_items: z.ZodInt;
|
|
250
|
+
max_item_size: z.ZodInt;
|
|
251
|
+
}, z.core.$strip>;
|
|
252
|
+
chunk: z.ZodBoolean;
|
|
253
|
+
max_transfer_size: z.ZodInt;
|
|
254
|
+
max_chunks: z.ZodInt;
|
|
255
|
+
syncProtocolVersion: z.ZodNumber;
|
|
256
|
+
batchFormatVersion: z.ZodNumber;
|
|
257
|
+
}, z.core.$strip>;
|
|
258
|
+
};
|
|
259
|
+
readonly 'storage/batch': {
|
|
260
|
+
readonly POST: readonly [z.ZodArray<z.ZodObject<{
|
|
261
|
+
deleted: z.ZodArray<z.ZodUUID>;
|
|
262
|
+
metadata: z.ZodRecord<z.ZodUUID, z.ZodObject<{
|
|
263
|
+
name: z.ZodOptional<z.ZodString>;
|
|
264
|
+
owner: z.ZodOptional<z.ZodUUID>;
|
|
265
|
+
trash: z.ZodOptional<z.ZodBoolean>;
|
|
266
|
+
}, z.core.$strip>>;
|
|
267
|
+
content: z.ZodRecord<z.ZodUUID, z.ZodObject<{
|
|
268
|
+
offset: z.ZodInt;
|
|
269
|
+
size: z.ZodInt;
|
|
270
|
+
}, z.core.$strip>>;
|
|
271
|
+
}, z.core.$strip>>, z.ZodArray<z.ZodObject<{
|
|
272
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
273
|
+
dataURL: z.ZodString;
|
|
274
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
275
|
+
id: z.ZodUUID;
|
|
276
|
+
immutable: z.ZodBoolean;
|
|
277
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
278
|
+
name: z.ZodString;
|
|
279
|
+
userId: z.ZodUUID;
|
|
280
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
281
|
+
size: z.ZodInt;
|
|
282
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
283
|
+
type: z.ZodString;
|
|
284
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
285
|
+
}, z.core.$strip>>];
|
|
286
|
+
};
|
|
287
|
+
readonly 'storage/item/:id': {
|
|
288
|
+
readonly GET: z.ZodObject<{
|
|
289
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
290
|
+
dataURL: z.ZodString;
|
|
291
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
292
|
+
id: z.ZodUUID;
|
|
293
|
+
immutable: z.ZodBoolean;
|
|
294
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
295
|
+
name: z.ZodString;
|
|
296
|
+
userId: z.ZodUUID;
|
|
297
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
298
|
+
size: z.ZodInt;
|
|
299
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
300
|
+
type: z.ZodString;
|
|
301
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
302
|
+
}, z.core.$strip>;
|
|
303
|
+
readonly DELETE: z.ZodObject<{
|
|
304
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
305
|
+
dataURL: z.ZodString;
|
|
306
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
307
|
+
id: z.ZodUUID;
|
|
308
|
+
immutable: z.ZodBoolean;
|
|
309
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
310
|
+
name: z.ZodString;
|
|
311
|
+
userId: z.ZodUUID;
|
|
312
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
313
|
+
size: z.ZodInt;
|
|
314
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
315
|
+
type: z.ZodString;
|
|
316
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
317
|
+
}, z.core.$strip>;
|
|
318
|
+
readonly PATCH: readonly [z.ZodObject<{
|
|
319
|
+
name: z.ZodOptional<z.ZodString>;
|
|
320
|
+
owner: z.ZodOptional<z.ZodUUID>;
|
|
321
|
+
trash: z.ZodOptional<z.ZodBoolean>;
|
|
322
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
323
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
324
|
+
dataURL: z.ZodString;
|
|
325
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
326
|
+
id: z.ZodUUID;
|
|
327
|
+
immutable: z.ZodBoolean;
|
|
328
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
329
|
+
name: z.ZodString;
|
|
330
|
+
userId: z.ZodUUID;
|
|
331
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
332
|
+
size: z.ZodInt;
|
|
333
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
334
|
+
type: z.ZodString;
|
|
335
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
336
|
+
}, z.core.$strip>];
|
|
337
|
+
};
|
|
338
|
+
readonly 'storage/directory/:id': {
|
|
339
|
+
readonly GET: z.ZodArray<z.ZodObject<{
|
|
340
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
341
|
+
dataURL: z.ZodString;
|
|
342
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
343
|
+
id: z.ZodUUID;
|
|
344
|
+
immutable: z.ZodBoolean;
|
|
345
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
346
|
+
name: z.ZodString;
|
|
347
|
+
userId: z.ZodUUID;
|
|
348
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
349
|
+
size: z.ZodInt;
|
|
350
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
351
|
+
type: z.ZodString;
|
|
352
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
353
|
+
}, z.core.$strip>>;
|
|
354
|
+
};
|
|
355
|
+
readonly 'storage/directory/:id/recursive': {
|
|
356
|
+
readonly GET: z.ZodArray<z.ZodObject<{
|
|
357
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
358
|
+
dataURL: z.ZodString;
|
|
359
|
+
hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
360
|
+
id: z.ZodUUID;
|
|
361
|
+
immutable: z.ZodBoolean;
|
|
362
|
+
modifiedAt: z.ZodCoercedDate<unknown>;
|
|
363
|
+
name: z.ZodString;
|
|
364
|
+
userId: z.ZodUUID;
|
|
365
|
+
parentId: z.ZodNullable<z.ZodUUID>;
|
|
366
|
+
size: z.ZodInt;
|
|
367
|
+
trashedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
368
|
+
type: z.ZodString;
|
|
369
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
370
|
+
path: z.ZodString;
|
|
371
|
+
}, z.core.$strip>>;
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
type StorageAPI = typeof StorageAPI;
|
|
375
|
+
declare module '@axium/core/api' {
|
|
376
|
+
interface $API extends StorageAPI {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
export {};
|
package/dist/common.js
CHANGED
|
@@ -1,30 +1,5 @@
|
|
|
1
|
+
import { $API, setServerConfig } from '@axium/core';
|
|
1
2
|
import * as z from 'zod';
|
|
2
|
-
export const StoragePublicConfig = z.object({
|
|
3
|
-
/** Configuration for batch updates */
|
|
4
|
-
batch: z.object({
|
|
5
|
-
/** Whether to enable sending multiple files per request */
|
|
6
|
-
enabled: z.boolean(),
|
|
7
|
-
/** Maximum number of items that can be included in a single batch update */
|
|
8
|
-
max_items: z.number(),
|
|
9
|
-
/** Maximum size in KiB per item */
|
|
10
|
-
max_item_size: z.number(),
|
|
11
|
-
}),
|
|
12
|
-
/** Whether to split files larger than `max_transfer_size` into multiple chunks */
|
|
13
|
-
chunk: z.boolean(),
|
|
14
|
-
/** Maximum size in MiB per transfer/request */
|
|
15
|
-
max_transfer_size: z.number(),
|
|
16
|
-
/** Maximum number of chunks */
|
|
17
|
-
max_chunks: z.number(),
|
|
18
|
-
});
|
|
19
|
-
export const syncProtocolVersion = 0;
|
|
20
|
-
export const StorageLimits = z.object({
|
|
21
|
-
/** The maximum size per item in MB */
|
|
22
|
-
item_size: z.number(),
|
|
23
|
-
/** Maximum number of items per user */
|
|
24
|
-
user_items: z.number(),
|
|
25
|
-
/** The maximum storage size per user in MB */
|
|
26
|
-
user_size: z.number(),
|
|
27
|
-
});
|
|
28
3
|
/**
|
|
29
4
|
* An update to file metadata.
|
|
30
5
|
*/
|
|
@@ -51,6 +26,29 @@ export const StorageItemMetadata = z.object({
|
|
|
51
26
|
type: z.string(),
|
|
52
27
|
metadata: z.record(z.string(), z.unknown()),
|
|
53
28
|
});
|
|
29
|
+
export const syncProtocolVersion = 0;
|
|
30
|
+
export const StorageLimits = z.object({
|
|
31
|
+
/** The maximum size per item in MB */
|
|
32
|
+
item_size: z.number(),
|
|
33
|
+
/** Maximum number of items per user */
|
|
34
|
+
user_items: z.number(),
|
|
35
|
+
/** The maximum storage size per user in MB */
|
|
36
|
+
user_size: z.number(),
|
|
37
|
+
});
|
|
38
|
+
export const StorageStats = z.object({
|
|
39
|
+
usedBytes: z.number().nonnegative(),
|
|
40
|
+
itemCount: z.number().nonnegative(),
|
|
41
|
+
lastModified: z.coerce.date(),
|
|
42
|
+
lastTrashed: z.coerce.date().nullable(),
|
|
43
|
+
});
|
|
44
|
+
export const UserStorageInfo = z.object({
|
|
45
|
+
...StorageStats.shape,
|
|
46
|
+
limits: StorageLimits,
|
|
47
|
+
});
|
|
48
|
+
export const UserStorage = z.object({
|
|
49
|
+
...UserStorageInfo.shape,
|
|
50
|
+
items: StorageItemMetadata.array(),
|
|
51
|
+
});
|
|
54
52
|
/**
|
|
55
53
|
* Formats:
|
|
56
54
|
*
|
|
@@ -70,3 +68,80 @@ export const StorageBatchUpdate = z.object({
|
|
|
70
68
|
metadata: z.record(z.uuid(), StorageItemUpdate),
|
|
71
69
|
content: z.record(z.uuid(), BatchedContentChange),
|
|
72
70
|
});
|
|
71
|
+
export const StoragePublicConfig = z.object({
|
|
72
|
+
/** Configuration for batch updates */
|
|
73
|
+
batch: z.object({
|
|
74
|
+
/** Whether to enable sending multiple files per request */
|
|
75
|
+
enabled: z.boolean(),
|
|
76
|
+
/** Maximum number of items that can be included in a single batch update */
|
|
77
|
+
max_items: z.int().positive(),
|
|
78
|
+
/** Maximum size in KiB per item */
|
|
79
|
+
max_item_size: z.int().positive(),
|
|
80
|
+
}),
|
|
81
|
+
/** Whether to split files larger than `max_transfer_size` into multiple chunks */
|
|
82
|
+
chunk: z.boolean(),
|
|
83
|
+
/** Maximum size in MiB per transfer/request */
|
|
84
|
+
max_transfer_size: z.int().positive(),
|
|
85
|
+
/** Maximum number of chunks */
|
|
86
|
+
max_chunks: z.int().positive(),
|
|
87
|
+
});
|
|
88
|
+
export const StorageConfig = StoragePublicConfig.safeExtend({
|
|
89
|
+
/** Whether the files app is enabled. Requires `enabled` */
|
|
90
|
+
app_enabled: z.boolean(),
|
|
91
|
+
/** Content Addressable Storage (CAS) configuration */
|
|
92
|
+
cas: z
|
|
93
|
+
.object({
|
|
94
|
+
/** Whether to use CAS */
|
|
95
|
+
enabled: z.boolean(),
|
|
96
|
+
/** Mime types to include when determining if CAS should be used */
|
|
97
|
+
include: z.string().array(),
|
|
98
|
+
/** Mime types to exclude when determining if CAS should be used */
|
|
99
|
+
exclude: z.string().array(),
|
|
100
|
+
})
|
|
101
|
+
.partial(),
|
|
102
|
+
/** Path to data directory */
|
|
103
|
+
data: z.string(),
|
|
104
|
+
/** Whether the storage API endpoints are enabled */
|
|
105
|
+
enabled: z.boolean(),
|
|
106
|
+
/** Default limits */
|
|
107
|
+
limits: StorageLimits,
|
|
108
|
+
/** How many days files are kept in the trash */
|
|
109
|
+
trash_duration: z.number(),
|
|
110
|
+
});
|
|
111
|
+
setServerConfig('@axium/storage', StorageConfig);
|
|
112
|
+
const StorageAPI = {
|
|
113
|
+
'users/:id/storage': {
|
|
114
|
+
OPTIONS: UserStorageInfo,
|
|
115
|
+
GET: UserStorage,
|
|
116
|
+
},
|
|
117
|
+
'users/:id/storage/root': {
|
|
118
|
+
GET: StorageItemMetadata.array(),
|
|
119
|
+
},
|
|
120
|
+
'users/:id/storage/trash': {
|
|
121
|
+
GET: StorageItemMetadata.array(),
|
|
122
|
+
},
|
|
123
|
+
'users/:id/storage/shared': {
|
|
124
|
+
GET: StorageItemMetadata.array(),
|
|
125
|
+
},
|
|
126
|
+
storage: {
|
|
127
|
+
OPTIONS: StoragePublicConfig.extend({
|
|
128
|
+
syncProtocolVersion: z.number(),
|
|
129
|
+
batchFormatVersion: z.number(),
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
'storage/batch': {
|
|
133
|
+
POST: [StorageBatchUpdate.array(), StorageItemMetadata.array()],
|
|
134
|
+
},
|
|
135
|
+
'storage/item/:id': {
|
|
136
|
+
GET: StorageItemMetadata,
|
|
137
|
+
DELETE: StorageItemMetadata,
|
|
138
|
+
PATCH: [StorageItemUpdate, StorageItemMetadata],
|
|
139
|
+
},
|
|
140
|
+
'storage/directory/:id': {
|
|
141
|
+
GET: StorageItemMetadata.array(),
|
|
142
|
+
},
|
|
143
|
+
'storage/directory/:id/recursive': {
|
|
144
|
+
GET: StorageItemMetadata.extend({ path: z.string() }).array(),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
Object.assign($API, StorageAPI);
|
package/dist/server/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { getConfig } from '@axium/core';
|
|
1
2
|
import { checkAuthForItem, checkAuthForUser } from '@axium/server/auth';
|
|
2
|
-
import { config } from '@axium/server/config';
|
|
3
3
|
import { database } from '@axium/server/database';
|
|
4
4
|
import { error, parseBody, withError } from '@axium/server/requests';
|
|
5
5
|
import { addRoute } from '@axium/server/routes';
|
|
@@ -13,7 +13,7 @@ addRoute({
|
|
|
13
13
|
path: '/api/storage',
|
|
14
14
|
OPTIONS() {
|
|
15
15
|
return {
|
|
16
|
-
...pick(
|
|
16
|
+
...pick(getConfig('@axium/storage'), 'batch', 'chunk', 'max_chunks', 'max_transfer_size'),
|
|
17
17
|
syncProtocolVersion,
|
|
18
18
|
batchFormatVersion,
|
|
19
19
|
};
|
|
@@ -23,13 +23,13 @@ addRoute({
|
|
|
23
23
|
path: '/api/storage/item/:id',
|
|
24
24
|
params: { id: z.uuid() },
|
|
25
25
|
async GET(request, { id: itemId }) {
|
|
26
|
-
if (!
|
|
26
|
+
if (!getConfig('@axium/storage').enabled)
|
|
27
27
|
error(503, 'User storage is disabled');
|
|
28
28
|
const { item } = await checkAuthForItem(request, 'storage', itemId, { read: true });
|
|
29
29
|
return parseItem(item);
|
|
30
30
|
},
|
|
31
31
|
async PATCH(request, { id: itemId }) {
|
|
32
|
-
if (!
|
|
32
|
+
if (!getConfig('@axium/storage').enabled)
|
|
33
33
|
error(503, 'User storage is disabled');
|
|
34
34
|
const body = await parseBody(request, StorageItemUpdate);
|
|
35
35
|
await checkAuthForItem(request, 'storage', itemId, { manage: true });
|
|
@@ -51,7 +51,7 @@ addRoute({
|
|
|
51
51
|
.catch(withError('Could not update item')));
|
|
52
52
|
},
|
|
53
53
|
async DELETE(request, { id: itemId }) {
|
|
54
|
-
if (!
|
|
54
|
+
if (!getConfig('@axium/storage').enabled)
|
|
55
55
|
error(503, 'User storage is disabled');
|
|
56
56
|
const auth = await checkAuthForItem(request, 'storage', itemId, { manage: true });
|
|
57
57
|
const item = parseItem(auth.item);
|
|
@@ -63,7 +63,7 @@ addRoute({
|
|
|
63
63
|
path: '/api/storage/directory/:id',
|
|
64
64
|
params: { id: z.uuid() },
|
|
65
65
|
async GET(request, { id: itemId }) {
|
|
66
|
-
if (!
|
|
66
|
+
if (!getConfig('@axium/storage').enabled)
|
|
67
67
|
error(503, 'User storage is disabled');
|
|
68
68
|
const { item } = await checkAuthForItem(request, 'storage', itemId, { read: true });
|
|
69
69
|
if (item.type != 'inode/directory')
|
|
@@ -81,7 +81,7 @@ addRoute({
|
|
|
81
81
|
path: '/api/storage/directory/:id/recursive',
|
|
82
82
|
params: { id: z.uuid() },
|
|
83
83
|
async GET(request, { id: itemId }) {
|
|
84
|
-
if (!
|
|
84
|
+
if (!getConfig('@axium/storage').enabled)
|
|
85
85
|
error(503, 'User storage is disabled');
|
|
86
86
|
const { item } = await checkAuthForItem(request, 'storage', itemId, { read: true });
|
|
87
87
|
if (item.type != 'inode/directory')
|
|
@@ -94,14 +94,14 @@ addRoute({
|
|
|
94
94
|
path: '/api/users/:id/storage',
|
|
95
95
|
params: { id: z.uuid() },
|
|
96
96
|
async OPTIONS(request, { id: userId }) {
|
|
97
|
-
if (!
|
|
97
|
+
if (!getConfig('@axium/storage').enabled)
|
|
98
98
|
error(503, 'User storage is disabled');
|
|
99
99
|
await checkAuthForUser(request, userId);
|
|
100
100
|
const [stats, limits] = await Promise.all([getUserStats(userId), getLimits(userId)]).catch(withError('Could not fetch data'));
|
|
101
101
|
return Object.assign(stats, { limits });
|
|
102
102
|
},
|
|
103
103
|
async GET(request, { id: userId }) {
|
|
104
|
-
if (!
|
|
104
|
+
if (!getConfig('@axium/storage').enabled)
|
|
105
105
|
error(503, 'User storage is disabled');
|
|
106
106
|
await checkAuthForUser(request, userId);
|
|
107
107
|
const [items, stats, limits] = await Promise.all([
|
|
@@ -116,7 +116,7 @@ addRoute({
|
|
|
116
116
|
path: '/api/users/:id/storage/root',
|
|
117
117
|
params: { id: z.uuid() },
|
|
118
118
|
async GET(request, { id: userId }) {
|
|
119
|
-
if (!
|
|
119
|
+
if (!getConfig('@axium/storage').enabled)
|
|
120
120
|
error(503, 'User storage is disabled');
|
|
121
121
|
await checkAuthForUser(request, userId);
|
|
122
122
|
const items = await database
|
|
@@ -148,7 +148,7 @@ addRoute({
|
|
|
148
148
|
path: '/api/users/:id/storage/shared',
|
|
149
149
|
params: { id: z.uuid() },
|
|
150
150
|
async GET(request, { id: userId }) {
|
|
151
|
-
if (!
|
|
151
|
+
if (!getConfig('@axium/storage').enabled)
|
|
152
152
|
error(503, 'User storage is disabled');
|
|
153
153
|
const { user } = await checkAuthForUser(request, userId);
|
|
154
154
|
const items = await database
|
|
@@ -166,7 +166,7 @@ addRoute({
|
|
|
166
166
|
path: '/api/users/:id/storage/trash',
|
|
167
167
|
params: { id: z.uuid() },
|
|
168
168
|
async GET(request, { id: userId }) {
|
|
169
|
-
if (!
|
|
169
|
+
if (!getConfig('@axium/storage').enabled)
|
|
170
170
|
error(503, 'User storage is disabled');
|
|
171
171
|
await checkAuthForUser(request, userId);
|
|
172
172
|
const items = await database
|
package/dist/server/batch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { getConfig } from '@axium/core';
|
|
1
2
|
import * as acl from '@axium/server/acl';
|
|
2
3
|
import { audit } from '@axium/server/audit';
|
|
3
4
|
import { requireSession } from '@axium/server/auth';
|
|
4
|
-
import config from '@axium/server/config';
|
|
5
5
|
import { database } from '@axium/server/database';
|
|
6
6
|
import { withError } from '@axium/server/requests';
|
|
7
7
|
import { addRoute } from '@axium/server/routes';
|
|
@@ -16,9 +16,9 @@ import { getRecursiveIds, getUserStats, parseItem } from './db.js';
|
|
|
16
16
|
addRoute({
|
|
17
17
|
path: '/api/storage/batch',
|
|
18
18
|
async POST(req) {
|
|
19
|
-
if (!
|
|
19
|
+
if (!getConfig('@axium/storage').enabled)
|
|
20
20
|
error(503, 'User storage is disabled');
|
|
21
|
-
if (!
|
|
21
|
+
if (!getConfig('@axium/storage').batch.enabled)
|
|
22
22
|
error(503, 'Batch updates are disabled');
|
|
23
23
|
const { userId, user } = await requireSession(req);
|
|
24
24
|
const [usage, limits] = await Promise.all([getUserStats(userId), getLimits(userId)]).catch(withError('Could not fetch usage and/or limits'));
|
|
@@ -91,14 +91,14 @@ addRoute({
|
|
|
91
91
|
.set({ size, modifiedAt: new Date(), hash })
|
|
92
92
|
.returningAll()
|
|
93
93
|
.executeTakeFirstOrThrow();
|
|
94
|
-
writeFileSync(join(
|
|
94
|
+
writeFileSync(join(getConfig('@axium/storage').data, result.id), content);
|
|
95
95
|
await tx.commit().execute();
|
|
96
96
|
results.set(itemId, parseItem(result));
|
|
97
97
|
}
|
|
98
98
|
const toDelete = await Array.fromAsync(getRecursiveIds(...header.deleted)).catch(withError('Could not get items to delete', 500));
|
|
99
99
|
const deleted = await tx.deleteFrom('storage').where('id', 'in', header.deleted).returningAll().execute();
|
|
100
100
|
for (const id of toDelete)
|
|
101
|
-
unlinkSync(join(
|
|
101
|
+
unlinkSync(join(getConfig('@axium/storage').data, id));
|
|
102
102
|
for (const item of deleted)
|
|
103
103
|
results.set(item.id, parseItem(item));
|
|
104
104
|
for (const [itemId, update] of Object.entries(header.metadata)) {
|
package/dist/server/config.d.ts
CHANGED
|
@@ -1,35 +1,5 @@
|
|
|
1
|
-
import { StorageLimits } from '../common.js';
|
|
1
|
+
import type { StorageLimits } from '../common.js';
|
|
2
2
|
import '../polyfills.js';
|
|
3
|
-
import * as z from 'zod';
|
|
4
|
-
declare const StorageConfig: z.ZodObject<{
|
|
5
|
-
batch: z.ZodObject<{
|
|
6
|
-
enabled: z.ZodBoolean;
|
|
7
|
-
max_items: z.ZodNumber;
|
|
8
|
-
max_item_size: z.ZodNumber;
|
|
9
|
-
}, z.core.$strip>;
|
|
10
|
-
chunk: z.ZodBoolean;
|
|
11
|
-
max_transfer_size: z.ZodNumber;
|
|
12
|
-
max_chunks: z.ZodNumber;
|
|
13
|
-
app_enabled: z.ZodBoolean;
|
|
14
|
-
cas: z.ZodOptional<z.ZodObject<{
|
|
15
|
-
enabled: z.ZodBoolean;
|
|
16
|
-
include: z.ZodArray<z.ZodString>;
|
|
17
|
-
exclude: z.ZodArray<z.ZodString>;
|
|
18
|
-
}, z.core.$strip>>;
|
|
19
|
-
data: z.ZodString;
|
|
20
|
-
enabled: z.ZodBoolean;
|
|
21
|
-
limits: z.ZodObject<{
|
|
22
|
-
item_size: z.ZodNumber;
|
|
23
|
-
user_items: z.ZodNumber;
|
|
24
|
-
user_size: z.ZodNumber;
|
|
25
|
-
}, z.core.$strip>;
|
|
26
|
-
trash_duration: z.ZodNumber;
|
|
27
|
-
}, z.core.$strip>;
|
|
28
|
-
declare module '@axium/server/config' {
|
|
29
|
-
interface Config {
|
|
30
|
-
storage: z.infer<typeof StorageConfig>;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
3
|
export declare const defaultCASMime: RegExp[];
|
|
34
4
|
declare module '@axium/server/audit' {
|
|
35
5
|
interface $EventTypes {
|
|
@@ -50,4 +20,3 @@ export type ExternalLimitHandler = (userId?: string) => StorageLimits | Promise<
|
|
|
50
20
|
*/
|
|
51
21
|
export declare function useLimits(handler: ExternalLimitHandler): void;
|
|
52
22
|
export declare function getLimits(userId?: string): Promise<StorageLimits>;
|
|
53
|
-
export {};
|
package/dist/server/config.js
CHANGED
|
@@ -1,62 +1,8 @@
|
|
|
1
|
-
import { Severity } from '@axium/core';
|
|
1
|
+
import { getConfig, Severity } from '@axium/core';
|
|
2
2
|
import { addEvent } from '@axium/server/audit';
|
|
3
|
-
import { addConfig, addConfigDefaults, config } from '@axium/server/config';
|
|
4
|
-
import { StorageLimits, StoragePublicConfig } from '../common.js';
|
|
5
|
-
import '../polyfills.js';
|
|
6
3
|
import * as z from 'zod';
|
|
7
|
-
|
|
8
|
-
/** Whether the files app is enabled. Requires `enabled` */
|
|
9
|
-
app_enabled: z.boolean(),
|
|
10
|
-
/** Content Addressable Storage (CAS) configuration */
|
|
11
|
-
cas: z
|
|
12
|
-
.object({
|
|
13
|
-
/** Whether to use CAS */
|
|
14
|
-
enabled: z.boolean(),
|
|
15
|
-
/** Mime types to include when determining if CAS should be used */
|
|
16
|
-
include: z.string().array(),
|
|
17
|
-
/** Mime types to exclude when determining if CAS should be used */
|
|
18
|
-
exclude: z.string().array(),
|
|
19
|
-
})
|
|
20
|
-
.optional(),
|
|
21
|
-
/** Path to data directory */
|
|
22
|
-
data: z.string(),
|
|
23
|
-
/** Whether the storage API endpoints are enabled */
|
|
24
|
-
enabled: z.boolean(),
|
|
25
|
-
/** Default limits */
|
|
26
|
-
limits: StorageLimits,
|
|
27
|
-
/** How many days files are kept in the trash */
|
|
28
|
-
trash_duration: z.number(),
|
|
29
|
-
});
|
|
30
|
-
addConfig({
|
|
31
|
-
storage: StorageConfig.optional(),
|
|
32
|
-
});
|
|
4
|
+
import '../polyfills.js';
|
|
33
5
|
export const defaultCASMime = [/video\/.*/, /audio\/.*/];
|
|
34
|
-
addConfigDefaults({
|
|
35
|
-
storage: {
|
|
36
|
-
app_enabled: true,
|
|
37
|
-
batch: {
|
|
38
|
-
enabled: false,
|
|
39
|
-
max_items: 100,
|
|
40
|
-
max_item_size: 100,
|
|
41
|
-
},
|
|
42
|
-
cas: {
|
|
43
|
-
enabled: true,
|
|
44
|
-
include: [],
|
|
45
|
-
exclude: [],
|
|
46
|
-
},
|
|
47
|
-
chunk: false,
|
|
48
|
-
data: '/srv/axium/storage',
|
|
49
|
-
enabled: true,
|
|
50
|
-
limits: {
|
|
51
|
-
user_size: 1000,
|
|
52
|
-
item_size: 100,
|
|
53
|
-
user_items: 10_000,
|
|
54
|
-
},
|
|
55
|
-
max_chunks: 10,
|
|
56
|
-
max_transfer_size: 100,
|
|
57
|
-
trash_duration: 30,
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
6
|
addEvent({
|
|
61
7
|
source: '@axium/storage',
|
|
62
8
|
name: 'storage_type_mismatch',
|
|
@@ -83,6 +29,6 @@ export async function getLimits(userId) {
|
|
|
83
29
|
return await _getLimits(userId);
|
|
84
30
|
}
|
|
85
31
|
catch {
|
|
86
|
-
return
|
|
32
|
+
return getConfig('@axium/storage').limits;
|
|
87
33
|
}
|
|
88
34
|
}
|
package/dist/server/db.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig } from '@axium/core';
|
|
2
2
|
import { database } from '@axium/server/database';
|
|
3
3
|
import { withError } from '@axium/server/requests';
|
|
4
4
|
import { unlinkSync } from 'node:fs';
|
|
@@ -62,5 +62,5 @@ export async function deleteRecursive(deleteSelf, ...itemId) {
|
|
|
62
62
|
toDelete.push(...itemId);
|
|
63
63
|
await database.deleteFrom('storage').where('id', '=', itemId).returningAll().execute().catch(withError('Could not delete item'));
|
|
64
64
|
for (const id of toDelete)
|
|
65
|
-
unlinkSync(join(
|
|
65
|
+
unlinkSync(join(getConfig('@axium/storage').data, id));
|
|
66
66
|
}
|
package/dist/server/hooks.d.ts
CHANGED
package/dist/server/hooks.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { getConfig } from '@axium/core';
|
|
1
2
|
import { formatBytes } from '@axium/core/format';
|
|
2
3
|
import { done, start } from '@axium/core/node/io';
|
|
3
|
-
import config from '@axium/server/config';
|
|
4
4
|
import { count, database } from '@axium/server/database';
|
|
5
5
|
import { mkdirSync } from 'node:fs';
|
|
6
6
|
import '../common.js';
|
|
7
7
|
import './index.js';
|
|
8
|
-
|
|
8
|
+
export function load() {
|
|
9
|
+
mkdirSync(getConfig('@axium/storage').data, { recursive: true });
|
|
10
|
+
}
|
|
9
11
|
export async function statusText() {
|
|
10
12
|
const { storage: items } = await count('storage');
|
|
11
13
|
const { size } = await database
|
|
@@ -16,7 +18,7 @@ export async function statusText() {
|
|
|
16
18
|
}
|
|
17
19
|
export async function clean(opt) {
|
|
18
20
|
start('Removing expired trash items');
|
|
19
|
-
const nDaysAgo = new Date(Date.now() - 86400000 *
|
|
21
|
+
const nDaysAgo = new Date(Date.now() - 86400000 * getConfig('@axium/storage').trash_duration);
|
|
20
22
|
await database
|
|
21
23
|
.deleteFrom('storage')
|
|
22
24
|
.where('trashedAt', 'is not', null)
|
package/dist/server/raw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { getConfig } from '@axium/core';
|
|
1
2
|
import { audit } from '@axium/server/audit';
|
|
2
3
|
import { checkAuthForItem, requireSession } from '@axium/server/auth';
|
|
3
|
-
import { config } from '@axium/server/config';
|
|
4
4
|
import { database } from '@axium/server/database';
|
|
5
5
|
import { error, withError } from '@axium/server/requests';
|
|
6
6
|
import { addRoute } from '@axium/server/routes';
|
|
@@ -14,7 +14,7 @@ import { getUserStats, parseItem } from './db.js';
|
|
|
14
14
|
addRoute({
|
|
15
15
|
path: '/raw/storage',
|
|
16
16
|
async PUT(request) {
|
|
17
|
-
if (!
|
|
17
|
+
if (!getConfig('@axium/storage').enabled)
|
|
18
18
|
error(503, 'User storage is disabled');
|
|
19
19
|
const { userId } = await requireSession(request);
|
|
20
20
|
const [usage, limits] = await Promise.all([getUserStats(userId), getLimits(userId)]).catch(withError('Could not fetch usage and/or limits'));
|
|
@@ -50,9 +50,9 @@ addRoute({
|
|
|
50
50
|
const isDirectory = type == 'inode/directory';
|
|
51
51
|
if (isDirectory && size > 0)
|
|
52
52
|
error(400, 'Directories can not have content');
|
|
53
|
-
const useCAS =
|
|
53
|
+
const useCAS = getConfig('@axium/storage').cas.enabled &&
|
|
54
54
|
!isDirectory &&
|
|
55
|
-
(defaultCASMime.some(pattern => pattern.test(type)) ||
|
|
55
|
+
(defaultCASMime.some(pattern => pattern.test(type)) || getConfig('@axium/storage').cas.include?.some(mime => type.match(mime)));
|
|
56
56
|
const hash = isDirectory ? null : createHash('BLAKE2b512').update(content).digest();
|
|
57
57
|
const tx = await database.startTransaction().execute();
|
|
58
58
|
try {
|
|
@@ -61,7 +61,7 @@ addRoute({
|
|
|
61
61
|
.values({ userId, hash, name, size, type, immutable: useCAS, parentId })
|
|
62
62
|
.returningAll()
|
|
63
63
|
.executeTakeFirstOrThrow());
|
|
64
|
-
const path = join(
|
|
64
|
+
const path = join(getConfig('@axium/storage').data, item.id);
|
|
65
65
|
if (!useCAS) {
|
|
66
66
|
if (!isDirectory)
|
|
67
67
|
writeFileSync(path, content);
|
|
@@ -81,7 +81,7 @@ addRoute({
|
|
|
81
81
|
await tx.commit().execute();
|
|
82
82
|
return item;
|
|
83
83
|
}
|
|
84
|
-
linkSync(join(
|
|
84
|
+
linkSync(join(getConfig('@axium/storage').data, existing.id), path);
|
|
85
85
|
await tx.commit().execute();
|
|
86
86
|
return item;
|
|
87
87
|
}
|
|
@@ -95,12 +95,12 @@ addRoute({
|
|
|
95
95
|
path: '/raw/storage/:id',
|
|
96
96
|
params: { id: z.uuid() },
|
|
97
97
|
async GET(request, { id: itemId }) {
|
|
98
|
-
if (!
|
|
98
|
+
if (!getConfig('@axium/storage').enabled)
|
|
99
99
|
error(503, 'User storage is disabled');
|
|
100
100
|
const { item } = await checkAuthForItem(request, 'storage', itemId, { read: true });
|
|
101
101
|
if (item.trashedAt)
|
|
102
102
|
error(410, 'Trashed items can not be downloaded');
|
|
103
|
-
const content = new Uint8Array(readFileSync(join(
|
|
103
|
+
const content = new Uint8Array(readFileSync(join(getConfig('@axium/storage').data, item.id)));
|
|
104
104
|
return new Response(content, {
|
|
105
105
|
headers: {
|
|
106
106
|
'Content-Type': item.type,
|
|
@@ -109,7 +109,7 @@ addRoute({
|
|
|
109
109
|
});
|
|
110
110
|
},
|
|
111
111
|
async POST(request, { id: itemId }) {
|
|
112
|
-
if (!
|
|
112
|
+
if (!getConfig('@axium/storage').enabled)
|
|
113
113
|
error(503, 'User storage is disabled');
|
|
114
114
|
const { item, session } = await checkAuthForItem(request, 'storage', itemId, { write: true });
|
|
115
115
|
if (item.immutable)
|
|
@@ -145,7 +145,7 @@ addRoute({
|
|
|
145
145
|
.set({ size, modifiedAt: new Date(), hash })
|
|
146
146
|
.returningAll()
|
|
147
147
|
.executeTakeFirstOrThrow();
|
|
148
|
-
writeFileSync(join(
|
|
148
|
+
writeFileSync(join(getConfig('@axium/storage').data, result.id), content);
|
|
149
149
|
await tx.commit().execute();
|
|
150
150
|
return parseItem(result);
|
|
151
151
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"description": "User file storage for Axium",
|
|
6
6
|
"funding": {
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build": "tsc"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@axium/client": ">=0.
|
|
43
|
-
"@axium/core": ">=0.
|
|
42
|
+
"@axium/client": ">=0.12.0",
|
|
43
|
+
"@axium/core": ">=0.18.0",
|
|
44
44
|
"@axium/server": ">=0.30.0",
|
|
45
45
|
"@sveltejs/kit": "^2.27.3",
|
|
46
46
|
"utilium": "^2.3.8"
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
"hooks": "./dist/server/hooks.js",
|
|
55
55
|
"routes": "./routes",
|
|
56
56
|
"cli": "./dist/server/cli.js",
|
|
57
|
-
"db": "./db.json"
|
|
57
|
+
"db": "./db.json",
|
|
58
|
+
"web_client_hooks": "./dist/common.js"
|
|
58
59
|
},
|
|
59
60
|
"client": {
|
|
60
61
|
"cli": "./dist/client/cli.js",
|
|
@@ -67,6 +68,30 @@
|
|
|
67
68
|
"icon": "folders"
|
|
68
69
|
}
|
|
69
70
|
],
|
|
70
|
-
"update_checks": true
|
|
71
|
+
"update_checks": true,
|
|
72
|
+
"config": {
|
|
73
|
+
"app_enabled": true,
|
|
74
|
+
"batch": {
|
|
75
|
+
"enabled": false,
|
|
76
|
+
"max_items": 100,
|
|
77
|
+
"max_item_size": 100
|
|
78
|
+
},
|
|
79
|
+
"cas": {
|
|
80
|
+
"enabled": true,
|
|
81
|
+
"include": [],
|
|
82
|
+
"exclude": []
|
|
83
|
+
},
|
|
84
|
+
"chunk": false,
|
|
85
|
+
"data": "/srv/axium/storage",
|
|
86
|
+
"enabled": true,
|
|
87
|
+
"limits": {
|
|
88
|
+
"user_size": 1000,
|
|
89
|
+
"item_size": 100,
|
|
90
|
+
"user_items": 10000
|
|
91
|
+
},
|
|
92
|
+
"max_chunks": 10,
|
|
93
|
+
"max_transfer_size": 100,
|
|
94
|
+
"trash_duration": 30
|
|
95
|
+
}
|
|
71
96
|
}
|
|
72
97
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@axium/storage/common';
|