Files
Lazuli/src/lib/server/db.ts

185 lines
5.5 KiB
TypeScript
Raw Normal View History

import knex from 'knex'
const connectionTypes = ['jellyfin', 'youtube-music']
2024-06-23 17:13:09 -04:00
export declare namespace Schemas {
interface Users {
id: string
username: string
passwordHash: string
}
interface JellyfinConnection {
id: string
userId: string
type: 'jellyfin'
serviceUserId: string
serverUrl: string
accessToken: string
}
interface YouTubeMusicConnection {
id: string
userId: string
type: 'youtube-music'
serviceUserId: string
accessToken: string
refreshToken: string
expiry: number
}
type Connections = JellyfinConnection | YouTubeMusicConnection
interface Mixes {
id: string
userId: string
name: string
thumbnailTag?: string
description?: string
trackCount: number
duration: number
}
interface MixItems {
mixId: string
connectionId: string
connectionType: ConnectionType
id: string
index: number
}
interface Songs {
connectionId: string
connectionType: ConnectionType
id: string
name: string
duration: number
thumbnailUrl: string
releaseDate?: string
artists?: {
id: string
name: string
}[]
album?: {
id: string
name: string
}
uploader?: {
id: string
name: string
}
isVideo: boolean
}
}
class Database {
2024-06-23 17:13:09 -04:00
public readonly db: knex.Knex
2024-06-23 17:13:09 -04:00
constructor(db: knex.Knex<'better-sqlite3'>) {
this.db = db
}
public uuid() {
2024-06-23 17:13:09 -04:00
return this.db.fn.uuid()
}
public get users() {
2024-06-23 17:13:09 -04:00
return this.db<Schemas.Users>('Users')
}
public get connections() {
2024-06-23 17:13:09 -04:00
return this.db<Schemas.Connections>('Connections')
}
public get mixes() {
2024-06-23 17:13:09 -04:00
return this.db<Schemas.Mixes>('Mixes')
}
public get mixItems() {
2024-06-23 17:13:09 -04:00
return this.db<Schemas.MixItems>('MixItems')
}
public get songs() {
2024-06-23 17:13:09 -04:00
return this.db<Schemas.Songs>('Songs')
}
2024-06-23 17:13:09 -04:00
private exists() {}
public static async createUsersTable(db: knex.Knex<'better-sqlite3'>) {
const exists = await db.schema.hasTable('Users')
if (exists) return
await db.schema.createTable('Users', (tb) => {
tb.uuid('id').primary(), tb.string('username').unique().notNullable().checkLength('<=', 30), tb.string('passwordHash').notNullable().checkLength('=', 60)
})
}
public static async createConnectionsTable(db: knex.Knex<'better-sqlite3'>) {
const exists = await db.schema.hasTable('Connections')
if (exists) return
await db.schema.createTable('Connections', (tb) => {
tb.uuid('id').primary(),
tb.uuid('userId').notNullable().references('id').inTable('Users'),
tb.enum('type', connectionTypes).notNullable(),
tb.string('serviceUserId'),
tb.string('serverUrl'),
tb.string('accessToken'),
tb.string('refreshToken'),
tb.integer('expiry')
})
}
public static async createMixesTable(db: knex.Knex<'better-sqlite3'>) {
const exists = await db.schema.hasTable('Mixes')
if (exists) return
await db.schema.createTable('Mixes', (tb) => {
tb.uuid('id').primary(),
tb.uuid('userId').notNullable().references('id').inTable('Users'),
tb.string('name').notNullable(),
tb.uuid('thumbnailTag'),
tb.string('description'),
tb.integer('trackCount').notNullable(),
tb.integer('duration').notNullable()
})
}
public static async createMixItemsTable(db: knex.Knex<'better-sqlite3'>) {
const exists = await db.schema.hasTable('MixItems')
if (exists) return
await db.schema.createTable('MixItems', (tb) => {
tb.uuid('mixId').notNullable().references('id').inTable('Mixes'),
tb.uuid('connectionId').notNullable().references('id').inTable('Connections'),
tb.enum('connectionType', connectionTypes).notNullable(),
tb.string('id').notNullable()
tb.integer('index').notNullable()
})
}
public static async createSongsTable(db: knex.Knex<'better-sqlite3'>) {
const exists = await db.schema.hasTable('Songs')
if (exists) return
await db.schema.createTable('Songs', (tb) => {
tb.uuid('connectionId').notNullable().references('id').inTable('Connections'),
tb.enum('connectionType', connectionTypes),
tb.string('id').notNullable(),
tb.string('name').notNullable(),
tb.integer('duration').notNullable(),
tb.string('thumbnailUrl').notNullable(),
tb.datetime('releaseDate', { precision: 3 }),
tb.json('artists'),
tb.json('album'),
tb.json('uploader'),
tb.boolean('isVideo').notNullable()
})
}
}
const db = knex<'better-sqlite3'>({ client: 'better-sqlite3', connection: { filename: './src/lib/server/lazuli.db' }, useNullAsDefault: false })
await Promise.all([Database.createUsersTable(db), Database.createConnectionsTable(db), Database.createMixesTable(db), Database.createMixItemsTable(db), Database.createSongsTable(db)])
export const DB = new Database(db)