182 lines
8.0 KiB
JavaScript
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);
|
|
}
|
|
});
|