Files
gongxue-base/apps/taro/src/services/routeGuard.ts

145 lines
5.6 KiB
TypeScript

import Taro from '@tarojs/taro';
import { appEnv, ensureRuntimeConfigLoaded, isH5Runtime } from '@/env';
import type { ApiEnvelope, CurrentUser } from '@/types';
import { getTenantContext, resolveTenant } from './api';
import { loadCurrentUser } from './auth';
let pendingGuardPath = '';
function hostFromRuntime() {
if (isH5Runtime() && typeof window !== 'undefined') return window.location.host;
return '';
}
export function safeRedirectPath(path: string) {
if (!path.startsWith('/pages/') || path.startsWith('/pages/student/login/') || path.startsWith('/pages/bootstrap/')) return landingPath();
if (appEnv.portal === 'tenant-admin') return path.startsWith('/pages/tenant-admin/') ? path : landingPath();
if (appEnv.portal === 'platform-admin') return path.startsWith('/pages/platform-admin/') ? path : landingPath();
return path.startsWith('/pages/student/') ? path : landingPath();
}
function loginUrl(redirectPath: string) {
return `/pages/student/login/index?redirect=${encodeURIComponent(safeRedirectPath(redirectPath))}`;
}
function forbiddenUrl(reason: string, redirectPath: string) {
return `/pages/student/login/index?reason=${encodeURIComponent(reason)}&redirect=${encodeURIComponent(safeRedirectPath(redirectPath))}`;
}
function redirectToAuthUrl(url: string) {
if (isH5Runtime() && typeof window !== 'undefined') {
window.location.replace(url);
return;
}
Taro.redirectTo({ url });
}
export function currentPagePath() {
if (isH5Runtime() && typeof window !== 'undefined') {
const pathname = window.location.pathname || '';
if (pathname.startsWith('/pages/')) return pathname;
if (!pathname || pathname === '/' || pathname.endsWith('/index.html')) return landingPath();
}
const instance = Taro.getCurrentInstance();
const path = instance.router?.path || '';
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
return normalizedPath;
}
function isPublicPath(path: string) {
return path === '/pages/bootstrap/index' || path === '/pages/student/login/index';
}
export function isPathAllowedForPortal(path: string) {
if (isPublicPath(path)) return true;
if (!path.startsWith('/pages/')) return true;
if (appEnv.portal === 'tenant-admin') return path.startsWith('/pages/tenant-admin/');
if (appEnv.portal === 'platform-admin') return path.startsWith('/pages/platform-admin/');
return path.startsWith('/pages/student/');
}
export function shouldGuardPath(path: string) {
if (isPublicPath(path)) return false;
if (!isPathAllowedForPortal(path)) return true;
if (appEnv.portal === 'tenant-admin') return path.startsWith('/pages/tenant-admin/');
if (appEnv.portal === 'platform-admin') return path.startsWith('/pages/platform-admin/');
return path.startsWith('/pages/student/') && path !== '/pages/student/region/index';
}
export function landingPath() {
if (appEnv.portal === 'tenant-admin') return '/pages/tenant-admin/workbench/index';
if (appEnv.portal === 'platform-admin') return '/pages/platform-admin/workbench/index';
return '/pages/student/home/index';
}
export async function ensureTenantResolved() {
await ensureRuntimeConfigLoaded();
const current = getTenantContext();
if (current?.tenantId) return current;
return resolveTenant({ host: hostFromRuntime() });
}
export async function requireSignedIn(redirectPath: string): Promise<ApiEnvelope<CurrentUser> | null> {
await ensureTenantResolved();
try {
return await loadCurrentUser();
} catch {
redirectToAuthUrl(loginUrl(redirectPath));
return null;
}
}
export async function requirePlatformAdmin(redirectPath: string) {
const payload = await requireSignedIn(redirectPath);
if (!payload) return null;
const user = payload.user || payload.item;
const roles = user?.roles || [];
if (user?.primaryRole === 'platform_admin' || roles.includes('platform_admin')) return payload;
redirectToAuthUrl(forbiddenUrl('当前账号不是平台管理员', redirectPath));
return null;
}
export async function requireTenantAdmin(redirectPath: string) {
const payload = await requireSignedIn(redirectPath);
if (!payload) return null;
const user = payload.user || payload.item;
const roles = user?.roles || [];
const allowedRoles = new Set(['tenant_owner', 'tenant_admin', 'tenant_operator', 'teacher', 'sales', 'agent']);
if (user?.primaryRole === 'platform_admin') return payload;
if (user?.primaryRole && allowedRoles.has(user.primaryRole)) return payload;
if (roles.some(role => allowedRoles.has(role) || role === 'platform_admin')) return payload;
redirectToAuthUrl(forbiddenUrl('当前账号没有租户后台权限', redirectPath));
return null;
}
export async function guardCurrentRoute() {
await ensureRuntimeConfigLoaded();
const path = currentPagePath();
if (!path || !shouldGuardPath(path)) return true;
if (!isPathAllowedForPortal(path)) {
redirectToAuthUrl(`/pages/bootstrap/index?redirect=${encodeURIComponent(landingPath())}`);
return false;
}
if (pendingGuardPath === path) return false;
pendingGuardPath = path;
try {
if (appEnv.portal === 'platform-admin') {
const payload = await requirePlatformAdmin(path);
return Boolean(payload);
}
if (appEnv.portal === 'tenant-admin') {
const payload = await requireTenantAdmin(path);
return Boolean(payload);
}
const payload = await requireSignedIn(path);
return Boolean(payload);
} finally {
pendingGuardPath = '';
}
}
export function redirectAfterLogin(rawRedirect?: string) {
const redirectPath = rawRedirect ? decodeURIComponent(rawRedirect) : landingPath();
Taro.redirectTo({ url: safeRedirectPath(redirectPath) });
}