From 8b1bb7fcfd475e5621029c5be3ab792d7ea4202f Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 28 Jul 2025 00:37:54 +0800 Subject: [PATCH] :sparkles: File bundle --- .../Client/src/components/BundleSelect.vue | 50 +++ .../Client/src/components/UploadArea.vue | 3 +- DysonNetwork.Drive/Client/src/router/index.ts | 7 +- .../Client/src/stores/services.ts | 2 +- .../Client/src/views/bundles.vue | 142 ++++++++ .../Client/src/views/dashboard/bundles.vue | 9 +- .../Client/src/views/dashboard/files.vue | 1 + DysonNetwork.Drive/Client/src/views/files.vue | 15 +- DysonNetwork.Drive/Client/src/views/index.vue | 303 ++++++------------ .../Storage/BundleController.cs | 11 +- DysonNetwork.Drive/Storage/CloudFile.cs | 2 +- .../DysonNetwork.Shared.csproj | 1 - .../DysonNetwork.Sphere.csproj | 1 - 13 files changed, 330 insertions(+), 217 deletions(-) create mode 100644 DysonNetwork.Drive/Client/src/components/BundleSelect.vue create mode 100644 DysonNetwork.Drive/Client/src/views/bundles.vue diff --git a/DysonNetwork.Drive/Client/src/components/BundleSelect.vue b/DysonNetwork.Drive/Client/src/components/BundleSelect.vue new file mode 100644 index 0000000..4279813 --- /dev/null +++ b/DysonNetwork.Drive/Client/src/components/BundleSelect.vue @@ -0,0 +1,50 @@ + + + diff --git a/DysonNetwork.Drive/Client/src/components/UploadArea.vue b/DysonNetwork.Drive/Client/src/components/UploadArea.vue index c399618..364820e 100644 --- a/DysonNetwork.Drive/Client/src/components/UploadArea.vue +++ b/DysonNetwork.Drive/Client/src/components/UploadArea.vue @@ -89,7 +89,7 @@ import type { SnFilePool } from '@/types/pool' import * as tus from 'tus-js-client' -const props = defineProps<{ filePool: string | null; modeAdvanced: boolean; pools: SnFilePool[] }>() +const props = defineProps<{ filePool: string | null; modeAdvanced: boolean; pools: SnFilePool[]; bundleId?: string }>() const filePass = ref('') const fileExpire = ref(null) @@ -117,6 +117,7 @@ function customRequest({ if (props.filePool) requestHeaders['X-FilePool'] = props.filePool if (filePass.value) requestHeaders['X-FilePass'] = filePass.value if (fileExpire.value) requestHeaders['X-FileExpire'] = fileExpire.value.toString() + if (props.bundleId) requestHeaders['X-FileBundle'] = props.bundleId const upload = new tus.Upload(file.file, { endpoint: '/api/tus', retryDelays: [0, 3000, 5000, 10000, 20000], diff --git a/DysonNetwork.Drive/Client/src/router/index.ts b/DysonNetwork.Drive/Client/src/router/index.ts index 6da02dc..96a7d21 100644 --- a/DysonNetwork.Drive/Client/src/router/index.ts +++ b/DysonNetwork.Drive/Client/src/router/index.ts @@ -15,6 +15,11 @@ const router = createRouter({ name: 'files', component: () => import('../views/files.vue'), }, + { + path: '/bundles/:bundleId', + name: 'bundleDetails', + component: () => import('../views/bundles.vue'), + }, { path: '/dashboard', name: 'dashboard', @@ -78,4 +83,4 @@ router.beforeEach(async (to, from, next) => { } }) -export default router \ No newline at end of file +export default router diff --git a/DysonNetwork.Drive/Client/src/stores/services.ts b/DysonNetwork.Drive/Client/src/stores/services.ts index 1f50576..2ef28c9 100644 --- a/DysonNetwork.Drive/Client/src/stores/services.ts +++ b/DysonNetwork.Drive/Client/src/stores/services.ts @@ -19,7 +19,7 @@ export const useServicesStore = defineStore('services', () => { } function getSerivceUrl(serviceName: string, ...parts: string[]): string | null { - let baseUrl = services.value[serviceName] || null + const baseUrl = services.value[serviceName] || null return baseUrl ? `${baseUrl}/${parts.join('/')}` : null } diff --git a/DysonNetwork.Drive/Client/src/views/bundles.vue b/DysonNetwork.Drive/Client/src/views/bundles.vue new file mode 100644 index 0000000..05c564c --- /dev/null +++ b/DysonNetwork.Drive/Client/src/views/bundles.vue @@ -0,0 +1,142 @@ + + + diff --git a/DysonNetwork.Drive/Client/src/views/dashboard/bundles.vue b/DysonNetwork.Drive/Client/src/views/dashboard/bundles.vue index 2d28eeb..34fc5e7 100644 --- a/DysonNetwork.Drive/Client/src/views/dashboard/bundles.vue +++ b/DysonNetwork.Drive/Client/src/views/dashboard/bundles.vue @@ -50,17 +50,12 @@ const tableColumns: DataTableColumns = [ }, ) }, + maxWidth: 80, }, { title: 'Description', key: 'description', - }, - { - title: 'Files', - key: 'files', - render(row: any) { - return row.files.length - }, + maxWidth: 180, }, { title: 'Expired At', diff --git a/DysonNetwork.Drive/Client/src/views/dashboard/files.vue b/DysonNetwork.Drive/Client/src/views/dashboard/files.vue index dd68c2f..f42c38b 100644 --- a/DysonNetwork.Drive/Client/src/views/dashboard/files.vue +++ b/DysonNetwork.Drive/Client/src/views/dashboard/files.vue @@ -99,6 +99,7 @@ const tableColumns: DataTableColumns = [ { title: 'Name', key: 'name', + maxWidth: 180, render(row: any) { return h( NButton, diff --git a/DysonNetwork.Drive/Client/src/views/files.vue b/DysonNetwork.Drive/Client/src/views/files.vue index a0a6785..debe2d6 100644 --- a/DysonNetwork.Drive/Client/src/views/files.vue +++ b/DysonNetwork.Drive/Client/src/views/files.vue @@ -165,6 +165,7 @@ const error = ref(null) const filePass = ref('') const fileId = route.params.fileId +const passcode = route.query.passcode as string | undefined const progress = ref(0) @@ -177,7 +178,11 @@ const currentUrl = window.location.href const fileInfo = ref(null) async function fetchFileInfo() { try { - const resp = await fetch('/api/files/' + fileId + '/info') + let url = '/api/files/' + fileId + '/info' + if (passcode) { + url += `?passcode=${passcode}` + } + const resp = await fetch(url) if (!resp.ok) { throw new Error('Failed to fetch file info: ' + resp.statusText) } @@ -192,7 +197,13 @@ const fileType = computed(() => { if (!fileInfo.value) return 'unknown' return fileInfo.value.mime_type?.split('/')[0] || 'unknown' }) -const fileSource = computed(() => `/api/files/${fileId}`) +const fileSource = computed(() => { + let url = `/api/files/${fileId}` + if (passcode) { + url += `?passcode=${passcode}` + } + return url +}) function downloadFile() { if (fileInfo.value.is_encrypted && !filePass.value) { diff --git a/DysonNetwork.Drive/Client/src/views/index.vue b/DysonNetwork.Drive/Client/src/views/index.vue index 833963c..8916557 100644 --- a/DysonNetwork.Drive/Client/src/views/index.vue +++ b/DysonNetwork.Drive/Client/src/views/index.vue @@ -1,133 +1,88 @@ diff --git a/DysonNetwork.Drive/Storage/BundleController.cs b/DysonNetwork.Drive/Storage/BundleController.cs index 739f837..7c68eb7 100644 --- a/DysonNetwork.Drive/Storage/BundleController.cs +++ b/DysonNetwork.Drive/Storage/BundleController.cs @@ -20,7 +20,7 @@ public class BundleController(AppDatabase db) : ControllerBase public Instant? ExpiredAt { get; set; } } - + [HttpGet("{id:guid}")] [Authorize] public async Task> GetBundle([FromRoute] Guid id, [FromQuery] string? passcode) @@ -35,13 +35,14 @@ public class BundleController(AppDatabase db) : ControllerBase .FirstOrDefaultAsync(); if (bundle is null) return NotFound(); if (!bundle.VerifyPasscode(passcode)) return Forbid(); - + return Ok(bundle); } [HttpGet("me")] [Authorize] public async Task>> ListBundles( + [FromQuery] string? term, [FromQuery] int offset = 0, [FromQuery] int take = 20 ) @@ -53,10 +54,12 @@ public class BundleController(AppDatabase db) : ControllerBase .Where(e => e.AccountId == accountId) .OrderByDescending(e => e.CreatedAt) .AsQueryable(); + if (!string.IsNullOrEmpty(term)) + query = query.Where(e => EF.Functions.ILike(e.Name, $"%{term}%")); var total = await query.CountAsync(); Response.Headers.Append("X-Total", total.ToString()); - + var bundles = await query .Skip(offset) .Take(take) @@ -114,7 +117,7 @@ public class BundleController(AppDatabase db) : ControllerBase return StatusCode(403, "You must have a subscription to change the slug of a bundle"); bundle.Slug = request.Slug; } - + if (request.Name != null) bundle.Name = request.Name; if (request.Description != null) bundle.Description = request.Description; if (request.ExpiredAt != null) bundle.ExpiredAt = request.ExpiredAt; diff --git a/DysonNetwork.Drive/Storage/CloudFile.cs b/DysonNetwork.Drive/Storage/CloudFile.cs index 7e8c8b7..680ec4c 100644 --- a/DysonNetwork.Drive/Storage/CloudFile.cs +++ b/DysonNetwork.Drive/Storage/CloudFile.cs @@ -1,9 +1,9 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; using Google.Protobuf; -using Newtonsoft.Json; using NodaTime; using NodaTime.Serialization.Protobuf; diff --git a/DysonNetwork.Shared/DysonNetwork.Shared.csproj b/DysonNetwork.Shared/DysonNetwork.Shared.csproj index 89f6c1c..03783fe 100644 --- a/DysonNetwork.Shared/DysonNetwork.Shared.csproj +++ b/DysonNetwork.Shared/DysonNetwork.Shared.csproj @@ -22,7 +22,6 @@ - diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index ea12a12..c8c8d6a 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -34,7 +34,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive -