diff --git a/scripts/clear-cache.ts b/scripts/clear-cache.ts new file mode 100644 index 00000000..e4d40d37 --- /dev/null +++ b/scripts/clear-cache.ts @@ -0,0 +1,32 @@ +import 'dotenv/config'; +import { storageEngine } from '../src/helpers/utils'; + +/** + * Clear all the cached files in the storage engine, + * as configured in .env + */ +async function main() { + if (process.argv.length < 2) { + console.error(`Usage: yarn ts-node scripts/clear-cache.ts`); + return process.exit(1); + } + + const engine = storageEngine(process.env.VOTE_REPORT_SUBDIR); + const list = await engine.list(); + + console.log(`> Deleting ${list.length} cached files on ${engine.constructor.name}`); + await engine.clear(); + + const finalList = await engine.list(); + console.log(`> Finished! ${finalList.length} files remaining`); +} + +(async () => { + try { + await main(); + process.exit(0); + } catch (e) { + console.error(e); + process.exit(1); + } +})(); diff --git a/scripts/list-cache.ts b/scripts/list-cache.ts new file mode 100644 index 00000000..c887aaa7 --- /dev/null +++ b/scripts/list-cache.ts @@ -0,0 +1,29 @@ +import 'dotenv/config'; +import { storageEngine } from '../src/helpers/utils'; + +/** + * Display the contents of the cached storage engine, + * as configured in .env + */ +async function main() { + if (process.argv.length < 2) { + console.error(`Usage: yarn ts-node scripts/list-cache.ts`); + return process.exit(1); + } + + const engine = storageEngine(process.env.VOTE_REPORT_SUBDIR); + const list = await engine.list(); + + console.log(`> Found ${list.length} cached items`); + console.log(list); +} + +(async () => { + try { + await main(); + process.exit(0); + } catch (e) { + console.error(e); + process.exit(1); + } +})(); diff --git a/src/lib/storage/aws.ts b/src/lib/storage/aws.ts index ba7f2620..f7b3055f 100644 --- a/src/lib/storage/aws.ts +++ b/src/lib/storage/aws.ts @@ -1,4 +1,10 @@ -import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'; +import { + S3Client, + GetObjectCommand, + PutObjectCommand, + ListObjectsV2Command, + DeleteObjectCommand +} from '@aws-sdk/client-s3'; import type { IStorage } from './types'; const CACHE_PATH = 'public'; @@ -54,6 +60,46 @@ class Aws implements IStorage { } } + async list() { + try { + const command = new ListObjectsV2Command({ + Bucket: process.env.AWS_BUCKET_NAME, + Prefix: this.#path() + }); + const list = await this.client.send(command); + + return list.Contents || []; + } catch (e) { + console.error('[storage:aws] List failed', e); + return []; + } + } + + async delete(key: string): Promise { + try { + const command = new DeleteObjectCommand({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: key + }); + return await this.client.send(command); + } catch (e) { + console.error('[storage:aws] File delete failed', e); + return false; + } + } + + async clear() { + const items = await this.list(); + + items.map(item => { + if (item.Key) { + this.delete(item.Key); + } + }); + + return true; + } + #path(key?: string) { return [CACHE_PATH, this.subDir?.replace(/^\/+|\/+$/, ''), key].filter(p => p).join('/'); } diff --git a/src/lib/storage/file.ts b/src/lib/storage/file.ts index afe3a531..7c9bd169 100644 --- a/src/lib/storage/file.ts +++ b/src/lib/storage/file.ts @@ -1,4 +1,4 @@ -import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs'; +import { writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'fs'; import type { IStorage } from './types'; const CACHE_PATH = `${__dirname}/../../../tmp`; @@ -14,6 +14,10 @@ class File implements IStorage { } } + clearAll(): Promise { + throw new Error('Method not implemented.'); + } + async set(key: string, value: string) { try { writeFileSync(this.#path(key), value); @@ -39,6 +43,34 @@ class File implements IStorage { } } + async delete(key: string) { + try { + return rmSync(this.#path(key)); + } catch (e) { + console.error('[storage:file] Fetch delete failed', e); + return false; + } + } + + async list() { + try { + return readdirSync(this.#path()); + } catch (e) { + console.error('[storage:file] List failed', e); + return []; + } + } + + async clear() { + const items = await this.list(); + + items.map(item => { + this.delete(item); + }); + + return true; + } + #path(key?: string) { return [CACHE_PATH, this.subDir?.replace(/^\/+|\/+$/, ''), key].filter(p => p).join('/'); } diff --git a/src/lib/storage/types.ts b/src/lib/storage/types.ts index 5778395f..b049edd5 100644 --- a/src/lib/storage/types.ts +++ b/src/lib/storage/types.ts @@ -3,6 +3,9 @@ export interface IStorage { set(key: string, value: string): Promise; get(key: string): Promise; + delete(key: string): Promise; + list(): Promise; + clear(): Promise; } export interface IStorageConstructor {