-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bcb9933
Showing
11 changed files
with
550 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Wren Example: Bare | ||
|
||
## Run | ||
|
||
``` | ||
deno task start | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
run: | ||
deno run -A main.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type Nullable<T> = T | null | undefined |