Add remote browser pages and website sync

Enable managed remote browser custom pages with login autofill and add website sync workflows so external admin surfaces can be handled inside SmartUp.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
liumangmang
2026-05-15 15:43:58 +08:00
parent a13a0070a5
commit 7adc7c00ab
43 changed files with 6615 additions and 641 deletions
+159
View File
@@ -70,6 +70,111 @@ export const upstreamsApi = {
api.get<any[]>(`/api/upstreams/${id}/snapshots`, { params: { limit, offset } }),
}
// ——— Websites ———
export interface WebsiteData {
id: number
name: string
site_type: string
base_url: string
api_prefix: string
auth_type: string
auth_config_masked: Record<string, any>
groups_endpoint: string
group_update_endpoint: string
enabled: boolean
auto_sync_enabled: boolean
timeout_seconds: number
last_status: string
last_checked_at: string | null
last_error: string | null
created_at: string
updated_at: string
}
export interface WebsiteForm {
name: string
site_type: string
base_url: string
api_prefix: string
auth_type: string
auth_config: Record<string, any>
groups_endpoint: string
group_update_endpoint: string
enabled: boolean
auto_sync_enabled: boolean
timeout_seconds: number
}
export interface WebsiteGroup {
id: string
name: string
rate_multiplier: string | null
raw: Record<string, any>
}
export interface BindingSourceGroup {
upstream_id: number
group_id: string
upstream_name: string
group_name: string
}
export interface GroupBindingData {
id: number
website_id: number
website_name: string
target_group_id: string
target_group_name: string
source_groups: BindingSourceGroup[]
percent: number
algorithm: string
enabled: boolean
created_at: string
updated_at: string
}
export interface GroupBindingForm {
website_id: number
target_group_id: string
target_group_name: string
source_groups: BindingSourceGroup[]
percent: number
algorithm: string
enabled: boolean
}
export interface WebsiteSyncLog {
id: number
website_id: number
binding_id: number | null
target_group_id: string
target_group_name: string
algorithm: string
percent: number
source_rates: Array<Record<string, any>>
old_rate: string | null
new_rate: string | null
status: string
message: string
created_at: string
}
export const websitesApi = {
list: () => api.get<WebsiteData[]>('/api/websites'),
create: (data: WebsiteForm) => api.post<WebsiteData>('/api/websites', data),
update: (id: number, data: Partial<WebsiteForm>) => api.put<WebsiteData>(`/api/websites/${id}`, data),
delete: (id: number) => api.delete(`/api/websites/${id}`),
test: (id: number) => api.post<{ success: boolean; message: string; detail?: string }>(`/api/websites/${id}/test`),
groups: (id: number) => api.get<WebsiteGroup[]>(`/api/websites/${id}/groups`),
listBindings: () => api.get<GroupBindingData[]>('/api/group-bindings'),
createBinding: (data: GroupBindingForm) => api.post<GroupBindingData>('/api/group-bindings', data),
updateBinding: (id: number, data: Partial<GroupBindingForm>) => api.put<GroupBindingData>(`/api/group-bindings/${id}`, data),
deleteBinding: (id: number) => api.delete(`/api/group-bindings/${id}`),
syncNow: (id: number) => api.post<WebsiteSyncLog>(`/api/group-bindings/${id}/sync-now`),
logs: (params?: { website_id?: number; binding_id?: number; limit?: number; offset?: number }) =>
api.get<WebsiteSyncLog[]>('/api/website-sync-logs', { params }),
}
// ——— Webhooks ———
export interface WebhookData {
id: number
@@ -118,6 +223,8 @@ export const logsApi = {
}
// ——— Custom Pages ———
export type CustomPageAccessMode = 'direct' | 'proxy' | 'remote_browser'
export interface CustomPageData {
id: number
name: string
@@ -126,7 +233,14 @@ export interface CustomPageData {
sort_order: number
enabled: boolean
use_proxy: boolean
access_mode: CustomPageAccessMode
description: string | null
login_username: string | null
login_username_selector: string | null
login_password_selector: string | null
login_submit_selector: string | null
login_autofill_enabled: boolean
login_password_configured: boolean
created_at: string
updated_at: string
}
@@ -138,7 +252,15 @@ export interface CustomPageForm {
sort_order: number
enabled: boolean
use_proxy: boolean
access_mode: CustomPageAccessMode
description?: string
login_username?: string
login_password?: string
login_username_selector?: string
login_password_selector?: string
login_submit_selector?: string
login_autofill_enabled?: boolean
login_password_clear?: boolean
}
export const customPagesApi = {
@@ -148,3 +270,40 @@ export const customPagesApi = {
update: (id: number, data: Partial<CustomPageForm>) => api.put<CustomPageData>(`/api/custom-pages/${id}`, data),
delete: (id: number) => api.delete(`/api/custom-pages/${id}`),
}
// ——— Remote browser sessions ———
export interface BrowserSessionData {
id: string
custom_page_id: number
url: string
title: string
}
export type BrowserEventPayload =
| { type: 'click' | 'dblclick' | 'mousemove' | 'mousedown' | 'mouseup'; x: number; y: number; button?: 'left' | 'right' | 'middle' }
| { type: 'type'; text: string }
| { type: 'key'; key: string }
| { type: 'scroll'; delta_x: number; delta_y: number; x?: number; y?: number }
| { type: 'reload' | 'back' | 'forward' }
| { type: 'resize'; width: number; height: number }
export const browserSessionsApi = {
create: (data: { custom_page_id: number; width: number; height: number }) =>
api.post<BrowserSessionData>('/api/browser-sessions', data),
get: (id: string) => api.get<BrowserSessionData>(`/api/browser-sessions/${id}`),
event: (id: string, data: BrowserEventPayload) =>
api.post<BrowserSessionData>(`/api/browser-sessions/${id}/events`, data),
close: (id: string) => api.delete(`/api/browser-sessions/${id}`),
screenshotUrl: (id: string, token?: string) => {
const params = new URLSearchParams({ t: String(Date.now()) })
if (token) params.set('token', token)
return `/api/browser-sessions/${id}/screenshot?${params.toString()}`
},
/** Build a WebSocket URL for the streaming endpoint. */
wsUrl: (id: string, token?: string) => {
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'
const params = new URLSearchParams()
if (token) params.set('token', token)
return `${proto}//${location.host}/api/browser-sessions/${id}/ws?${params.toString()}`
},
}