2024-06-21 03:35:00 -04:00
|
|
|
import knex from 'knex'
|
|
|
|
|
|
|
|
|
|
const connectionTypes = ['jellyfin', 'youtube-music']
|
|
|
|
|
|
2024-06-23 17:13:09 -04:00
|
|
|
export declare namespace Schemas {
|
2024-06-21 03:35:00 -04:00
|
|
|
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
|
|
|
|
|
}
|
2024-03-24 16:03:31 -04:00
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
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
|
2024-03-24 16:03:31 -04:00
|
|
|
}
|
2024-06-21 03:35:00 -04:00
|
|
|
uploader?: {
|
|
|
|
|
id: string
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
isVideo: boolean
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Database {
|
2024-06-23 17:13:09 -04:00
|
|
|
public readonly db: knex.Knex
|
2024-06-21 03:35:00 -04:00
|
|
|
|
2024-06-23 17:13:09 -04:00
|
|
|
constructor(db: knex.Knex<'better-sqlite3'>) {
|
|
|
|
|
this.db = db
|
2024-06-21 03:35:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public uuid() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db.fn.uuid()
|
2024-06-21 03:35:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public get users() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db<Schemas.Users>('Users')
|
2024-06-21 03:35:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public get connections() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db<Schemas.Connections>('Connections')
|
2024-03-24 16:03:31 -04:00
|
|
|
}
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
public get mixes() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db<Schemas.Mixes>('Mixes')
|
2024-03-24 16:03:31 -04:00
|
|
|
}
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
public get mixItems() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db<Schemas.MixItems>('MixItems')
|
2024-03-24 16:03:31 -04:00
|
|
|
}
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
public get songs() {
|
2024-06-23 17:13:09 -04:00
|
|
|
return this.db<Schemas.Songs>('Songs')
|
2024-06-21 03:35:00 -04:00
|
|
|
}
|
|
|
|
|
|
2024-06-23 17:13:09 -04:00
|
|
|
private exists() {}
|
2024-06-21 03:35:00 -04:00
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
})
|
2024-03-24 16:03:31 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-21 03:35:00 -04:00
|
|
|
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)
|