feat(db): ✨ add key-value db
This commit is contained in:
parent
0fc2da45ab
commit
03aff06804
128
src/db/mod.ts
Normal file
128
src/db/mod.ts
Normal file
|
@ -0,0 +1,128 @@
|
|||
import { Credential, Group, Machine, Ressource, Service, User } from '@models'
|
||||
|
||||
//!TODO link ressources (get, list)
|
||||
//!TODO Purge unused ressources (delete)
|
||||
|
||||
export class Db {
|
||||
#kv: Deno.Kv
|
||||
static async init(path?: string) {
|
||||
const kv = await Deno.openKv(path ?? Deno.env.get('DB_PATH'))
|
||||
return new Db(kv)
|
||||
}
|
||||
|
||||
private constructor(kv: Deno.Kv) {
|
||||
this.#kv = kv
|
||||
}
|
||||
|
||||
get storage() {
|
||||
return this.#kv
|
||||
}
|
||||
|
||||
get prefix() {
|
||||
return {
|
||||
ressource: 'ressource',
|
||||
}
|
||||
}
|
||||
|
||||
get ressource() {
|
||||
return {
|
||||
credential: this.#ressourceStorage<Credential>('credential', Credential),
|
||||
group: this.#ressourceStorage<Group>('group', Group),
|
||||
machine: this.#ressourceStorage<Machine>('machine', Machine),
|
||||
service: this.#ressourceStorage<Service>('service', Service),
|
||||
user: this.#ressourceStorage<User>('user', User),
|
||||
}
|
||||
}
|
||||
|
||||
#ressourceStorage<T extends Ressource>(
|
||||
type: RessourceType<T>,
|
||||
Builder: RessourceBuilder<T>,
|
||||
) {
|
||||
return {
|
||||
get: (ressource: Pick<T, 'uuid'>) =>
|
||||
this.#get<T>(type, Builder, ressource),
|
||||
set: (ressources: T[]) => this.#set<T>(type, ressources),
|
||||
delete: (ressources: Pick<T, 'uuid'>[]) =>
|
||||
this.#delete<T>(type, ressources),
|
||||
list: (filter: (ressource: T) => boolean = () => true) =>
|
||||
this.#list<T>(type, Builder, filter),
|
||||
}
|
||||
}
|
||||
|
||||
async #get<T extends Ressource>(
|
||||
type: RessourceType<T>,
|
||||
Builder: RessourceBuilder<T>,
|
||||
entry: Pick<T, 'uuid'>,
|
||||
): Promise<T> {
|
||||
const json = await this.#kv.get<RessourceJson<T>>([
|
||||
this.prefix.ressource,
|
||||
type,
|
||||
entry.uuid,
|
||||
])
|
||||
if (json.value) {
|
||||
//@ts-expect-error Type union of Ressource types for Builder
|
||||
return Builder.fromJSON(json.value)
|
||||
}
|
||||
throw new ReferenceError(
|
||||
`no ressource.${type} was found with uuid: "${entry.uuid}"`,
|
||||
)
|
||||
}
|
||||
#set<T extends Ressource>(type: RessourceType<T>, entries: T[]) {
|
||||
const atomic = this.#kv.atomic()
|
||||
|
||||
for (const entry of entries) {
|
||||
//! TODO check if `refs` exists
|
||||
const key = [this.prefix.ressource, type, entry.uuid]
|
||||
atomic.set(key, entry.toJSON())
|
||||
}
|
||||
|
||||
return atomic.commit()
|
||||
}
|
||||
#delete<T extends Ressource>(
|
||||
type: RessourceType<T>,
|
||||
entries: Pick<T, 'uuid'>[],
|
||||
): Promise<Deno.KvCommitResult | Deno.KvCommitError> {
|
||||
const atomic = this.#kv.atomic()
|
||||
|
||||
for (const entry of entries) {
|
||||
//! TODO check if `refs` exists
|
||||
atomic.delete([this.prefix.ressource, type, entry.uuid])
|
||||
}
|
||||
|
||||
return atomic.commit()
|
||||
}
|
||||
async *#list<T extends Ressource>(
|
||||
type: RessourceType<T>,
|
||||
Builder: RessourceBuilder<T>,
|
||||
filter: (entry: T) => boolean,
|
||||
): AsyncGenerator<T, void, void> {
|
||||
const list = this.#kv.list<RessourceJson<T>>({
|
||||
prefix: [this.prefix.ressource, type],
|
||||
})
|
||||
for await (const entry of list) {
|
||||
const value = entry.value
|
||||
//@ts-expect-error Type union of Ressource types for Builder
|
||||
const ressource = Builder.fromJSON(value) as T
|
||||
if (filter(ressource)) {
|
||||
yield ressource
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RessourceType<T extends Ressource> = T extends Credential ? 'credential'
|
||||
: T extends Group ? 'group'
|
||||
: T extends Machine ? 'machine'
|
||||
: T extends Service ? 'service'
|
||||
: T extends User ? 'user'
|
||||
: never
|
||||
|
||||
type RessourceBuilder<T extends Ressource> = T extends Credential
|
||||
? typeof Credential
|
||||
: T extends Group ? typeof Group
|
||||
: T extends Machine ? typeof Machine
|
||||
: T extends Service ? typeof Service
|
||||
: T extends User ? typeof User
|
||||
: never
|
||||
|
||||
type RessourceJson<T extends Ressource> = ReturnType<T['toJSON']>
|
Loading…
Reference in a new issue