Files
FrozenFriendsYume/packages/backend/built/models/repositories/drive-file.js
T
2026-07-26 18:25:37 +09:00

182 lines
8.0 KiB
JavaScript

import { db } from "../../db/postgre.js";
import { DriveFile } from "../entities/drive-file.js";
import { toPuny } from "../../misc/convert-host.js";
import { awaitAll } from "../../prelude/await-all.js";
import config from "../../config/index.js";
import { appendQuery, query } from "../../prelude/url.js";
import { DriveFolders, Users } from "../index.js";
import { deepClone } from "../../misc/clone.js";
import { fetchMetaSync } from "../../misc/fetch-meta.js";
export const DriveFileRepository = db.getRepository(DriveFile).extend({
validateFileName (name) {
return name.trim().length > 0 && name.length <= 200 && name.indexOf("\\") === -1 && name.indexOf("/") === -1 && name.indexOf("..") === -1;
},
getPublicProperties (file) {
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;
},
isImage (file) {
return !!file.type && [
"image/png",
"image/apng",
"image/gif",
"image/jpeg",
"image/webp",
"image/svg+xml",
"image/avif"
].includes(file.type);
},
isStreamableMedia (file) {
return file.type.startsWith("video/") || file.type.startsWith("audio/");
},
withStreamQuery (file, url) {
if (url == null || file.allowDownload || !this.isStreamableMedia(file)) return url;
return appendQuery(url, query({
stream: "1"
}));
},
getPublicUrl (file, thumbnail = false) {
// リモートかつメディアプロキシ
if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
return appendQuery(config.mediaProxy, query({
url: file.uri,
thumbnail: thumbnail ? "1" : undefined
}));
}
if (file.isLink && config.proxyRemoteFiles) {
const url = this.getDatabasePrefetchUrl(file, thumbnail);
if (url != null) return `${config.url}/proxy/${encodeURIComponent(new URL(url).pathname)}?${query({
url: url
})}`;
}
const url = thumbnail ? file.thumbnailUrl || (this.isImage(file) ? file.webpublicUrl || file.url : null) : file.webpublicUrl || file.url;
return thumbnail ? url : this.withStreamQuery(file, url);
},
getDatabasePrefetchUrl (file, thumbnail = false) {
return thumbnail ? file.thumbnailUrl ?? file.webpublicUrl ?? file.url : file.webpublicUrl ?? file.url;
},
getFinalUrl (url) {
if (!config.proxyRemoteFiles) return url;
if (!url.startsWith('https://') && !url.startsWith('http://')) return url;
if (url.startsWith(`${config.url}/files`)) return url;
if (url.startsWith(`${config.url}/static-assets`)) return url;
if (url.startsWith(`${config.url}/identicon`)) return url;
if (url.startsWith(`${config.url}/avatar`)) return url;
const meta = fetchMetaSync();
const baseUrl = meta ? meta.objectStorageBaseUrl ?? `${meta.objectStorageUseSSL ? "https" : "http"}://${meta.objectStorageEndpoint}${meta.objectStoragePort ? `:${meta.objectStoragePort}` : ""}/${meta.objectStorageBucket}` : null;
if (baseUrl !== null && url.startsWith(baseUrl)) return url;
return `${config.url}/proxy/${encodeURIComponent(new URL(url).pathname)}?${query({
url: url
})}`;
},
getFinalUrlMaybe (url) {
if (url == null) return null;
return this.getFinalUrl(url);
},
async calcDriveUsageOf (user) {
const id = typeof user === "object" ? user.id : user;
const { sum } = await this.createQueryBuilder("file").where("file.userId = :id", {
id: id
}).andWhere("file.isLink = FALSE").andWhere("file.isDatabase = FALSE").select("SUM(file.size)", "sum").getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDatabaseUsageOf (user) {
const id = typeof user === "object" ? user.id : user;
const { sum } = await this.createQueryBuilder("file").where("file.userId = :id", {
id: id
}).andWhere("file.isLink = FALSE").andWhere("file.isDatabase = TRUE").select("SUM(file.size)", "sum").getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfHost (host) {
const { sum } = await this.createQueryBuilder("file").where("file.userHost = :host", {
host: toPuny(host)
}).andWhere("file.isLink = FALSE").andWhere("file.isDatabase = FALSE").select("SUM(file.size)", "sum").getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfLocal () {
const { sum } = await this.createQueryBuilder("file").where("file.userHost IS NULL").andWhere("file.isLink = FALSE").andWhere("file.isDatabase = FALSE").select("SUM(file.size)", "sum").getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfRemote () {
const { sum } = await this.createQueryBuilder("file").where("file.userHost IS NOT NULL").andWhere("file.isLink = FALSE").andWhere("file.isDatabase = FALSE").select("SUM(file.size)", "sum").getRawOne();
return parseInt(sum, 10) || 0;
},
async pack (src, options) {
const opts = Object.assign({
detail: false,
self: false
}, options);
const file = typeof src === "object" ? src : await this.findOneByOrFail({
id: src
});
return await awaitAll({
id: file.id,
createdAt: file.createdAt.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
allowDownload: file.allowDownload,
isDatabase: file.isDatabase,
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, options) {
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({
id: file.id,
createdAt: file.createdAt.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
allowDownload: file.allowDownload,
isDatabase: file.isDatabase,
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, options) {
const items = await Promise.all(files.map((f)=>this.packNullable(f, options)));
return items.filter((x)=>x != null);
}
});