mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-14 09:49:58 -05:00
Chore: Implement Server Adapters & Request Manager
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import type {
|
||||
PlatformAdapter,
|
||||
PlatformFeature,
|
||||
ServerLaunchConfig,
|
||||
DiscordPresence,
|
||||
AppUpdateInfo,
|
||||
} from '$lib/platform-adapters/types'
|
||||
|
||||
export class CapacitorAdapter implements PlatformAdapter {
|
||||
async init() {}
|
||||
|
||||
isSupported(feature: PlatformFeature): boolean {
|
||||
const supported: PlatformFeature[] = ['biometric-auth', 'filesystem']
|
||||
return supported.includes(feature)
|
||||
}
|
||||
|
||||
async launchServer(_config: ServerLaunchConfig) {}
|
||||
async stopServer() {}
|
||||
async getServerStatus(): Promise<'running' | 'stopped' | 'error'> {
|
||||
return 'stopped'
|
||||
}
|
||||
|
||||
async readFile(path: string): Promise<Uint8Array> {
|
||||
const { Filesystem, Directory } = await import('@capacitor/filesystem')
|
||||
const result = await Filesystem.readFile({ path, directory: Directory.Data })
|
||||
const base64 = result.data as string
|
||||
const binary = atob(base64)
|
||||
const bytes = new Uint8Array(binary.length)
|
||||
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
|
||||
return bytes
|
||||
}
|
||||
|
||||
async writeFile(path: string, data: Uint8Array): Promise<void> {
|
||||
const { Filesystem, Directory } = await import('@capacitor/filesystem')
|
||||
const binary = String.fromCharCode(...data)
|
||||
const base64 = btoa(binary)
|
||||
await Filesystem.writeFile({ path, data: base64, directory: Directory.Data })
|
||||
}
|
||||
|
||||
async pickFolder(): Promise<string | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
try {
|
||||
const { NativeBiometric } = await import('capacitor-native-biometric')
|
||||
await NativeBiometric.verifyIdentity({ reason: 'Authenticate to access Moku', title: 'Biometric Auth' })
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async storeCredential(key: string, value: string): Promise<void> {
|
||||
const { NativeBiometric } = await import('capacitor-native-biometric')
|
||||
await NativeBiometric.setCredentials({ username: key, password: value, server: 'moku' })
|
||||
}
|
||||
|
||||
async getCredential(key: string): Promise<string | null> {
|
||||
try {
|
||||
const { NativeBiometric } = await import('capacitor-native-biometric')
|
||||
const result = await NativeBiometric.getCredentials({ server: 'moku' })
|
||||
return result.username === key ? result.password : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async setTitle(_title: string) {}
|
||||
async minimize() {}
|
||||
async maximize() {}
|
||||
async close() {}
|
||||
|
||||
async setDiscordPresence(_presence: DiscordPresence) {}
|
||||
async clearDiscordPresence() {}
|
||||
|
||||
async getVersion(): Promise<string> {
|
||||
const { App } = await import('@capacitor/app')
|
||||
const info = await App.getInfo()
|
||||
return info.version
|
||||
}
|
||||
|
||||
async openExternal(url: string): Promise<void> {
|
||||
const { Browser } = await import('@capacitor/browser')
|
||||
await Browser.open({ url })
|
||||
}
|
||||
|
||||
async checkForAppUpdate(): Promise<AppUpdateInfo | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
async installAppUpdate(): Promise<void> {}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { readFile, writeFile } from '@tauri-apps/plugin-fs'
|
||||
import { open as openUrl } from '@tauri-apps/plugin-shell'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
import { check } from '@tauri-apps/plugin-updater'
|
||||
import { relaunch } from '@tauri-apps/plugin-process'
|
||||
import type {
|
||||
PlatformAdapter,
|
||||
PlatformFeature,
|
||||
ServerLaunchConfig,
|
||||
DiscordPresence,
|
||||
AppUpdateInfo,
|
||||
} from '$lib/platform-adapters/types'
|
||||
|
||||
export class TauriAdapter implements PlatformAdapter {
|
||||
async init() {
|
||||
await invoke('init_app')
|
||||
}
|
||||
|
||||
isSupported(feature: PlatformFeature): boolean {
|
||||
const supported: PlatformFeature[] = [
|
||||
'server-management',
|
||||
'biometric-auth',
|
||||
'native-window',
|
||||
'filesystem',
|
||||
'app-updates',
|
||||
'discord-rpc',
|
||||
]
|
||||
return supported.includes(feature)
|
||||
}
|
||||
|
||||
async launchServer(config: ServerLaunchConfig) {
|
||||
await invoke('launch_server', { config })
|
||||
}
|
||||
|
||||
async stopServer() {
|
||||
await invoke('stop_server')
|
||||
}
|
||||
|
||||
async getServerStatus(): Promise<'running' | 'stopped' | 'error'> {
|
||||
return invoke('get_server_status')
|
||||
}
|
||||
|
||||
async readFile(path: string): Promise<Uint8Array> {
|
||||
return readFile(path)
|
||||
}
|
||||
|
||||
async writeFile(path: string, data: Uint8Array) {
|
||||
await writeFile(path, data)
|
||||
}
|
||||
|
||||
async pickFolder(): Promise<string | null> {
|
||||
const result = await open({ directory: true, multiple: false })
|
||||
return typeof result === 'string' ? result : null
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
return invoke('authenticate_biometric')
|
||||
}
|
||||
|
||||
async storeCredential(key: string, value: string) {
|
||||
await invoke('store_credential', { key, value })
|
||||
}
|
||||
|
||||
async getCredential(key: string): Promise<string | null> {
|
||||
return invoke('get_credential', { key })
|
||||
}
|
||||
|
||||
async setTitle(title: string) {
|
||||
await invoke('set_window_title', { title })
|
||||
}
|
||||
|
||||
async minimize() {
|
||||
await invoke('minimize_window')
|
||||
}
|
||||
|
||||
async maximize() {
|
||||
await invoke('maximize_window')
|
||||
}
|
||||
|
||||
async close() {
|
||||
await invoke('close_window')
|
||||
}
|
||||
|
||||
async setDiscordPresence(presence: DiscordPresence) {
|
||||
await invoke('set_discord_presence', { presence })
|
||||
}
|
||||
|
||||
async clearDiscordPresence() {
|
||||
await invoke('clear_discord_presence')
|
||||
}
|
||||
|
||||
async getVersion(): Promise<string> {
|
||||
return getVersion()
|
||||
}
|
||||
|
||||
async openExternal(url: string) {
|
||||
await openUrl(url)
|
||||
}
|
||||
|
||||
async checkForAppUpdate(): Promise<AppUpdateInfo | null> {
|
||||
const update = await check()
|
||||
if (!update?.available) return null
|
||||
return {
|
||||
version: update.version,
|
||||
url: update.body ?? '',
|
||||
notes: update.body,
|
||||
}
|
||||
}
|
||||
|
||||
async installAppUpdate() {
|
||||
const update = await check()
|
||||
if (update?.available) {
|
||||
await update.downloadAndInstall()
|
||||
await relaunch()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
export type PlatformFeature =
|
||||
| 'server-management'
|
||||
| 'biometric-auth'
|
||||
| 'native-window'
|
||||
| 'filesystem'
|
||||
| 'app-updates'
|
||||
| 'discord-rpc'
|
||||
|
||||
export interface ServerLaunchConfig {
|
||||
jarPath: string
|
||||
port: number
|
||||
dataPath: string
|
||||
}
|
||||
|
||||
export interface DiscordPresence {
|
||||
title: string
|
||||
chapter: string
|
||||
startTimestamp?: number
|
||||
}
|
||||
|
||||
export interface AppUpdateInfo {
|
||||
version: string
|
||||
url: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export interface PlatformAdapter {
|
||||
init(): Promise<void>
|
||||
isSupported(feature: PlatformFeature): boolean
|
||||
|
||||
launchServer(config: ServerLaunchConfig): Promise<void>
|
||||
stopServer(): Promise<void>
|
||||
getServerStatus(): Promise<'running' | 'stopped' | 'error'>
|
||||
|
||||
readFile(path: string): Promise<Uint8Array>
|
||||
writeFile(path: string, data: Uint8Array): Promise<void>
|
||||
pickFolder(): Promise<string | null>
|
||||
|
||||
authenticateBiometric(): Promise<boolean>
|
||||
storeCredential(key: string, value: string): Promise<void>
|
||||
getCredential(key: string): Promise<string | null>
|
||||
|
||||
setTitle(title: string): Promise<void>
|
||||
minimize(): Promise<void>
|
||||
maximize(): Promise<void>
|
||||
close(): Promise<void>
|
||||
|
||||
setDiscordPresence(presence: DiscordPresence): Promise<void>
|
||||
clearDiscordPresence(): Promise<void>
|
||||
|
||||
getVersion(): Promise<string>
|
||||
openExternal(url: string): Promise<void>
|
||||
checkForAppUpdate(): Promise<AppUpdateInfo | null>
|
||||
installAppUpdate(): Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import type {
|
||||
PlatformAdapter,
|
||||
PlatformFeature,
|
||||
ServerLaunchConfig,
|
||||
DiscordPresence,
|
||||
AppUpdateInfo,
|
||||
} from '$lib/platform-adapters/types'
|
||||
|
||||
export class WebAdapter implements PlatformAdapter {
|
||||
async init() {}
|
||||
|
||||
isSupported(_feature: PlatformFeature): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
async launchServer(_config: ServerLaunchConfig) {}
|
||||
async stopServer() {}
|
||||
async getServerStatus(): Promise<'running' | 'stopped' | 'error'> {
|
||||
return 'stopped'
|
||||
}
|
||||
|
||||
async readFile(_path: string): Promise<Uint8Array> {
|
||||
return new Uint8Array()
|
||||
}
|
||||
|
||||
async writeFile(_path: string, _data: Uint8Array) {}
|
||||
|
||||
async pickFolder(): Promise<string | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
return false
|
||||
}
|
||||
|
||||
async storeCredential(_key: string, _value: string) {}
|
||||
|
||||
async getCredential(_key: string): Promise<string | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
async setTitle(title: string) {
|
||||
document.title = title
|
||||
}
|
||||
|
||||
async minimize() {}
|
||||
async maximize() {}
|
||||
async close() {}
|
||||
|
||||
async setDiscordPresence(_presence: DiscordPresence) {}
|
||||
async clearDiscordPresence() {}
|
||||
|
||||
async getVersion(): Promise<string> {
|
||||
return __APP_VERSION__
|
||||
}
|
||||
|
||||
async openExternal(url: string) {
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
async checkForAppUpdate(): Promise<AppUpdateInfo | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
async installAppUpdate() {}
|
||||
}
|
||||
Reference in New Issue
Block a user