@axium/storage 0.18.11 → 0.19.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.
@@ -1,4 +1,4 @@
1
- import type { GetItemOptions, StorageItemUpdate, UserStorage, UserStorageInfo } from '../common.js';
1
+ import type { GetItemOptions, StorageItemUpdate, UserStorage, UserStorageInfo, UserStorageOptions } from '../common.js';
2
2
  import { StorageItemMetadata } from '../common.js';
3
3
  import '../polyfills.js';
4
4
  export interface UploadOptions {
@@ -27,7 +27,7 @@ export declare function getDirectoryMetadata(parentId: string): Promise<StorageI
27
27
  export declare function downloadItem(fileId: string): Promise<Blob>;
28
28
  export declare function updateItemMetadata(fileId: string, metadata: StorageItemUpdate): Promise<StorageItemMetadata>;
29
29
  export declare function deleteItem(fileId: string): Promise<StorageItemMetadata>;
30
- export declare function getUserStorage(userId: string): Promise<UserStorage>;
30
+ export declare function getUserStorage(userId: string, options?: UserStorageOptions): Promise<UserStorage>;
31
31
  export declare function getUserStats(userId: string): Promise<UserStorageInfo>;
32
32
  export declare function getUserTrash(userId: string): Promise<StorageItemMetadata[]>;
33
33
  export declare function itemsSharedWith(userId: string): Promise<StorageItemMetadata[]>;
@@ -143,8 +143,8 @@ export async function updateItemMetadata(fileId, metadata) {
143
143
  export async function deleteItem(fileId) {
144
144
  return await fetchAPI('DELETE', 'storage/item/:id', undefined, fileId);
145
145
  }
146
- export async function getUserStorage(userId) {
147
- return await fetchAPI('GET', 'users/:id/storage', undefined, userId);
146
+ export async function getUserStorage(userId, options = {}) {
147
+ return await fetchAPI('GET', 'users/:id/storage', options, userId);
148
148
  }
149
149
  export async function getUserStats(userId) {
150
150
  return await fetchAPI('OPTIONS', 'users/:id/storage', undefined, userId);
@@ -11,5 +11,5 @@ export function copyShortURL(item) {
11
11
  export function formatItemName(item) {
12
12
  if (!item?.name)
13
13
  return text('storage.generic.no_name_in_dialog');
14
- return `<strong>${item.name.length > 23 ? item.name.slice(0, 20) + '...' : item.name}</strong>`;
14
+ return item.name.length > 23 ? item.name.slice(0, 20) + '...' : item.name;
15
15
  }
package/dist/common.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare const StorageItemUpdate: z.ZodObject<{
9
9
  }, z.core.$strip>;
10
10
  export type StorageItemUpdate = z.infer<typeof StorageItemUpdate>;
11
11
  export declare const GetItemOptions: z.ZodObject<{
12
- parents: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
12
+ parents: z.ZodOptional<z.ZodBoolean>;
13
13
  }, z.core.$strip>;
14
14
  export interface GetItemOptions extends z.infer<typeof GetItemOptions> {
15
15
  }
@@ -57,6 +57,10 @@ export declare const StorageItemMetadata: z.ZodObject<{
57
57
  export interface StorageItemMetadata<T extends Record<string, unknown> = Record<string, unknown>> extends z.infer<typeof StorageItemMetadata> {
58
58
  metadata: T;
59
59
  }
60
+ export declare const StorageItemSorting: z.ZodObject<{
61
+ descending: z.ZodOptional<z.ZodBoolean>;
62
+ by: z.ZodLiteral<"name" | "createdAt" | "modifiedAt" | "size">;
63
+ }, z.core.$strip>;
60
64
  export declare const syncProtocolVersion = 0;
61
65
  export declare const StorageLimits: z.ZodObject<{
62
66
  item_size: z.ZodInt;
@@ -140,6 +144,14 @@ export declare const UserStorage: z.ZodObject<{
140
144
  }, z.core.$strip>;
141
145
  export interface UserStorage extends z.infer<typeof UserStorage> {
142
146
  }
147
+ export declare const UserStorageOptions: z.ZodDefault<z.ZodObject<{
148
+ sort: z.ZodOptional<z.ZodObject<{
149
+ descending: z.ZodOptional<z.ZodBoolean>;
150
+ by: z.ZodLiteral<"name" | "createdAt" | "modifiedAt" | "size">;
151
+ }, z.core.$strip>>;
152
+ }, z.core.$strip>>;
153
+ export interface UserStorageOptions extends z.infer<typeof UserStorageOptions> {
154
+ }
143
155
  /**
144
156
  * Formats:
145
157
  *
@@ -281,7 +293,12 @@ declare const StorageAPI: {
281
293
  lastModified: z.ZodCoercedDate<unknown>;
282
294
  lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
283
295
  }, z.core.$strip>;
284
- readonly GET: z.ZodObject<{
296
+ readonly GET: readonly [z.ZodDefault<z.ZodObject<{
297
+ sort: z.ZodOptional<z.ZodObject<{
298
+ descending: z.ZodOptional<z.ZodBoolean>;
299
+ by: z.ZodLiteral<"name" | "createdAt" | "modifiedAt" | "size">;
300
+ }, z.core.$strip>>;
301
+ }, z.core.$strip>>, z.ZodObject<{
285
302
  items: z.ZodArray<z.ZodObject<{
286
303
  createdAt: z.ZodCoercedDate<unknown>;
287
304
  dataURL: z.ZodString;
@@ -332,7 +349,7 @@ declare const StorageAPI: {
332
349
  itemCount: z.ZodInt;
333
350
  lastModified: z.ZodCoercedDate<unknown>;
334
351
  lastTrashed: z.ZodNullable<z.ZodCoercedDate<unknown>>;
335
- }, z.core.$strip>;
352
+ }, z.core.$strip>];
336
353
  };
337
354
  readonly 'users/:id/storage/root': {
338
355
  readonly GET: z.ZodArray<z.ZodObject<{
@@ -590,7 +607,7 @@ declare const StorageAPI: {
590
607
  };
591
608
  readonly 'storage/item/:id': {
592
609
  readonly GET: readonly [z.ZodObject<{
593
- parents: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
610
+ parents: z.ZodOptional<z.ZodBoolean>;
594
611
  }, z.core.$strip>, z.ZodObject<{
595
612
  createdAt: z.ZodCoercedDate<unknown>;
596
613
  dataURL: z.ZodString;
package/dist/common.js CHANGED
@@ -12,7 +12,7 @@ export const StorageItemUpdate = z
12
12
  .partial();
13
13
  export const GetItemOptions = z
14
14
  .object({
15
- parents: z.union([z.boolean(), z.stringbool()]),
15
+ parents: z.boolean(),
16
16
  })
17
17
  .partial();
18
18
  export const StorageItemMetadata = z.object({
@@ -33,6 +33,10 @@ export const StorageItemMetadata = z.object({
33
33
  acl: AccessControl.array().optional(),
34
34
  parents: z.object({ id: z.uuid(), name: z.string() }).array().optional(),
35
35
  });
36
+ export const StorageItemSorting = z.object({
37
+ descending: z.boolean().optional(),
38
+ by: z.literal(['createdAt', 'modifiedAt', 'name', 'size']),
39
+ });
36
40
  export const syncProtocolVersion = 0;
37
41
  export const StorageLimits = z.object({
38
42
  /** The maximum size per item in MB */
@@ -56,6 +60,12 @@ export const UserStorage = z.object({
56
60
  ...UserStorageInfo.shape,
57
61
  items: StorageItemMetadata.array(),
58
62
  });
63
+ export const UserStorageOptions = z
64
+ .object({
65
+ sort: StorageItemSorting,
66
+ })
67
+ .partial()
68
+ .default({});
59
69
  /**
60
70
  * Formats:
61
71
  *
@@ -135,7 +145,7 @@ export const UploadInitResult = z.discriminatedUnion('status', [
135
145
  const StorageAPI = {
136
146
  'users/:id/storage': {
137
147
  OPTIONS: UserStorageInfo,
138
- GET: UserStorage,
148
+ GET: [UserStorageOptions, UserStorage],
139
149
  },
140
150
  'users/:id/storage/root': {
141
151
  GET: StorageItemMetadata.array(),
@@ -6,7 +6,7 @@ import { error, json, parseBody, parseSearch, withError } from '@axium/server/re
6
6
  import { addRoute } from '@axium/server/routes';
7
7
  import { pick } from 'utilium';
8
8
  import * as z from 'zod';
9
- import { batchFormatVersion, GetItemOptions, StorageItemInit, StorageItemUpdate, syncProtocolVersion } from '../common.js';
9
+ import { batchFormatVersion, GetItemOptions, StorageItemInit, StorageItemUpdate, syncProtocolVersion, UserStorageOptions, } from '../common.js';
10
10
  import '../polyfills.js';
11
11
  import { getLimits } from './config.js';
12
12
  import { deleteRecursive, getParents, getRecursive, getUserStats, parseItem } from './db.js';
@@ -127,6 +127,7 @@ addRoute({
127
127
  async GET(request, { id: userId }) {
128
128
  if (!getConfig('@axium/storage').enabled)
129
129
  error(503, 'User storage is disabled');
130
+ const { sort } = parseSearch(request, UserStorageOptions);
130
131
  await checkAuthForUser(request, userId);
131
132
  const [items, stats, limits] = await Promise.all([
132
133
  database
@@ -135,6 +136,8 @@ addRoute({
135
136
  .select(acl.from('storage'))
136
137
  .where('userId', '=', userId)
137
138
  .where('trashedAt', 'is', null)
139
+ .limit(250)
140
+ .$if(!!sort, qb => qb.orderBy(sort.by, sort.descending ? 'desc' : 'asc'))
138
141
  .execute(),
139
142
  getUserStats(userId),
140
143
  getLimits(userId),
package/lib/List.svelte CHANGED
@@ -142,7 +142,7 @@
142
142
  items.splice(activeIndex, 1);
143
143
  }}
144
144
  >
145
- <p>{@html text('storage.List.trash_confirm', { $html: true, name: activeItemName })}</p>
145
+ <p>{text('storage.List.trash_confirm', { name: activeItemName })}</p>
146
146
  </FormDialog>
147
147
  <FormDialog
148
148
  bind:dialog={dialogs.download}
@@ -155,7 +155,7 @@
155
155
  } else open(activeItem!.dataURL, '_blank');
156
156
  }}
157
157
  >
158
- <p>{@html text('storage.generic.download_confirm', { $html: true, name: activeItemName })}</p>
158
+ <p>{text('storage.generic.download_confirm', { name: activeItemName })}</p>
159
159
  </FormDialog>
160
160
 
161
161
  <style>
@@ -133,7 +133,7 @@
133
133
  if (index !== -1) items.splice(index, 1);
134
134
  }}
135
135
  >
136
- <p>{@html text('storage.SidebarItem.delete_confirm', { $html: true, name: itemName })}</p>
136
+ <p>{text('storage.SidebarItem.delete_confirm', { name: itemName })}</p>
137
137
  </FormDialog>
138
138
  <FormDialog
139
139
  bind:dialog={dialogs.download}
@@ -144,7 +144,7 @@
144
144
  >
145
145
  <p>
146
146
  {text('storage.SidebarItem.download_disclaimer')} <br />
147
- {@html text('storage.generic.download_confirm', { $html: true, name: itemName })}
147
+ {text('storage.generic.download_confirm', { name: itemName })}
148
148
  </p>
149
149
  </FormDialog>
150
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/storage",
3
- "version": "0.18.11",
3
+ "version": "0.19.0",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "description": "User file storage for Axium",
6
6
  "funding": {
@@ -19,7 +19,11 @@ export async function load({ url, route, parent }) {
19
19
  },
20
20
  { name: text('page.files.tab.trash'), href: '/files/trash', icon: 'trash', active: route.id.endsWith('/files/trash') },
21
21
  { name: text('page.files.tab.shared'), href: '/files/shared', icon: 'user-group', active: route.id.endsWith('/files/shared') },
22
- ] satisfies { name: string; href: LayoutRouteId; icon: string; active: boolean }[];
22
+ { href: '/files/usage', icon: 'chart-pie-simple', active: route.id.endsWith('/files/usage'), mobile: true },
23
+ ] satisfies (
24
+ | { name: string; href: LayoutRouteId; icon: string; active: boolean }
25
+ | { href: LayoutRouteId; icon: string; active: boolean; mobile: true }
26
+ )[];
23
27
 
24
28
  return { session, tabs };
25
29
  }
@@ -65,7 +65,7 @@
65
65
  items.splice(activeIndex, 1);
66
66
  }}
67
67
  >
68
- <p>{@html text('page.files.trash_page.restore_confirm', { $html: true, name: activeItemName })}</p>
68
+ <p>{text('page.files.trash_page.restore_confirm', { name: activeItemName })}</p>
69
69
  </FormDialog>
70
70
  <FormDialog
71
71
  bind:dialog={deleteDialog}
@@ -78,7 +78,7 @@
78
78
  }}
79
79
  >
80
80
  <p>
81
- {@html text('page.files.trash_page.delete_confirm', { $html: true, name: activeItemName })}
81
+ {text('page.files.trash_page.delete_confirm', { name: activeItemName })}
82
82
  </p>
83
83
  </FormDialog>
84
84
 
@@ -1,14 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { text } from '@axium/client';
3
3
  import { NumberBar } from '@axium/client/components';
4
- import '@axium/client/styles/list';
5
4
  import { formatBytes } from '@axium/core/format';
6
5
  import { List } from '@axium/storage/components';
7
6
 
8
7
  const { data } = $props();
9
8
  const { limits } = data.info;
10
9
 
11
- let items = $state(data.info.items.filter(i => i.type != 'inode/directory').sort((a, b) => Math.sign(b.size - a.size)));
10
+ let items = $state(data.info.items.filter(i => i.type != 'inode/directory'));
12
11
  const usedBytes = $state(data.info.usedBytes);
13
12
 
14
13
  let barText = $derived(
@@ -8,7 +8,7 @@ export async function load({ parent }) {
8
8
 
9
9
  if (!session) redirect(307, '/login?after=/files/usage');
10
10
 
11
- const info = await getUserStorage(session.userId);
11
+ const info = await getUserStorage(session.userId, { sort: { by: 'size', descending: true } });
12
12
 
13
13
  return { info };
14
14
  }