feat: fail closed before domain runtime entry
This commit is contained in:
parent
a35d61e227
commit
0990e96c0e
6 changed files with 160 additions and 8 deletions
|
|
@ -40,6 +40,12 @@ interface ServerProfile {
|
|||
channelSubtitle?: string;
|
||||
}
|
||||
|
||||
interface DomainAccessStatus {
|
||||
blockers: string[];
|
||||
runtimeReady: boolean;
|
||||
stage: 'checking' | 'login-required' | 'identity-verified' | 'runtime-ready';
|
||||
}
|
||||
|
||||
function findFirstDocument(nodes: DocTreeNode[]): string | null {
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'document') return node.path;
|
||||
|
|
@ -90,6 +96,7 @@ export default function App() {
|
|||
const [moduleMessage, setModuleMessage] = useState('');
|
||||
const [lastChannelReceipt, setLastChannelReceipt] = useState('');
|
||||
const [worldEntered, setWorldEntered] = useState(false);
|
||||
const [domainAccess, setDomainAccess] = useState<DomainAccessStatus>({ blockers: [], runtimeReady: false, stage: 'checking' });
|
||||
|
||||
const storageMode = repositoryStatus?.remote ? 'server' : 'local';
|
||||
const storageLabel = storageMode === 'server' ? '服务器已托管' : '仅本机';
|
||||
|
|
@ -165,6 +172,19 @@ export default function App() {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const refreshDomainAccess = useCallback(async () => {
|
||||
const server = (window as any).hololake?.server;
|
||||
if (!server?.domainAccess) {
|
||||
setDomainAccess({ blockers: ['desktop_runtime_required'], runtimeReady: false, stage: 'login-required' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setDomainAccess(await server.domainAccess('DOM-FIFTH-0001'));
|
||||
} catch {
|
||||
setDomainAccess({ blockers: ['domain_access_probe_failed'], runtimeReady: false, stage: 'login-required' });
|
||||
}
|
||||
}, []);
|
||||
|
||||
const refreshChannel = useCallback(async () => {
|
||||
try {
|
||||
const [channel, registry] = await Promise.all([api.getChannel(), api.getModules()]);
|
||||
|
|
@ -179,8 +199,9 @@ export default function App() {
|
|||
refreshTree();
|
||||
refreshRepositoryStatus();
|
||||
refreshServerSession();
|
||||
refreshDomainAccess();
|
||||
refreshChannel();
|
||||
}, [refreshTree, refreshRepositoryStatus, refreshServerSession, refreshChannel]);
|
||||
}, [refreshTree, refreshRepositoryStatus, refreshServerSession, refreshDomainAccess, refreshChannel]);
|
||||
|
||||
const changeModuleState = useCallback(async (moduleId: string, installed: boolean, mounted: boolean) => {
|
||||
setModuleBusy(true);
|
||||
|
|
@ -358,7 +379,13 @@ export default function App() {
|
|||
<>
|
||||
{!worldEntered && (
|
||||
<WorldEntry
|
||||
onEnterFifth={() => {
|
||||
access={domainAccess}
|
||||
onEnterFifthRuntime={() => {
|
||||
setActiveRoute('fifth');
|
||||
setActiveModule('knowledge');
|
||||
setWorldEntered(true);
|
||||
}}
|
||||
onEnterLocalWorkspace={() => {
|
||||
setActiveRoute('fifth');
|
||||
setActiveModule('knowledge');
|
||||
setWorldEntered(true);
|
||||
|
|
@ -547,6 +574,7 @@ export default function App() {
|
|||
onApplied={() => {
|
||||
refreshRepositoryStatus();
|
||||
refreshServerSession();
|
||||
refreshDomainAccess();
|
||||
}}
|
||||
/>
|
||||
<HumanSettings
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { useState } from 'react';
|
||||
import { BookOpen, Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
|
||||
import { Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
|
||||
|
||||
type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
|
||||
|
||||
interface Props {
|
||||
onEnterFifth: () => void;
|
||||
access: { blockers: string[]; runtimeReady: boolean; stage: 'checking' | 'login-required' | 'identity-verified' | 'runtime-ready' };
|
||||
onEnterFifthRuntime: () => void;
|
||||
onEnterLocalWorkspace: () => void;
|
||||
onOpenConnection: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -16,7 +18,7 @@ const domains: Array<{ id: DomainId; number: string; name: string; summary: stri
|
|||
{ id: 'fifth', number: '05', name: '第五域 · 光湖本源域', summary: '零点原核的工程本体 · 语言架构层' },
|
||||
];
|
||||
|
||||
export function WorldEntry({ onEnterFifth, onOpenConnection }: Props) {
|
||||
export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace, onOpenConnection }: Props) {
|
||||
const [stage, setStage] = useState<'lighthouse' | 'directory' | 'fifth'>('lighthouse');
|
||||
const [selectedDomain, setSelectedDomain] = useState<DomainId | null>(null);
|
||||
|
||||
|
|
@ -58,11 +60,11 @@ export function WorldEntry({ onEnterFifth, onOpenConnection }: Props) {
|
|||
<div><dt><ShieldCheck aria-hidden="true" /><span>稳定编号</span></dt><dd>DOM-FIFTH-0001</dd></div>
|
||||
<div><dt><Crosshair aria-hidden="true" /><span>定位</span></dt><dd>零点原核的工程本体 · 语言架构层</dd></div>
|
||||
<div><dt><Link2 aria-hidden="true" /><span>接入方式</span></dt><dd>本地终端节点 / 云端常驻节点</dd></div>
|
||||
<div><dt><DoorOpen aria-hidden="true" /><span>状态</span></dt><dd>公开门厅可查看 · 进入后装载独立域运行体</dd></div>
|
||||
<div><dt><DoorOpen aria-hidden="true" /><span>状态</span></dt><dd>{access.stage === 'runtime-ready' ? '节点、域清单、会话能力与连接回执已验证' : access.stage === 'identity-verified' ? '账户与节点已验证 · 尚缺域清单、会话能力和连接回执' : access.stage === 'checking' ? '正在核对真实接入证据' : '公开门厅可查看 · 尚未登录或接入节点'}</dd></div>
|
||||
</dl>
|
||||
<div className="fifth-actions">
|
||||
<button className="fifth-primary" type="button" onClick={onEnterFifth}><LogIn aria-hidden="true" />进入第五域</button>
|
||||
<button type="button" onClick={() => setStage('directory')}><BookOpen aria-hidden="true" />查看公开介绍</button>
|
||||
<button className="fifth-primary" type="button" onClick={access.runtimeReady ? onEnterFifthRuntime : onOpenConnection}><LogIn aria-hidden="true" />{access.runtimeReady ? '进入第五域运行体' : '登录或接入节点'}</button>
|
||||
<button type="button" onClick={onEnterLocalWorkspace}><DoorOpen aria-hidden="true" />进入仅本机工作空间</button>
|
||||
<button type="button" onClick={() => setStage('lighthouse')}><Landmark aria-hidden="true" />返回灯塔</button>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Reference in a new issue