fix: License key format, login populates license, case-insensitive email
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
- admin.service.ts: createLicense() now uses CORR-XXXX-XXXX-XXXX format instead of raw hex hash - admin.service.ts: getLicenses() flattens owner_email in response to match frontend expected shape - auth.service.ts: Login/register responses now include full license object so frontend can populate auth store - auth.service.ts: Email lookups are case-insensitive (LOWER()) to prevent duplicate accounts from case variations - LoginView/RegisterView: Call setLicense() after setAuth() - AdminLicenses: Handle null expires_at (was showing Dec 31, 1969), fix nullable types, fix query param name (per_page → limit) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ export interface AuthResponse {
|
||||
refresh_token: string
|
||||
requires_totp: boolean
|
||||
user: User
|
||||
license: License | null
|
||||
}
|
||||
|
||||
export interface ServerConnection {
|
||||
|
||||
@@ -39,6 +39,9 @@ async function handleLogin() {
|
||||
}
|
||||
|
||||
authStore.setAuth(response)
|
||||
if (response.license) {
|
||||
authStore.setLicense(response.license)
|
||||
}
|
||||
router.push('/')
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
@@ -68,6 +71,9 @@ async function handleTotpVerify() {
|
||||
})
|
||||
|
||||
authStore.setAuth(response)
|
||||
if (response.license) {
|
||||
authStore.setLicense(response.license)
|
||||
}
|
||||
router.push('/')
|
||||
} catch (err: unknown) {
|
||||
totpCode.value = ''
|
||||
|
||||
@@ -57,6 +57,9 @@ async function handleRegister() {
|
||||
})
|
||||
|
||||
authStore.setAuth(response)
|
||||
if (response.license) {
|
||||
authStore.setLicense(response.license)
|
||||
}
|
||||
router.push('/setup')
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
|
||||
@@ -9,20 +9,20 @@ interface License {
|
||||
id: string
|
||||
license_key: string
|
||||
owner_email: string
|
||||
server_name: string
|
||||
server_name: string | null
|
||||
status: 'active' | 'suspended' | 'expired' | 'revoked'
|
||||
created_at: string
|
||||
expires_at: string
|
||||
expires_at: string | null
|
||||
}
|
||||
|
||||
interface LicenseDetail {
|
||||
id: string
|
||||
license_key: string
|
||||
owner_email: string
|
||||
server_name: string
|
||||
server_name: string | null
|
||||
status: string
|
||||
created_at: string
|
||||
expires_at: string
|
||||
expires_at: string | null
|
||||
team_count: number
|
||||
wipe_count: number
|
||||
server_connection: {
|
||||
@@ -67,8 +67,11 @@ const statusBadgeClass: Record<string, string> = {
|
||||
revoked: 'bg-red-500/10 text-red-400',
|
||||
}
|
||||
|
||||
function formatDate(iso: string): string {
|
||||
return new Date(iso).toLocaleDateString('en-US', {
|
||||
function formatDate(iso: string | null | undefined): string {
|
||||
if (!iso) return '—'
|
||||
const d = new Date(iso)
|
||||
if (isNaN(d.getTime()) || d.getTime() === 0) return '—'
|
||||
return d.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
@@ -80,7 +83,7 @@ async function fetchLicenses() {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
page: page.value.toString(),
|
||||
per_page: perPage.toString(),
|
||||
limit: perPage.toString(),
|
||||
})
|
||||
if (searchQuery.value.trim()) params.set('search', searchQuery.value.trim())
|
||||
if (statusFilter.value !== 'all') params.set('status', statusFilter.value)
|
||||
|
||||
Reference in New Issue
Block a user