♻️ Changed the way to clean upload folder

This commit is contained in:
2025-07-27 13:44:21 +08:00
parent e1ebd44ea8
commit 52addc91df
5 changed files with 25 additions and 157 deletions

View File

@@ -105,24 +105,17 @@ import {
NP, NP,
NInput, NInput,
NSwitch, NSwitch,
NSelect,
NTag,
NCollapseTransition, NCollapseTransition,
NDatePicker, NDatePicker,
type UploadCustomRequestOptions, type UploadCustomRequestOptions,
type UploadSettledFileInfo, type UploadSettledFileInfo,
type SelectOption,
type SelectRenderTag,
type UploadFileInfo, type UploadFileInfo,
useMessage, useMessage,
NDivider,
NTooltip,
} from 'naive-ui' } from 'naive-ui'
import { computed, h, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { CloudUploadRound } from '@vicons/material' import { CloudUploadRound } from '@vicons/material'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import type { SnFilePool } from '@/types/pool' import type { SnFilePool } from '@/types/pool'
import { formatBytes } from './format'
import FilePoolSelect from '@/components/FilePoolSelect.vue' import FilePoolSelect from '@/components/FilePoolSelect.vue'
@@ -146,141 +139,6 @@ async function fetchPools() {
} }
onMounted(() => fetchPools()) onMounted(() => fetchPools())
const renderSingleSelectTag: SelectRenderTag = ({ option }) => {
return h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
},
},
[option.name as string],
)
}
const perkPrivilegeList = ['Stellar', 'Nova', 'Supernova']
function renderPoolSelectLabel(option: SelectOption & SnFilePool) {
const policy: any = option.policy_config
return h(
'div',
{
style: {
padding: '8px 2px',
},
},
[
h('div', null, [option.name as string]),
option.description &&
h(
'div',
{
style: {
fontSize: '0.875rem',
opacity: '0.75',
},
},
option.description,
),
h(
'div',
{
style: {
display: 'flex',
marginBottom: '4px',
fontSize: '0.75rem',
opacity: '0.75',
},
},
[
policy.max_file_size && h('span', `Max ${formatBytes(policy.max_file_size)}`),
policy.accept_types &&
h(
NTooltip,
{},
{
trigger: () => h('span', `Accept limited types`),
default: () => h('span', policy.accept_types.join(', ')),
},
),
policy.require_privilege &&
h('span', `Require ${perkPrivilegeList[policy.require_privilege - 1]} Program`),
h('span', `Cost x${option.billing_config.cost_multiplier.toFixed(1)} NSD`)
]
.filter((el) => el)
.flatMap((el, idx, arr) =>
idx < arr.length - 1 ? [el, h(NDivider, { vertical: true })] : [el],
),
),
h(
'div',
{
style: {
display: 'flex',
gap: '0.25rem',
marginTop: '2px',
marginLeft: '-2px',
marginRight: '-2px',
},
},
[
policy.public_usable &&
h(
NTag,
{
type: 'info',
size: 'small',
round: true,
},
{ default: () => 'Public Shared' },
),
policy.public_indexable &&
h(
NTag,
{
type: 'success',
size: 'small',
round: true,
},
{ default: () => 'Public Indexable' },
),
policy.allow_encryption &&
h(
NTag,
{
type: 'warning',
size: 'small',
round: true,
},
{ default: () => 'Allow Encryption' },
),
policy.allow_anonymous &&
h(
NTag,
{
type: 'info',
size: 'small',
round: true,
},
{ default: () => 'Allow Anonymous' },
),
policy.enable_recycle &&
h(
NTag,
{
type: 'info',
size: 'small',
round: true,
},
{ default: () => 'Recycle Enabled' },
),
],
),
],
)
}
const modeAdvanced = ref(false) const modeAdvanced = ref(false)
const filePool = ref<string | null>(null) const filePool = ref<string | null>(null)
@@ -296,10 +154,8 @@ const messageDisplay = useMessage()
function customRequest({ function customRequest({
file, file,
data,
headers, headers,
withCredentials, withCredentials,
action,
onFinish, onFinish,
onError, onError,
onProgress, onProgress,
@@ -311,6 +167,8 @@ function customRequest({
const upload = new tus.Upload(file.file, { const upload = new tus.Upload(file.file, {
endpoint: '/api/tus', endpoint: '/api/tus',
retryDelays: [0, 3000, 5000, 10000, 20000], retryDelays: [0, 3000, 5000, 10000, 20000],
removeFingerprintOnSuccess: false,
uploadDataDuringCreation: false,
metadata: { metadata: {
filename: file.name, filename: file.name,
'content-type': file.type ?? 'application/octet-stream', 'content-type': file.type ?? 'application/octet-stream',
@@ -319,6 +177,7 @@ function customRequest({
...requestHeaders, ...requestHeaders,
...headers, ...headers,
}, },
onShouldRetry: () => false,
onError: function (error) { onError: function (error) {
if (error instanceof tus.DetailedError) { if (error instanceof tus.DetailedError) {
const failedBody = error.originalResponse?.getBody() const failedBody = error.originalResponse?.getBody()
@@ -364,14 +223,14 @@ function createThumbnailUrl(
} }
function customDownload(file: UploadFileInfo) { function customDownload(file: UploadFileInfo) {
const { url, name } = file const { url } = file
if (!url) return if (!url) return
window.open(url.replace('/api', ''), '_blank') window.open(url.replace('/api', ''), '_blank')
} }
function customPreview(file: UploadFileInfo, detail: { event: MouseEvent }) { function customPreview(file: UploadFileInfo, detail: { event: MouseEvent }) {
detail.event.preventDefault() detail.event.preventDefault()
const { url, type } = file const { url } = file
if (!url) return if (!url) return
window.open(url.replace('/api', ''), '_blank') window.open(url.replace('/api', ''), '_blank')
} }

View File

@@ -7,12 +7,27 @@ namespace DysonNetwork.Drive.Storage;
public class CloudFileUnusedRecyclingJob( public class CloudFileUnusedRecyclingJob(
AppDatabase db, AppDatabase db,
FileReferenceService fileRefService, FileReferenceService fileRefService,
ILogger<CloudFileUnusedRecyclingJob> logger ILogger<CloudFileUnusedRecyclingJob> logger,
IConfiguration configuration
) )
: IJob : IJob
{ {
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
{ {
logger.LogInformation("Cleaning tus cloud files...");
var storePath = configuration["Tus:StorePath"];
if (Directory.Exists(storePath))
{
var oneHourAgo = SystemClock.Instance.GetCurrentInstant() - Duration.FromHours(1);
var files = Directory.GetFiles(storePath);
foreach (var file in files)
{
var creationTime = File.GetCreationTime(file).ToUniversalTime();
if (creationTime < oneHourAgo.ToDateTimeUtc())
File.Delete(file);
}
}
logger.LogInformation("Marking unused cloud files..."); logger.LogInformation("Marking unused cloud files...");
var recyclablePools = await db.Pools var recyclablePools = await db.Pools

View File

@@ -48,11 +48,9 @@ public class FileExpirationJob(AppDatabase db, FileService fileService, ILogger<
if (remainingReferences == 0) if (remainingReferences == 0)
{ {
var file = await db.Files.FirstOrDefaultAsync(f => f.Id == fileId); var file = await db.Files.FirstOrDefaultAsync(f => f.Id == fileId);
if (file != null) if (file == null) continue;
{ logger.LogInformation("Deleting file {fileId} as all references have expired", fileId);
logger.LogInformation("Deleting file {fileId} as all references have expired", fileId); await fileService.DeleteFileAsync(file);
await fileService.DeleteFileAsync(file);
}
} }
else else
{ {

View File

@@ -170,7 +170,6 @@ public class FileService(
// await db.SaveChangesAsync(); // await db.SaveChangesAsync();
// // Since the file content is a duplicate, we can delete the new upload and we are done. // // Since the file content is a duplicate, we can delete the new upload and we are done.
// await stream.DisposeAsync(); // await stream.DisposeAsync();
// await store.DeleteFileAsync(file.Id, CancellationToken.None);
// return file; // return file;
// } // }
@@ -432,7 +431,6 @@ public class FileService(
} }
finally finally
{ {
await store.DeleteFileAsync(fileId, CancellationToken.None);
await nfs._PurgeCacheAsync(fileId); await nfs._PurgeCacheAsync(fileId);
} }
} }

View File

@@ -142,8 +142,6 @@ public abstract class TusService
{ {
eventContext.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; eventContext.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
await eventContext.HttpContext.Response.WriteAsync(ex.Message); await eventContext.HttpContext.Response.WriteAsync(ex.Message);
if (eventContext.Store is TusDiskStore disk)
await disk.DeleteFileAsync(file.Id, eventContext.CancellationToken);
} }
finally finally
{ {