Merge pull request #77 from mollersuite/db-file-handler

DB file handler script thingy
This commit is contained in:
May 2024-05-23 22:28:42 -07:00 committed by GitHub
commit 37f88d6984
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 253 additions and 157 deletions

View file

@ -4,30 +4,12 @@ import { readFile, writeFile } from "fs/promises"
import { FileVisibility } from "./files.js"; import { FileVisibility } from "./files.js";
import { AccountSchemas } from "./schemas/index.js"; import { AccountSchemas } from "./schemas/index.js";
import { z } from "zod" import { z } from "zod"
import DbFile from "./dbfile.js";
// this is probably horrible // this is probably horrible
// but i don't even care anymore // but i don't even care anymore
export let Accounts: Account[] = [] export let Db = new DbFile<Account[]>("accounts",[])
/*
export interface Account {
id : string
username : string
email? : string
password : {
hash : string
salt : string
}
files : string[]
admin : boolean
defaultFileVisibility : FileVisibility
customCSS? : string
embed? : {
color? : string
largeImage? : boolean
}
}*/
export type Account = z.infer<typeof AccountSchemas.Account> export type Account = z.infer<typeof AccountSchemas.Account>
@ -42,7 +24,7 @@ export type Account = z.infer<typeof AccountSchemas.Account>
export async function create(username:string,pwd:string,admin:boolean=false):Promise<string> { export async function create(username:string,pwd:string,admin:boolean=false):Promise<string> {
let accId = crypto.randomBytes(12).toString("hex") let accId = crypto.randomBytes(12).toString("hex")
Accounts.push( Db.data.push(
{ {
id: accId, id: accId,
username: username, username: username,
@ -54,7 +36,7 @@ export async function create(username:string,pwd:string,admin:boolean=false):Pro
} }
) )
await save() await Db.save()
return accId return accId
} }
@ -64,7 +46,7 @@ export async function create(username:string,pwd:string,admin:boolean=false):Pro
* @returns An Account, if it exists * @returns An Account, if it exists
*/ */
export function getFromUsername(username:string) { export function getFromUsername(username:string) {
return Accounts.find(e => e.username == username) return Db.data.find(e => e.username == username)
} }
/** /**
@ -73,7 +55,7 @@ export function getFromUsername(username:string) {
* @returns An Account, if it exists * @returns An Account, if it exists
*/ */
export function getFromId(id:string) { export function getFromId(id:string) {
return Accounts.find(e => e.id == id) return Db.data.find(e => e.id == id)
} }
/** /**
@ -92,8 +74,8 @@ export function getFromToken(token:string) {
* @param id The target account's ID * @param id The target account's ID
*/ */
export function deleteAccount(id:string) { export function deleteAccount(id:string) {
Accounts.splice(Accounts.findIndex(e => e.id == id),1) Db.data.splice(Db.data.findIndex(e => e.id == id),1)
return save() return Db.save()
} }
export namespace password { export namespace password {
@ -121,11 +103,11 @@ export namespace password {
*/ */
export function set(id:string,password:string) { export function set(id:string,password:string) {
let acc = Accounts.find(e => e.id == id) let acc = Db.data.find(e => e.id == id)
if (!acc) return if (!acc) return
acc.password = hash(password) acc.password = hash(password)
return save() return Db.save()
} }
@ -135,7 +117,7 @@ export namespace password {
* @param password Password to check * @param password Password to check
*/ */
export function check(id:string,password:string) { export function check(id:string,password:string) {
let acc = Accounts.find(e => e.id == id) let acc = Db.data.find(e => e.id == id)
if (!acc) return if (!acc) return
return acc.password.hash == hash(password,acc.password.salt).hash return acc.password.hash == hash(password,acc.password.salt).hash
@ -153,12 +135,12 @@ export namespace files {
// maybe replace with a obj like // maybe replace with a obj like
// { x:true } // { x:true }
// for faster lookups? not sure if it would be faster // for faster lookups? not sure if it would be faster
let acc = Accounts.find(e => e.id == accountId) let acc = Db.data.find(e => e.id == accountId)
if (!acc) return if (!acc) return
if (acc.files.find(e => e == fileId)) return if (acc.files.find(e => e == fileId)) return
acc.files.push(fileId) acc.files.push(fileId)
if (!noWrite) return save() if (!noWrite) return Db.save()
} }
/** /**
@ -169,31 +151,19 @@ export namespace files {
* @returns A Promise which resolves when accounts.json finishes writing, if `noWrite` is `false` * @returns A Promise which resolves when accounts.json finishes writing, if `noWrite` is `false`
*/ */
export function deindex(accountId:string,fileId:string, noWrite:boolean=false) { export function deindex(accountId:string,fileId:string, noWrite:boolean=false) {
let acc = Accounts.find(e => e.id == accountId) let acc = Db.data.find(e => e.id == accountId)
if (!acc) return if (!acc) return
let fi = acc.files.findIndex(e => e == fileId) let fi = acc.files.findIndex(e => e == fileId)
if (fi >= 0) { if (fi >= 0) {
acc.files.splice(fi,1) acc.files.splice(fi,1)
if (!noWrite) return save() if (!noWrite) return Db.save()
} }
} }
} }
/** Db.read()
* @description Saves accounts.json .then(() => {
* @returns A promise which resolves when accounts.json finishes writing if (!Db.data.find(e => e.admin)) {
*/
export function save() {
return writeFile(`${process.cwd()}/.data/accounts.json`,JSON.stringify(Accounts))
.catch((err) => console.error(err))
}
readFile(`${process.cwd()}/.data/accounts.json`)
.then((buf) => {
Accounts = JSON.parse(buf.toString())
}).catch(err => console.error(err))
.finally(() => {
if (!Accounts.find(e => e.admin)) {
create("admin","admin",true) create("admin","admin",true)
} }
}) })

View file

@ -4,13 +4,15 @@ import type { Context } from "hono"
import { readFile, writeFile } from "fs/promises" import { readFile, writeFile } from "fs/promises"
import { z } from "zod" import { z } from "zod"
import { AuthSchemas } from "./schemas/index.js" import { AuthSchemas } from "./schemas/index.js"
export let AuthTokens: AuthToken[] = [] import DbFile from "./dbfile.js"
export let AuthTokenTO: { [key: string]: NodeJS.Timeout } = {} export let AuthTokenTO: { [key: string]: NodeJS.Timeout } = {}
export type TokenPermission = z.infer<typeof AuthSchemas.Scope> export type TokenPermission = z.infer<typeof AuthSchemas.Scope>
export type TokenType = z.infer<typeof AuthSchemas.TokenType> export type TokenType = z.infer<typeof AuthSchemas.TokenType>
export type AuthToken = z.infer<typeof AuthSchemas.AuthToken> export type AuthToken = z.infer<typeof AuthSchemas.AuthToken>
export const Db = new DbFile<AuthToken[]>("tokens", [])
export function create( export function create(
id: string, id: string,
expire: number | null = 24 * 60 * 60 * 1000, expire: number | null = 24 * 60 * 60 * 1000,
@ -28,10 +30,10 @@ export function create(
type != "User" ? tokenPermissions || ["user"] : undefined, type != "User" ? tokenPermissions || ["user"] : undefined,
}) })
AuthTokens.push(token) Db.data.push(token)
tokenTimer(token) tokenTimer(token)
save() Db.save()
return token.token return token.token
} }
@ -46,7 +48,7 @@ export function tokenFor(ctx: Context) {
} }
function getToken(token: string) { function getToken(token: string) {
return AuthTokens.find( return Db.data.find(
(e) => e.token == token && (e.expire == null || Date.now() < e.expire) (e) => e.token == token && (e.expire == null || Date.now() < e.expire)
) )
} }
@ -84,23 +86,14 @@ export function invalidate(token: string) {
clearTimeout(AuthTokenTO[token]) clearTimeout(AuthTokenTO[token])
} }
AuthTokens.splice( Db.data.splice(
AuthTokens.findIndex((e) => e.token == token), Db.data.findIndex((e) => e.token == token),
1 1
) )
save() Db.save()
} }
export function save() { Db.read()
writeFile(
`${process.cwd()}/.data/tokens.json`,
JSON.stringify(AuthTokens)
).catch((err) => console.error(err))
}
readFile(`${process.cwd()}/.data/tokens.json`)
.then((buf) => { .then((buf) => {
AuthTokens = JSON.parse(buf.toString()) Db.data.forEach((e) => tokenTimer(e))
AuthTokens.forEach((e) => tokenTimer(e))
}) })
.catch((err) => console.error(err))

156
src/server/lib/dbfile.ts Normal file
View file

@ -0,0 +1,156 @@
import { readFile, writeFile, rename, readdir } from "fs/promises"
import path from "node:path"
const DATADIR = `./.data`
const TICK = 500
export type Write = ReturnType<typeof writeFile>
// this is fucking stupid why did i write this
class Activity {
_write: () => Promise<any>
destroy: () => void
goal: number = Date.now()
lastWrite: number = Date.now()
clock? : { type: "precise", id: NodeJS.Timeout } | { type: "tick", id: NodeJS.Timeout, lastGoal: number }
constructor(writeFunc: () => Promise<any>, destroyFunc: () => void) {
this._write = writeFunc
this.destroy = destroyFunc
}
write() {
this.lastWrite = Date.now();
return this._write()
}
finish() {
this.stopClock()
this.write()
this.destroy()
}
tick() {
if (!this.clock || !("lastGoal" in this.clock)) return
if (Date.now() > this.goal) return this.finish();
if (this.goal == this.clock.lastGoal)
this.startPreciseClock()
else
this.clock.lastGoal = this.goal
if (Date.now()-this.lastWrite > 15000)
this.write()
}
stopClock() {
if (this.clock) clearTimeout(this.clock.id)
}
startTickClock() {
this.stopClock()
this.clock = {
type: "tick",
id: setInterval(this.tick.bind(this), TICK),
lastGoal: this.goal
}
}
startPreciseClock() {
this.stopClock()
this.clock = {
type: "precise",
id: setTimeout(this.finish.bind(this), this.goal-Date.now())
}
}
set() {
this.goal = Date.now()+5000
if (!this.clock || this.clock.type != "tick")
this.startTickClock()
}
}
export default class DbFile<Structure extends ({}|[])> {
name: string
data: Structure
activity?: Activity
private writeInProgress?: Promise<void>
private rewriteNeeded: boolean = false
private readonly files: string[]
constructor(name: string, defaultData: Structure) {
this.name = name
this.data = defaultData
this.files = [`${name}.json`, `${name}-b.json`].map(e => path.join(DATADIR, e))
}
private async findAvailable() {
return (await readdir(DATADIR))
.filter(e => e.match(new RegExp(`^${this.name}(?:-b)?.json$`)))
.map(e => path.join(DATADIR, e))
}
/**
* @description Write files to disk; doesn't care about preventing corruption aside from the 2 copies
*/
private async write() {
let data = JSON.stringify(this.data)
for (let x of this.files)
await writeFile(x, data)
}
/**
* @description Write files to disk; checks if a write is in progress first
*/
private async queueWrite(): Promise<void> {
if (this.writeInProgress) { // if write in progress
this.rewriteNeeded = true // signify that a rewrite is needed
return this.writeInProgress
}
this.writeInProgress = this.write()
await this.writeInProgress; // wait for it to complete
delete this.writeInProgress; // then remove it
if (this.rewriteNeeded) return this.queueWrite() // queues up another write if needed
}
/**
* @description Starts saving data to disk
*/
async save() {
if (!this.activity)
this.activity =
new Activity(
this.queueWrite.bind(this),
() => delete this.activity
)
this.activity.set()
}
private async tryRead(path: string) {
return JSON.parse((await readFile(path)).toString())
}
async read() {
let availFiles = await this.findAvailable()
for (let x of availFiles) {
let data = await this.tryRead(x).catch(_ => null)
if (data !== null) {
this.data = data
break
}
}
}
}

View file

@ -11,7 +11,7 @@ import * as Accounts from "./accounts.js"
import { z } from "zod" import { z } from "zod"
import * as schemas from "./schemas/files.js" import * as schemas from "./schemas/files.js"
import { issuesToMessage } from "./middleware.js" import { issuesToMessage } from "./middleware.js"
import file from "../routes/api/v1/file/index.js" import DbFile from "./dbfile.js"
export let alphanum = Array.from( export let alphanum = Array.from(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
@ -435,9 +435,9 @@ export class UploadStream extends Writable {
if (!this.uploadId) this.setUploadId(generateFileId()) if (!this.uploadId) this.setUploadId(generateFileId())
let ogf = this.files.files[this.uploadId!] let ogf = this.files.db.data[this.uploadId!]
this.files.files[this.uploadId!] = { this.files.db.data[this.uploadId!] = {
filename: this.name, filename: this.name,
mime: this.mime, mime: this.mime,
messageids: this.messages, messageids: this.messages,
@ -459,7 +459,7 @@ export class UploadStream extends Writable {
delete this.files.locks[this.uploadId!] delete this.files.locks[this.uploadId!]
if (ogf?.messageids) await this.files.api.deleteMessages(ogf.messageids) if (ogf?.messageids) await this.files.api.deleteMessages(ogf.messageids)
await this.files.write() await this.files.db.save()
if (this.owner) Accounts.files.index(this.owner, this.uploadId!) if (this.owner) Accounts.files.index(this.owner, this.uploadId!)
return this.uploadId return this.uploadId
} }
@ -508,7 +508,7 @@ export class UploadStream extends Writable {
if (!check.success) if (!check.success)
return this.destroy(new WebError(400, issuesToMessage(check.error.issues))) return this.destroy(new WebError(400, issuesToMessage(check.error.issues)))
if (this.files.files[id] && this.files.files[id].owner != this.owner) if (this.files.db.data[id] && this.files.db.data[id].owner != this.owner)
return this.destroy(new WebError(403, "you don't own this file")) return this.destroy(new WebError(403, "you don't own this file"))
if (this.files.locks[id]) if (this.files.locks[id])
@ -587,50 +587,27 @@ export class UploadStream extends Writable {
export default class Files { export default class Files {
config: Configuration config: Configuration
api: API api: API
files: { [key: string]: FilePointer } = {} db = new DbFile<{ [key: string]: FilePointer }>("files", {})
data_directory: string = `${process.cwd()}/.data`
locks: Record<string, boolean> = {} // I'll, like, do something more proper later locks: Record<string, boolean> = {} // I'll, like, do something more proper later
constructor(config: Configuration) { constructor(config: Configuration) {
this.config = config this.config = config
this.api = new API(config.discordToken!, config) this.api = new API(config.discordToken!, config)
this.db.read()
readFile(this.data_directory + "/files.json")
.then((buf) => {
this.files = JSON.parse(buf.toString() || "{}")
})
.catch(console.error)
} }
createWriteStream(owner?: string) { createWriteStream(owner?: string) {
return new UploadStream(this, owner) return new UploadStream(this, owner)
} }
// fs
/**
* @description Saves file database
*
*/
async write(): Promise<void> {
await writeFile(
this.data_directory + "/files.json",
JSON.stringify(
this.files,
null,
process.env.NODE_ENV === "development" ? 4 : undefined
)
)
}
/** /**
* @description Update a file from monofile 1.x to 2.x * @description Update a file from monofile 1.x to 2.x
* @param uploadId Target file's ID * @param uploadId Target file's ID
*/ */
async update(uploadId: string) { async update(uploadId: string) {
let target_file = this.files[uploadId] let target_file = this.db.data[uploadId]
let attachments: APIAttachment[] = [] let attachments: APIAttachment[] = []
for (let message of target_file.messageids) { for (let message of target_file.messageids) {
@ -668,8 +645,8 @@ export default class Files {
uploadId: string, uploadId: string,
range?: { start: number; end: number } range?: { start: number; end: number }
): Promise<ReadStream> { ): Promise<ReadStream> {
if (this.files[uploadId]) { if (this.db.data[uploadId]) {
let file = this.files[uploadId] let file = this.db.data[uploadId]
if (!file.sizeInBytes || !file.chunkSize) if (!file.sizeInBytes || !file.chunkSize)
await this.update(uploadId) await this.update(uploadId)
return new ReadStream(this, file, range) return new ReadStream(this, file, range)
@ -684,7 +661,7 @@ export default class Files {
* @param noWrite Whether or not the change should be written to disk. Enable for bulk deletes * @param noWrite Whether or not the change should be written to disk. Enable for bulk deletes
*/ */
async unlink(uploadId: string, noWrite: boolean = false): Promise<void> { async unlink(uploadId: string, noWrite: boolean = false): Promise<void> {
let target = this.files[uploadId] let target = this.db.data[uploadId]
if (!target) return if (!target) return
await this.api.deleteMessages(target.messageids) await this.api.deleteMessages(target.messageids)
@ -693,14 +670,14 @@ export default class Files {
let id = files.deindex(target.owner, uploadId, noWrite) let id = files.deindex(target.owner, uploadId, noWrite)
if (id) await id if (id) await id
} }
delete this.files[uploadId] delete this.db.data[uploadId]
if (!noWrite) if (!noWrite)
return this.write() return this.db.save()
} }
async chown(uploadId: string, newOwner?: string, noWrite: boolean = false) { async chown(uploadId: string, newOwner?: string, noWrite: boolean = false) {
let target = this.files[uploadId] let target = this.db.data[uploadId]
if (target.owner) { if (target.owner) {
let i = files.deindex(target.owner, uploadId, Boolean(newOwner && noWrite)) let i = files.deindex(target.owner, uploadId, Boolean(newOwner && noWrite))
if (i) await i if (i) await i
@ -713,24 +690,24 @@ export default class Files {
} }
if (!noWrite) if (!noWrite)
return this.write() return this.db.save()
} }
async mv(uploadId: string, newId: string, noWrite: boolean = false) { async mv(uploadId: string, newId: string, noWrite: boolean = false) {
let target = this.files[uploadId] let target = this.db.data[uploadId]
if (target.owner) { if (target.owner) {
let owner = Accounts.getFromId(target.owner) let owner = Accounts.getFromId(target.owner)
if (owner) { if (owner) {
owner.files.splice(owner.files.indexOf(uploadId), 1, newId) owner.files.splice(owner.files.indexOf(uploadId), 1, newId)
if (!noWrite) if (!noWrite)
await Accounts.save() await Accounts.Db.save()
} }
} }
this.files[newId] = target this.db.data[newId] = target
delete this.files[uploadId] delete this.db.data[uploadId]
if (!noWrite) if (!noWrite)
return this.write() return this.db.save()
} }
} }

View file

@ -40,7 +40,7 @@ export default function (files: Files) {
} }
Accounts.password.set(targetAccount.id, body.password) Accounts.password.set(targetAccount.id, body.password)
auth.AuthTokens.filter((e) => e.account == targetAccount?.id).forEach( auth.Db.data.filter((e) => e.account == targetAccount?.id).forEach(
(v) => { (v) => {
auth.invalidate(v.token) auth.invalidate(v.token)
} }
@ -70,7 +70,7 @@ export default function (files: Files) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
} }
Accounts.save() Accounts.Db.save()
return ctx.text("OK") return ctx.text("OK")
}) })
@ -80,7 +80,7 @@ export default function (files: Files) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
} }
let targetFile = files.files[body.target] let targetFile = files.db.data[body.target]
if (!targetFile) { if (!targetFile) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
@ -106,7 +106,7 @@ export default function (files: Files) {
let accId = targetAccount.id let accId = targetAccount.id
auth.AuthTokens.filter((e) => e.account == accId).forEach((v) => { auth.Db.data.filter((e) => e.account == accId).forEach((v) => {
auth.invalidate(v.token) auth.invalidate(v.token)
}) })
@ -140,7 +140,7 @@ export default function (files: Files) {
return writeFile( return writeFile(
process.cwd() + "/.data/files.json", process.cwd() + "/.data/files.json",
JSON.stringify(files.files) JSON.stringify(files.db.data)
).then(cpl) ).then(cpl)
} else return cpl() } else return cpl()
}) })
@ -151,7 +151,7 @@ export default function (files: Files) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
} }
let targetFile = files.files[body.target] let targetFile = files.db.data[body.target]
if (!targetFile) { if (!targetFile) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
} }
@ -172,8 +172,8 @@ export default function (files: Files) {
} }
targetFile.owner = newOwner ? newOwner.id : undefined targetFile.owner = newOwner ? newOwner.id : undefined
return files return files.db
.write() .save()
.then(() => ctx.text("ok", 200)) .then(() => ctx.text("ok", 200))
.catch(() => ctx.text("error", 500)) .catch(() => ctx.text("error", 500))
}) })
@ -184,12 +184,12 @@ export default function (files: Files) {
return ctx.text("inappropriate body", 400) return ctx.text("inappropriate body", 400)
} }
let targetFile = files.files[body.target] let targetFile = files.db.data[body.target]
if (!targetFile) { if (!targetFile) {
return ctx.text("not found", 404) return ctx.text("not found", 404)
} }
if (files.files[body.new]) { if (files.db.data[body.new]) {
return ctx.status(400) return ctx.status(400)
} }
@ -197,14 +197,14 @@ export default function (files: Files) {
Accounts.files.deindex(targetFile.owner, body.target) Accounts.files.deindex(targetFile.owner, body.target)
Accounts.files.index(targetFile.owner, body.new) Accounts.files.index(targetFile.owner, body.new)
} }
delete files.files[body.target] delete files.db.data[body.target]
files.files[body.new] = targetFile files.db.data[body.new] = targetFile
return files return files.db
.write() .save()
.then(() => ctx.status(200)) .then(() => ctx.status(200))
.catch(() => { .catch(() => {
files.files[body.target] = body.new files.db.data[body.target] = body.new
if (targetFile.owner) { if (targetFile.owner) {
Accounts.files.deindex(targetFile.owner, body.new) Accounts.files.deindex(targetFile.owner, body.new)

View file

@ -150,7 +150,7 @@ export default function (files: Files) {
) )
) { ) {
acc.defaultFileVisibility = body.defaultFileVisibility acc.defaultFileVisibility = body.defaultFileVisibility
Accounts.save() Accounts.Db.save()
return ctx.text( return ctx.text(
`dfv has been set to ${acc.defaultFileVisibility}` `dfv has been set to ${acc.defaultFileVisibility}`
) )
@ -170,7 +170,7 @@ export default function (files: Files) {
const body = await ctx.req.json() const body = await ctx.req.json()
let accId = acc.id let accId = acc.id
auth.AuthTokens.filter((e) => e.account == accId).forEach((v) => { auth.Db.data.filter((e) => e.account == accId).forEach((v) => {
auth.invalidate(v.token) auth.invalidate(v.token)
}) })
@ -187,7 +187,7 @@ export default function (files: Files) {
return writeFile( return writeFile(
process.cwd() + "/.data/files.json", process.cwd() + "/.data/files.json",
JSON.stringify(files.files) JSON.stringify(files.db.data)
).then(cpl) ).then(cpl)
} else cpl() } else cpl()
} }
@ -235,7 +235,7 @@ export default function (files: Files) {
} }
acc.username = body.username acc.username = body.username
Accounts.save() Accounts.Db.save()
if (acc.email) { if (acc.email) {
return sendMail( return sendMail(
@ -339,7 +339,7 @@ export default function (files: Files) {
ctx.req.param("code").toUpperCase() == vcode.code ctx.req.param("code").toUpperCase() == vcode.code
) { ) {
acc.email = vcode.email acc.email = vcode.email
Accounts.save() Accounts.Db.save()
let e = verificationCodes.get(acc?.id || "")?.expiry let e = verificationCodes.get(acc?.id || "")?.expiry
if (e) clearTimeout(e) if (e) clearTimeout(e)
@ -361,7 +361,7 @@ export default function (files: Files) {
if (acc.email) { if (acc.email) {
delete acc.email delete acc.email
Accounts.save() Accounts.Db.save()
return ctx.text("email detached") return ctx.text("email detached")
} else return ServeError(ctx, 400, "email not attached") } else return ServeError(ctx, 400, "email not attached")
} }
@ -490,7 +490,7 @@ export default function (files: Files) {
Accounts.password.set(accId, body.password) Accounts.password.set(accId, body.password)
auth.AuthTokens.filter((e) => e.account == accId).forEach((v) => { auth.Db.data.filter((e) => e.account == accId).forEach((v) => {
auth.invalidate(v.token) auth.invalidate(v.token)
}) })
@ -517,7 +517,7 @@ export default function (files: Files) {
let accId = acc.id let accId = acc.id
auth.AuthTokens.filter((e) => e.account == accId).forEach((v) => { auth.Db.data.filter((e) => e.account == accId).forEach((v) => {
auth.invalidate(v.token) auth.invalidate(v.token)
}) })
@ -535,13 +535,13 @@ export default function (files: Files) {
let accId = acc.id let accId = acc.id
return ctx.json({ return ctx.json({
...acc, ...acc,
sessionCount: auth.AuthTokens.filter( sessionCount: auth.Db.data.filter(
(e) => (e) =>
e.type == "User" && e.type == "User" &&
e.account == accId && e.account == accId &&
(e.expire == null || e.expire > Date.now()) (e.expire == null || e.expire > Date.now())
).length, ).length,
sessionExpires: auth.AuthTokens.find( sessionExpires: auth.Db.data.find(
(e) => e.token == sessionToken (e) => e.token == sessionToken
)?.expire, )?.expire,
password: undefined, password: undefined,

View file

@ -30,7 +30,7 @@ export default function (files: Files) {
return ctx.json( return ctx.json(
acc.files acc.files
.map((e) => { .map((e) => {
let fp = files.files[e] let fp = files.db.data[e]
if (!fp) { if (!fp) {
Accounts.files.deindex(accId, e) Accounts.files.deindex(accId, e)
return null return null
@ -66,7 +66,7 @@ export default function (files: Files) {
body.target.forEach((e: string) => { body.target.forEach((e: string) => {
if (!acc.files.includes(e)) return if (!acc.files.includes(e)) return
let fp = files.files[e] let fp = files.db.data[e]
switch (body.action) { switch (body.action) {
case "delete": case "delete":
@ -81,15 +81,15 @@ export default function (files: Files) {
) )
) )
return return
files.files[e].visibility = body.value files.db.data[e].visibility = body.value
modified++ modified++
break break
case "setTag": case "setTag":
if (!body.value) delete files.files[e].tag if (!body.value) delete files.db.data[e].tag
else { else {
if (body.value.toString().length > 30) return if (body.value.toString().length > 30) return
files.files[e].tag = body.value files.db.data[e].tag = body.value
.toString() .toString()
.toLowerCase() .toLowerCase()
} }
@ -98,11 +98,11 @@ export default function (files: Files) {
} }
}) })
return Accounts.save() return Accounts.Db.save()
.then(() => { .then(() => {
writeFile( writeFile(
process.cwd() + "/.data/files.json", process.cwd() + "/.data/files.json",
JSON.stringify(files.files) JSON.stringify(files.db.data)
) )
}) })
.then(() => ctx.text(`modified ${modified} files`)) .then(() => ctx.text(`modified ${modified} files`))

View file

@ -38,7 +38,7 @@ type HonoEnv = {
const router = new Hono<HonoEnv>() const router = new Hono<HonoEnv>()
function getTargetToken(ctx: Context<HonoEnv, "/:token", BlankInput>) { function getTargetToken(ctx: Context<HonoEnv, "/:token", BlankInput>) {
return auth.AuthTokens.find( return auth.Db.data.find(
e => e =>
e.account == ctx.get("target").id e.account == ctx.get("target").id
&& e.token == ctx.req.param("token") && e.token == ctx.req.param("token")
@ -59,7 +59,7 @@ export default function (files: Files) {
router.get("/", async (ctx) => { router.get("/", async (ctx) => {
return ctx.json( return ctx.json(
auth.AuthTokens.filter(e => e.account == ctx.get("target").id) auth.Db.data.filter(e => e.account == ctx.get("target").id)
) )
}) })
@ -73,7 +73,7 @@ export default function (files: Files) {
(c) => c.req.query("type")?.split(",") (c) => c.req.query("type")?.split(",")
), ),
async (ctx) => { async (ctx) => {
let targets = auth.AuthTokens.filter( let targets = auth.Db.data.filter(
e => e =>
e.account == ctx.get("target").id e.account == ctx.get("target").id
&& ctx.get("parsedScheme").has(e.type) && ctx.get("parsedScheme").has(e.type)

View file

@ -188,7 +188,7 @@ const validators: {
validator: (actor, target, params) => { validator: (actor, target, params) => {
if (!actor.admin) return [400, "only admins can modify suspensions"] if (!actor.admin) return [400, "only admins can modify suspensions"]
if (params.suspension) if (params.suspension)
auth.AuthTokens auth.Db.data
.filter(e => e.account == target.id) .filter(e => e.account == target.id)
.forEach(e => auth.invalidate(e.token)) .forEach(e => auth.invalidate(e.token))
return params.suspension || undefined return params.suspension || undefined
@ -320,7 +320,7 @@ export default function (files: Files) {
return [200, "OK"] as Message return [200, "OK"] as Message
}) })
await Accounts.save() await Accounts.Db.save()
if (messages.length == 1) if (messages.length == 1)
return ctx.text( return ctx.text(
@ -337,7 +337,7 @@ export default function (files: Files) {
if (actor == target && !verifyPoi(actor.id, ctx.req.query("poi"))) if (actor == target && !verifyPoi(actor.id, ctx.req.query("poi")))
return ServeError(ctx, 403, "invalid proof of identity provided") return ServeError(ctx, 403, "invalid proof of identity provided")
auth.AuthTokens.filter((e) => e.account == target?.id).forEach((token) => { auth.Db.data.filter((e) => e.account == target?.id).forEach((token) => {
auth.invalidate(token.token) auth.invalidate(token.token)
}) })
@ -367,7 +367,7 @@ export default function (files: Files) {
auth.getPermissions(sessionToken)?.includes("email") auth.getPermissions(sessionToken)?.includes("email")
? acc.email ? acc.email
: undefined, : undefined,
activeSessions: auth.AuthTokens.filter( activeSessions: auth.Db.data.filter(
(e) => (e) =>
e.type == "User" && e.type == "User" &&
e.account == acc.id && e.account == acc.id &&

View file

@ -99,7 +99,7 @@ export default function(files: Files) {
parser.on("field", async (k,v) => { parser.on("field", async (k,v) => {
if (k == "uploadId") { if (k == "uploadId") {
if (files.files[v] && ctx.req.method == "POST") if (files.db.data[v] && ctx.req.method == "POST")
return file.destroy(new WebError(409, "file already exists")) return file.destroy(new WebError(409, "file already exists"))
file.setUploadId(v) file.setUploadId(v)
// I'M GONNA KILL MYSELF!!!! // I'M GONNA KILL MYSELF!!!!

View file

@ -26,7 +26,7 @@ export default function(files: Files, apiRoot: Hono) {
let acc = ctx.get("account") as Accounts.Account let acc = ctx.get("account") as Accounts.Account
let file = files.files[fileId] let file = files.db.data[fileId]
ctx.header("Accept-Ranges", "bytes") ctx.header("Accept-Ranges", "bytes")
ctx.header("Access-Control-Allow-Origin", "*") ctx.header("Access-Control-Allow-Origin", "*")
ctx.header("Content-Security-Policy", "sandbox allow-scripts") ctx.header("Content-Security-Policy", "sandbox allow-scripts")

View file

@ -17,8 +17,8 @@ export default function(files: Files) {
router.get("/", async (ctx) => router.get("/", async (ctx) =>
ctx.json({ ctx.json({
version: pkg.version, version: pkg.version,
files: Object.keys(files.files).length, files: Object.keys(files.db.data).length,
totalSize: Object.values(files.files).filter(e => e.sizeInBytes).reduce((acc,cur)=>acc+cur.sizeInBytes!,0), totalSize: Object.values(files.db.data).filter(e => e.sizeInBytes).reduce((acc,cur)=>acc+cur.sizeInBytes!,0),
maxDiscordFiles: config.maxDiscordFiles, maxDiscordFiles: config.maxDiscordFiles,
maxDiscordFileSize: config.maxDiscordFileSize, maxDiscordFileSize: config.maxDiscordFileSize,
accounts: config.accounts, accounts: config.accounts,

View file

@ -61,7 +61,7 @@ export default function (files: Files) {
router.get("/", requiresAccount, ctx => { router.get("/", requiresAccount, ctx => {
let sessionToken = auth.tokenFor(ctx) let sessionToken = auth.tokenFor(ctx)
return ctx.json({ return ctx.json({
expiry: auth.AuthTokens.find( expiry: auth.Db.data.find(
(e) => e.token == sessionToken (e) => e.token == sessionToken
)?.expire, )?.expire,
}) })

View file

@ -30,7 +30,7 @@ export default function (files: Files) {
} }
currentAccount.email = code.data currentAccount.email = code.data
await Accounts.save() await Accounts.Db.save()
return ctx.redirect('/') return ctx.redirect('/')
} else return ServeError(ctx, 404, "code not found") } else return ServeError(ctx, 404, "code not found")

View file

@ -17,7 +17,7 @@ export default function (files: Files) {
let acc = ctx.get("account") as Accounts.Account let acc = ctx.get("account") as Accounts.Account
const fileId = ctx.req.param("fileId") const fileId = ctx.req.param("fileId")
const host = ctx.req.header("Host") const host = ctx.req.header("Host")
const file = files.files[fileId] const file = files.db.data[fileId]
if (file) { if (file) {
if (file.visibility == "private" && acc?.id != file.owner) { if (file.visibility == "private" && acc?.id != file.owner) {
return ServeError(ctx, 403, "you do not own this file") return ServeError(ctx, 403, "you do not own this file")

View file

@ -28,7 +28,7 @@ program
.alias("ls") .alias("ls")
.description("List files in the database") .description("List files in the database")
.action(() => { .action(() => {
Object.keys(files.files).forEach((e) => console.log(e)) Object.keys(files.db.data).forEach((e) => console.log(e))
}) })
program program
@ -40,7 +40,7 @@ program
.action(async (id, options) => { .action(async (id, options) => {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1000)) await new Promise<void>((resolve) => setTimeout(() => resolve(), 1000))
let fp = files.files[id] let fp = files.db.data[id]
if (!fp) throw `file ${id} not found` if (!fp) throw `file ${id} not found`