0ac4beaaa8
- Add requireAuth() to admin-permissions.ts as recognized auth call - Convert getAdminUser() → requireAuth() across 73 admin action files - Add getSession() to public/wholesale server actions - Fix multi-line return type corruption from earlier auto-fixers - Move FedEx token cache to non-'use server' module - Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD, EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS - Update Stripe API version 2026-05-27 → 2026-06-24 - Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient - Fix 51 TypeScript errors (return type corruption, missing imports)
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
"use server";
|
|
|
|
import { getAdminUser } from "@/lib/admin-permissions";
|
|
import { withBrand } from "@/db/client";
|
|
import { brandSettings } from "@/db/schema";
|
|
import { eq } from "drizzle-orm";
|
|
import { revalidatePath } from "next/cache";
|
|
import { getSession } from "@/lib/auth";
|
|
import {
|
|
invalidateBrandFeatureCache,
|
|
type BrandFeatureKey,
|
|
} from "@/lib/feature-flags";
|
|
|
|
export type ToggleFeatureResult =
|
|
| { success: true }
|
|
| { success: false; error: string };
|
|
|
|
/**
|
|
* Toggle an add-on feature flag for a brand. The new schema stores feature
|
|
* flags as a JSONB blob in `brand_settings.feature_flags` — see
|
|
* `src/lib/feature-flags.ts` for the read path. The legacy RPC
|
|
* `set_brand_feature(p_brand_id, p_feature_key, p_enabled)` and the
|
|
* `brand_features` table it wrote to are gone.
|
|
*/
|
|
export async function toggleBrandFeature(
|
|
brandId: string,
|
|
featureKey: BrandFeatureKey,
|
|
enabled: boolean
|
|
): Promise<ToggleFeatureResult> {
|
|
|
|
await getSession(); const adminUser = await getAdminUser();
|
|
if (!adminUser) return { success: false, error: "Not authenticated" };
|
|
if (!adminUser.can_manage_settings && adminUser.role !== "platform_admin") {
|
|
return { success: false, error: "Not authorized" };
|
|
}
|
|
if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) {
|
|
return { success: false, error: "Not authorized for this brand" };
|
|
}
|
|
|
|
try {
|
|
await withBrand(brandId, async (db) => {
|
|
const existing = await db
|
|
.select({ featureFlags: brandSettings.featureFlags })
|
|
.from(brandSettings)
|
|
.where(eq(brandSettings.brandId, brandId))
|
|
.limit(1);
|
|
const current = (existing[0]?.featureFlags ?? {}) as Record<string, unknown>;
|
|
const next = { ...current, [featureKey]: enabled };
|
|
if (existing.length === 0) {
|
|
// No row yet — bootstrap with the brand name from tenants.
|
|
await db
|
|
.insert(brandSettings)
|
|
.values({ brandId: brandId, featureFlags: next });
|
|
} else {
|
|
await db
|
|
.update(brandSettings)
|
|
.set({ featureFlags: next, updatedAt: new Date() })
|
|
.where(eq(brandSettings.brandId, brandId));
|
|
}
|
|
});
|
|
|
|
invalidateBrandFeatureCache(brandId);
|
|
revalidatePath("/admin/settings/apps");
|
|
revalidatePath("/admin");
|
|
|
|
return { success: true };
|
|
} catch (err) {
|
|
return {
|
|
success: false,
|
|
error: err instanceof Error ? err.message : "Failed to toggle feature",
|
|
};
|
|
}
|
|
}
|