feat(permissions): add yacht, company, membership, reservation keys
This commit is contained in:
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { pgTable, text, boolean, timestamp, jsonb, index, uniqueIndex } from 'drizzle-orm/pg-core';
|
||||||
pgTable,
|
|
||||||
text,
|
|
||||||
boolean,
|
|
||||||
timestamp,
|
|
||||||
jsonb,
|
|
||||||
index,
|
|
||||||
uniqueIndex,
|
|
||||||
} from 'drizzle-orm/pg-core';
|
|
||||||
import { ports } from './ports';
|
import { ports } from './ports';
|
||||||
|
|
||||||
// ─── Permission Types ─────────────────────────────────────────────────────────
|
// ─── Permission Types ─────────────────────────────────────────────────────────
|
||||||
@@ -92,6 +84,29 @@ export type RolePermissions = {
|
|||||||
generate: boolean;
|
generate: boolean;
|
||||||
manage: boolean;
|
manage: boolean;
|
||||||
};
|
};
|
||||||
|
yachts: {
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
edit: boolean;
|
||||||
|
delete: boolean;
|
||||||
|
transfer: boolean;
|
||||||
|
};
|
||||||
|
companies: {
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
edit: boolean;
|
||||||
|
delete: boolean;
|
||||||
|
};
|
||||||
|
memberships: {
|
||||||
|
view: boolean;
|
||||||
|
manage: boolean;
|
||||||
|
};
|
||||||
|
reservations: {
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
activate: boolean;
|
||||||
|
cancel: boolean;
|
||||||
|
};
|
||||||
admin: {
|
admin: {
|
||||||
manage_users: boolean;
|
manage_users: boolean;
|
||||||
view_audit_log: boolean;
|
view_audit_log: boolean;
|
||||||
@@ -132,7 +147,9 @@ export const account = pgTable('account', {
|
|||||||
id: text('id').primaryKey(),
|
id: text('id').primaryKey(),
|
||||||
accountId: text('account_id').notNull(),
|
accountId: text('account_id').notNull(),
|
||||||
providerId: text('provider_id').notNull(),
|
providerId: text('provider_id').notNull(),
|
||||||
userId: text('user_id').notNull().references(() => user.id),
|
userId: text('user_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => user.id),
|
||||||
accessToken: text('access_token'),
|
accessToken: text('access_token'),
|
||||||
refreshToken: text('refresh_token'),
|
refreshToken: text('refresh_token'),
|
||||||
idToken: text('id_token'),
|
idToken: text('id_token'),
|
||||||
@@ -163,7 +180,9 @@ export const verification = pgTable('verification', {
|
|||||||
export const userProfiles = pgTable(
|
export const userProfiles = pgTable(
|
||||||
'user_profiles',
|
'user_profiles',
|
||||||
{
|
{
|
||||||
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
userId: text('user_id').notNull().unique(), // references Better Auth user ID
|
userId: text('user_id').notNull().unique(), // references Better Auth user ID
|
||||||
displayName: text('display_name').notNull(),
|
displayName: text('display_name').notNull(),
|
||||||
avatarUrl: text('avatar_url'),
|
avatarUrl: text('avatar_url'),
|
||||||
@@ -179,10 +198,15 @@ export const userProfiles = pgTable(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const roles = pgTable('roles', {
|
export const roles = pgTable('roles', {
|
||||||
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
name: text('name').notNull(),
|
name: text('name').notNull(),
|
||||||
description: text('description'),
|
description: text('description'),
|
||||||
permissions: jsonb('permissions').$type<RolePermissions>().notNull().default({} as RolePermissions),
|
permissions: jsonb('permissions')
|
||||||
|
.$type<RolePermissions>()
|
||||||
|
.notNull()
|
||||||
|
.default({} as RolePermissions),
|
||||||
isGlobal: boolean('is_global').notNull().default(true),
|
isGlobal: boolean('is_global').notNull().default(true),
|
||||||
isSystem: boolean('is_system').notNull().default(false),
|
isSystem: boolean('is_system').notNull().default(false),
|
||||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
@@ -192,7 +216,9 @@ export const roles = pgTable('roles', {
|
|||||||
export const portRoleOverrides = pgTable(
|
export const portRoleOverrides = pgTable(
|
||||||
'port_role_overrides',
|
'port_role_overrides',
|
||||||
{
|
{
|
||||||
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
portId: text('port_id')
|
portId: text('port_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => ports.id, { onDelete: 'cascade' }),
|
.references(() => ports.id, { onDelete: 'cascade' }),
|
||||||
@@ -215,7 +241,9 @@ export const portRoleOverrides = pgTable(
|
|||||||
export const userPortRoles = pgTable(
|
export const userPortRoles = pgTable(
|
||||||
'user_port_roles',
|
'user_port_roles',
|
||||||
{
|
{
|
||||||
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
userId: text('user_id').notNull(), // references Better Auth user ID
|
userId: text('user_id').notNull(), // references Better Auth user ID
|
||||||
portId: text('port_id')
|
portId: text('port_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
|
|||||||
@@ -19,82 +19,332 @@ import type { RolePermissions } from './schema/users';
|
|||||||
|
|
||||||
const ALL_PERMISSIONS: RolePermissions = {
|
const ALL_PERMISSIONS: RolePermissions = {
|
||||||
clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true },
|
clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true },
|
||||||
interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true },
|
interests: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
change_stage: true,
|
||||||
|
generate_eoi: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
berths: { view: true, edit: true, import: true, manage_waiting_list: true },
|
berths: { view: true, edit: true, import: true, manage_waiting_list: true },
|
||||||
documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true },
|
documents: {
|
||||||
expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true },
|
view: true,
|
||||||
invoices: { view: true, create: true, edit: true, delete: true, send: true, record_payment: true, export: true },
|
create: true,
|
||||||
|
send_for_signing: true,
|
||||||
|
upload_signed: true,
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
export: true,
|
||||||
|
scan_receipt: true,
|
||||||
|
},
|
||||||
|
invoices: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
send: true,
|
||||||
|
record_payment: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
files: { view: true, upload: true, delete: true, manage_folders: true },
|
files: { view: true, upload: true, delete: true, manage_folders: true },
|
||||||
email: { view: true, send: true, configure_account: true },
|
email: { view: true, send: true, configure_account: true },
|
||||||
reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true },
|
reminders: {
|
||||||
|
view_own: true,
|
||||||
|
view_all: true,
|
||||||
|
create: true,
|
||||||
|
edit_own: true,
|
||||||
|
edit_all: true,
|
||||||
|
assign_others: true,
|
||||||
|
},
|
||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: true },
|
document_templates: { view: true, generate: true, manage: true },
|
||||||
admin: { manage_users: true, view_audit_log: true, manage_settings: true, manage_webhooks: true, manage_reports: true, manage_custom_fields: true, manage_forms: true, manage_tags: true, system_backup: true },
|
yachts: { view: true, create: true, edit: true, delete: true, transfer: true },
|
||||||
|
companies: { view: true, create: true, edit: true, delete: true },
|
||||||
|
memberships: { view: true, manage: true },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: true },
|
||||||
|
admin: {
|
||||||
|
manage_users: true,
|
||||||
|
view_audit_log: true,
|
||||||
|
manage_settings: true,
|
||||||
|
manage_webhooks: true,
|
||||||
|
manage_reports: true,
|
||||||
|
manage_custom_fields: true,
|
||||||
|
manage_forms: true,
|
||||||
|
manage_tags: true,
|
||||||
|
system_backup: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const DIRECTOR_PERMISSIONS: RolePermissions = {
|
const DIRECTOR_PERMISSIONS: RolePermissions = {
|
||||||
clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true },
|
clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true },
|
||||||
interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true },
|
interests: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
change_stage: true,
|
||||||
|
generate_eoi: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
berths: { view: true, edit: true, import: true, manage_waiting_list: true },
|
berths: { view: true, edit: true, import: true, manage_waiting_list: true },
|
||||||
documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true },
|
documents: {
|
||||||
expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true },
|
view: true,
|
||||||
invoices: { view: true, create: true, edit: true, delete: true, send: true, record_payment: true, export: true },
|
create: true,
|
||||||
|
send_for_signing: true,
|
||||||
|
upload_signed: true,
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
export: true,
|
||||||
|
scan_receipt: true,
|
||||||
|
},
|
||||||
|
invoices: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: true,
|
||||||
|
send: true,
|
||||||
|
record_payment: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
files: { view: true, upload: true, delete: true, manage_folders: true },
|
files: { view: true, upload: true, delete: true, manage_folders: true },
|
||||||
email: { view: true, send: true, configure_account: true },
|
email: { view: true, send: true, configure_account: true },
|
||||||
reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true },
|
reminders: {
|
||||||
|
view_own: true,
|
||||||
|
view_all: true,
|
||||||
|
create: true,
|
||||||
|
edit_own: true,
|
||||||
|
edit_all: true,
|
||||||
|
assign_others: true,
|
||||||
|
},
|
||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: true },
|
document_templates: { view: true, generate: true, manage: true },
|
||||||
admin: { manage_users: true, view_audit_log: true, manage_settings: true, manage_webhooks: true, manage_reports: true, manage_custom_fields: true, manage_forms: true, manage_tags: true, system_backup: false },
|
yachts: { view: true, create: true, edit: true, delete: true, transfer: true },
|
||||||
|
companies: { view: true, create: true, edit: true, delete: true },
|
||||||
|
memberships: { view: true, manage: true },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: true },
|
||||||
|
admin: {
|
||||||
|
manage_users: true,
|
||||||
|
view_audit_log: true,
|
||||||
|
manage_settings: true,
|
||||||
|
manage_webhooks: true,
|
||||||
|
manage_reports: true,
|
||||||
|
manage_custom_fields: true,
|
||||||
|
manage_forms: true,
|
||||||
|
manage_tags: true,
|
||||||
|
system_backup: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SALES_MANAGER_PERMISSIONS: RolePermissions = {
|
const SALES_MANAGER_PERMISSIONS: RolePermissions = {
|
||||||
clients: { view: true, create: true, edit: true, delete: false, merge: true, export: true },
|
clients: { view: true, create: true, edit: true, delete: false, merge: true, export: true },
|
||||||
interests: { view: true, create: true, edit: true, delete: false, change_stage: true, generate_eoi: true, export: true },
|
interests: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
change_stage: true,
|
||||||
|
generate_eoi: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
berths: { view: true, edit: false, import: false, manage_waiting_list: true },
|
berths: { view: true, edit: false, import: false, manage_waiting_list: true },
|
||||||
documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: false },
|
documents: {
|
||||||
expenses: { view: true, create: true, edit: true, delete: false, export: true, scan_receipt: true },
|
view: true,
|
||||||
invoices: { view: true, create: true, edit: true, delete: false, send: true, record_payment: true, export: true },
|
create: true,
|
||||||
|
send_for_signing: true,
|
||||||
|
upload_signed: true,
|
||||||
|
delete: false,
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
export: true,
|
||||||
|
scan_receipt: true,
|
||||||
|
},
|
||||||
|
invoices: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
send: true,
|
||||||
|
record_payment: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
files: { view: true, upload: true, delete: false, manage_folders: true },
|
files: { view: true, upload: true, delete: false, manage_folders: true },
|
||||||
email: { view: true, send: true, configure_account: true },
|
email: { view: true, send: true, configure_account: true },
|
||||||
reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true },
|
reminders: {
|
||||||
|
view_own: true,
|
||||||
|
view_all: true,
|
||||||
|
create: true,
|
||||||
|
edit_own: true,
|
||||||
|
edit_all: true,
|
||||||
|
assign_others: true,
|
||||||
|
},
|
||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: false },
|
document_templates: { view: true, generate: true, manage: false },
|
||||||
admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: true, system_backup: false },
|
yachts: { view: true, create: true, edit: true, delete: false, transfer: true },
|
||||||
|
companies: { view: true, create: true, edit: true, delete: false },
|
||||||
|
memberships: { view: true, manage: true },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: true },
|
||||||
|
admin: {
|
||||||
|
manage_users: false,
|
||||||
|
view_audit_log: false,
|
||||||
|
manage_settings: false,
|
||||||
|
manage_webhooks: false,
|
||||||
|
manage_reports: false,
|
||||||
|
manage_custom_fields: false,
|
||||||
|
manage_forms: false,
|
||||||
|
manage_tags: true,
|
||||||
|
system_backup: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SALES_AGENT_PERMISSIONS: RolePermissions = {
|
const SALES_AGENT_PERMISSIONS: RolePermissions = {
|
||||||
clients: { view: true, create: true, edit: true, delete: false, merge: false, export: true },
|
clients: { view: true, create: true, edit: true, delete: false, merge: false, export: true },
|
||||||
interests: { view: true, create: true, edit: true, delete: false, change_stage: true, generate_eoi: true, export: true },
|
interests: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
change_stage: true,
|
||||||
|
generate_eoi: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
berths: { view: true, edit: false, import: false, manage_waiting_list: true },
|
berths: { view: true, edit: false, import: false, manage_waiting_list: true },
|
||||||
documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: false },
|
documents: {
|
||||||
expenses: { view: true, create: true, edit: true, delete: false, export: true, scan_receipt: true },
|
view: true,
|
||||||
invoices: { view: true, create: true, edit: true, delete: false, send: true, record_payment: true, export: true },
|
create: true,
|
||||||
|
send_for_signing: true,
|
||||||
|
upload_signed: true,
|
||||||
|
delete: false,
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
export: true,
|
||||||
|
scan_receipt: true,
|
||||||
|
},
|
||||||
|
invoices: {
|
||||||
|
view: true,
|
||||||
|
create: true,
|
||||||
|
edit: true,
|
||||||
|
delete: false,
|
||||||
|
send: true,
|
||||||
|
record_payment: true,
|
||||||
|
export: true,
|
||||||
|
},
|
||||||
files: { view: true, upload: true, delete: false, manage_folders: false },
|
files: { view: true, upload: true, delete: false, manage_folders: false },
|
||||||
email: { view: true, send: true, configure_account: true },
|
email: { view: true, send: true, configure_account: true },
|
||||||
reminders: { view_own: true, view_all: false, create: true, edit_own: true, edit_all: false, assign_others: false },
|
reminders: {
|
||||||
|
view_own: true,
|
||||||
|
view_all: false,
|
||||||
|
create: true,
|
||||||
|
edit_own: true,
|
||||||
|
edit_all: false,
|
||||||
|
assign_others: false,
|
||||||
|
},
|
||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: false },
|
document_templates: { view: true, generate: true, manage: false },
|
||||||
admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: true, system_backup: false },
|
yachts: { view: true, create: true, edit: true, delete: false, transfer: false },
|
||||||
|
companies: { view: true, create: true, edit: false, delete: false },
|
||||||
|
memberships: { view: true, manage: false },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: false },
|
||||||
|
admin: {
|
||||||
|
manage_users: false,
|
||||||
|
view_audit_log: false,
|
||||||
|
manage_settings: false,
|
||||||
|
manage_webhooks: false,
|
||||||
|
manage_reports: false,
|
||||||
|
manage_custom_fields: false,
|
||||||
|
manage_forms: false,
|
||||||
|
manage_tags: true,
|
||||||
|
system_backup: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const VIEWER_PERMISSIONS: RolePermissions = {
|
const VIEWER_PERMISSIONS: RolePermissions = {
|
||||||
clients: { view: true, create: false, edit: false, delete: false, merge: false, export: false },
|
clients: { view: true, create: false, edit: false, delete: false, merge: false, export: false },
|
||||||
interests: { view: true, create: false, edit: false, delete: false, change_stage: false, generate_eoi: false, export: false },
|
interests: {
|
||||||
|
view: true,
|
||||||
|
create: false,
|
||||||
|
edit: false,
|
||||||
|
delete: false,
|
||||||
|
change_stage: false,
|
||||||
|
generate_eoi: false,
|
||||||
|
export: false,
|
||||||
|
},
|
||||||
berths: { view: true, edit: false, import: false, manage_waiting_list: false },
|
berths: { view: true, edit: false, import: false, manage_waiting_list: false },
|
||||||
documents: { view: true, create: false, send_for_signing: false, upload_signed: false, delete: false },
|
documents: {
|
||||||
expenses: { view: true, create: false, edit: false, delete: false, export: false, scan_receipt: false },
|
view: true,
|
||||||
invoices: { view: true, create: false, edit: false, delete: false, send: false, record_payment: false, export: false },
|
create: false,
|
||||||
|
send_for_signing: false,
|
||||||
|
upload_signed: false,
|
||||||
|
delete: false,
|
||||||
|
},
|
||||||
|
expenses: {
|
||||||
|
view: true,
|
||||||
|
create: false,
|
||||||
|
edit: false,
|
||||||
|
delete: false,
|
||||||
|
export: false,
|
||||||
|
scan_receipt: false,
|
||||||
|
},
|
||||||
|
invoices: {
|
||||||
|
view: true,
|
||||||
|
create: false,
|
||||||
|
edit: false,
|
||||||
|
delete: false,
|
||||||
|
send: false,
|
||||||
|
record_payment: false,
|
||||||
|
export: false,
|
||||||
|
},
|
||||||
files: { view: true, upload: false, delete: false, manage_folders: false },
|
files: { view: true, upload: false, delete: false, manage_folders: false },
|
||||||
email: { view: true, send: false, configure_account: false },
|
email: { view: true, send: false, configure_account: false },
|
||||||
reminders: { view_own: true, view_all: false, create: false, edit_own: false, edit_all: false, assign_others: false },
|
reminders: {
|
||||||
|
view_own: true,
|
||||||
|
view_all: false,
|
||||||
|
create: false,
|
||||||
|
edit_own: false,
|
||||||
|
edit_all: false,
|
||||||
|
assign_others: false,
|
||||||
|
},
|
||||||
calendar: { connect: false, view_events: true },
|
calendar: { connect: false, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: false, export: false },
|
reports: { view_dashboard: true, view_analytics: false, export: false },
|
||||||
document_templates: { view: true, generate: false, manage: false },
|
document_templates: { view: true, generate: false, manage: false },
|
||||||
admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: false, system_backup: false },
|
yachts: { view: true, create: false, edit: false, delete: false, transfer: false },
|
||||||
|
companies: { view: true, create: false, edit: false, delete: false },
|
||||||
|
memberships: { view: true, manage: false },
|
||||||
|
reservations: { view: true, create: false, activate: false, cancel: false },
|
||||||
|
admin: {
|
||||||
|
manage_users: false,
|
||||||
|
view_audit_log: false,
|
||||||
|
manage_settings: false,
|
||||||
|
manage_webhooks: false,
|
||||||
|
manage_reports: false,
|
||||||
|
manage_custom_fields: false,
|
||||||
|
manage_forms: false,
|
||||||
|
manage_tags: false,
|
||||||
|
system_backup: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─── Seed Function ────────────────────────────────────────────────────────────
|
// ─── Seed Function ────────────────────────────────────────────────────────────
|
||||||
@@ -158,7 +408,8 @@ async function seed() {
|
|||||||
{
|
{
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
name: 'sales_agent',
|
name: 'sales_agent',
|
||||||
description: 'Standard sales role. View/create/edit clients and interests, manage own reminders.',
|
description:
|
||||||
|
'Standard sales role. View/create/edit clients and interests, manage own reminders.',
|
||||||
permissions: SALES_AGENT_PERMISSIONS,
|
permissions: SALES_AGENT_PERMISSIONS,
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
|
|||||||
@@ -224,6 +224,10 @@ export function makeFullPermissions(): RolePermissions {
|
|||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: true },
|
document_templates: { view: true, generate: true, manage: true },
|
||||||
|
yachts: { view: true, create: true, edit: true, delete: true, transfer: true },
|
||||||
|
companies: { view: true, create: true, edit: true, delete: true },
|
||||||
|
memberships: { view: true, manage: true },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: true },
|
||||||
admin: {
|
admin: {
|
||||||
manage_users: true,
|
manage_users: true,
|
||||||
view_audit_log: true,
|
view_audit_log: true,
|
||||||
@@ -289,6 +293,10 @@ export function makeViewerPermissions(): RolePermissions {
|
|||||||
calendar: { connect: false, view_events: true },
|
calendar: { connect: false, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: false, export: false },
|
reports: { view_dashboard: true, view_analytics: false, export: false },
|
||||||
document_templates: { view: true, generate: false, manage: false },
|
document_templates: { view: true, generate: false, manage: false },
|
||||||
|
yachts: { view: true, create: false, edit: false, delete: false, transfer: false },
|
||||||
|
companies: { view: true, create: false, edit: false, delete: false },
|
||||||
|
memberships: { view: true, manage: false },
|
||||||
|
reservations: { view: true, create: false, activate: false, cancel: false },
|
||||||
admin: {
|
admin: {
|
||||||
manage_users: false,
|
manage_users: false,
|
||||||
view_audit_log: false,
|
view_audit_log: false,
|
||||||
@@ -354,6 +362,10 @@ export function makeSalesAgentPermissions(): RolePermissions {
|
|||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: false, export: false },
|
reports: { view_dashboard: true, view_analytics: false, export: false },
|
||||||
document_templates: { view: true, generate: true, manage: false },
|
document_templates: { view: true, generate: true, manage: false },
|
||||||
|
yachts: { view: true, create: true, edit: true, delete: false, transfer: false },
|
||||||
|
companies: { view: true, create: true, edit: false, delete: false },
|
||||||
|
memberships: { view: true, manage: false },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: false },
|
||||||
admin: {
|
admin: {
|
||||||
manage_users: false,
|
manage_users: false,
|
||||||
view_audit_log: false,
|
view_audit_log: false,
|
||||||
@@ -419,6 +431,10 @@ export function makeSalesManagerPermissions(): RolePermissions {
|
|||||||
calendar: { connect: true, view_events: true },
|
calendar: { connect: true, view_events: true },
|
||||||
reports: { view_dashboard: true, view_analytics: true, export: true },
|
reports: { view_dashboard: true, view_analytics: true, export: true },
|
||||||
document_templates: { view: true, generate: true, manage: false },
|
document_templates: { view: true, generate: true, manage: false },
|
||||||
|
yachts: { view: true, create: true, edit: true, delete: false, transfer: true },
|
||||||
|
companies: { view: true, create: true, edit: true, delete: false },
|
||||||
|
memberships: { view: true, manage: true },
|
||||||
|
reservations: { view: true, create: true, activate: true, cancel: true },
|
||||||
admin: {
|
admin: {
|
||||||
manage_users: false,
|
manage_users: false,
|
||||||
view_audit_log: true,
|
view_audit_log: true,
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { describe, it, expect, vi } from 'vitest';
|
|||||||
|
|
||||||
import { withPermission, deepMerge, type AuthContext } from '@/lib/api/helpers';
|
import { withPermission, deepMerge, type AuthContext } from '@/lib/api/helpers';
|
||||||
import {
|
import {
|
||||||
makeFullPermissions,
|
|
||||||
makeViewerPermissions,
|
makeViewerPermissions,
|
||||||
makeSalesAgentPermissions,
|
makeSalesAgentPermissions,
|
||||||
makeSalesManagerPermissions,
|
makeSalesManagerPermissions,
|
||||||
@@ -237,7 +236,9 @@ describe('deepMerge — permission override merging', () => {
|
|||||||
|
|
||||||
it('override with full-permission block gives full access', () => {
|
it('override with full-permission block gives full access', () => {
|
||||||
const base = makeViewerPermissions() as Record<string, unknown>;
|
const base = makeViewerPermissions() as Record<string, unknown>;
|
||||||
const override = { clients: { create: true, edit: true, delete: true, merge: true, export: true } };
|
const override = {
|
||||||
|
clients: { create: true, edit: true, delete: true, merge: true, export: true },
|
||||||
|
};
|
||||||
const result = deepMerge(base, override) as RolePermissions;
|
const result = deepMerge(base, override) as RolePermissions;
|
||||||
expect(result.clients.create).toBe(true);
|
expect(result.clients.create).toBe(true);
|
||||||
expect(result.clients.view).toBe(true); // preserved from base
|
expect(result.clients.view).toBe(true); // preserved from base
|
||||||
@@ -250,3 +251,73 @@ describe('deepMerge — permission override merging', () => {
|
|||||||
expect(result.events).toEqual(['c']);
|
expect(result.events).toEqual(['c']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── new resources (yachts, companies, memberships, reservations) ────────────
|
||||||
|
|
||||||
|
describe('new resources (yachts, companies, memberships, reservations)', () => {
|
||||||
|
it('super_admin bypasses all new resource permissions', async () => {
|
||||||
|
const ctx = makeCtx({ isSuperAdmin: true, permissions: null });
|
||||||
|
const handler = vi.fn(okHandler());
|
||||||
|
const wrapped = withPermission('yachts', 'transfer', handler);
|
||||||
|
const res = await wrapped(makeRequest(), ctx, {});
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('viewer can yachts.view but not yachts.transfer', async () => {
|
||||||
|
const ctx = makeCtx({ permissions: makeViewerPermissions() });
|
||||||
|
const viewRes = await withPermission('yachts', 'view', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(viewRes.status).toBe(200);
|
||||||
|
const transferRes = await withPermission('yachts', 'transfer', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(transferRes.status).toBe(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sales_manager can yachts.transfer and memberships.manage', async () => {
|
||||||
|
const ctx = makeCtx({ permissions: makeSalesManagerPermissions() });
|
||||||
|
const transferRes = await withPermission('yachts', 'transfer', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(transferRes.status).toBe(200);
|
||||||
|
const manageRes = await withPermission('memberships', 'manage', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(manageRes.status).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sales_agent can reservations.activate but not reservations.cancel', async () => {
|
||||||
|
const ctx = makeCtx({ permissions: makeSalesAgentPermissions() });
|
||||||
|
const activateRes = await withPermission('reservations', 'activate', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(activateRes.status).toBe(200);
|
||||||
|
const cancelRes = await withPermission('reservations', 'cancel', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(cancelRes.status).toBe(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sales_agent cannot companies.delete', async () => {
|
||||||
|
const ctx = makeCtx({ permissions: makeSalesAgentPermissions() });
|
||||||
|
const res = await withPermission('companies', 'delete', vi.fn(okHandler()))(
|
||||||
|
makeRequest(),
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
expect(res.status).toBe(403);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user