8f2fd89847
Build & Push Docker Image / build (push) Successful in 2m49s
- Add POST /admin/tools/import with format auto-detect, CSV delimiters
(comma/semicolon/tab), per-row validation via CreateToolBody, bulk insert,
audit log entries per imported tool; gated by new 'tool-import' feature
flag (premium/enterprise; admins always pass)
- Add tool-import-dialog UI (format tabs, delimiter select, textarea, file
upload, result/error list) behind hasFeature('tool-import')
- Replace FieldHelp question marks and bare GuideHelp links with a NetBox-style
'Hilfe/Help' outline button (HelpCircle + text) in form headers only
- Sync locales to 482 keys per language (de/en), update handbook docs
(administration import section, index/plaene feature tables), regenerate
API client + zod schemas, add yaml dependency
37 lines
1.4 KiB
TypeScript
37 lines
1.4 KiB
TypeScript
import { type Request, type Response, type NextFunction } from "express";
|
|
|
|
const TIER_FEATURES: Record<string, string[]> = {
|
|
free: ["browse", "rate", "search"],
|
|
premium: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist", "tool-import"],
|
|
enterprise: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist", "tool-import", "sso", "audit-export", "api-access"],
|
|
};
|
|
|
|
export function getEntitlements(tier: string | undefined, role: string | undefined): string[] {
|
|
if (role === "admin") {
|
|
const all = new Set<string>();
|
|
for (const features of Object.values(TIER_FEATURES)) {
|
|
for (const f of features) all.add(f);
|
|
}
|
|
return [...all];
|
|
}
|
|
return TIER_FEATURES[tier ?? "free"] ?? TIER_FEATURES.free;
|
|
}
|
|
|
|
export function hasFeature(tier: string | undefined, feature: string, role?: string): boolean {
|
|
return getEntitlements(tier, role).includes(feature);
|
|
}
|
|
|
|
export function requireFeature(feature: string) {
|
|
return (req: Request, res: Response, next: NextFunction): void => {
|
|
if (!req.session.user) {
|
|
res.status(401).json({ error: "Authentication required" });
|
|
return;
|
|
}
|
|
if (!hasFeature(req.session.user.tier, feature, req.session.user.role)) {
|
|
res.status(403).json({ error: `Feature "${feature}" requires a higher tier` });
|
|
return;
|
|
}
|
|
next();
|
|
};
|
|
}
|