Compare commits

..

No commits in common. "ae7b911b16ea29c351acb9f39becbd3d3cd1c082" and "8f3aeec10b7599a377966544c3481bed96bb2342" have entirely different histories.

14 changed files with 127 additions and 119 deletions

View file

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

View file

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

4
mod.ts
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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