mirror of
https://git.joinfirefish.org/firefish/firefish.git
synced 2024-05-19 04:21:11 +02:00
225 lines
5.9 KiB
TypeScript
225 lines
5.9 KiB
TypeScript
import { db } from "@/db/postgre.js";
|
|
import { DriveFile } from "@/models/entities/drive-file.js";
|
|
import type { User } from "@/models/entities/user.js";
|
|
import { toPuny } from "backend-rs";
|
|
import { awaitAll } from "@/prelude/await-all.js";
|
|
import type { Packed } from "@/misc/schema.js";
|
|
import config from "@/config/index.js";
|
|
import { query, appendQuery } from "@/prelude/url.js";
|
|
import { Users, DriveFolders } from "../index.js";
|
|
import { deepClone } from "@/misc/clone.js";
|
|
|
|
type PackOptions = {
|
|
detail?: boolean;
|
|
self?: boolean;
|
|
withUser?: boolean;
|
|
};
|
|
|
|
export const DriveFileRepository = db.getRepository(DriveFile).extend({
|
|
validateFileName(name: string): boolean {
|
|
return (
|
|
name.trim().length > 0 &&
|
|
name.length <= 200 &&
|
|
name.indexOf("\\") === -1 &&
|
|
name.indexOf("/") === -1 &&
|
|
name.indexOf("..") === -1
|
|
);
|
|
},
|
|
|
|
getPublicProperties(file: DriveFile): DriveFile["properties"] {
|
|
if (file.properties.orientation != null) {
|
|
const properties = deepClone(file.properties);
|
|
if (file.properties.orientation >= 5) {
|
|
[properties.width, properties.height] = [
|
|
properties.height,
|
|
properties.width,
|
|
];
|
|
}
|
|
properties.orientation = undefined;
|
|
return properties;
|
|
}
|
|
|
|
return file.properties;
|
|
},
|
|
|
|
getPublicUrl(file: DriveFile, thumbnail = false): string | null {
|
|
// リモートかつメディアプロキシ
|
|
if (
|
|
file.uri != null &&
|
|
file.userHost != null &&
|
|
config.mediaProxy != null
|
|
) {
|
|
return appendQuery(
|
|
config.mediaProxy,
|
|
query({
|
|
url: file.uri,
|
|
thumbnail: thumbnail ? "1" : undefined,
|
|
}),
|
|
);
|
|
}
|
|
|
|
// リモートかつ期限切れはローカルプロキシを試みる
|
|
if (file.uri != null && file.isLink && config.proxyRemoteFiles) {
|
|
const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey;
|
|
|
|
if (key && !key.match("/")) {
|
|
// 古いものはここにオブジェクトストレージキーが入ってるので除外
|
|
return `${config.url}/files/${key}`;
|
|
}
|
|
}
|
|
|
|
const isImage =
|
|
file.type &&
|
|
[
|
|
"image/png",
|
|
"image/apng",
|
|
"image/gif",
|
|
"image/jpeg",
|
|
"image/webp",
|
|
"image/svg+xml",
|
|
"image/avif",
|
|
].includes(file.type);
|
|
|
|
return thumbnail
|
|
? file.thumbnailUrl || (isImage ? file.webpublicUrl || file.url : null)
|
|
: file.webpublicUrl || file.url;
|
|
},
|
|
|
|
async calcDriveUsageOf(
|
|
user: User["id"] | { id: User["id"] },
|
|
): Promise<number> {
|
|
const id = typeof user === "object" ? user.id : user;
|
|
|
|
const { sum } = await this.createQueryBuilder("file")
|
|
.where("file.userId = :id", { id: id })
|
|
.andWhere("file.isLink = FALSE")
|
|
.select("SUM(file.size)", "sum")
|
|
.getRawOne();
|
|
|
|
return parseInt(sum, 10) || 0;
|
|
},
|
|
|
|
async calcDriveUsageOfHost(host: string): Promise<number> {
|
|
const { sum } = await this.createQueryBuilder("file")
|
|
.where("file.userHost = :host", { host: toPuny(host) })
|
|
.andWhere("file.isLink = FALSE")
|
|
.select("SUM(file.size)", "sum")
|
|
.getRawOne();
|
|
|
|
return parseInt(sum, 10) || 0;
|
|
},
|
|
|
|
async calcDriveUsageOfLocal(): Promise<number> {
|
|
const { sum } = await this.createQueryBuilder("file")
|
|
.where("file.userHost IS NULL")
|
|
.andWhere("file.isLink = FALSE")
|
|
.select("SUM(file.size)", "sum")
|
|
.getRawOne();
|
|
|
|
return parseInt(sum, 10) || 0;
|
|
},
|
|
|
|
async calcDriveUsageOfRemote(): Promise<number> {
|
|
const { sum } = await this.createQueryBuilder("file")
|
|
.where("file.userHost IS NOT NULL")
|
|
.andWhere("file.isLink = FALSE")
|
|
.select("SUM(file.size)", "sum")
|
|
.getRawOne();
|
|
|
|
return parseInt(sum, 10) || 0;
|
|
},
|
|
|
|
async pack(
|
|
src: DriveFile["id"] | DriveFile,
|
|
options?: PackOptions,
|
|
): Promise<Packed<"DriveFile">> {
|
|
const opts = Object.assign(
|
|
{
|
|
detail: false,
|
|
self: false,
|
|
},
|
|
options,
|
|
);
|
|
|
|
const file =
|
|
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
|
|
|
|
return await awaitAll<Packed<"DriveFile">>({
|
|
id: file.id,
|
|
createdAt: file.createdAt.toISOString(),
|
|
name: file.name,
|
|
type: file.type,
|
|
md5: file.md5,
|
|
size: file.size,
|
|
isSensitive: file.isSensitive,
|
|
usageHint: file.usageHint,
|
|
blurhash: file.blurhash,
|
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
|
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
|
thumbnailUrl: this.getPublicUrl(file, true),
|
|
comment: file.comment,
|
|
folderId: file.folderId,
|
|
folder:
|
|
opts.detail && file.folderId
|
|
? DriveFolders.pack(file.folderId, {
|
|
detail: true,
|
|
})
|
|
: null,
|
|
userId: opts.withUser ? file.userId : null,
|
|
user: opts.withUser && file.userId ? Users.pack(file.userId) : null,
|
|
});
|
|
},
|
|
|
|
async packNullable(
|
|
src: DriveFile["id"] | DriveFile,
|
|
options?: PackOptions,
|
|
): Promise<Packed<"DriveFile"> | null> {
|
|
const opts = Object.assign(
|
|
{
|
|
detail: false,
|
|
self: false,
|
|
},
|
|
options,
|
|
);
|
|
|
|
const file =
|
|
typeof src === "object" ? src : await this.findOneBy({ id: src });
|
|
if (file == null) return null;
|
|
|
|
return await awaitAll<Packed<"DriveFile">>({
|
|
id: file.id,
|
|
createdAt: file.createdAt.toISOString(),
|
|
name: file.name,
|
|
type: file.type,
|
|
md5: file.md5,
|
|
size: file.size,
|
|
isSensitive: file.isSensitive,
|
|
usageHint: file.usageHint,
|
|
blurhash: file.blurhash,
|
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
|
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
|
thumbnailUrl: this.getPublicUrl(file, true),
|
|
comment: file.comment,
|
|
folderId: file.folderId,
|
|
folder:
|
|
opts.detail && file.folderId
|
|
? DriveFolders.pack(file.folderId, {
|
|
detail: true,
|
|
})
|
|
: null,
|
|
userId: opts.withUser ? file.userId : null,
|
|
user: opts.withUser && file.userId ? Users.pack(file.userId) : null,
|
|
});
|
|
},
|
|
|
|
async packMany(
|
|
files: (DriveFile["id"] | DriveFile)[],
|
|
options?: PackOptions,
|
|
): Promise<Packed<"DriveFile">[]> {
|
|
const items = await Promise.all(
|
|
files.map((f) => this.packNullable(f, options)),
|
|
);
|
|
return items.filter((x): x is Packed<"DriveFile"> => x != null);
|
|
},
|
|
});
|