chore: refactor admin plugin schema (#3371)

* admin plugin type isolation

* lint

* import

* import
This commit is contained in:
KinfeMichael Tariku
2025-07-14 22:18:07 -07:00
committed by GitHub
parent dbaa342269
commit be511008f1
5 changed files with 134 additions and 126 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { getTestInstance } from "../../test-utils/test-instance";
import { admin, type UserWithRole } from "./admin";
import { admin } from "./admin";
import { type UserWithRole } from "./types";
import { adminClient } from "./client";
import { createAccessControl } from "../access";
import { createAuthClient } from "../../client";
+9 -124
View File
@@ -5,101 +5,22 @@ import {
createAuthMiddleware,
getSessionFromCtx,
} from "../../api";
import {
type BetterAuthPlugin,
type InferOptionSchema,
type AuthPluginSchema,
type Session,
type User,
type Where,
} from "../../types";
import { type BetterAuthPlugin, type Session, type Where } from "../../types";
import { deleteSessionCookie, setSessionCookie } from "../../cookies";
import { getDate } from "../../utils/date";
import { getEndpointResponse } from "../../utils/plugin-helper";
import { mergeSchema } from "../../db/schema";
import { type AccessControl, type Role } from "../access";
import { type AccessControl } from "../access";
import { ADMIN_ERROR_CODES } from "./error-codes";
import { defaultStatements } from "./access";
import { hasPermission } from "./has-permission";
export interface UserWithRole extends User {
role?: string;
banned?: boolean | null;
banReason?: string | null;
banExpires?: Date | null;
}
export interface SessionWithImpersonatedBy extends Session {
impersonatedBy?: string;
}
export interface AdminOptions {
/**
* The default role for a user
*
* @default "user"
*/
defaultRole?: string;
/**
* Roles that are considered admin roles.
*
* Any user role that isn't in this list, even if they have the permission,
* will not be considered an admin.
*
* @default ["admin"]
*/
adminRoles?: string | string[];
/**
* A default ban reason
*
* By default, no reason is provided
*/
defaultBanReason?: string;
/**
* Number of seconds until the ban expires
*
* By default, the ban never expires
*/
defaultBanExpiresIn?: number;
/**
* Duration of the impersonation session in seconds
*
* By default, the impersonation session lasts 1 hour
*/
impersonationSessionDuration?: number;
/**
* Custom schema for the admin plugin
*/
schema?: InferOptionSchema<typeof schema>;
/**
* Configure the roles and permissions for the admin
* plugin.
*/
ac?: AccessControl;
/**
* Custom permissions for roles.
*/
roles?: {
[key in string]?: Role;
};
/**
* List of user ids that should have admin access
*
* If this is set, the `adminRole` option is ignored
*/
adminUserIds?: string[];
/**
* Message to show when a user is banned
*
* By default, the message is "You have been banned from this application"
*/
bannedUserMessage?: string;
}
export type InferAdminRolesFromOption<O extends AdminOptions | undefined> =
O extends { roles: Record<string, unknown> }
? keyof O["roles"]
: "user" | "admin";
import {
type AdminOptions,
type UserWithRole,
type SessionWithImpersonatedBy,
type InferAdminRolesFromOption,
} from "./types";
import { schema } from "./schema";
function parseRoles(roles: string | string[]): string {
return Array.isArray(roles) ? roles.join(",") : roles;
@@ -1383,39 +1304,3 @@ export const admin = <O extends AdminOptions>(options?: O) => {
options: options as any,
} satisfies BetterAuthPlugin;
};
const schema = {
user: {
fields: {
role: {
type: "string",
required: false,
input: false,
},
banned: {
type: "boolean",
defaultValue: false,
required: false,
input: false,
},
banReason: {
type: "string",
required: false,
input: false,
},
banExpires: {
type: "date",
required: false,
input: false,
},
},
},
session: {
fields: {
impersonatedBy: {
type: "string",
required: false,
},
},
},
} satisfies AuthPluginSchema;
@@ -1,5 +1,5 @@
import { defaultRoles } from "./access";
import type { AdminOptions } from "./admin";
import type { AdminOptions } from "./types";
type PermissionExclusive =
| {
@@ -0,0 +1,39 @@
import { type AuthPluginSchema } from "../../types";
export const schema = {
user: {
fields: {
role: {
type: "string",
required: false,
input: false,
},
banned: {
type: "boolean",
defaultValue: false,
required: false,
input: false,
},
banReason: {
type: "string",
required: false,
input: false,
},
banExpires: {
type: "date",
required: false,
input: false,
},
},
},
session: {
fields: {
impersonatedBy: {
type: "string",
required: false,
},
},
},
} satisfies AuthPluginSchema;
export type AdminSchema = typeof schema;
@@ -0,0 +1,83 @@
import { type Session, type User } from "../../types";
import { type InferOptionSchema } from "../../types";
import { type AccessControl, type Role } from "../access";
import { type AdminSchema } from "./schema";
export interface UserWithRole extends User {
role?: string;
banned?: boolean | null;
banReason?: string | null;
banExpires?: Date | null;
}
export interface SessionWithImpersonatedBy extends Session {
impersonatedBy?: string;
}
export interface AdminOptions {
/**
* The default role for a user
*
* @default "user"
*/
defaultRole?: string;
/**
* Roles that are considered admin roles.
*
* Any user role that isn't in this list, even if they have the permission,
* will not be considered an admin.
*
* @default ["admin"]
*/
adminRoles?: string | string[];
/**
* A default ban reason
*
* By default, no reason is provided
*/
defaultBanReason?: string;
/**
* Number of seconds until the ban expires
*
* By default, the ban never expires
*/
defaultBanExpiresIn?: number;
/**
* Duration of the impersonation session in seconds
*
* By default, the impersonation session lasts 1 hour
*/
impersonationSessionDuration?: number;
/**
* Custom schema for the admin plugin
*/
schema?: InferOptionSchema<AdminSchema>;
/**
* Configure the roles and permissions for the admin
* plugin.
*/
ac?: AccessControl;
/**
* Custom permissions for roles.
*/
roles?: {
[key in string]?: Role;
};
/**
* List of user ids that should have admin access
*
* If this is set, the `adminRole` option is ignored
*/
adminUserIds?: string[];
/**
* Message to show when a user is banned
*
* By default, the message is "You have been banned from this application"
*/
bannedUserMessage?: string;
}
export type InferAdminRolesFromOption<O extends AdminOptions | undefined> =
O extends { roles: Record<string, unknown> }
? keyof O["roles"]
: "user" | "admin";