mirror of
https://github.com/wgh136/nysoure.git
synced 2025-12-16 15:51:14 +00:00
feat: Add tag field to file models, and enhance file upload functionality with optional fields
This commit is contained in:
96
frontend/package-lock.json
generated
96
frontend/package-lock.json
generated
@@ -15,7 +15,6 @@
|
|||||||
"masonic": "^4.1.0",
|
"masonic": "^4.1.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-i18next": "^15.5.1",
|
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-router": "^7.5.3",
|
"react-router": "^7.5.3",
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@types/spark-md5": "^3.0.5",
|
"@types/spark-md5": "^3.0.5",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"daisyui": "^5.0.35",
|
"daisyui": "^5.5.5",
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.22.0",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-prettier": "^5.4.1",
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
@@ -281,15 +280,6 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
|
||||||
"version": "7.27.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
|
|
||||||
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
|
||||||
@@ -2558,9 +2548,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "5.0.35",
|
"version": "5.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.35.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.5.tgz",
|
||||||
"integrity": "sha512-AWi11n/x5++mps55jcwrBf0Lmip1euWY0FYcH/05SFGmoqrU7S7/aIUWaiaeqlJ5EcmEZ/7zEY73aOxMv6hcIg==",
|
"integrity": "sha512-ekvI93ZkWIJoCOtDl0D2QMxnWvTejk9V5nWBqRv+7t0xjiBXqAK5U6o6JE2RPvlIC3EqwNyUoIZSdHX9MZK3nw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -3618,15 +3608,6 @@
|
|||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/html-parse-stringify": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"void-elements": "3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/html-url-attributes": {
|
"node_modules/html-url-attributes": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
||||||
@@ -3670,38 +3651,6 @@
|
|||||||
"url": "https://github.com/sponsors/typicode"
|
"url": "https://github.com/sponsors/typicode"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/i18next": {
|
|
||||||
"version": "25.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.1.1.tgz",
|
|
||||||
"integrity": "sha512-FZcp3vk3PXc8onasbsWYahfeDIWX4LkKr4vd01xeXrmqyNXlVNtVecEIw2K1o8z3xYrHMcd1bwYQub+3g7zqCw==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://locize.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://locize.com/i18next.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.26.10"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
@@ -5681,32 +5630,6 @@
|
|||||||
"react": "^19.1.0"
|
"react": "^19.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-i18next": {
|
|
||||||
"version": "15.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.1.tgz",
|
|
||||||
"integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.25.0",
|
|
||||||
"html-parse-stringify": "^3.0.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"i18next": ">= 23.2.3",
|
|
||||||
"react": ">= 16.8.0",
|
|
||||||
"typescript": "^5"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"react-native": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-icons": {
|
"node_modules/react-icons": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||||
@@ -6418,7 +6341,7 @@
|
|||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -6727,15 +6650,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/void-elements": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@types/spark-md5": "^3.0.5",
|
"@types/spark-md5": "^3.0.5",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"daisyui": "^5.0.35",
|
"daisyui": "^5.5.5",
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.22.0",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-prettier": "^5.4.1",
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
|
|||||||
@@ -258,6 +258,9 @@ export const i18nData = {
|
|||||||
"Survival time": "存活时间",
|
"Survival time": "存活时间",
|
||||||
"Characters": "角色",
|
"Characters": "角色",
|
||||||
"Aliases (one per line)": "别名(每行一个)",
|
"Aliases (one per line)": "别名(每行一个)",
|
||||||
|
"File Size": "文件大小",
|
||||||
|
"Tag": "标签",
|
||||||
|
"Optional": "可选",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-TW": {
|
"zh-TW": {
|
||||||
@@ -517,6 +520,11 @@ export const i18nData = {
|
|||||||
"Private": "私有",
|
"Private": "私有",
|
||||||
"View {count} more replies": "查看另外 {count} 條回覆",
|
"View {count} more replies": "查看另外 {count} 條回覆",
|
||||||
"Survival time": "存活時間",
|
"Survival time": "存活時間",
|
||||||
|
"Characters": "角色",
|
||||||
|
"Aliases (one per line)": "別名(每行一個)",
|
||||||
|
"File Size": "檔案大小",
|
||||||
|
"Tag": "標籤",
|
||||||
|
"Optional": "可選",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ export interface RFile {
|
|||||||
hash?: string;
|
hash?: string;
|
||||||
storage_name?: string;
|
storage_name?: string;
|
||||||
created_at: number; // unix timestamp
|
created_at: number; // unix timestamp
|
||||||
|
tag?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadingFile {
|
export interface UploadingFile {
|
||||||
|
|||||||
@@ -479,6 +479,7 @@ class Network {
|
|||||||
fileSize: number,
|
fileSize: number,
|
||||||
resourceId: number,
|
resourceId: number,
|
||||||
storageId: number,
|
storageId: number,
|
||||||
|
tag: string,
|
||||||
): Promise<Response<UploadingFile>> {
|
): Promise<Response<UploadingFile>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.post(`${this.apiBaseUrl}/files/upload/init`, {
|
axios.post(`${this.apiBaseUrl}/files/upload/init`, {
|
||||||
@@ -487,6 +488,7 @@ class Network {
|
|||||||
file_size: fileSize,
|
file_size: fileSize,
|
||||||
resource_id: resourceId,
|
resource_id: resourceId,
|
||||||
storage_id: storageId,
|
storage_id: storageId,
|
||||||
|
tag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -529,6 +531,9 @@ class Network {
|
|||||||
description: string,
|
description: string,
|
||||||
resourceId: number,
|
resourceId: number,
|
||||||
redirectUrl: string,
|
redirectUrl: string,
|
||||||
|
fileSize: number,
|
||||||
|
md5: string,
|
||||||
|
tag: string,
|
||||||
): Promise<Response<RFile>> {
|
): Promise<Response<RFile>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.post(`${this.apiBaseUrl}/files/redirect`, {
|
axios.post(`${this.apiBaseUrl}/files/redirect`, {
|
||||||
@@ -536,6 +541,9 @@ class Network {
|
|||||||
description,
|
description,
|
||||||
resource_id: resourceId,
|
resource_id: resourceId,
|
||||||
redirect_url: redirectUrl,
|
redirect_url: redirectUrl,
|
||||||
|
file_size: fileSize,
|
||||||
|
md5,
|
||||||
|
tag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -546,6 +554,7 @@ class Network {
|
|||||||
description: string,
|
description: string,
|
||||||
resourceId: number,
|
resourceId: number,
|
||||||
storageId: number,
|
storageId: number,
|
||||||
|
tag: string,
|
||||||
): Promise<Response<RFile>> {
|
): Promise<Response<RFile>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.post(`${this.apiBaseUrl}/files/upload/url`, {
|
axios.post(`${this.apiBaseUrl}/files/upload/url`, {
|
||||||
@@ -554,6 +563,7 @@ class Network {
|
|||||||
description,
|
description,
|
||||||
resource_id: resourceId,
|
resource_id: resourceId,
|
||||||
storage_id: storageId,
|
storage_id: storageId,
|
||||||
|
tag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -566,11 +576,13 @@ class Network {
|
|||||||
fileId: string,
|
fileId: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
description: string,
|
description: string,
|
||||||
|
tag: string,
|
||||||
): Promise<Response<RFile>> {
|
): Promise<Response<RFile>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.put(`${this.apiBaseUrl}/files/${fileId}`, {
|
axios.put(`${this.apiBaseUrl}/files/${fileId}`, {
|
||||||
filename,
|
filename,
|
||||||
description,
|
description,
|
||||||
|
tag,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -801,6 +802,11 @@ function FileTile({ file }: { file: RFile }) {
|
|||||||
{file.storage_name}
|
{file.storage_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
{file.tag && (
|
||||||
|
<Badge className={"badge-soft badge-warning text-xs mr-2"}>
|
||||||
|
{file.tag}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
<Badge className={"badge-soft badge-info text-xs mr-2"}>
|
<Badge className={"badge-soft badge-info text-xs mr-2"}>
|
||||||
<MdOutlineAccessTime size={16} className={"inline-block"} />
|
<MdOutlineAccessTime size={16} className={"inline-block"} />
|
||||||
{new Date(file.created_at * 1000).toISOString().substring(0, 10)}
|
{new Date(file.created_at * 1000).toISOString().substring(0, 10)}
|
||||||
@@ -919,11 +925,72 @@ function Files({
|
|||||||
files: RFile[];
|
files: RFile[];
|
||||||
resource: ResourceDetails;
|
resource: ResourceDetails;
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
// Extract unique tags from all files
|
||||||
|
const allTags = useMemo(() => {
|
||||||
|
const tags = new Set<string>();
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (file.tag) {
|
||||||
|
tags.add(file.tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Array.from(tags).sort();
|
||||||
|
}, [files]);
|
||||||
|
|
||||||
|
// Filter files based on selected tags
|
||||||
|
const filteredFiles = useMemo(() => {
|
||||||
|
if (selectedTags.size === 0) {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
return files.filter((file) => file.tag && selectedTags.has(file.tag));
|
||||||
|
}, [files, selectedTags]);
|
||||||
|
|
||||||
|
const toggleTag = (tag: string) => {
|
||||||
|
setSelectedTags((prev) => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has(tag)) {
|
||||||
|
newSet.delete(tag);
|
||||||
|
} else {
|
||||||
|
newSet.add(tag);
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"pt-3"}>
|
<div className={"pt-3"}>
|
||||||
{files.map((file) => {
|
{allTags.length > 0 && (
|
||||||
|
<form className="filter mb-4">
|
||||||
|
{allTags.map((tag) => (
|
||||||
|
<input
|
||||||
|
key={tag}
|
||||||
|
className="btn"
|
||||||
|
type="checkbox"
|
||||||
|
aria-label={tag}
|
||||||
|
checked={selectedTags.has(tag)}
|
||||||
|
onChange={() => toggleTag(tag)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{selectedTags.size > 0 && (
|
||||||
|
<input
|
||||||
|
className="btn btn-square"
|
||||||
|
type="reset"
|
||||||
|
value="×"
|
||||||
|
onClick={() => setSelectedTags(new Set())}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
{filteredFiles.map((file) => {
|
||||||
return <FileTile file={file} key={file.id}></FileTile>;
|
return <FileTile file={file} key={file.id}></FileTile>;
|
||||||
})}
|
})}
|
||||||
|
{filteredFiles.length === 0 && selectedTags.size > 0 && (
|
||||||
|
<div className="text-center text-base-content/60 py-8">
|
||||||
|
{t("No files match the selected tags")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={"h-2"}></div>
|
<div className={"h-2"}></div>
|
||||||
{(app.canUpload() || (app.allowNormalUserUpload && app.isLoggedIn())) && (
|
{(app.canUpload() || (app.allowNormalUserUpload && app.isLoggedIn())) && (
|
||||||
<div className={"flex flex-row-reverse"}>
|
<div className={"flex flex-row-reverse"}>
|
||||||
@@ -954,6 +1021,10 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
const [storage, setStorage] = useState<Storage | null>(null);
|
const [storage, setStorage] = useState<Storage | null>(null);
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
const [description, setDescription] = useState<string>("");
|
const [description, setDescription] = useState<string>("");
|
||||||
|
const [tag, setTag] = useState<string>("");
|
||||||
|
const [fileSize, setFileSize] = useState<string>("");
|
||||||
|
const [fileSizeUnit, setFileSizeUnit] = useState<string>("MB");
|
||||||
|
const [md5, setMd5] = useState<string>("");
|
||||||
|
|
||||||
const [fileUrl, setFileUrl] = useState<string>("");
|
const [fileUrl, setFileUrl] = useState<string>("");
|
||||||
|
|
||||||
@@ -985,11 +1056,38 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let fileSizeNum = 0;
|
||||||
|
if (fileSize) {
|
||||||
|
const size = parseFloat(fileSize);
|
||||||
|
if (isNaN(size)) {
|
||||||
|
setError(t("File size must be a number"));
|
||||||
|
setSubmitting(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Convert to bytes based on unit
|
||||||
|
switch (fileSizeUnit) {
|
||||||
|
case "B":
|
||||||
|
fileSizeNum = size;
|
||||||
|
break;
|
||||||
|
case "KB":
|
||||||
|
fileSizeNum = size * 1024;
|
||||||
|
break;
|
||||||
|
case "MB":
|
||||||
|
fileSizeNum = size * 1024 * 1024;
|
||||||
|
break;
|
||||||
|
case "GB":
|
||||||
|
fileSizeNum = size * 1024 * 1024 * 1024;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const res = await network.createRedirectFile(
|
const res = await network.createRedirectFile(
|
||||||
filename,
|
filename,
|
||||||
description,
|
description,
|
||||||
resourceId,
|
resourceId,
|
||||||
redirectUrl,
|
redirectUrl,
|
||||||
|
fileSizeNum,
|
||||||
|
md5,
|
||||||
|
tag,
|
||||||
);
|
);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -1046,6 +1144,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
description,
|
description,
|
||||||
resourceId,
|
resourceId,
|
||||||
storage.id,
|
storage.id,
|
||||||
|
tag,
|
||||||
);
|
);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -1119,15 +1218,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
<p className={"text-sm font-bold p-2"}>{t("Type")}</p>
|
<p className={"text-sm font-bold p-2"}>{t("Type")}</p>
|
||||||
<form className="filter mb-2">
|
<form className="filter mb-2">
|
||||||
<input
|
<input
|
||||||
className="btn btn-square"
|
className="btn"
|
||||||
type="reset"
|
|
||||||
value="×"
|
|
||||||
onClick={() => {
|
|
||||||
setFileType(null);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="btn text-sm"
|
|
||||||
type="radio"
|
type="radio"
|
||||||
name="type"
|
name="type"
|
||||||
aria-label={t("Redirect")}
|
aria-label={t("Redirect")}
|
||||||
@@ -1136,7 +1227,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="btn text-sm"
|
className="btn"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="type"
|
name="type"
|
||||||
aria-label={t("Upload")}
|
aria-label={t("Upload")}
|
||||||
@@ -1145,7 +1236,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="btn text-sm"
|
className="btn"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="type"
|
name="type"
|
||||||
aria-label={t("File Url")}
|
aria-label={t("File Url")}
|
||||||
@@ -1153,6 +1244,14 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
setFileType(FileType.serverTask);
|
setFileType(FileType.serverTask);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
className="btn btn-square"
|
||||||
|
type="reset"
|
||||||
|
value="×"
|
||||||
|
onClick={() => {
|
||||||
|
setFileType(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{fileType === FileType.redirect && (
|
{fileType === FileType.redirect && (
|
||||||
@@ -1183,6 +1282,45 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
setDescription(e.target.value);
|
setDescription(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input w-full my-2"
|
||||||
|
placeholder={t("Tag") + " (" + t("Optional") + ")"}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTag(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="join w-full">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="input flex-1 join-item"
|
||||||
|
placeholder={t("File Size") + " (" + t("Optional") + ")"}
|
||||||
|
value={fileSize}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFileSize(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
className="select w-24 join-item"
|
||||||
|
value={fileSizeUnit}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFileSizeUnit(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="B">B</option>
|
||||||
|
<option value="KB">KB</option>
|
||||||
|
<option value="MB">MB</option>
|
||||||
|
<option value="GB">GB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input w-full my-2"
|
||||||
|
placeholder={"MD5" + " (" + t("Optional") + ")"}
|
||||||
|
onChange={(e) => {
|
||||||
|
setMd5(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1239,6 +1377,14 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
setDescription(e.target.value);
|
setDescription(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input w-full my-2"
|
||||||
|
placeholder={t("Tag") + " (" + t("Optional") + ")"}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTag(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1311,6 +1457,14 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
|
|||||||
setDescription(e.target.value);
|
setDescription(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input w-full my-2"
|
||||||
|
placeholder={t("Tag") + " (" + t("Optional") + ")"}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTag(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1340,6 +1494,8 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) {
|
|||||||
|
|
||||||
const [description, setDescription] = useState(file.description);
|
const [description, setDescription] = useState(file.description);
|
||||||
|
|
||||||
|
const [tag, setTag] = useState(file.tag || "");
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const reload = useContext(context);
|
const reload = useContext(context);
|
||||||
@@ -1349,7 +1505,7 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await network.updateFile(file.id, filename, description);
|
const res = await network.updateFile(file.id, filename, description, tag);
|
||||||
const dialog = document.getElementById(
|
const dialog = document.getElementById(
|
||||||
`update_file_info_dialog_${file.id}`,
|
`update_file_info_dialog_${file.id}`,
|
||||||
) as HTMLDialogElement;
|
) as HTMLDialogElement;
|
||||||
@@ -1397,6 +1553,12 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) {
|
|||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<Input
|
||||||
|
type={"text"}
|
||||||
|
label={t("Tag") + " (" + t("Optional") + ")"}
|
||||||
|
value={tag}
|
||||||
|
onChange={(e) => setTag(e.target.value)}
|
||||||
|
/>
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<button className="btn btn-ghost">{t("Close")}</button>
|
<button className="btn btn-ghost">{t("Close")}</button>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://localhost:3000",
|
// target: "http://localhost:3000",
|
||||||
// target: "https://res.nyne.dev",
|
target: "https://nysoure.com",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
"https://www.moyu.moe": {
|
"https://www.moyu.moe": {
|
||||||
|
|||||||
Reference in New Issue
Block a user