feat: build licensed desktop image processor

This commit is contained in:
2026-08-26 07:26:35 +08:00
parent 6ff3d71921
commit 1f31424015
32 changed files with 6515 additions and 2 deletions
+102
View File
@@ -0,0 +1,102 @@
import { app, net } from 'electron'
import type {
LicenseSession,
VersionInfo,
} from '../../src/shared/types'
import { buildDesktopRequestHeaders } from './server-api-headers'
interface ApiResult<T> {
code: string
msg: string
data: T
}
export interface DevicePayload {
deviceId: string
deviceName: string
osName: string
osVersion: string
appVersion: string
}
export class ServerApiError extends Error {
constructor(
public readonly code: string,
message: string,
) {
super(message)
this.name = 'ServerApiError'
}
}
const DEFAULT_API_BASE_URL = 'https://apimall.v2.jxxyk.com/api'
export class DesktopServerApi {
private readonly baseUrl: string
constructor(baseUrl = process.env.IMAGE_TOOL_API_BASE_URL || DEFAULT_API_BASE_URL) {
this.baseUrl = this.validateBaseUrl(baseUrl)
}
activate(activationCode: string, device: DevicePayload): Promise<LicenseSession> {
return this.post<LicenseSession>('/desktop/activate', {
activationCode,
...device,
})
}
login(token: string, device: DevicePayload): Promise<LicenseSession> {
return this.post<LicenseSession>('/desktop/login', {
token,
...device,
})
}
checkVersion(currentVersion: string): Promise<VersionInfo> {
return this.post<VersionInfo>('/desktop/version/check', {
currentVersion,
platform: 'windows',
})
}
private async post<T>(pathname: string, body: unknown): Promise<T> {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 15_000)
try {
const response = await net.fetch(`${this.baseUrl}${pathname}`, {
method: 'POST',
headers: buildDesktopRequestHeaders(app.getVersion()),
body: JSON.stringify(body),
signal: controller.signal,
})
if (!response.ok) {
throw new ServerApiError(String(response.status), `授权服务暂时不可用(${response.status})`)
}
const result = (await response.json()) as ApiResult<T>
if (result.code !== '0') {
throw new ServerApiError(result.code, result.msg || '授权校验失败')
}
return result.data
} catch (error) {
if (error instanceof ServerApiError) {
throw error
}
if (error instanceof Error && error.name === 'AbortError') {
throw new ServerApiError('NETWORK_TIMEOUT', '连接授权服务超时,请检查网络后重试')
}
throw new ServerApiError('NETWORK_ERROR', '无法连接授权服务,请检查网络后重试')
} finally {
clearTimeout(timeout)
}
}
private validateBaseUrl(value: string): string {
const url = new URL(value.trim())
const localDevelopment = url.protocol === 'http:'
&& ['127.0.0.1', 'localhost'].includes(url.hostname)
if (url.protocol !== 'https:' && !(localDevelopment && !app.isPackaged)) {
throw new Error('生产授权服务必须使用 HTTPS')
}
return url.toString().replace(/\/$/, '')
}
}