39 lines
3.7 KiB
JavaScript
39 lines
3.7 KiB
JavaScript
export const DESKTOP_CLIENT_ID = 'jiansun-detail-desktop'
|
|
export const DESKTOP_SCOPES = ['projects:read', 'projects:write', 'assets:read', 'assets:write', 'models:read']
|
|
export class DesktopClientError extends Error {
|
|
constructor(message, status = 400, code = 'invalid_request') { super(message); this.status = status; this.code = code }
|
|
}
|
|
export function desktopAssert(condition, message, status = 400, code = 'invalid_request') { if (!condition) throw new DesktopClientError(message, status, code) }
|
|
export function desktopId(value) { desktopAssert(typeof value === 'string' && /^[a-zA-Z0-9_-]{1,80}$/.test(value), '项目编号无效'); return value }
|
|
export function desktopRedirect(value) {
|
|
let url; try { url = new URL(value) } catch { throw new DesktopClientError('客户端回调地址无效') }
|
|
desktopAssert(url.protocol === 'http:' && url.hostname === '127.0.0.1' && Number(url.port) >= 1024 && Number(url.port) <= 65535 && url.pathname === '/oauth/callback' && !url.search && !url.hash && !url.username && !url.password, '仅允许当前电脑的客户端回调地址')
|
|
return url.href
|
|
}
|
|
export function desktopAuthorization(input) {
|
|
desktopAssert(input.client_id === DESKTOP_CLIENT_ID && input.response_type === 'code' && input.code_challenge_method === 'S256', '授权请求不符合客户端协议')
|
|
desktopAssert(typeof input.state === 'string' && /^[\w-]{32,128}$/.test(input.state), '授权状态无效')
|
|
desktopAssert(typeof input.code_challenge === 'string' && /^[\w-]{43}$/.test(input.code_challenge), '客户端校验参数无效')
|
|
return { clientId: DESKTOP_CLIENT_ID, redirectUri: desktopRedirect(input.redirect_uri), state: input.state, challenge: input.code_challenge }
|
|
}
|
|
export function desktopPortal(value) {
|
|
const url = new URL(value)
|
|
desktopAssert(!url.username && !url.password && url.pathname === '/' && !url.search && !url.hash, '素材库地址应只包含域名和端口')
|
|
desktopAssert((url.protocol === 'https:' && url.hostname === 'sc.jiansun.vip' && !url.port) || (url.protocol === 'http:' && ['127.0.0.1', 'localhost'].includes(url.hostname) && Number(url.port) >= 1024), '只允许正式素材库或本机开发服务')
|
|
return url.origin
|
|
}
|
|
export function desktopProjectBody(input) {
|
|
desktopAssert(input && typeof input === 'object' && JSON.stringify(input).length <= 1_500_000, '项目内容过大')
|
|
const doc = input.document
|
|
desktopAssert(doc?.version === 1 && typeof doc.name === 'string' && doc.name.length <= 120 && Array.isArray(doc.screens) && doc.screens.length > 0 && doc.screens.length <= 12, '客户端项目格式无效')
|
|
const width = doc.screens[0].width
|
|
desktopAssert([750, 1440].includes(width) && doc.screens.every(s => s.width === width && Number.isInteger(s.height) && s.height >= 400 && s.height <= 4608 && Array.isArray(s.layers) && s.layers.length <= 100) && doc.screens.reduce((sum,s) => sum + s.height,0) <= 30000, '项目画布超出限制')
|
|
desktopAssert(['draft', 'active', 'completed'].includes(input.status) && Number.isInteger(input.revision) && input.revision >= 0, '项目状态或版本无效')
|
|
const conversation = input.conversation || []
|
|
desktopAssert(Array.isArray(conversation) && conversation.length <= 200 && conversation.every(m => typeof m.id === 'string' && ['user','assistant'].includes(m.role) && typeof m.text === 'string' && m.text.length <= 16000), '对话记录无效')
|
|
desktopAssert(conversation.reduce((sum,m) => sum + m.text.length,0) <= 180000, '对话记录过大,请新开对话')
|
|
const composer=input.composer||''
|
|
desktopAssert(typeof composer==='string'&&composer.length<=6000,'对话草稿过长')
|
|
return { document: doc, conversation, composer, status: input.status, revision: input.revision, sourceWebId: input.sourceWebId ? desktopId(input.sourceWebId) : null }
|
|
}
|