's'
This commit is contained in:
14
lib/api.ts
14
lib/api.ts
@@ -6,22 +6,28 @@ function joinUrl(base: string, path: string): string {
|
||||
return `${b}${p}`
|
||||
}
|
||||
|
||||
export type ApiFetchInit = RequestInit & { pbToken?: string | null }
|
||||
|
||||
export async function apiFetch(
|
||||
bind: BindPayload | null,
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
init?: ApiFetchInit,
|
||||
): Promise<Response> {
|
||||
if (!bind) {
|
||||
return Promise.reject(new Error('尚未绑定 PC(请先在「配对」扫码)'))
|
||||
}
|
||||
const headers = new Headers(init?.headers)
|
||||
const { pbToken, ...rest } = init ?? {}
|
||||
const headers = new Headers(rest.headers)
|
||||
if (bind.token?.trim()) {
|
||||
headers.set('Authorization', `Bearer ${bind.token.trim()}`)
|
||||
}
|
||||
return fetch(joinUrl(bind.baseUrl, path), { ...init, headers })
|
||||
if (pbToken?.trim()) {
|
||||
headers.set('X-PB-Token', pbToken.trim())
|
||||
}
|
||||
return fetch(joinUrl(bind.baseUrl, path), { ...rest, headers })
|
||||
}
|
||||
|
||||
export async function apiJson<T>(bind: BindPayload | null, path: string, init?: RequestInit): Promise<T> {
|
||||
export async function apiJson<T>(bind: BindPayload | null, path: string, init?: ApiFetchInit): Promise<T> {
|
||||
const res = await apiFetch(bind, path, init)
|
||||
if (!res.ok) {
|
||||
const t = await res.text()
|
||||
|
||||
29
lib/authStorage.ts
Normal file
29
lib/authStorage.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import type { PbUserRecord } from './pocketbase'
|
||||
|
||||
const KEY = 'pb_auth_session_v1'
|
||||
|
||||
export type StoredPbSession = {
|
||||
token: string
|
||||
record: PbUserRecord
|
||||
}
|
||||
|
||||
export async function loadPbSession(): Promise<StoredPbSession | null> {
|
||||
try {
|
||||
const raw = await AsyncStorage.getItem(KEY)
|
||||
if (!raw) return null
|
||||
const j = JSON.parse(raw) as StoredPbSession
|
||||
if (!j?.token || !j?.record?.id) return null
|
||||
return j
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function savePbSession(s: StoredPbSession): Promise<void> {
|
||||
await AsyncStorage.setItem(KEY, JSON.stringify(s))
|
||||
}
|
||||
|
||||
export async function clearPbSession(): Promise<void> {
|
||||
await AsyncStorage.removeItem(KEY)
|
||||
}
|
||||
21
lib/liveTelemetry.ts
Normal file
21
lib/liveTelemetry.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { apiFetch } from '@/lib/api'
|
||||
import type { BindPayload } from '@/lib/types'
|
||||
import { pbCreateLiveSession } from '@/lib/pocketbase'
|
||||
|
||||
export async function reportLiveControlEvent(
|
||||
bind: BindPayload | null,
|
||||
pbToken: string | null,
|
||||
userId: string | undefined,
|
||||
process: string,
|
||||
event: 'start' | 'restart' | 'stop',
|
||||
): Promise<void> {
|
||||
if (!bind || !pbToken?.trim() || !userId) return
|
||||
let machine: Record<string, unknown> = {}
|
||||
try {
|
||||
const r = await apiFetch(bind, '/host/machine_summary', { pbToken: pbToken.trim() })
|
||||
if (r.ok) machine = (await r.json()) as Record<string, unknown>
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
}
|
||||
await pbCreateLiveSession(pbToken.trim(), userId, { process, event, machine })
|
||||
}
|
||||
99
lib/pocketbase.ts
Normal file
99
lib/pocketbase.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/** PocketBase REST(与 payjsapi 同源实例 https://pocketbase.hackrobot.cn) */
|
||||
|
||||
export const POCKETBASE_URL = 'https://pocketbase.hackrobot.cn'
|
||||
|
||||
export type PbUserRecord = {
|
||||
id: string
|
||||
email?: string
|
||||
name?: string
|
||||
live_allowed?: boolean
|
||||
[k: string]: unknown
|
||||
}
|
||||
|
||||
export type PbAuthResponse = {
|
||||
token: string
|
||||
record: PbUserRecord
|
||||
}
|
||||
|
||||
function joinPb(path: string): string {
|
||||
const b = POCKETBASE_URL.replace(/\/$/, '')
|
||||
const p = path.startsWith('/') ? path : `/${path}`
|
||||
return `${b}${p}`
|
||||
}
|
||||
|
||||
export async function pbAuthWithPassword(email: string, password: string): Promise<PbAuthResponse> {
|
||||
const res = await fetch(joinPb('/api/collections/users/auth-with-password'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ identity: email.trim(), password }),
|
||||
})
|
||||
const data = (await res.json().catch(() => ({}))) as Record<string, unknown>
|
||||
if (!res.ok) {
|
||||
const msg =
|
||||
typeof data.message === 'string'
|
||||
? data.message
|
||||
: typeof data.data === 'object' && data.data && typeof (data.data as { message?: string }).message === 'string'
|
||||
? (data.data as { message: string }).message
|
||||
: `登录失败 (${res.status})`
|
||||
throw new Error(msg)
|
||||
}
|
||||
const token = String(data.token ?? '')
|
||||
const record = data.record as PbUserRecord | undefined
|
||||
if (!token || !record?.id) throw new Error('登录响应异常')
|
||||
return { token, record }
|
||||
}
|
||||
|
||||
export async function pbAuthRefresh(token: string): Promise<PbAuthResponse> {
|
||||
const res = await fetch(joinPb('/api/collections/users/auth-refresh'), {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${token.trim()}` },
|
||||
})
|
||||
const data = (await res.json().catch(() => ({}))) as Record<string, unknown>
|
||||
if (!res.ok) {
|
||||
throw new Error(typeof data.message === 'string' ? data.message : `刷新失败 (${res.status})`)
|
||||
}
|
||||
const newToken = String(data.token ?? '')
|
||||
const record = data.record as PbUserRecord | undefined
|
||||
if (!newToken || !record?.id) throw new Error('刷新响应异常')
|
||||
return { token: newToken, record }
|
||||
}
|
||||
|
||||
export function isLiveAllowed(record: PbUserRecord | null | undefined): boolean {
|
||||
if (!record) return false
|
||||
return record.live_allowed !== false
|
||||
}
|
||||
|
||||
export type LiveSessionPayload = {
|
||||
process: string
|
||||
event: 'start' | 'restart' | 'stop'
|
||||
machine: Record<string, unknown>
|
||||
}
|
||||
|
||||
/** 需在 PocketBase 新建集合 `live_sessions`,见项目内说明 */
|
||||
export async function pbCreateLiveSession(
|
||||
token: string,
|
||||
userId: string,
|
||||
payload: LiveSessionPayload,
|
||||
): Promise<void> {
|
||||
const res = await fetch(joinPb('/api/collections/live_sessions/records'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token.trim()}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user: userId,
|
||||
process: payload.process,
|
||||
event: payload.event,
|
||||
machine: payload.machine,
|
||||
}),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const t = await res.text()
|
||||
if (res.status === 404) {
|
||||
console.warn('[live_sessions] 集合未创建或规则未放行:', t)
|
||||
return
|
||||
}
|
||||
console.warn('[live_sessions] 写入失败:', res.status, t)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user