diff --git a/src/api.ts b/src/api.ts index 3662cccc..c629f081 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,17 +1,14 @@ import { createApiClient } from '@accentor/api-client-js' -import type { App } from 'vue' -export default { - install: (app: App) => { - let url: string - if (import.meta.env.VITE_APP_API_URL != undefined) { - url = import.meta.env.VITE_APP_API_URL - } else if (import.meta.env.PROD) { - url = '/api' - } else { - url = 'http://localhost:3000/api' - } - app.provide('apiBaseUrl', url) - app.provide('api', createApiClient(url)) - } +let url: string +if (import.meta.env.VITE_APP_API_URL != undefined) { + url = import.meta.env.VITE_APP_API_URL +} else if (import.meta.env.PROD) { + url = '/api' +} else { + url = 'http://localhost:3000/api' } + +export const baseURL = url + +export default createApiClient(url) diff --git a/src/db-client.ts b/src/db-client.ts index 7391aef5..0aa252f1 100644 --- a/src/db-client.ts +++ b/src/db-client.ts @@ -7,11 +7,41 @@ export default class SqliteClient { listenersForTable: Map> = new Map() - constructor(dbFile: string) { + constructor(dbFile: string, migrations: string[]) { const DbWorker: any = Comlink.wrap( new Worker(new URL('./db-worker.ts', import.meta.url), { type: 'module' }) ) - this.sqliteWorker = new DbWorker().then((worker: any) => worker.init(dbFile).then(() => worker)) + this.sqliteWorker = new DbWorker().then((worker: any) => + worker.init(dbFile).then(async () => { + await worker.executeSqlNoResult( + 'CREATE TABLE IF NOT EXISTS migrations (text VARCHAR PRIMARY KEY);' + ) + for (const m of migrations) { + const migrationResult = await this.executeRawSelect( + worker, + 'SELECT * FROM migrations WHERE text = ?;', + m + ) + if (migrationResult.length == 0) { + await worker.executeSqlNoResult(m) + await worker.executeSqlNoResult('INSERT INTO migrations VALUES (?)', m) + } + } + return worker + }) + ) + } + + executeRawSelect(worker: any, statement: String, ...bindParameters: any[]): Promise { + return new Promise((resolve) => { + worker.executeSql( + statement, + bindParameters.map(unref), + Comlink.proxy((resultRows: any[]) => { + resolve(resultRows) + }) + ) + }) } getRefsForTables(tables: string[]): Ref[] { diff --git a/src/db/user_dao.ts b/src/db/user_dao.ts new file mode 100644 index 00000000..8c6f5366 --- /dev/null +++ b/src/db/user_dao.ts @@ -0,0 +1,41 @@ +import type SqliteClient from '@/db-client' +import api from '@/api' +import { useAuthStore } from '@/stores/auth' +import type { UnwrapNestedRefs } from 'vue' + +export default class UserDao { + client: SqliteClient + authStore: UnwrapNestedRefs<{ secret: string | null; deviceId: string | null }> + + constructor(client: SqliteClient) { + this.client = client + this.authStore = useAuthStore() + } + + async refresh() { + let done = false + const generator = api.users.index({ + secret: this.authStore.secret!, + device_id: this.authStore.deviceId! + }) + while (!done) { + const obj = await generator.next() + for (const row of obj.value) { + await this.client.executeMutation( + 'users', + 'INSERT INTO users VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET name=?, permission=?;', + row.id, + row.name, + row.permission, + row.name, + row.permission + ) + } + done = obj.done! + } + } + + getAll() { + return this.client.executeSelect(['users'], 'SELECT * FROM users;') + } +} diff --git a/src/main.ts b/src/main.ts index ec194555..05b023dd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,6 @@ import { aliases, mdi } from 'vuetify/iconsets/mdi' import App from './App.vue' import router from './router' import sqlitePlugin from './sqlite' -import api from './api' const app = createApp(App) @@ -50,6 +49,5 @@ app.use( app.use(createPinia()) app.use(router) app.use(sqlitePlugin, { file: '/accentor.sqlite3' }) -app.use(api) app.mount('#app') diff --git a/src/sqlite.ts b/src/sqlite.ts index 3cd7db28..b59ad0e3 100644 --- a/src/sqlite.ts +++ b/src/sqlite.ts @@ -1,4 +1,5 @@ import SqliteClient from './db-client' +import UserDao from './db/user_dao' import type { App } from 'vue' interface Options { @@ -7,11 +8,12 @@ interface Options { export default { install: (app: App, options: Options) => { - const client = new SqliteClient(options.file) - client.executeMutation( - 'users', + const client = new SqliteClient(options.file, [ 'CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, name VARCHAR, permission VARCHAR);' - ) - app.provide('db', client) + ]) + const daos = { + users: new UserDao(client) + } + app.provide('db', daos) } } diff --git a/src/stores/auth.ts b/src/stores/auth.ts index d4b64ff9..92038116 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -1,12 +1,11 @@ import { defineStore } from 'pinia' import { useStorage } from '@vueuse/core' -import { computed, inject, type Ref } from 'vue' -import type { AuthTokenModule } from '@accentor/api-client-js' +import { computed, type Ref } from 'vue' +import api from '@/api' import { useBaseStore } from './base' export const useAuthStore = defineStore('auth', () => { const baseStore = useBaseStore() - const api: { auth_tokens: AuthTokenModule } = inject('api')! const secret: Ref = useStorage('auth_secret', null) const deviceId: Ref = useStorage('auth_device_id', null) diff --git a/src/views/AppRootView.vue b/src/views/AppRootView.vue index 2ede1a02..1517f36a 100644 --- a/src/views/AppRootView.vue +++ b/src/views/AppRootView.vue @@ -2,13 +2,11 @@ import { RouterView, useRouter } from 'vue-router' import { watchEffect, ref, inject } from 'vue' import { useAuthStore } from '@/stores/auth' -import type { UserModule } from '@accentor/api-client-js' -import type SqliteClient from '../db-client' +import type UserDao from '@/db/user_dao' const router = useRouter() const authStore = useAuthStore() -const api: { users: UserModule } = inject('api')! -const client: SqliteClient = inject('db')! +const { users }: { users: UserDao } = inject('db')! const drawer = ref(false) const loading = ref(false) @@ -25,23 +23,7 @@ function logout() { async function loadData() { loading.value = true - let done = false - const generator = api.users.index({ secret: authStore.secret!, device_id: authStore.deviceId! }) - while (!done) { - const obj = await generator.next() - for (let row of obj.value) { - await client.executeMutation( - 'users', - 'INSERT INTO users VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET name=?, permission=?;', - row.id, - row.name, - row.permission, - row.name, - row.permission - ) - } - done = obj.done! - } + await users.refresh() loading.value = false } diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 04b5bce5..9b91ec65 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,8 +1,8 @@