Skip to content

Commit

Permalink
init repo
Browse files Browse the repository at this point in the history
  • Loading branch information
amycatgirl committed May 14, 2024
0 parents commit bcb9933
Show file tree
Hide file tree
Showing 11 changed files with 550 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Wren Example: Bare

## Run

```
deno task start
```
6 changes: 6 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"importMap": "./import_map.json",
"tasks": {
"start": "deno run -A -r main.ts"
}
}
301 changes: 301 additions & 0 deletions deno.lock

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions import_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"imports": {
"http/": "https://deno.land/std@0.224.0/http/",
"wren/": "https://deno.land/x/wren@0.9.0/",
"ulid/": "https://deno.land/std@0.224.0/ulid/",
"path/": "https://deno.land/std@0.224.0/path/",
"fs/": "https://deno.land/std@0.224.0/fs/"
}
}
2 changes: 2 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
run:
deno run -A main.ts
22 changes: 22 additions & 0 deletions main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { serve } from 'wren/mod.ts';
import CreateObjectRoute from "./routes/create_object.ts"
import GetObjectRoute from "./routes/get_object.ts";
import { StorageSingleton } from "./storage.ts";

const storage = StorageSingleton.getInstance()

const routes = [
CreateObjectRoute,
GetObjectRoute
];

await storage.removeLeftovers()

setInterval(() => {
storage.manager.purgeExpired()
}, 1000)

serve(routes);

// Run on different port
// serve(routes, { port: 3000 });
27 changes: 27 additions & 0 deletions routes/create_object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BadRequest, Created, NotAcceptable } from "wren/response.ts";
import { POST } from "wren/route.ts";
import { StorageSingleton } from "../storage.ts";

const CreateObjectRoute = POST("/create", async (req) => {
const form = await req.formData();
const file = form.get("file");
const until: unknown = form.get("until");
const storage = StorageSingleton.getInstance();

if (!file) {
return BadRequest();
} else if (file instanceof File) {
const { id, filename, stored_until } = await storage.storeFile(file, (until as number | undefined) ?? 1);

console.log(`[CREATE] File ${filename} was created`)
return Created({
id,
filename,
message: `Saved ${filename} until ${new Date(stored_until)}`,
});
} else {
return NotAcceptable();
}
});

export default CreateObjectRoute;
30 changes: 30 additions & 0 deletions routes/get_object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
BadRequest,
InternalServerError,
NotFound,
OK,
} from "wren/response.ts";
import { GET } from "wren/route.ts";
import { StorageSingleton } from "../storage.ts";

const GetObjectRoute = GET("/objects/:id", async ({ params }) => {
const storage = StorageSingleton.getInstance();
const object = storage.getFile(params.id as string);

if (!params) return BadRequest();
if (!object) return NotFound();

console.log(`[GET] Found ${object.filename}, serving...`)

try {
const file = await object.openFile();

if (!file) throw new Error(`Could not open file ${object.filename}`)

return OK(file, { "Content-type": object.mimeType });
} catch (error) {
return InternalServerError({ message: error.message });
}
});

export default GetObjectRoute;
145 changes: 145 additions & 0 deletions storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import type { Nullable } from "./utilities.ts";
import { ulid } from "ulid/mod.ts";
import { join, resolve } from "path/mod.ts";
import { emptyDir } from "fs/mod.ts";

interface IStorageObject {
id: string;
mimeType: string;
filename: string;
readonly path: string;

openFile(): Promise<Uint8Array>;
deleteFile(): Promise<void>;
}

interface ITimedObject extends IStorageObject {
stored_until: number;

isExpired(): boolean;
}

export class StorageObject implements IStorageObject {
id: string;
mimeType: string;
filename: string;
readonly path: string;

constructor(id: string, mimeType: string, filename: string, path: string) {
this.id = id;
this.mimeType = mimeType;
this.filename = filename;
this.path = path;
}

async openFile(): Promise<Uint8Array> {
return await Deno.readFile(resolve(this.path));
}

async deleteFile(): Promise<void> {
await Deno.remove(this.path);
}
}

export class TimedObject extends StorageObject implements ITimedObject {
stored_until: number;

constructor(
id: string,
mimeType: string,
filename: string,
path: string,
amount_of_hours?: number
) {
super(id, mimeType, filename, path);

this.stored_until = Date.now() + (3600 * (amount_of_hours ?? 1) * 1000)
console.log
}

isExpired(): boolean {
return Date.now() >= this.stored_until;
}
}

class ObjectManager {
private storage: Array<ITimedObject> = [];


addObject(object: StorageObject, until?: number): TimedObject {
const newObject = new TimedObject(
object.id,
object.mimeType,
object.filename,
object.path,
until,
);

this.storage.push(newObject)
return newObject;
}

getObjects(): Nullable<TimedObject[]> {
return this.storage;
}

findObject(key: string): Nullable<TimedObject> {
const obj = this.storage.find((ti) => ti.id == key)
return obj;
}

purgeExpired() {
this.storage.forEach((value, index) => {
if (value.isExpired()) {
value.deleteFile();

console.log(`[DELETE] File ${value.filename} was deleted.`);

this.storage.splice(index, 1)
}
});
}
}

class StorageManager {
manager: ObjectManager = new ObjectManager();
ROOT_PATH = "./tmpst/";
private lastId = 0;

async storeFile(blob: File, until?: number) {
await Deno.writeFile(
resolve(join(this.ROOT_PATH, blob.name)),
blob.stream(),
);
const object = new StorageObject(
ulid(),
blob.type,
blob.name,
this.ROOT_PATH + blob.name,
);

return this.manager.addObject(object, until);
}

getFile(id: string) {
return this.manager.findObject(id);
}

async removeLeftovers() {
await emptyDir(this.ROOT_PATH)
}
}

export class StorageSingleton {
private static instance: StorageManager;

private constructor() {}

public static getInstance() {
if (!StorageSingleton.instance) {
StorageSingleton.instance = new StorageManager();
}

return StorageSingleton.instance;
}
}
Binary file added tmpst/wave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Nullable<T> = T | null | undefined

0 comments on commit bcb9933

Please sign in to comment.