Compare commits

...

4 commits

14 changed files with 119 additions and 127 deletions

1
.env.example Normal file
View file

@ -0,0 +1 @@
DB_PATH = './db.sqlite'

View file

@ -1,6 +1,6 @@
{
"name": "@cohabit/ressources",
"version": "0.1.1",
"version": "0.1.2",
"exports": {
".": "./mod.ts",
"./models": "./src/models/mod.ts"
@ -14,16 +14,11 @@
"semiColons": false
},
"imports": {
"@/": "./",
"@models": "./src/models/mod.ts",
"@models/": "./src/models/src/",
"@api": "./src/api/server.ts",
"@api/": "./src/api/",
"@db": "./src/db/mod.ts",
"@db/": "./src/db/",
"@std/json": "jsr:@std/json@^0.223.0"
},
"exclude": [
"./docs"
]
"./docs",
"./tmp"
],
"unstable": ["kv"]
}

4
mod.ts
View file

@ -1,2 +1,2 @@
export * from '@models'
export { Db } from '@db'
export * from './src/models/mod.ts'
export { Db } from './src/db/mod.ts'

View file

@ -6,15 +6,15 @@ import {
type Ressource,
Service,
User,
} from '@models'
import type { CredentialCategory } from '@models/credential.ts'
} from '../models/mod.ts'
import type { CredentialCategory } from '../models/src/credential.ts'
//!TODO link ressources (get, list)
//!TODO Purge unused ressources (delete)
export class Db {
#kv: Deno.Kv
static async init(path?: string) {
static async init(path?: string): Promise<Db> {
const kv = await Deno.openKv(path ?? Deno.env.get('DB_PATH'))
return new Db(kv)
}
@ -23,11 +23,11 @@ export class Db {
this.#kv = kv
}
get storage() {
get storage(): Deno.Kv {
return this.#kv
}
get prefix() {
get prefix(): { ressource: 'ressource' } {
return {
ressource: 'ressource',
}

View file

@ -1,11 +1,7 @@
export {
Ref,
type RefResolver,
type RefString,
} from '@/src/models/utils/ref.ts'
export { Credential } from '@models/credential.ts'
export { Group } from '@models/group.ts'
export { Machine } from '@models/machine.ts'
export type { Ressource } from '@models/ressource.ts'
export { Service } from '@models/service.ts'
export { User } from '@models/user.ts'
export { Ref, type RefResolver, type RefString } from './utils/ref.ts'
export { Credential } from './src/credential.ts'
export { Group } from './src/group.ts'
export { Machine } from './src/machine.ts'
export type { Ressource } from './src/ressource.ts'
export { Service } from './src/service.ts'
export { User } from './src/user.ts'

View file

@ -1,7 +1,7 @@
import type { Base64String, ToJson, UUID } from '@/types.ts'
import { Ressource } from '@models/ressource.ts'
import type { Ref } from '@models'
import { Avatar } from '@/src/models/utils/avatar.ts'
import type { Base64String, ToJson, UUID } from '../../../types.ts'
import { Ressource } from './ressource.ts'
import type { Ref } from '../utils/ref.ts'
import { Avatar } from '../utils/avatar.ts'
export class Credential<T extends CredentialCategory> extends Ressource {
static fromJSON<T extends CredentialCategory>(
@ -65,11 +65,11 @@ export class Credential<T extends CredentialCategory> extends Ressource {
return 'credential'
}
get category() {
get category(): T {
return this.#category
}
get store() {
get store(): Readonly<CredentialStore<T>['store']> {
return this.#store
}

View file

@ -1,7 +1,7 @@
import type { Posix, ToJson, UUID } from '@/types.ts'
import { Ressource } from '@models/ressource.ts'
import { Ref } from '@models'
import { Avatar } from '@/src/models/utils/avatar.ts'
import type { Posix, ToJson, UUID } from '../../../types.ts'
import { Ressource } from './ressource.ts'
import { Ref } from '../utils/ref.ts'
import { Avatar } from '../utils/avatar.ts'
export class Group extends Ressource {
static fromJSON(
@ -50,13 +50,7 @@ export class Group extends Ressource {
}
#posix?: Posix
#permissions: Readonly<{
[serviceOrMachine: UUID]: {
read: boolean
write: boolean
execute: boolean
}
}>
#permissions: Readonly<GroupPermissions>
#groups: readonly Ref<Group>[]
private constructor(
@ -93,15 +87,15 @@ export class Group extends Ressource {
return 'group'
}
get posix() {
get posix(): Posix | undefined {
return this.#posix
}
get permissions() {
get permissions(): Readonly<GroupPermissions> {
return this.#permissions
}
get groups() {
get groups(): readonly Ref<Group>[] {
return this.#groups
}
@ -134,11 +128,13 @@ export interface Group extends Ressource {
type: 'group'
posix: Posix | undefined
groups: readonly Ref<Group>[]
permissions: Readonly<{
[serviceOrMachine: UUID]: {
read: boolean
write: boolean
execute: boolean
}
}>
permissions: Readonly<GroupPermissions>
}
export type GroupPermissions = {
[serviceOrMachine: UUID]: {
read: boolean
write: boolean
execute: boolean
}
}

View file

@ -1,8 +1,8 @@
import { Ref } from '@/src/models/utils/ref.ts'
import type { ToJson, UrlString } from '@/types.ts'
import type { Group } from '@models/group.ts'
import { Ressource } from '@models/ressource.ts'
import { Avatar } from '@/src/models/utils/avatar.ts'
import { Ref } from '../utils/ref.ts'
import type { ToJson, UrlString } from '../../../types.ts'
import type { Group } from './group.ts'
import { Ressource } from './ressource.ts'
import { Avatar } from '../utils/avatar.ts'
export class Machine extends Ressource {
static fromJSON(
@ -14,7 +14,7 @@ export class Machine extends Ressource {
}
static create(
{ tags, url, status, groups, avatar }: Pick<
{ tags, url, status, groups, avatar, name }: Pick<
Machine,
'name' | 'tags' | 'url' | 'status' | 'avatar' | 'groups'
>,
@ -53,13 +53,7 @@ export class Machine extends Ressource {
#tags: readonly string[]
#url: UrlString
#status:
| 'ready'
| 'busy'
| 'unavailable'
| 'discontinued'
| 'error'
| 'unknown'
#status: MachineStatus
#groups: readonly Ref<Group>[]
private constructor(
@ -83,16 +77,16 @@ export class Machine extends Ressource {
get type(): 'machine' {
return 'machine'
}
get tags() {
get tags(): readonly string[] {
return this.#tags
}
get url() {
get url(): UrlString {
return this.#url
}
get status() {
get status(): MachineStatus {
return this.#status
}
get groups() {
get groups(): readonly Ref<Group>[] {
return this.#groups
}
@ -128,12 +122,14 @@ export interface Machine extends Ressource {
type: 'machine'
tags: readonly string[]
url: UrlString
status:
| 'ready'
| 'busy'
| 'unavailable'
| 'discontinued'
| 'error'
| 'unknown'
status: MachineStatus
groups: readonly Ref<Group>[]
}
export type MachineStatus =
| 'ready'
| 'busy'
| 'unavailable'
| 'discontinued'
| 'error'
| 'unknown'

View file

@ -1,6 +1,6 @@
import { Ref } from '@/src/models/utils/ref.ts'
import type { DateString, ToJson, UrlString, UUID } from '@/types.ts'
import { regex } from '@/utils.ts'
import { Ref } from '../utils/ref.ts'
import type { DateString, ToJson, UrlString, UUID } from '../../../types.ts'
import { regex } from '../../../utils.ts'
export class Ressource {
static fromJSON(

View file

@ -1,8 +1,8 @@
import { Ref } from '@/src/models/utils/ref.ts'
import type { ToJson, UrlString } from '@/types.ts'
import type { Group } from '@models/group.ts'
import { Ressource } from '@models/ressource.ts'
import { Avatar } from '@/src/models/utils/avatar.ts'
import { Ref } from '../utils/ref.ts'
import type { ToJson, UrlString } from '../../../types.ts'
import type { Group } from './group.ts'
import { Ressource } from './ressource.ts'
import { Avatar } from '../utils/avatar.ts'
export class Service extends Ressource {
static fromJSON(
@ -52,7 +52,7 @@ export class Service extends Ressource {
return this.create({ name, avatar, tags, url, category, groups })
}
#category: 'web' | 'fs' | 'git'
#category: ServiceCategory
#url: UrlString
#groups: readonly Ref<Group>[]
#tags: readonly string[]
@ -92,19 +92,19 @@ export class Service extends Ressource {
return 'service'
}
get category() {
get category(): ServiceCategory {
return this.#category
}
get tags() {
get tags(): readonly string[] {
return this.#tags
}
get url() {
get url(): UrlString {
return this.#url
}
get groups() {
get groups(): readonly Ref<Group>[] {
return this.#groups
}
@ -138,8 +138,10 @@ export class Service extends Ressource {
export interface Service extends Ressource {
type: 'service'
category: 'web' | 'fs' | 'git'
category: ServiceCategory
tags: readonly string[]
url: UrlString
groups: readonly Ref<Group>[]
}
export type ServiceCategory = 'web' | 'fs' | 'git'

View file

@ -1,10 +1,10 @@
import { Ref } from '@/src/models/utils/ref.ts'
import type { Login, MailAddress, Posix, ToJson } from '@/types.ts'
import { regex, toLogin } from '@/utils.ts'
import type { Credential, CredentialCategory } from '@models/credential.ts'
import type { Group } from '@models/group.ts'
import { Ressource } from '@models/ressource.ts'
import { Avatar } from '@/src/models/utils/avatar.ts'
import { Ref } from '../utils/ref.ts'
import type { Login, MailAddress, Posix, ToJson } from '../../../types.ts'
import { regex, toLogin } from '../../../utils.ts'
import type { Credential, CredentialCategory } from './credential.ts'
import type { Group } from './group.ts'
import { Ressource } from './ressource.ts'
import { Avatar } from '../utils/avatar.ts'
export class User extends Ressource {
static fromJSON(json: ToJson<User>): User {
@ -150,25 +150,25 @@ export class User extends Ressource {
get type(): 'user' {
return 'user'
}
get firstname() {
get firstname(): string {
return this.#firstname
}
get lastname() {
get lastname(): string {
return this.#lastname
}
get login() {
get login(): Login {
return this.#login
}
get mail() {
get mail(): MailAddress {
return this.#mail
}
get groups() {
get groups(): readonly Ref<Group>[] {
return this.#groups
}
get posix() {
get posix(): Posix | undefined {
return this.#posix
}
get credentials() {
get credentials(): readonly Ref<Credential<CredentialCategory>>[] {
return this.#credentials
}

View file

@ -1,4 +1,4 @@
import type { UrlString } from '@/types.ts'
import type { UrlString } from '../../../types.ts'
export class Avatar {
static fromEmoji(emoji: string): UrlString {

View file

@ -1,5 +1,5 @@
import type { Db } from '@/mod.ts'
import type { UUID } from '@/types.ts'
import type { Db } from '../../db/mod.ts'
import type { UUID } from '../../../types.ts'
import {
Credential,
Group,
@ -7,7 +7,7 @@ import {
type Ressource,
Service,
User,
} from '@models'
} from '../mod.ts'
export type RefString<T extends Ressource> =
| `@ref/${T['type']}#${UUID}`
@ -22,7 +22,9 @@ export class Ref<T extends Ressource> extends String {
return `@ref/${type}#${uuid}` as const
}
static parse<T extends Ressource>(string: RefString<T>) {
static parse<T extends Ressource>(
string: RefString<T>,
): { type: T['type']; uuid: UUID } {
const [_, value] = string.split('/')
const [type, uuid] = value.split('#') as [T['type'], UUID]
@ -33,11 +35,13 @@ export class Ref<T extends Ressource> extends String {
return new Ref<T>(ressource)
}
static fromString<T extends Ressource>(string: RefString<T>) {
static fromString<T extends Ressource>(string: RefString<T>): Ref<T> {
return new Ref<T>(Ref.parse(string))
}
static dbResolver(db: Db) {
static dbResolver(
db: Db,
): <T extends Ressource>(ref: RefString<T>) => Promise<T> {
return <T extends Ressource>(ref: RefString<T>): Promise<T> => {
const { type, uuid } = Ref.parse(ref)
//@ts-expect-error force type casting to fix
@ -45,27 +49,29 @@ export class Ref<T extends Ressource> extends String {
}
}
static restResolver(endpoint: string | URL) {
return async <T extends Ressource>(ref: RefString<T>) => {
static restResolver(
endpoint: string | URL,
): <T extends Ressource>(ref: RefString<T>) => Promise<T> {
return async <T extends Ressource>(ref: RefString<T>): Promise<T> => {
const { type, uuid } = Ref.parse(ref)
const url = new URL(`${type}s/${uuid}`, endpoint)
const response = await fetch(url)
const json = await response.json()
if (type === 'user') {
return User.fromJSON(json)
return User.fromJSON(json) as unknown as T
}
if (type === 'machine') {
return Machine.fromJSON(json)
return Machine.fromJSON(json) as unknown as T
}
if (type === 'service') {
return Service.fromJSON(json)
return Service.fromJSON(json) as unknown as T
}
if (type === 'group') {
return Group.fromJSON(json)
return Group.fromJSON(json) as unknown as T
}
if (type === 'credential') {
return Credential.fromJSON(json)
return Credential.fromJSON(json) as unknown as T
}
throw new TypeError(`unknown ref type "${type}"`)
@ -81,23 +87,23 @@ export class Ref<T extends Ressource> extends String {
#type: T['type']
#uuid: UUID
get type() {
get type(): T['type'] {
return this.#type
}
get uuid() {
get uuid(): UUID {
return this.#uuid
}
ref(resolver: RefResolver<T>) {
ref(resolver: RefResolver<T>): T | Promise<T> {
return resolver(this.toString())
}
toString() {
toString(): `@ref/${T['type']}#${UUID}` {
return Ref.#toString<T>({ uuid: this.uuid, type: this.type })
}
toJSON() {
toJSON(): `@ref/${T['type']}#${UUID}` {
return this.toString()
}
}

View file

@ -1,4 +1,4 @@
import type { Login } from '@/types.ts'
import type { Login } from './types.ts'
export function toLogin({
firstname,