diff --git a/apps/server/src/modules/bookmarks/index.ts b/apps/server/src/modules/bookmarks/index.ts
index 7c7c226..b219cca 100644
--- a/apps/server/src/modules/bookmarks/index.ts
+++ b/apps/server/src/modules/bookmarks/index.ts
@@ -1,4 +1,4 @@
-import type { ModuleMetadata } from '../registry'
+import type { ModuleMetadata } from '@/modules/registry'
export const bookmarksModule: ModuleMetadata = {
id: 'bookmarks',
diff --git a/apps/server/src/routes/login.tsx b/apps/server/src/routes/login.tsx
index 9d557bd..b7880d5 100644
--- a/apps/server/src/routes/login.tsx
+++ b/apps/server/src/routes/login.tsx
@@ -1,4 +1,4 @@
-import { createFileRoute, Link, redirect, useRouter } from '@tanstack/react-router'
+import { createFileRoute, redirect, useRouter } from '@tanstack/react-router'
import { useState } from 'react'
import { authClient } from '@/server/auth/client'
import { checkInitialized, getSession } from '@/server/auth/functions'
@@ -95,12 +95,6 @@ function LoginPage() {
{loading ? '登录中...' : '登录'}
-
-
-
- 无法登录?使用恢复密钥
-
-
diff --git a/apps/server/src/routes/recover.tsx b/apps/server/src/routes/recover.tsx
deleted file mode 100644
index 95074eb..0000000
--- a/apps/server/src/routes/recover.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import { createFileRoute, Link, redirect, useRouter } from '@tanstack/react-router'
-import { useState } from 'react'
-import { checkInitialized, recoverAccount } from '@/server/auth/functions'
-
-export const Route = createFileRoute('/recover' as never)({
- beforeLoad: async () => {
- const initialized = await checkInitialized()
- if (!initialized) {
- throw redirect({ to: '/setup' as never })
- }
- },
- component: RecoverPage,
-})
-
-function RecoverPage() {
- const router = useRouter()
- const [recoveryKey, setRecoveryKey] = useState('')
- const [newPassword, setNewPassword] = useState('')
- const [confirmPassword, setConfirmPassword] = useState('')
- const [error, setError] = useState('')
- const [loading, setLoading] = useState(false)
- const [success, setSuccess] = useState(false)
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault()
- setError('')
-
- if (newPassword !== confirmPassword) {
- setError('两次输入的密码不一致')
- return
- }
-
- if (newPassword.length < 8) {
- setError('密码至少需要 8 个字符')
- return
- }
-
- setLoading(true)
-
- try {
- await recoverAccount({ data: { recoveryKey: recoveryKey.trim(), newPassword } })
- setSuccess(true)
- } catch (err) {
- setError(err instanceof Error ? err.message : '恢复失败,请检查恢复密钥是否正确')
- } finally {
- setLoading(false)
- }
- }
-
- if (success) {
- return (
-
-
-
-
-
-
-
-
密码已成功重置,所有会话已失效。请使用新密码重新登录。
-
-
-
-
-
-
-
- )
- }
-
- return (
-
- )
-}
diff --git a/apps/server/src/routes/setup.tsx b/apps/server/src/routes/setup.tsx
index ba1fc81..62a59d9 100644
--- a/apps/server/src/routes/setup.tsx
+++ b/apps/server/src/routes/setup.tsx
@@ -1,7 +1,7 @@
import { createFileRoute, redirect, useRouter } from '@tanstack/react-router'
import { useState } from 'react'
import { authClient } from '@/server/auth/client'
-import { checkInitialized, completeSetup } from '@/server/auth/functions'
+import { checkInitialized } from '@/server/auth/functions'
export const Route = createFileRoute('/setup' as never)({
beforeLoad: async () => {
@@ -21,8 +21,6 @@ function SetupPage() {
const [confirmPassword, setConfirmPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
- const [recoveryKey, setRecoveryKey] = useState(null)
- const [copied, setCopied] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
@@ -52,151 +50,92 @@ function SetupPage() {
return
}
- const result = await completeSetup()
- setRecoveryKey(result.recoveryKey)
- setLoading(false)
- }
-
- const handleCopy = async () => {
- if (!recoveryKey) return
- await navigator.clipboard.writeText(recoveryKey)
- setCopied(true)
- setTimeout(() => setCopied(false), 2000)
- }
-
- const handleContinue = () => {
router.navigate({ to: '/' as never })
}
-
- if (recoveryKey) {
- return (
-
-
-
-
-
-
-
-
请妥善保存你的恢复密钥(仅显示一次)
-
如果无法登录且无法访问服务器终端,可通过此密钥重置账户。
-
-
-
-
- {recoveryKey}
-
-
-
-
-
-
-
-
+ ;
+
+
- )
- }
- return (
-
-
-
-
-
+
+
+
+ setEmail(e.target.value)}
+ className="w-full px-4 py-3 rounded-xl bg-slate-50 border-0 ring-1 ring-slate-200 focus:ring-2 focus:ring-indigo-500 outline-none transition-all text-slate-700 placeholder:text-slate-400"
+ placeholder="your@email.com"
+ disabled={loading}
+ />
+
+
+
+
+ setPassword(e.target.value)}
+ className="w-full px-4 py-3 rounded-xl bg-slate-50 border-0 ring-1 ring-slate-200 focus:ring-2 focus:ring-indigo-500 outline-none transition-all text-slate-700 placeholder:text-slate-400"
+ placeholder="至少 8 个字符"
+ disabled={loading}
+ />
+
+
+
+
+ setConfirmPassword(e.target.value)}
+ className="w-full px-4 py-3 rounded-xl bg-slate-50 border-0 ring-1 ring-slate-200 focus:ring-2 focus:ring-indigo-500 outline-none transition-all text-slate-700 placeholder:text-slate-400"
+ placeholder="再次输入密码"
+ disabled={loading}
+ />
+
+
+ {error &&
{error}
}
+
+
+
- )
+
}
diff --git a/apps/server/src/server/api/routers/index.ts b/apps/server/src/server/api/routers/index.ts
index ede4e3e..d2d5191 100644
--- a/apps/server/src/server/api/routers/index.ts
+++ b/apps/server/src/server/api/routers/index.ts
@@ -1,5 +1,5 @@
import * as bookmarks from '@/modules/bookmarks/router'
-import { os } from '../server'
+import { os } from '@/server/api/server'
export const router = os.router({
bookmarks,
diff --git a/apps/server/src/server/auth/functions.ts b/apps/server/src/server/auth/functions.ts
index 596b248..40b6075 100644
--- a/apps/server/src/server/auth/functions.ts
+++ b/apps/server/src/server/auth/functions.ts
@@ -1,23 +1,8 @@
import { createServerFn } from '@tanstack/react-start'
import { getRequestHeaders } from '@tanstack/react-start/server'
-import { hashPassword, verifyPassword } from 'better-auth/crypto'
-import { eq } from 'drizzle-orm'
-import { z } from 'zod'
import { auth } from '@/server/auth'
import * as authSchema from '@/server/auth/schema'
import { db } from '@/server/db'
-import { systemSettings } from '@/server/db/schema/system'
-
-const RECOVERY_KEY_PREFIX = 'kairos-recovery-'
-const RECOVERY_KEY_SETTING = 'recovery_key_hash'
-
-function generateRecoveryKey(): string {
- const bytes = crypto.getRandomValues(new Uint8Array(32))
- const hex = Array.from(bytes)
- .map((b) => b.toString(16).padStart(2, '0'))
- .join('')
- return `${RECOVERY_KEY_PREFIX}${hex}`
-}
export const getSession = createServerFn({ method: 'GET' }).handler(async () => {
const headers = getRequestHeaders()
@@ -28,60 +13,3 @@ export const checkInitialized = createServerFn({ method: 'GET' }).handler(async
const users = await db.select({ id: authSchema.user.id }).from(authSchema.user).limit(1)
return users.length > 0
})
-
-export const completeSetup = createServerFn({ method: 'POST' }).handler(async () => {
- const headers = getRequestHeaders()
- const sessionData = await auth.api.getSession({ headers })
- if (!sessionData?.user) {
- throw new Error('Not authenticated')
- }
-
- const users = await db.select({ id: authSchema.user.id }).from(authSchema.user).limit(2)
- if (users.length !== 1) {
- throw new Error('Invalid system state')
- }
-
- const existing = await db.select().from(systemSettings).where(eq(systemSettings.key, RECOVERY_KEY_SETTING))
- if (existing.length > 0) {
- throw new Error('Setup already completed')
- }
-
- const recoveryKey = generateRecoveryKey()
- const hash = await hashPassword(recoveryKey)
-
- await db.insert(systemSettings).values({ key: RECOVERY_KEY_SETTING, value: hash })
-
- return { recoveryKey }
-})
-
-const recoverAccountSchema = z.object({
- recoveryKey: z.string().min(1),
- newPassword: z.string().min(8),
-})
-
-export const recoverAccount = createServerFn({ method: 'POST' })
- .inputValidator((data: z.input
) => recoverAccountSchema.parse(data))
- .handler(async ({ data }) => {
- const stored = await db.select().from(systemSettings).where(eq(systemSettings.key, RECOVERY_KEY_SETTING))
- if (stored.length === 0 || !stored[0]) {
- throw new Error('Recovery not available')
- }
-
- const isValid = await verifyPassword({ hash: stored[0].value, password: data.recoveryKey })
- if (!isValid) {
- throw new Error('Invalid recovery key')
- }
-
- const owner = await db.select().from(authSchema.user).limit(1)
- if (owner.length === 0 || !owner[0]) {
- throw new Error('No owner found')
- }
-
- const newHash = await hashPassword(data.newPassword)
-
- await db.update(authSchema.account).set({ password: newHash }).where(eq(authSchema.account.userId, owner[0].id))
-
- await db.delete(authSchema.session).where(eq(authSchema.session.userId, owner[0].id))
-
- return { success: true }
- })
diff --git a/apps/server/src/server/db/schema/index.ts b/apps/server/src/server/db/schema/index.ts
index 6d357ab..b56a0c0 100644
--- a/apps/server/src/server/db/schema/index.ts
+++ b/apps/server/src/server/db/schema/index.ts
@@ -1,3 +1,2 @@
export * from '../../../modules/bookmarks/schema'
export * from '../../auth/schema'
-export * from './system'
diff --git a/apps/server/src/server/db/schema/system.ts b/apps/server/src/server/db/schema/system.ts
deleted file mode 100644
index 65efb34..0000000
--- a/apps/server/src/server/db/schema/system.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { pgTable, text, timestamp } from 'drizzle-orm/pg-core'
-
-export const systemSettings = pgTable('system_settings', {
- key: text('key').primaryKey(),
- value: text('value').notNull(),
- createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
- updatedAt: timestamp('updated_at', { withTimezone: true })
- .notNull()
- .defaultNow()
- .$onUpdateFn(() => new Date()),
-})