files: replace ttl with expires_at

This commit is contained in:
Alice Gaudon 2021-03-30 16:19:37 +02:00
parent 33947012e4
commit c8d7c664e2
5 changed files with 63 additions and 15 deletions

View File

@ -44,6 +44,7 @@ import MakeMagicLinksSessionNotUniqueMigration from "swaf/auth/magic_link/MakeMa
import AddPasswordToUsersMigration from "swaf/auth/password/AddPasswordToUsersMigration"; import AddPasswordToUsersMigration from "swaf/auth/password/AddPasswordToUsersMigration";
import DropNameFromUsers from "swaf/auth/migrations/DropNameFromUsers"; import DropNameFromUsers from "swaf/auth/migrations/DropNameFromUsers";
import packageJson = require('./package.json'); import packageJson = require('./package.json');
import ReplaceTtlWithExpiresAtFilesTable from "./migrations/ReplaceTtlWithExpiresAtFilesTable";
export default class App extends Application { export default class App extends Application {
public constructor( public constructor(
@ -69,7 +70,8 @@ export default class App extends Application {
AddUsedToMagicLinksMigration, AddUsedToMagicLinksMigration,
MakeMagicLinksSessionNotUniqueMigration, MakeMagicLinksSessionNotUniqueMigration,
AddPasswordToUsersMigration, AddPasswordToUsersMigration,
DropNameFromUsers, DummyMigration,
ReplaceTtlWithExpiresAtFilesTable,
]; ];
} }

View File

@ -0,0 +1,46 @@
import Migration from "swaf/db/Migration";
import FileModel from "../models/FileModel";
import {logger} from "swaf/Logger";
export default class ReplaceTtlWithExpiresAtFilesTable extends Migration {
public async install(): Promise<void> {
await this.query(`ALTER TABLE files
ADD COLUMN expires_at DATETIME NULL`);
const files = await FileModel.select().get();
for (const file of files) {
logger.debug(file);
if (file.created_at && typeof file.ttl === 'number' && file.ttl > 0) {
file.expires_at = new Date(file.created_at.getTime() + file.ttl * 1000);
}
const callbacks: (() => Promise<void>)[] = [];
await file.save(this.getCurrentConnection(), callback => {
callbacks.push(callback);
});
for (const c of callbacks) await c();
}
await this.query(`ALTER TABLE files
DROP COLUMN ttl`);
}
public async rollback(): Promise<void> {
await this.query(`ALTER TABLE files
ADD COLUMN ttl INT UNSIGNED NOT NULL DEFAULT 0`);
const files = await FileModel.select().get();
for (const file of files) {
if (file.created_at && file.expires_at) {
file.ttl = Math.ceil((file.expires_at.getTime() - file.created_at.getTime()) / 1000);
}
const callbacks: (() => Promise<void>)[] = [];
await file.save(this.getCurrentConnection(), callback => {
callbacks.push(callback);
});
for (const c of callbacks) await c();
}
await this.query(`ALTER TABLE files
DROP COLUMN expires_at`);
}
}

View File

@ -28,16 +28,22 @@ export default class FileModel extends Model {
public readonly storage_path?: string = undefined; public readonly storage_path?: string = undefined;
public readonly size?: number = undefined; public readonly size?: number = undefined;
public created_at?: Date = undefined; public created_at?: Date = undefined;
public readonly ttl?: number = undefined; public expires_at?: Date | null = undefined;
/**
* @deprecated
*/
public ttl?: number = undefined;
protected init(): void { protected init(): void {
this.setValidation('user_id').defined().exists(User, 'id'); this.setValidation('user_id').defined().exists(User, 'id');
this.setValidation('slug').defined().minLength(1).maxLength(259).unique(FileModel, 'slug').unique(URLRedirect, 'slug'); this.setValidation('slug').defined().minLength(1).maxLength(259)
.unique(this, 'slug')
.unique(URLRedirect, 'slug');
this.setValidation('real_name').defined().minLength(1).maxLength(259); this.setValidation('real_name').defined().minLength(1).maxLength(259);
this.setValidation('storage_type').defined().maxLength(64); this.setValidation('storage_type').defined().maxLength(64);
this.setValidation('storage_path').defined().maxLength(1745); this.setValidation('storage_path').defined().maxLength(1745);
this.setValidation('size').defined().min(0); this.setValidation('size').defined().min(0);
this.setValidation('ttl').defined().min(0).max(4294967295);
} }
public getURL(domain: string = config.get<string>('public_url')): string { public getURL(domain: string = config.get<string>('public_url')): string {
@ -46,15 +52,8 @@ export default class FileModel extends Model {
}); });
} }
public getExpirationDate(): Date | null {
if (!this.created_at) return new Date();
if (this.ttl === 0) return null;
return new Date(this.created_at.getTime() + this.getOrFail('ttl') * 1000);
}
public shouldBeDeleted(): boolean { public shouldBeDeleted(): boolean {
const expirationDate = this.getExpirationDate(); const expirationDate = this.expires_at;
if (!expirationDate) return false; if (!expirationDate) return false;
return new Date().getTime() >= expirationDate.getTime(); return new Date().getTime() >= expirationDate.getTime();
} }

View File

@ -28,7 +28,9 @@ export default class URLRedirect extends Model {
protected init(): void { protected init(): void {
this.setValidation('user_id').defined().exists(User, 'id'); this.setValidation('user_id').defined().exists(User, 'id');
this.setValidation('slug').defined().minLength(1).maxLength(259).unique(URLRedirect, 'slug').unique(FileModel, 'slug'); this.setValidation('slug').defined().minLength(1).maxLength(259)
.unique(this, 'slug')
.unique(FileModel, 'slug');
this.setValidation('target_url').defined().maxLength(1745).regexp(/^https?:\/\/.{3,259}?\/?/i); this.setValidation('target_url').defined().maxLength(1745).regexp(/^https?:\/\/.{3,259}?\/?/i);
} }

View File

@ -81,8 +81,7 @@
</td> </td>
<td><pre>{{ file.real_name }}</pre></td> <td><pre>{{ file.real_name }}</pre></td>
<td>{{ (file.size / (1024 * 1024)).toFixed(2) }}MB</td> <td>{{ (file.size / (1024 * 1024)).toFixed(2) }}MB</td>
{% set expires_at = file.getExpirationDate() %} <td>{% if file.expires_at %}{{ file.expires_at.toISOString() }}{% else %}Never{% endif %}</td>
<td>{% if expires_at %}{{ expires_at.toISOString() }}{% else %}Never{% endif %}</td>
<td class="actions"> <td class="actions">
{% if file.shouldBeDeleted() %} {% if file.shouldBeDeleted() %}
Pending deletion Pending deletion