feat(desktop): remember login credentials
Desktop Client Build / Resolve Build Metadata (push) Successful in 49s
Frontend CI / Frontend (push) Successful in 5m47s
Desktop Client Build / Build Desktop Client (push) Successful in 42m28s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 2m38s

This commit is contained in:
2026-05-14 21:49:27 +08:00
parent 639516f374
commit 5bbbbc5cf1
5 changed files with 181 additions and 16 deletions
@@ -0,0 +1,87 @@
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { app, safeStorage } from 'electron/main'
export interface DesktopLoginCredentials {
identifier: string
password: string
}
interface PersistedLoginCredentials {
version?: number
identifier?: unknown
encryptedPassword?: unknown
}
function credentialsPath(): string {
return join(app.getPath('userData'), 'login-credentials.json')
}
function normalizeCredentials(input: unknown): PersistedLoginCredentials {
if (!input || typeof input !== 'object') {
return {}
}
return input as PersistedLoginCredentials
}
export function getSavedLoginCredentials(): DesktopLoginCredentials | null {
try {
if (!existsSync(credentialsPath())) {
return null
}
const persisted = normalizeCredentials(JSON.parse(readFileSync(credentialsPath(), 'utf8')))
const identifier = typeof persisted.identifier === 'string' ? persisted.identifier : ''
const encryptedPassword =
typeof persisted.encryptedPassword === 'string' ? persisted.encryptedPassword : ''
if (!identifier || !encryptedPassword || !safeStorage.isEncryptionAvailable()) {
return null
}
const password = safeStorage.decryptString(Buffer.from(encryptedPassword, 'base64'))
return password ? { identifier, password } : null
} catch (error) {
console.warn('[login-credentials] read failed', {
message: error instanceof Error ? error.message : String(error),
})
return null
}
}
export function saveLoginCredentials(credentials: DesktopLoginCredentials): DesktopLoginCredentials {
const identifier = credentials.identifier.trim()
if (!identifier || !credentials.password) {
clearSavedLoginCredentials()
return { identifier: '', password: '' }
}
if (!safeStorage.isEncryptionAvailable()) {
throw new Error('login_credentials_encryption_unavailable')
}
const encryptedPassword = safeStorage.encryptString(credentials.password).toString('base64')
writeFileSync(
credentialsPath(),
JSON.stringify({
version: 1,
identifier,
encryptedPassword,
updatedAt: new Date().toISOString(),
}),
'utf8',
)
return { identifier, password: credentials.password }
}
export function clearSavedLoginCredentials(): null {
try {
rmSync(credentialsPath(), { force: true })
} catch (error) {
console.warn('[login-credentials] clear failed', {
message: error instanceof Error ? error.message : String(error),
})
}
return null
}