fixed uploads

This commit is contained in:
Holly Stubbs 2025-01-26 10:34:56 +00:00
parent a0fcc376b7
commit 30ff3dbdcd
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
7 changed files with 86 additions and 46 deletions

View file

@ -1,12 +1,15 @@
import Controller from "./Controller";
import type DashboardViewModel from "../models/home/DashboardViewModel";
import UserService from "../services/UserService";
import DomainService from "../services/DomainService";
import ArrayUtility from "../utilities/ArrayUtility";
export default class HomeController extends Controller {
public async Index_Get_AllowAnonymous() {
if (this.session) {
const dashboardViewModel: DashboardViewModel = {
recentUploads: await UserService.GetRecentUploads(this.session.userId)
recentUploads: await UserService.GetRecentUploads(this.session.userId),
domains: ArrayUtility.ToIdKeyedDict(await DomainService.LoadDomains())
}
return this.view("dashboard",dashboardViewModel);

View file

@ -35,7 +35,12 @@ fastify.register(FastifyView, {
fastify.register(FastifyFormBody);
fastify.register(FastifyMultipart);
fastify.register(FastifyMultipart, {
limits: {
files: 1,
fileSize: 104857600
}
});
fastify.register(FastifyCookie, {
secret: Config.session.secret,

View file

@ -1,5 +1,7 @@
import type Domain from "../../entities/Domain";
import Media from "../../entities/Media";
export default interface DashboardViewModel {
recentUploads: Array<Media>
recentUploads: Array<Media>,
domains: { [key: string]: Domain }
}

View file

@ -1,13 +1,14 @@
// ! Hashed File Store (not file system!!)
import { join } from "path";
import { existsSync, mkdirSync, createWriteStream, rename, stat, writeFile, rm, rmSync, } from "fs";
import { existsSync, mkdirSync, createWriteStream, rename, stat, writeFile, rm, rmSync, createReadStream } from "fs";
import { Console } from "hsconsole";
import { yellow } from "dyetty";
import { createHash, randomBytes } from "crypto";
import FunkyArray from "funky-array";
import HashFSFileInformation from "./HashFSFileInformation";
import type { BusboyFileStream } from "@fastify/busboy";
import { pipeline } from "stream/promises";
export default class HashFS {
public static STARTUP_DIR: string;
@ -104,49 +105,50 @@ export default class HashFS {
if (err) {
return reject(err);
}
resolve(fileInfo);
})
});
}
public AddFromStream(stream: BusboyFileStream) {
return new Promise<HashFSFileInformation>(async (resolve, reject) => {
const hasher = createHash("sha1");
hasher.setEncoding("hex");
const tempFilePath = join(this.tempPath, randomBytes(16).toString("base64url"));
const tempFile = createWriteStream(tempFilePath);
tempFile.on("close", async () => {
const hash: string = hasher.read();
const fileInfo = new HashFSFileInformation();
fileInfo.fileHash = hash;
fileInfo.fileSize = fileSize;
if (await this.FileExists(hash)) {
fileInfo.fileExistsAlready = true;
rm(tempFilePath, err => {
if (err) {
return reject(err);
}
this.logInfo(`File with hash "${hash}" already exists.`);
resolve(fileInfo);
});
} else {
rename(tempFilePath, this.GetFilePath(hash), err => {
if (err) {
return reject(err);
}
this.logInfo(`Stored file as ${hash}`);
resolve(fileInfo);
});
}
});
stream.pipe(tempFile);
stream.pipe(hasher);
let fileSize = 0;
stream.on("data", chunk => fileSize += chunk.length);
const tempFilePath = join(this.tempPath, randomBytes(16).toString("base64url"));
await pipeline(stream, createWriteStream(tempFilePath));
const readStream = createReadStream(tempFilePath);
for await (const chunk of readStream) {
fileSize += chunk.length;
hasher.write(chunk)
}
hasher.end();
const hash: string = hasher.read();
const fileInfo = new HashFSFileInformation();
fileInfo.fileHash = hash;
fileInfo.fileSize = fileSize;
if (await this.FileExists(hash)) {
fileInfo.fileExistsAlready = true;
rm(tempFilePath, err => {
if (err) {
return reject(err);
}
this.logInfo(`File with hash "${hash}" already exists.`);
resolve(fileInfo);
});
} else {
rename(tempFilePath, this.GetFilePath(hash), err => {
if (err) {
return reject(err);
}
this.logInfo(`Stored file as ${hash}`);
resolve(fileInfo);
});
}
});
}

13
services/DomainService.ts Normal file
View file

@ -0,0 +1,13 @@
import { Console } from "hsconsole";
import DomainRepo from "../repos/DomainRepo";
export default abstract class DomainService {
public static async LoadDomains() {
try {
return await DomainRepo.SelectAll();
} catch (e) {
Console.printError(`EUS server service error:\n${e}`);
throw e;
}
}
}

10
utilities/ArrayUtility.ts Normal file
View file

@ -0,0 +1,10 @@
export default abstract class ArrayUtility {
public static ToIdKeyedDict(array: Array<any>) {
const dict: { [key: string]: any } = {};
for (const item of array) {
dict[item.Id] = item;
}
return dict;
}
}

View file

@ -11,15 +11,20 @@
</div>
</div>
<div class="card-body">
<table>
<tbody>
<% for (const upload of recentUploads) { %>
<tr>
<td><%= upload.FileName %></td>
</tr>
<% } %>
</tbody>
</table>
<div class="row row-cols-1">
<% for (const upload of recentUploads) { %>
<div class="col">
<div class="row">
<div class="col-auto">
<% if (upload.MediaType.startsWith("image/")) { %>
<img src="<%= domains[upload.DomainId].HasHttps ? "https" : "http" %>://<%= domains[upload.DomainId].Domain %>/<%= upload.MediaTag %>" height="30" width="50">
<% } %>
</div>
<div class="col"><%= upload.FileName %></div>
</div>
</div>
<% } %>
</div>
</div>
</div>
</div>