diff --git a/src/app/app.vue b/src/app/app.vue index 2e1e6be4..d05c1503 100644 --- a/src/app/app.vue +++ b/src/app/app.vue @@ -1,14 +1,12 @@ diff --git a/src/app/components/setup/welcome.vue b/src/app/components/setup/welcome.vue deleted file mode 100644 index f69f6e0a..00000000 --- a/src/app/components/setup/welcome.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/src/app/layouts/Footer.vue b/src/app/layouts/Footer.vue deleted file mode 100644 index 7d9fbbd2..00000000 --- a/src/app/layouts/Footer.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/src/app/layouts/Header.vue b/src/app/layouts/Header.vue deleted file mode 100644 index 30335870..00000000 --- a/src/app/layouts/Header.vue +++ /dev/null @@ -1,111 +0,0 @@ - - - diff --git a/src/app/layouts/default.vue b/src/app/layouts/default.vue new file mode 100644 index 00000000..e11354c6 --- /dev/null +++ b/src/app/layouts/default.vue @@ -0,0 +1,146 @@ + + + diff --git a/src/app/layouts/setup.vue b/src/app/layouts/setup.vue new file mode 100644 index 00000000..76d08eca --- /dev/null +++ b/src/app/layouts/setup.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/app/pages/setup.vue b/src/app/pages/setup.vue deleted file mode 100644 index cfd19e46..00000000 --- a/src/app/pages/setup.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - diff --git a/src/app/components/setup/lang.vue b/src/app/pages/setup/1.vue similarity index 73% rename from src/app/components/setup/lang.vue rename to src/app/pages/setup/1.vue index ce7fbb56..fd4a0fca 100644 --- a/src/app/components/setup/lang.vue +++ b/src/app/pages/setup/1.vue @@ -6,40 +6,34 @@
+
Continue
diff --git a/src/app/pages/setup/3.vue b/src/app/pages/setup/3.vue new file mode 100644 index 00000000..cdbe69f3 --- /dev/null +++ b/src/app/pages/setup/3.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/app/components/setup/adminUser.vue b/src/app/pages/setup/4.vue similarity index 65% rename from src/app/components/setup/adminUser.vue rename to src/app/pages/setup/4.vue index f3bedaa2..06d92864 100644 --- a/src/app/components/setup/adminUser.vue +++ b/src/app/pages/setup/4.vue @@ -12,7 +12,7 @@ form="newAccount" type="text" autocomplete="username" - :class="inputClass" + class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-200 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0" />
@@ -23,7 +23,7 @@ form="newAccount" type="password" autocomplete="new-password" - :class="inputClass" + class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-200 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0" />
@@ -36,33 +36,21 @@ class="ml-2" />
+ Create Account - diff --git a/src/app/stores/setup.ts b/src/app/stores/setup.ts index 6ea61e74..ba7224e3 100644 --- a/src/app/stores/setup.ts +++ b/src/app/stores/setup.ts @@ -25,5 +25,31 @@ export const useSetupStore = defineStore('Setup', () => { return response.success; } - return { signup, updateHostPort, runMigration }; + type SetupError = { + title: string; + message: string; + }; + + const error = ref(null); + + function handleError(e: SetupError) { + error.value = e; + } + + const step = ref(1); + const totalSteps = ref(6); + function setStep(i: number) { + step.value = i; + } + + return { + signup, + updateHostPort, + runMigration, + error, + handleError, + step, + totalSteps, + setStep, + }; }); diff --git a/src/app/utils/api.ts b/src/app/utils/api.ts index 206d6a86..0dda4d85 100644 --- a/src/app/utils/api.ts +++ b/src/app/utils/api.ts @@ -131,14 +131,14 @@ class API { password: string; accept: boolean; }) { - return $fetch('/api/account/setup', { + return $fetch('/api/setup/account', { method: 'post', body: { username, password, accept }, }); } async setupHostPort({ host, port }: { host: string; port: number }) { - return $fetch('/api/admin/hostport', { + return $fetch('/api/setup/hostport', { method: 'post', body: { host, port }, }); diff --git a/src/server/api/account/setup.post.ts b/src/server/api/setup/account.post.ts similarity index 77% rename from src/server/api/account/setup.post.ts rename to src/server/api/setup/account.post.ts index f70749a1..b1329b9a 100644 --- a/src/server/api/account/setup.post.ts +++ b/src/server/api/setup/account.post.ts @@ -3,13 +3,14 @@ export default defineEventHandler(async (event) => { event, validateZod(passwordSetupType, event) ); - const users = await Database.user.findAll(); - if (users.length !== 0) { + const setupDone = await Database.setup.done(); + if (setupDone) { throw createError({ statusCode: 400, statusMessage: 'Invalid state', }); } await Database.user.create(username, password); + await Database.setup.set(5); return { success: true }; }); diff --git a/src/server/api/setup/hostport.post.ts b/src/server/api/setup/hostport.post.ts new file mode 100644 index 00000000..0704fbe4 --- /dev/null +++ b/src/server/api/setup/hostport.post.ts @@ -0,0 +1,16 @@ +export default defineEventHandler(async (event) => { + const { host, port } = await readValidatedBody( + event, + validateZod(hostPortType, event) + ); + const setupDone = await Database.setup.done(); + if (setupDone) { + throw createError({ + statusCode: 400, + statusMessage: 'Invalid state', + }); + } + await Database.system.updateClientsHostPort(host, port); + await Database.setup.set('success'); + return { success: true }; +}); diff --git a/src/server/middleware/session.ts b/src/server/middleware/session.ts index a03e0a96..b10d880a 100644 --- a/src/server/middleware/session.ts +++ b/src/server/middleware/session.ts @@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => { // Handle api routes if ( !url.pathname.startsWith('/api/') || - url.pathname === '/api/account/setup' || + url.pathname.startsWith('/api/setup/') || url.pathname === '/api/session' || url.pathname === '/api/lang' || url.pathname === '/api/release' || diff --git a/src/server/middleware/setup.ts b/src/server/middleware/setup.ts index 5377895f..78a86858 100644 --- a/src/server/middleware/setup.ts +++ b/src/server/middleware/setup.ts @@ -7,22 +7,21 @@ export default defineEventHandler(async (event) => { return; } - const users = await Database.user.findAll(); - if (users.length === 0) { - // If not setup - if (url.pathname.startsWith('/setup')) { - return; + const setupDone = await Database.setup.done(); + if (!setupDone) { + const parsedSetup = url.pathname.match(/\/setup\/(\d)/); + if (!parsedSetup) { + return sendRedirect(event, `/setup/1`, 302); } - if (url.pathname.startsWith('/api/')) { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid State', - }); + const [_, currentSetup] = parsedSetup; + const step = await Database.setup.get(); + if (step.toString() === currentSetup) { + return; } - return sendRedirect(event, '/setup', 302); + return sendRedirect(event, `/setup/${step}`, 302); } else { // If already set up - if (!url.pathname.startsWith('/setup')) { + if (!url.pathname.startsWith('/setup/')) { return; } return sendRedirect(event, '/login', 302); diff --git a/src/services/database/lowdb.ts b/src/services/database/lowdb.ts index 5acc55fd..f6e8f7be 100644 --- a/src/services/database/lowdb.ts +++ b/src/services/database/lowdb.ts @@ -27,10 +27,35 @@ import { type Lang, type Statistics, } from './repositories/system'; +import { SetupRepository, type Steps } from './repositories/setup'; const DEBUG = debug('LowDB'); -export class LowDBSystem extends SystemRepository { +export class LowDBSetup extends SetupRepository { + #db: Low; + constructor(db: Low) { + super(); + this.#db = db; + } + async done() { + if (this.#db.data.setup === 'success') { + return true; + } + return false; + } + + async get() { + return this.#db.data.setup; + } + + async set(step: Steps) { + this.#db.update((v) => { + v.setup = step; + }); + } +} + +class LowDBSystem extends SystemRepository { #db: Low; constructor(db: Low) { super(); @@ -87,7 +112,7 @@ export class LowDBSystem extends SystemRepository { } } -export class LowDBUser extends UserRepository { +class LowDBUser extends UserRepository { #db: Low; constructor(db: Low) { super(); @@ -155,7 +180,7 @@ export class LowDBUser extends UserRepository { } } -export class LowDBClient extends ClientRepository { +class LowDBClient extends ClientRepository { #db: Low; constructor(db: Low) { super(); @@ -252,6 +277,7 @@ export class LowDBClient extends ClientRepository { export default class LowDB extends DatabaseProvider { #db: Low; + setup: LowDBSetup; system: LowDBSystem; user: LowDBUser; client: LowDBClient; @@ -259,6 +285,7 @@ export default class LowDB extends DatabaseProvider { private constructor(db: Low) { super(); this.#db = db; + this.setup = new LowDBSetup(this.#db); this.system = new LowDBSystem(this.#db); this.user = new LowDBUser(this.#db); this.client = new LowDBClient(this.#db); diff --git a/src/services/database/migrations/1.ts b/src/services/database/migrations/1.ts index 20575cdc..79f9f60f 100644 --- a/src/services/database/migrations/1.ts +++ b/src/services/database/migrations/1.ts @@ -15,6 +15,7 @@ export async function run1(db: Low) { const database: Database = { migrations: [], + setup: 1, system: { general: { sessionTimeout: 3600, // 1 hour diff --git a/src/services/database/repositories/database.ts b/src/services/database/repositories/database.ts index dfbaee21..adaaacd0 100644 --- a/src/services/database/repositories/database.ts +++ b/src/services/database/repositories/database.ts @@ -1,10 +1,12 @@ import type { ClientRepository, Client } from './client'; +import type { SetupRepository, Steps } from './setup'; import type { System, SystemRepository } from './system'; import type { User, UserRepository } from './user'; // Represent data structure export type Database = { migrations: string[]; + setup: Steps; system: System; users: User[]; clients: Record; @@ -12,6 +14,7 @@ export type Database = { export const DEFAULT_DATABASE: Database = { migrations: [], + setup: 1, system: null as never, users: [], clients: {}, @@ -37,6 +40,7 @@ export abstract class DatabaseProvider { */ abstract disconnect(): Promise; + abstract setup: SetupRepository; abstract system: SystemRepository; abstract user: UserRepository; abstract client: ClientRepository; diff --git a/src/services/database/repositories/setup.ts b/src/services/database/repositories/setup.ts new file mode 100644 index 00000000..a6adeba8 --- /dev/null +++ b/src/services/database/repositories/setup.ts @@ -0,0 +1,11 @@ +export type Steps = 1 | 2 | 3 | 4 | 5 | 'success'; + +/** + * Interface for setup-related database operations. + * This interface provides methods for managing setup data. + */ +export abstract class SetupRepository { + abstract done(): Promise; + abstract get(): Promise; + abstract set(step: Steps): Promise; +}