Skip to content

Commit

Permalink
Merge pull request #144 from waifuvault/add-one-time-downlaods
Browse files Browse the repository at this point in the history
one time download added
  • Loading branch information
VictoriqueMoe authored Apr 2, 2024
2 parents 280febe + ba951e0 commit a2db4ba
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 14 deletions.
9 changes: 9 additions & 0 deletions src/controllers/serve/FileServerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FileService } from "../../services/FileService.js";
import { FileProtectedException } from "../../model/exceptions/FileProtectedException.js";
import { MimeService } from "../../services/MimeService.js";
import type { Response } from "express";
import { FileUploadModel } from "../../model/db/FileUpload.model.js";

@Hidden()
@Controller("/")
Expand All @@ -25,6 +26,7 @@ export class FileServerController {
await this.hasPassword(resource, password);
const [buff, entry] = await this.fileService.getEntry(resource, requestedFileName, password);
const mimeType = await this.mimeService.findMimeTypeFromBuffer(buff, entry.fullFileNameOnSystem);
res.on("finish", () => this.postProcess(entry));
if (mimeType) {
res.contentType(mimeType);
} else {
Expand All @@ -34,6 +36,13 @@ export class FileServerController {
res.send(buff);
}

private async postProcess(entry: FileUploadModel): Promise<void> {
const hasOneTimeDownload = entry.settings?.oneTimeDownload ?? false;
if (hasOneTimeDownload) {
await this.fileService.processDelete([entry.token]);
}
}

private async hasPassword(resource: string, password?: string): Promise<boolean> {
resource = Path.parse(resource).name;
const resourceIsProtected = await this.isFilePasswordProtected(resource);
Expand Down
44 changes: 38 additions & 6 deletions src/model/dto/FileUploadResponseDto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import { Description, Name, Nullable, Property } from "@tsed/schema";
import { Default, Description, Name, Nullable, Property } from "@tsed/schema";
import { FileUploadModel } from "../db/FileUpload.model.js";
import { Builder } from "builder-pattern";
import { ObjectUtils } from "../../utils/Utils.js";
import { EntrySettings } from "../../utils/typeings.js";

class ResponseOptions implements Required<Omit<EntrySettings, "password">> {
@Property(Boolean)
@Description("If the filename is hidden")
@Default(false)
public hideFilename = false;

@Property(Boolean)
@Default(false)
@Description("If this file will be deleted when it is accessed")
public oneTimeDownload = false;

@Property(Boolean)
@Default(false)
@Description("Does this file require a password")
public protected = false;
}

@Name("WaifuResponse")
@Description("This is a standard response for the service, containing info about the entry")
Expand All @@ -14,15 +32,16 @@ export class FileUploadResponseDto {
@Description("Location of the uploaded file")
public url: string;

@Property()
@Description("Does this file require a password")
public protected: boolean;

@Property()
@Description("How long this file will exist for")
@Nullable(Number, String)
@Default(Number)
public retentionPeriod: string | number | null = null;

@Property()
@Description("The options for this entry")
public options: ResponseOptions;

public static fromModel(fileUploadModel: FileUploadModel, baseUrl: string, format = false): FileUploadResponseDto {
const builder = Builder(FileUploadResponseDto)
.token(fileUploadModel.token)
Expand All @@ -33,7 +52,8 @@ export class FileUploadResponseDto {
} else {
builder.retentionPeriod(fileUploadModel.expiresIn);
}
builder.protected(!!fileUploadModel.settings?.password);

builder.options(FileUploadResponseDto.makeOptions(fileUploadModel.settings));
return builder.build();
}

Expand All @@ -47,4 +67,16 @@ export class FileUploadResponseDto {
}
return `${baseUrl}/f/${fileUploadModel.fileName}/${originalFileName}`;
}

private static makeOptions(settings: EntrySettings | null): ResponseOptions {
const options = Builder(ResponseOptions);
if (!settings) {
return options.build();
}
options.oneTimeDownload(settings?.oneTimeDownload ?? false);
options.hideFilename(settings?.hideFilename ?? false);
options.protected(!!settings.password);

return options.build();
}
}
9 changes: 8 additions & 1 deletion src/model/rest/FileUploadParameters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Description, Name, Optional, Pattern, Property } from "@tsed/schema";
import { Default, Description, Name, Optional, Pattern, Property } from "@tsed/schema";

@Name("WaifuUploadParameters")
@Description("Upload parameters for put requests")
Expand All @@ -17,6 +17,7 @@ export class FileUploadParameters {
@Optional()
@Property()
@Name("hide_filename")
@Default(false)
public hideFilename?: boolean;

@Description(
Expand All @@ -25,4 +26,10 @@ export class FileUploadParameters {
@Optional()
@Property()
public password?: string;

@Description("If this is true, then the file will be deleted as soon as it is accessed")
@Optional()
@Property()
@Default(false)
public one_time_download?: boolean;
}
4 changes: 0 additions & 4 deletions src/public/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,6 @@ public class Example {
</div>
</div>
</div>

<footer class="pt-3 mt-4 text-body-secondary border-top">
&copy; Victoria
</footer>
</div>
</main>
<%- include('snippets/scripts.ejs'); %>
Expand Down
14 changes: 11 additions & 3 deletions src/services/FileService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ export class FileService {
public async processUpload(
ip: string,
source: PlatformMulterFile | string,
{ password, hideFilename, expires }: FileUploadParameters,
options: FileUploadParameters,
secretToken?: string,
): Promise<[FileUploadResponseDto, boolean]> {
const { expires, password } = options;
let resourcePath: string | undefined;
let originalFileName: string | undefined;
try {
Expand All @@ -71,7 +72,7 @@ export class FileService {
}
}

uploadEntry.settings(await this.buildEntrySettings(hideFilename, password));
uploadEntry.settings(await this.buildEntrySettings(options));

const ext = FileUtils.getExtension(originalFileName);
if (ext) {
Expand Down Expand Up @@ -145,14 +146,21 @@ export class FileService {
return null;
}

private async buildEntrySettings(hideFilename?: boolean, password?: string): Promise<EntrySettings | null> {
private async buildEntrySettings({
password,
hideFilename,
one_time_download,
}: FileUploadParameters): Promise<EntrySettings | null> {
const retObj: EntrySettings = {};
if (password) {
retObj["password"] = await this.hashPassword(password);
}
if (hideFilename) {
retObj["hideFilename"] = hideFilename;
}
if (one_time_download) {
retObj["oneTimeDownload"] = one_time_download;
}
return Object.keys(retObj).length === 0 ? null : retObj;
}

Expand Down
1 change: 1 addition & 0 deletions src/utils/typeings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type HttpErrorRenderObj<T extends Exception> = {
export type EntrySettings = {
hideFilename?: boolean;
password?: string;
oneTimeDownload?: boolean;
};

export type AvScanResult = {
Expand Down

0 comments on commit a2db4ba

Please sign in to comment.