mirror of
https://github.com/stack-auth/stack.git
synced 2026-07-20 21:29:36 +08:00
fix(agent-auth): make agent-auth independently enableable via shared resolver
The dashboard treated agent-auth as a sub-app whose enablement inherited from the authentication parent, while the backend and both enable paths keyed off agent-auth's own flag. The dashboard page therefore stayed 'App is not enabled' after enabling. Add a shared isAppEnabledInInstalledApps resolver (used by backend + dashboard) and mark agent-auth independentlyEnableable so it is governed by its own flag; other sub-apps (e.g. fraud-protection) keep parent-inheritance. Co-Authored-By: madison@stack-auth.com <madison.w.kennedy@gmail.com>
This commit is contained in:
parent
e26f326d8c
commit
2f58d3ecd2
@ -1,6 +1,7 @@
|
||||
import { getSoleTenancyFromProjectBranch } from "@/lib/tenancies";
|
||||
import { AGENT_AUTH_APP_ID } from "./constants";
|
||||
import { StatusError } from "@hexclave/shared/dist/utils/errors";
|
||||
import { isAppEnabledInInstalledApps } from "@hexclave/shared/dist/apps/apps-config";
|
||||
import type { SmartRequest } from "@/route-handlers/smart-request";
|
||||
|
||||
export function getHeader(req: SmartRequest, name: string): string | null {
|
||||
@ -26,7 +27,7 @@ export async function getAgentAuthTenancy(projectId: string) {
|
||||
if (!tenancy) {
|
||||
throw new StatusError(StatusError.NotFound, "Agent auth project not found");
|
||||
}
|
||||
if (!tenancy.config.apps.installed[AGENT_AUTH_APP_ID]?.enabled) {
|
||||
if (!isAppEnabledInInstalledApps(tenancy.config.apps.installed, AGENT_AUTH_APP_ID)) {
|
||||
throw new StatusError(StatusError.NotFound, "Not found");
|
||||
}
|
||||
return tenancy;
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { ALL_APPS_FRONTEND, hasNavigationItems } from "@/lib/apps-frontend";
|
||||
import { ALL_APPS, getParentAppId, type AppId } from "@hexclave/shared/dist/apps/apps-config";
|
||||
|
||||
type InstalledAppConfig = {
|
||||
enabled?: boolean,
|
||||
} | undefined;
|
||||
|
||||
export type InstalledAppsMap = Record<string, InstalledAppConfig>;
|
||||
import { ALL_APPS, isAppEnabledInInstalledApps, type AppId, type InstalledAppsMap } from "@hexclave/shared/dist/apps/apps-config";
|
||||
|
||||
/**
|
||||
* Get all available app IDs, filtering out alpha apps in production
|
||||
@ -25,15 +19,10 @@ export function getAllAvailableAppIds(): AppId[] {
|
||||
|
||||
/**
|
||||
* Determines whether an app is enabled.
|
||||
* - Regular apps are enabled via their own config entry.
|
||||
* - Sub-apps are enabled when their parent app is enabled.
|
||||
* Uses the shared app registry enablement rules.
|
||||
*/
|
||||
export function isAppEnabled(installedApps: InstalledAppsMap, appId: AppId): boolean {
|
||||
const parentAppId = getParentAppId(appId);
|
||||
if (parentAppId != null) {
|
||||
return installedApps[parentAppId]?.enabled ?? false;
|
||||
}
|
||||
return installedApps[appId]?.enabled ?? false;
|
||||
return isAppEnabledInInstalledApps(installedApps, appId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,4 +50,3 @@ export function getUninstalledAppIds(installedApps: AppId[]): AppId[] {
|
||||
const installedSet = new Set(installedApps);
|
||||
return getAllAvailableAppIds().filter(appId => !installedSet.has(appId));
|
||||
}
|
||||
|
||||
|
||||
28
packages/shared/src/apps/apps-config.test.ts
Normal file
28
packages/shared/src/apps/apps-config.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { type InstalledAppsMap, isAppEnabledInInstalledApps } from "./apps-config";
|
||||
|
||||
describe("isAppEnabledInInstalledApps", () => {
|
||||
test("independently-enableable apps use their own enabled flag", () => {
|
||||
const installedApps: InstalledAppsMap = {
|
||||
authentication: { enabled: true },
|
||||
"agent-auth": { enabled: false },
|
||||
};
|
||||
|
||||
expect(isAppEnabledInInstalledApps(installedApps, "agent-auth")).toBe(false);
|
||||
|
||||
installedApps["agent-auth"] = { enabled: true };
|
||||
expect(isAppEnabledInInstalledApps(installedApps, "agent-auth")).toBe(true);
|
||||
});
|
||||
|
||||
test("normal sub-apps still inherit from their parent", () => {
|
||||
const installedApps: InstalledAppsMap = {
|
||||
analytics: { enabled: true },
|
||||
"session-replays": { enabled: false },
|
||||
};
|
||||
|
||||
expect(isAppEnabledInInstalledApps(installedApps, "session-replays")).toBe(true);
|
||||
|
||||
installedApps.analytics = { enabled: false };
|
||||
expect(isAppEnabledInInstalledApps(installedApps, "session-replays")).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -46,10 +46,17 @@ type App = {
|
||||
tags: (keyof typeof ALL_APP_TAGS)[],
|
||||
stage: "alpha" | "beta" | "stable",
|
||||
parentAppId?: ParentAppId,
|
||||
independentlyEnableable?: boolean,
|
||||
};
|
||||
|
||||
export type AppId = keyof typeof ALL_APPS;
|
||||
|
||||
export type InstalledAppConfig = {
|
||||
enabled?: boolean,
|
||||
} | undefined;
|
||||
|
||||
export type InstalledAppsMap = Record<string, InstalledAppConfig>;
|
||||
|
||||
export const ALL_APPS = {
|
||||
"authentication": {
|
||||
displayName: "Authentication",
|
||||
@ -63,6 +70,7 @@ export const ALL_APPS = {
|
||||
tags: ["auth", "security", "developers"],
|
||||
stage: "alpha",
|
||||
parentAppId: "authentication",
|
||||
independentlyEnableable: true,
|
||||
},
|
||||
"fraud-protection": {
|
||||
displayName: "Fraud Protection",
|
||||
@ -199,3 +207,17 @@ export function getParentAppId(appId: AppId): AppId | null {
|
||||
const app = ALL_APPS[appId];
|
||||
return "parentAppId" in app ? app.parentAppId : null;
|
||||
}
|
||||
|
||||
export function isAppEnabledInInstalledApps(installedApps: InstalledAppsMap, appId: AppId): boolean {
|
||||
const app = ALL_APPS[appId];
|
||||
if ("independentlyEnableable" in app) {
|
||||
return installedApps[appId]?.enabled === true;
|
||||
}
|
||||
|
||||
const parentAppId = getParentAppId(appId);
|
||||
if (parentAppId != null) {
|
||||
return isAppEnabledInInstalledApps(installedApps, parentAppId);
|
||||
}
|
||||
|
||||
return installedApps[appId]?.enabled === true;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user