forked from wangziqi/gongxue-base
145 lines
5.6 KiB
TypeScript
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) });
|
|
}
|