Files
webshop/shop/lib/actions/licserver-config.ts
DanielS 6b9023c871
All checks were successful
Staging Build / build (push) Successful in 2m48s
feat(wizard): connect license lookup panel to real lic server api endpoint
2026-07-15 01:01:01 +02:00

152 lines
4.5 KiB
TypeScript

'use server'
import { createClient } from '@/lib/supabase/server'
import { createAdminClient } from '@/lib/supabase/admin'
export interface LicServerConfig {
base_url: string | null
api_key: string | null
}
/**
* Read LicServer config from the settings table.
* Falls back to environment variables if DB values are empty.
* Returns null config for unauthenticated callers.
*/
export async function getLicServerConfig(): Promise<LicServerConfig> {
const supabase = await createClient()
// Auth guard — never expose config to unauthenticated callers
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { base_url: null, api_key: null }
const { data } = await supabase
.from('settings')
.select('licserver_base_url, licserver_api_key')
.eq('id', 'licserver')
.single()
return {
base_url: data?.licserver_base_url || process.env.LICSERVER_BASE_URL || null,
api_key: data?.licserver_api_key || process.env.LICSERVER_API_KEY || null,
}
}
/**
* Save LicServer config. Admin-only — checks role before writing.
*/
export async function saveLicServerConfig(
base_url: string,
api_key: string
): Promise<{ success: boolean; error?: string }> {
const supabase = await createClient()
// Auth + role check
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { success: false, error: 'Nicht angemeldet' }
const { data: userData } = await supabase
.from('users')
.select('role')
.eq('id', user.id)
.single()
// Only 'admin' exists in schema — 'superadmin' removed
if (!userData || userData.role !== 'admin') {
return { success: false, error: 'Keine Berechtigung (nur Admins)' }
}
const { error } = await supabase
.from('settings')
.upsert({
id: 'licserver',
licserver_base_url: base_url.trim() || null,
licserver_api_key: api_key.trim() || null,
updated_at: new Date().toISOString(),
})
if (error) return { success: false, error: error.message }
return { success: true }
}
/**
* Test the connection with current config.
* Calls GET /api-v1/partners?pageSize=1 and returns true if 200.
*/
export async function testLicServerConnection(): Promise<{ ok: boolean; message: string }> {
const cfg = await getLicServerConfig()
if (!cfg.base_url || !cfg.api_key) {
return { ok: false, message: 'URL oder API-Key nicht konfiguriert' }
}
try {
const res = await fetch(`${cfg.base_url}/api-v1/partners?pageSize=1`, {
headers: { 'X-Api-Key': cfg.api_key, Accept: 'application/json' },
signal: AbortSignal.timeout(5_000),
})
if (res.ok) return { ok: true, message: `Verbindung OK (HTTP ${res.status})` }
return { ok: false, message: `LicServer antwortete mit HTTP ${res.status}` }
} catch (err: any) {
return { ok: false, message: `Nicht erreichbar: ${err.message}` }
}
}
/**
* System version of getLicServerConfig that bypasses client auth check.
* Used internally by automated background jobs / cron routes.
*/
export async function getLicServerConfigSystem(): Promise<LicServerConfig> {
const admin = createAdminClient()
const { data } = await admin
.from('settings')
.select('licserver_base_url, licserver_api_key')
.eq('id', 'licserver')
.single()
return {
base_url: data?.licserver_base_url || process.env.LICSERVER_BASE_URL || null,
api_key: data?.licserver_api_key || process.env.LICSERVER_API_KEY || null,
}
}
export async function lookupLicenseFromLicServer(id: string) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) throw new Error('Nicht autorisiert.')
const cfg = await getLicServerConfigSystem()
if (!cfg.base_url || !cfg.api_key) {
throw new Error('LicServer ist nicht konfiguriert.')
}
const res = await fetch(`${cfg.base_url}/v1/licenses/${encodeURIComponent(id.trim())}`, {
headers: { 'X-Api-Key': cfg.api_key, 'Accept': 'application/json' },
signal: AbortSignal.timeout(5_000),
})
if (res.status === 404) return null
if (!res.ok) {
throw new Error(`Lizenzserver antwortete mit Fehler: HTTP ${res.status}`)
}
const data = await res.json()
return {
licenseKey: data.id,
status: 'active' as const,
product: data.productId || 'Unbekanntes Produkt',
edition: data.serialNumber ? `Seriennummer: ${data.serialNumber}` : 'Standard',
version: '1.0.0',
seats: 1,
customer: data.filename || 'Lizenz-Datei',
contact: '—',
issuedAt: '',
expiresAt: '',
maintenanceUntil: '',
modules: [],
}
}