Merge pull request #50 from oiov/dev

refact file size caculate
This commit is contained in:
oiov
2025-07-12 16:32:32 +08:00
committed by GitHub
12 changed files with 68 additions and 38 deletions
@@ -29,6 +29,9 @@ export async function GET(req: NextRequest) {
channel: configs.s3_config_01.channel,
});
} catch (error) {
return NextResponse.json({ error: "Error listing files" }, { status: 500 });
return NextResponse.json(
{ error: "Error listing buckets" },
{ status: 500 },
);
}
}
+2
View File
@@ -24,6 +24,7 @@ export async function GET(req: NextRequest) {
const name = url.searchParams.get("name") || "";
const fileSize = url.searchParams.get("fileSize") || "";
const mimeType = url.searchParams.get("mimeType") || "";
const status = url.searchParams.get("status") || "";
const configs = await getMultipleConfigs(["s3_config_01"]);
if (!configs.s3_config_01.enabled) {
@@ -56,6 +57,7 @@ export async function GET(req: NextRequest) {
platform: configs.s3_config_01.platform,
name,
size: Number(fileSize || 0),
status: Number(status === "0" ? 0 : 1),
mimeType,
});
+4 -1
View File
@@ -23,6 +23,9 @@ export async function GET(req: NextRequest) {
channel: configs.s3_config_01.channel,
});
} catch (error) {
return NextResponse.json({ error: "Error listing files" }, { status: 500 });
return NextResponse.json(
{ error: "Error listing buckets" },
{ status: 500 },
);
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ import { NextRequest, NextResponse } from "next/server";
import { createUserFile } from "@/lib/dto/files";
import { checkUserStatus } from "@/lib/dto/user";
import { getCurrentUser } from "@/lib/session";
import { bytesToStorageValue } from "@/lib/utils";
export async function POST(request: NextRequest) {
try {
@@ -44,7 +45,7 @@ export async function POST(request: NextRequest) {
channel: body.channel || "",
platform: body.platform || "",
providerName: body.providerName || "",
size: body.size,
size: bytesToStorageValue(body.size),
bucket: body.bucket,
lastModified: body.lastModified
? new Date(body.lastModified)
+19 -23
View File
@@ -25,6 +25,7 @@ import {
downloadFileFromUrl,
formatDate,
formatFileSize,
storageValueToBytes,
truncateMiddle,
} from "@/lib/utils";
import { ClickableTooltip } from "@/components/ui/tooltip";
@@ -47,7 +48,6 @@ import {
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { Modal } from "../ui/modal";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { Skeleton } from "../ui/skeleton";
import { Switch } from "../ui/switch";
import { TableCell, TableRow } from "../ui/table";
@@ -312,6 +312,10 @@ export default function UserFileList({
)}
<div className={cn("col-span-3 items-center space-x-3 text-sm")}>
<ClickableTooltip
className={cn(
"flex cursor-pointer items-center justify-start gap-1 break-all text-start",
file.status !== 1 && "text-muted-foreground",
)}
content={
<div className="w-72 space-y-1 text-wrap p-3 text-start">
{file.mimeType.startsWith("image/") &&
@@ -328,20 +332,13 @@ export default function UserFileList({
</div>
}
>
<div
className={cn(
"flex items-center justify-start gap-1 break-all text-start",
file.status !== 1 && "text-muted-foreground",
)}
>
{truncateMiddle(file.path)}
{file.status === 1 && (
<CopyButton
className="size-6"
value={getFileUrl(file.path)}
/>
)}
</div>
{truncateMiddle(file.path)}
{file.status === 1 && (
<CopyButton
className="size-6"
value={getFileUrl(file.path)}
/>
)}
</ClickableTooltip>
</div>
<div className="col-span-2 hidden items-center text-xs sm:flex">
@@ -350,10 +347,11 @@ export default function UserFileList({
</Badge>
</div>
<div className="col-span-1 flex items-center text-nowrap text-xs">
{formatFileSize(file.size || 0)}
{formatFileSize(storageValueToBytes(file.size) || 0)}
</div>
<div className="col-span-1 hidden items-center text-xs sm:flex">
<ClickableTooltip
className="cursor-pointer truncate"
content={
<>
<p>{file.user.name}</p>
@@ -361,9 +359,7 @@ export default function UserFileList({
</>
}
>
<div className="truncate">
{file.user.name ?? file.user.email}
</div>
{file.user.name ?? file.user.email}
</ClickableTooltip>
</div>
<div className="col-span-1 hidden items-center text-nowrap text-xs sm:flex">
@@ -495,6 +491,7 @@ export default function UserFileList({
{React.cloneElement(getFileIcon(file, bucketInfo), { size: 40 })}
<div className="w-full text-center">
<ClickableTooltip
className="mx-auto line-clamp-2 max-w-[60px] cursor-pointer break-all px-2 pb-1 text-left text-xs font-medium text-muted-foreground group-hover:text-blue-500 sm:max-w-[100px]"
content={
<div className="max-w-[300px] space-y-1 p-3 text-start">
{file.mimeType.startsWith("image/") &&
@@ -511,7 +508,8 @@ export default function UserFileList({
{file.path}
</p>
<p className="mt-1 text-xs text-muted-foreground">
<strong>Size:</strong> {formatFileSize(file.size || 0)}
<strong>Size:</strong>{" "}
{formatFileSize(storageValueToBytes(file.size) || 0)}
</p>
<p className="mt-1 text-xs text-muted-foreground">
<strong>Type:</strong> {file.mimeType || "-"}
@@ -573,9 +571,7 @@ export default function UserFileList({
</div>
}
>
<div className="mx-auto line-clamp-2 max-w-[60px] break-all px-2 pb-1 text-left text-xs font-medium text-muted-foreground group-hover:text-blue-500 sm:max-w-[100px]">
{truncateMiddle(file.path || "")}
</div>
{truncateMiddle(file.path || "")}
</ClickableTooltip>
</div>
</div>
+8 -4
View File
@@ -93,6 +93,7 @@ export default function UserFileManager({ user, action }: FileListProps) {
name: "",
fileSize: "",
mimeType: "",
status: "1",
});
// const isAdmin = action.includes("/admin");
@@ -107,7 +108,7 @@ export default function UserFileManager({ user, action }: FileListProps) {
const { data: files, isLoading: isLoadingFiles } = useSWR<FileListData>(
bucketInfo.bucket
? `${action}/r2/files?bucket=${bucketInfo.bucket}&page=${currentPage}&pageSize=${pageSize}&name=${searchParams.name}&fileSize=${searchParams.fileSize}&mimeType=${searchParams.mimeType}`
? `${action}/r2/files?bucket=${bucketInfo.bucket}&page=${currentPage}&pageSize=${pageSize}&name=${searchParams.name}&fileSize=${searchParams.fileSize}&mimeType=${searchParams.mimeType}&status=${searchParams.status}`
: null,
fetcher,
{
@@ -133,8 +134,9 @@ export default function UserFileManager({ user, action }: FileListProps) {
}, [r2Configs]);
const handleRefresh = () => {
setSelectedFiles([]);
mutate(
`${action}/r2/files?bucket=${bucketInfo.bucket}&page=${currentPage}&pageSize=${pageSize}&name=${searchParams.name}&fileSize=${searchParams.fileSize}&mimeType=${searchParams.mimeType}`,
`${action}/r2/files?bucket=${bucketInfo.bucket}&page=${currentPage}&pageSize=${pageSize}&name=${searchParams.name}&fileSize=${searchParams.fileSize}&mimeType=${searchParams.mimeType}&status=${searchParams.status}`,
undefined,
);
};
@@ -205,11 +207,12 @@ export default function UserFileManager({ user, action }: FileListProps) {
name: "",
fileSize: "",
mimeType: "",
status: "1",
});
setCurrentPage(1);
}}
>
<SelectTrigger className="w-[80px] rounded-r-none">
<SelectTrigger className="w-[80px] rounded-r-none text-sm">
<SelectValue placeholder="Select a type" />
</SelectTrigger>
<SelectContent>
@@ -217,6 +220,7 @@ export default function UserFileManager({ user, action }: FileListProps) {
{ lebal: "Name", value: "name" },
{ lebal: "Size", value: "fileSize" },
{ lebal: "Type", value: "mimeType" },
{ lebal: "Status", value: "status" },
].map((item) => (
<SelectItem key={item.value} value={item.value}>
{t(item.lebal)}
@@ -225,7 +229,7 @@ export default function UserFileManager({ user, action }: FileListProps) {
</SelectContent>
</Select>
<Input
className="min-w-28 rounded-l-none border-l-0 sm:w-48 sm:flex-none"
className="min-w-28 rounded-l-none border-l-0 placeholder:text-xs sm:w-48 sm:flex-none"
placeholder={`Search by ${currentSearchType}...`}
value={searchParams[currentSearchType] || ""}
onChange={(e) => {
+2 -2
View File
@@ -41,7 +41,7 @@ export {
TooltipArrow,
};
export const ClickableTooltip = ({ children, content }) => {
export const ClickableTooltip = ({ children, content, className = "" }) => {
const [open, setOpen] = useState(false);
const handleClick = (e) => {
@@ -65,7 +65,7 @@ export const ClickableTooltip = ({ children, content }) => {
onFocus={(e) => e.preventDefault()} // 阻止焦点事件
onBlur={(e) => e.preventDefault()}
>
<div onClick={handleClick} className="cursor-pointer truncate">
<div onClick={handleClick} className={className}>
{children}
</div>
</TooltipTrigger>
+1 -1
View File
@@ -97,7 +97,7 @@ export function useFileUpload({ bucketInfo, userId, api }: Props) {
userId,
name: extractKey.fileName,
originalName: extractKey.nameWithoutExtension,
mimeType: item.file.type,
mimeType: item.file.type || "-",
path: item.fileName,
etag,
storageClass: "",
+4 -3
View File
@@ -1,6 +1,7 @@
import { Prisma, UserFile } from "@prisma/client";
import { prisma } from "../db";
import { bytesToStorageValue, storageValueToBytes } from "../utils";
export interface UserFileData extends UserFile {
user: {
@@ -127,14 +128,14 @@ export async function getUserFiles(options: QueryUserFileOptions = {}) {
const where: Prisma.UserFileWhereInput = {
bucket,
...(status && { status }),
...(status !== undefined && { status }),
...(userId && { userId }),
...(providerName && { providerName }),
...(channel && { channel }),
...(platform && { platform }),
...(shortUrlId && { shortUrlId }),
...(name && { name: { contains: name, mode: "insensitive" } }),
...(size && { size: { gte: size } }),
...(size && { size: { gte: bytesToStorageValue(size) } }),
...(mimeType && {
mimeType: { contains: mimeType, mode: "insensitive" },
}),
@@ -164,7 +165,7 @@ export async function getUserFiles(options: QueryUserFileOptions = {}) {
return {
total,
totalSize: totalSize._sum.size || 0,
totalSize: storageValueToBytes(totalSize._sum.size || 0),
list: files,
};
} catch (error) {
+20
View File
@@ -571,3 +571,23 @@ export function generateFileKey(fileName: string, prefix?: string): string {
return `${year}/${month}/${day}/${fileName}`;
}
const SIZE_THRESHOLD = 1000;
export function bytesToStorageValue(bytes: number): number {
if (bytes < SIZE_THRESHOLD) {
return bytes;
} else {
return Math.ceil(bytes / SIZE_THRESHOLD);
}
}
export function storageValueToBytes(
storageValue: number,
originalBytes?: number,
): number {
if (storageValue < SIZE_THRESHOLD) {
return storageValue;
} else {
return storageValue * SIZE_THRESHOLD;
}
}
@@ -28,8 +28,8 @@ ON "user_files"("userId", "providerName", "status", "lastModified", "createdAt")
ALTER TABLE "user_files" ADD CONSTRAINT "user_files_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- BigInt
ALTER TABLE "plans" ADD COLUMN "stMaxFileSize" TEXT NOT NULL DEFAULT '26214400';
ALTER TABLE "plans" ADD COLUMN "stMaxTotalSize" TEXT NOT NULL DEFAULT '524288000';
ALTER TABLE "plans" ADD COLUMN "stMaxFileCount" INTEGER NOT NULL DEFAULT 1000;
+1 -1
View File
File diff suppressed because one or more lines are too long