feat: add real account workspace controls
This commit is contained in:
@@ -17,7 +17,16 @@ import {
|
||||
sessions,
|
||||
} from "@/modules/core/server/schema";
|
||||
import { getSystemSettings } from "@/modules/core/server/system-settings";
|
||||
import type { Account, AuthContext, Membership, Organization, Permission, PublicAccount, Session } from "@/modules/core/types";
|
||||
import type {
|
||||
Account,
|
||||
AccountOrganizationOption,
|
||||
AuthContext,
|
||||
Membership,
|
||||
Organization,
|
||||
Permission,
|
||||
PublicAccount,
|
||||
Session,
|
||||
} from "@/modules/core/types";
|
||||
|
||||
const SESSION_COOKIE = "teatea_session";
|
||||
const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
|
||||
@@ -80,6 +89,23 @@ function toMembership(row: MembershipRow, role: RoleRow): Membership {
|
||||
};
|
||||
}
|
||||
|
||||
function toOrganizationOption(
|
||||
organization: OrganizationRow,
|
||||
roleLabel: string,
|
||||
activeOrganizationId: string | null,
|
||||
): AccountOrganizationOption {
|
||||
return {
|
||||
id: organization.id,
|
||||
name: organization.name,
|
||||
slug: organization.slug,
|
||||
status: organization.status,
|
||||
registrationEnabled: organization.registrationEnabled,
|
||||
oidcEnabled: organization.oidcEnabled,
|
||||
roleLabel,
|
||||
isActive: organization.id === activeOrganizationId,
|
||||
};
|
||||
}
|
||||
|
||||
export function toPublicAccount(account: AccountRow | Account, roleKey?: string, organization?: Organization): PublicAccount {
|
||||
const fallbackRole = "role" in account ? account.role : "viewer";
|
||||
const fallbackOrganization = "organization" in account ? account.organization : undefined;
|
||||
@@ -505,16 +531,73 @@ export async function getCurrentAuthContext(): Promise<AuthContext | null> {
|
||||
const permissions = [...new Set([...platformPermissions, ...organizationPermissions])];
|
||||
const organization = organizationRow ? toOrganization(organizationRow) : undefined;
|
||||
const membership = membershipRow ? toMembership(membershipRow.membership, membershipRow.role) : undefined;
|
||||
const canAccessAllOrganizations =
|
||||
platformPermissions.includes("platform:manage") || platformPermissions.includes("organization:read");
|
||||
const organizationOptions = canAccessAllOrganizations
|
||||
? (
|
||||
await database
|
||||
.select()
|
||||
.from(organizations)
|
||||
.where(eq(organizations.status, "active"))
|
||||
).map((item) => toOrganizationOption(item, platformRole?.label ?? "平台账号", row.session.activeOrganizationId))
|
||||
: (
|
||||
await database
|
||||
.select({ membership: memberships, organization: organizations, role: roles })
|
||||
.from(memberships)
|
||||
.innerJoin(organizations, eq(organizations.id, memberships.organizationId))
|
||||
.innerJoin(roles, eq(roles.id, memberships.roleId))
|
||||
.where(
|
||||
and(
|
||||
eq(memberships.accountId, row.account.id),
|
||||
eq(memberships.status, "active"),
|
||||
eq(organizations.status, "active"),
|
||||
eq(roles.isEnabled, true),
|
||||
),
|
||||
)
|
||||
).map((item) => toOrganizationOption(item.organization, item.role.label, row.session.activeOrganizationId));
|
||||
|
||||
return {
|
||||
account: toPublicAccount(row.account, platformRole?.key ?? membership?.roleKey, organization),
|
||||
organization,
|
||||
organizations: organizationOptions,
|
||||
membership,
|
||||
permissions,
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
||||
export async function switchActiveOrganization(organizationId: string): Promise<AuthContext> {
|
||||
const context = await getCurrentAuthContext();
|
||||
if (!context) {
|
||||
throw new Error("未登录或会话已过期");
|
||||
}
|
||||
|
||||
const target = context.organizations.find((organization) => organization.id === organizationId);
|
||||
if (!target) {
|
||||
throw new Error("无权切换到该机构");
|
||||
}
|
||||
|
||||
const database = getDatabase();
|
||||
await database.update(sessions).set({ activeOrganizationId: organizationId }).where(eq(sessions.id, context.session.id));
|
||||
|
||||
await recordAuditLog({
|
||||
actor: context.account,
|
||||
organizationId,
|
||||
action: "session.organization.switch",
|
||||
targetType: "organization",
|
||||
targetId: organizationId,
|
||||
result: "success",
|
||||
reason: `切换机构:${target.name}`,
|
||||
});
|
||||
|
||||
const nextContext = await getCurrentAuthContext();
|
||||
if (!nextContext) {
|
||||
throw new Error("机构已切换,请重新登录");
|
||||
}
|
||||
|
||||
return nextContext;
|
||||
}
|
||||
|
||||
export async function removeCurrentSession(): Promise<PublicAccount | null> {
|
||||
const context = await getCurrentAuthContext();
|
||||
const cookieStore = await cookies();
|
||||
|
||||
@@ -162,6 +162,14 @@ export type Account = {
|
||||
|
||||
export type PublicAccount = Omit<Account, "passwordHash" | "passwordSalt">;
|
||||
|
||||
export type AccountOrganizationOption = Pick<
|
||||
Organization,
|
||||
"id" | "name" | "slug" | "status" | "registrationEnabled" | "oidcEnabled"
|
||||
> & {
|
||||
roleLabel: string;
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
export type Session = {
|
||||
id: string;
|
||||
accountId: string;
|
||||
@@ -263,6 +271,7 @@ export type AppData = {
|
||||
export type AuthContext = {
|
||||
account: PublicAccount;
|
||||
organization?: Organization;
|
||||
organizations: AccountOrganizationOption[];
|
||||
membership?: Membership;
|
||||
permissions: Permission[];
|
||||
session: Session;
|
||||
|
||||
Reference in New Issue
Block a user