feat: implement permissions management with new Permission interface and refactor API key handlers

This commit is contained in:
sai80082
2026-04-24 15:48:34 +05:30
parent 28cd757366
commit c32e38bac7
7 changed files with 38 additions and 46 deletions
+26
View File
@@ -0,0 +1,26 @@
export interface Permission {
name: string;
description: string;
}
export const IMPORT_PERMISSIONS: Permission[] = [
{ name: "asset.read", description: "Check for existing assets" },
{ name: "asset.upload", description: "Upload new assets" },
{ name: "album.read", description: "Read album data" },
{ name: "album.create", description: "Create new albums" },
{ name: "album.update", description: "Add assets to albums" },
{ name: "tag.create", description: "Tag imported assets" },
{ name: "tag.asset", description: "Assign tags to assets" },
];
export const WORKFLOW_PERMISSIONS: Permission[] = [
{ name: "asset.read", description: "Query and filter assets" },
{ name: "asset.update", description: "Favorite, archive, update metadata" },
{ name: "album.read", description: "Read album data" },
{ name: "album.create", description: "Create new albums" },
{ name: "album.update", description: "Add/remove assets from albums" },
{ name: "tag.create", description: "Create and assign tags" },
];
export const getPermissionNames = (permissions: Permission[]): string[] =>
permissions.map((p) => p.name);
@@ -5,17 +5,10 @@ import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
import { getUserHeaders } from "@/helpers/user.helper";
import { and, eq } from "drizzle-orm";
import { NextApiRequest, NextApiResponse } from "next";
import { IMPORT_PERMISSIONS, getPermissionNames } from "@/config/permissions";
export const IMPORT_API_KEY_SETTING = "import_api_key";
const IMPORT_KEY_NAME = "Power Tools Import Key";
const IMPORT_PERMISSIONS = [
"asset.read",
"asset.upload",
"album.read",
"album.create",
"album.update",
"tag.create",
];
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
@@ -26,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const immichRes = await fetch(`${ENV.IMMICH_URL}/api/api-keys`, {
method: "POST",
headers: getUserHeaders(currentUser, { "Content-Type": "application/json" }),
body: JSON.stringify({ name: IMPORT_KEY_NAME, permissions: IMPORT_PERMISSIONS }),
body: JSON.stringify({ name: IMPORT_KEY_NAME, permissions: getPermissionNames(IMPORT_PERMISSIONS) }),
});
if (!immichRes.ok) {
@@ -5,17 +5,10 @@ import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
import { getUserHeaders } from "@/helpers/user.helper";
import { and, eq } from "drizzle-orm";
import { NextApiRequest, NextApiResponse } from "next";
import { WORKFLOW_PERMISSIONS, getPermissionNames } from "@/config/permissions";
const WORKFLOW_API_KEY_SETTING = "workflow_api_key";
const WORKFLOW_KEY_NAME = "Power Tools Workflow Key";
const WORKFLOW_PERMISSIONS = [
"asset.read",
"asset.update",
"album.read",
"album.create",
"album.update",
"tag.create",
];
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
@@ -27,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const immichRes = await fetch(`${ENV.IMMICH_URL}/api/api-keys`, {
method: "POST",
headers: getUserHeaders(currentUser, { "Content-Type": "application/json" }),
body: JSON.stringify({ name: WORKFLOW_KEY_NAME, permissions: WORKFLOW_PERMISSIONS }),
body: JSON.stringify({ name: WORKFLOW_KEY_NAME, permissions: getPermissionNames(WORKFLOW_PERMISSIONS) }),
});
if (!immichRes.ok) {
+2 -9
View File
@@ -1,14 +1,7 @@
import { ENV } from "@/config/environment";
import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
import { NextApiRequest, NextApiResponse } from "next";
const WORKFLOW_PERMISSIONS = [
"asset.read",
"asset.update",
"album.create",
"album.update",
"tag.create",
];
import { WORKFLOW_PERMISSIONS, getPermissionNames } from "@/config/permissions";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
@@ -19,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { apiKey, requiredPermissions } = req.body;
if (!apiKey) return res.status(400).json({ message: "apiKey is required" });
const REQUIRED_PERMISSIONS: string[] = Array.isArray(requiredPermissions) ? requiredPermissions : WORKFLOW_PERMISSIONS;
const REQUIRED_PERMISSIONS: string[] = Array.isArray(requiredPermissions) ? requiredPermissions : getPermissionNames(WORKFLOW_PERMISSIONS);
try {
const keyRes = await fetch(`${ENV.IMMICH_URL}/api/api-keys/me`, {
+1 -8
View File
@@ -22,14 +22,7 @@ import PageLayout from "@/components/layouts/PageLayout";
import Header from "@/components/shared/Header";
import ApiKeyGate from "@/components/shared/ApiKeyGate";
const IMPORT_PERMISSIONS = [
{ name: "asset.read", description: "Check for existing assets" },
{ name: "asset.upload", description: "Upload new assets" },
{ name: "album.read", description: "Read album data" },
{ name: "album.create", description: "Create new albums" },
{ name: "album.update", description: "Add assets to albums" },
{ name: "tag.create", description: "Tag imported assets" },
];
import { IMPORT_PERMISSIONS } from "@/config/permissions";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
+4 -3
View File
@@ -12,6 +12,7 @@ import toast from "react-hot-toast";
import API from "@/lib/api";
import { useConfig } from "@/contexts/ConfigContext";
import Link from "next/link";
import { WORKFLOW_PERMISSIONS } from "@/config/permissions";
function ConnectionStatus({ label, url, icon: Icon }: { label: string; url: string; icon: any }) {
return (
@@ -74,7 +75,7 @@ export default function SettingsPage() {
}
};
const IMPORT_REQUIRED_PERMISSIONS = ["asset.read", "asset.upload", "album.read", "album.create", "album.update", "tag.create"];
const IMPORT_REQUIRED_PERMISSIONS = ["asset.read", "asset.upload", "album.read", "album.create", "album.update", "tag.create", "tag.asset"];
const saveImportApiKey = async () => {
if (!importApiKey.trim()) {
@@ -269,8 +270,8 @@ export default function SettingsPage() {
)}
{!validationResult && (
<div className="flex flex-wrap gap-1">
{["asset.read", "asset.update", "album.create", "album.update", "tag.create"].map((p) => (
<Badge key={p} variant="outline" className="text-[10px] h-5 font-mono font-normal">{p}</Badge>
{WORKFLOW_PERMISSIONS.map((p) => (
<Badge key={p.name} variant="outline" className="text-[10px] h-5 font-mono font-normal">{p.name}</Badge>
))}
</div>
)}
+1 -8
View File
@@ -39,14 +39,7 @@ import Loader from "@/components/ui/loader";
import API from "@/lib/api";
import ApiKeyGate from "@/components/shared/ApiKeyGate";
const WORKFLOW_PERMISSIONS = [
{ name: "asset.read", description: "Query and filter assets" },
{ name: "asset.update", description: "Favorite, archive, update metadata" },
{ name: "album.read", description: "Read album data" },
{ name: "album.create", description: "Create new albums" },
{ name: "album.update", description: "Add/remove assets from albums" },
{ name: "tag.create", description: "Create and assign tags" },
];
import { WORKFLOW_PERMISSIONS } from "@/config/permissions";
function generateId() {
return Math.random().toString(36).substring(2, 15);