refactor: replace existing organization_role and audit_log tables with new definitions

- Deleted the old SQL files for organization_role and audit_log.
- Introduced new SQL file defining organization_role and audit_log with updated foreign key constraints and indexes.
- Updated metadata snapshots to reflect the new table structures and relationships.
- Adjusted access control permissions for backup and notification operations to include update capabilities.
This commit is contained in:
Mauricio Siu
2026-03-15 23:02:23 -06:00
parent 5410a56638
commit 947100c041
10 changed files with 224 additions and 7795 deletions

View File

@@ -1,12 +0,0 @@
CREATE TABLE "organization_role" (
"id" text PRIMARY KEY NOT NULL,
"organization_id" text NOT NULL,
"role" text NOT NULL,
"permission" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp
);
--> statement-breakpoint
ALTER TABLE "organization_role" ADD CONSTRAINT "organization_role_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "organizationRole_organizationId_idx" ON "organization_role" USING btree ("organization_id");--> statement-breakpoint
CREATE INDEX "organizationRole_role_idx" ON "organization_role" USING btree ("role");

View File

@@ -0,0 +1,31 @@
CREATE TABLE "organization_role" (
"id" text PRIMARY KEY NOT NULL,
"organization_id" text NOT NULL,
"role" text NOT NULL,
"permission" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "audit_log" (
"id" text PRIMARY KEY NOT NULL,
"organization_id" text,
"user_id" text,
"user_email" text NOT NULL,
"user_role" text NOT NULL,
"action" text NOT NULL,
"resource_type" text NOT NULL,
"resource_id" text,
"resource_name" text,
"metadata" text,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "organization_role" ADD CONSTRAINT "organization_role_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "organizationRole_organizationId_idx" ON "organization_role" USING btree ("organization_id");--> statement-breakpoint
CREATE INDEX "organizationRole_role_idx" ON "organization_role" USING btree ("role");--> statement-breakpoint
CREATE INDEX "auditLog_organizationId_idx" ON "audit_log" USING btree ("organization_id");--> statement-breakpoint
CREATE INDEX "auditLog_userId_idx" ON "audit_log" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "auditLog_createdAt_idx" ON "audit_log" USING btree ("created_at");

View File

@@ -1,19 +0,0 @@
CREATE TABLE "audit_log" (
"id" text PRIMARY KEY NOT NULL,
"organization_id" text NOT NULL,
"user_id" text NOT NULL,
"user_email" text NOT NULL,
"user_role" text NOT NULL,
"action" text NOT NULL,
"resource_type" text NOT NULL,
"resource_id" text,
"resource_name" text,
"metadata" text,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "auditLog_organizationId_idx" ON "audit_log" USING btree ("organization_id");--> statement-breakpoint
CREATE INDEX "auditLog_userId_idx" ON "audit_log" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "auditLog_createdAt_idx" ON "audit_log" USING btree ("created_at");

View File

@@ -1,5 +1,5 @@
{
"id": "5886356f-9277-465b-8ae1-54e2ba28cce4",
"id": "e6eacbcd-0e09-4fa0-91be-730d3cc20d84",
"prevId": "a293a443-ceaf-418e-8e34-ff46e183995f",
"version": "7",
"dialect": "postgresql",
@@ -917,6 +917,159 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.audit_log": {
"name": "audit_log",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"organization_id": {
"name": "organization_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"user_email": {
"name": "user_email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"user_role": {
"name": "user_role",
"type": "text",
"primaryKey": false,
"notNull": true
},
"action": {
"name": "action",
"type": "text",
"primaryKey": false,
"notNull": true
},
"resource_type": {
"name": "resource_type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"resource_id": {
"name": "resource_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"resource_name": {
"name": "resource_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"metadata": {
"name": "metadata",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"auditLog_organizationId_idx": {
"name": "auditLog_organizationId_idx",
"columns": [
{
"expression": "organization_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"auditLog_userId_idx": {
"name": "auditLog_userId_idx",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"auditLog_createdAt_idx": {
"name": "auditLog_createdAt_idx",
"columns": [
{
"expression": "created_at",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"audit_log_organization_id_organization_id_fk": {
"name": "audit_log_organization_id_organization_id_fk",
"tableFrom": "audit_log",
"tableTo": "organization",
"columnsFrom": [
"organization_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"audit_log_user_id_user_id_fk": {
"name": "audit_log_user_id_user_id_fk",
"tableFrom": "audit_log",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.application": {
"name": "application",
"schema": "",

File diff suppressed because it is too large Load Diff

View File

@@ -1048,15 +1048,8 @@
{
"idx": 149,
"version": "7",
"when": 1773184273215,
"tag": "0149_clumsy_speedball",
"breakpoints": true
},
{
"idx": 150,
"version": "7",
"when": 1773374115852,
"tag": "0150_glorious_pyro",
"when": 1773637297592,
"tag": "0149_rare_radioactive_man",
"breakpoints": true
}
]

View File

@@ -77,22 +77,21 @@ export const backupRouter = createTRPCRouter({
.input(apiCreateBackup)
.mutation(async ({ input, ctx }) => {
try {
const newBackup = await createBackup(input);
const backup = await findBackupById(newBackup.backupId);
const serviceId =
backup.postgresId ||
backup.mysqlId ||
backup.mariadbId ||
backup.mongoId ||
backup.composeId;
input.postgresId ||
input.mysqlId ||
input.mariadbId ||
input.mongoId ||
input.composeId;
if (serviceId) {
await checkServicePermissionAndAccess(ctx, serviceId, {
backup: ["create"],
});
}
const newBackup = await createBackup(input);
const backup = await findBackupById(newBackup.backupId);
if (IS_CLOUD && backup.enabled) {
const databaseType = backup.databaseType;
let serverId = "";
@@ -168,21 +167,22 @@ export const backupRouter = createTRPCRouter({
.input(apiUpdateBackup)
.mutation(async ({ input, ctx }) => {
try {
await updateBackupById(input.backupId, input);
const backup = await findBackupById(input.backupId);
const existing = await findBackupById(input.backupId);
const serviceId =
backup.postgresId ||
backup.mysqlId ||
backup.mariadbId ||
backup.mongoId ||
backup.composeId;
existing.postgresId ||
existing.mysqlId ||
existing.mariadbId ||
existing.mongoId ||
existing.composeId;
if (serviceId) {
await checkServicePermissionAndAccess(ctx, serviceId, {
backup: ["create"],
backup: ["update"],
});
}
await updateBackupById(input.backupId, input);
const backup = await findBackupById(input.backupId);
if (IS_CLOUD) {
if (backup.enabled) {
await updateJob({

View File

@@ -107,7 +107,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateSlack: withPermission("notification", "create")
updateSlack: withPermission("notification", "update")
.input(apiUpdateSlack)
.mutation(async ({ input, ctx }) => {
try {
@@ -172,7 +172,7 @@ export const notificationRouter = createTRPCRouter({
}
}),
updateTelegram: withPermission("notification", "create")
updateTelegram: withPermission("notification", "update")
.input(apiUpdateTelegram)
.mutation(async ({ input, ctx }) => {
try {
@@ -238,7 +238,7 @@ export const notificationRouter = createTRPCRouter({
}
}),
updateDiscord: withPermission("notification", "create")
updateDiscord: withPermission("notification", "update")
.input(apiUpdateDiscord)
.mutation(async ({ input, ctx }) => {
try {
@@ -309,7 +309,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateEmail: withPermission("notification", "create")
updateEmail: withPermission("notification", "update")
.input(apiUpdateEmail)
.mutation(async ({ input, ctx }) => {
try {
@@ -375,7 +375,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateResend: withPermission("notification", "create")
updateResend: withPermission("notification", "update")
.input(apiUpdateResend)
.mutation(async ({ input, ctx }) => {
try {
@@ -561,7 +561,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateGotify: withPermission("notification", "create")
updateGotify: withPermission("notification", "update")
.input(apiUpdateGotify)
.mutation(async ({ input, ctx }) => {
try {
@@ -626,7 +626,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateNtfy: withPermission("notification", "create")
updateNtfy: withPermission("notification", "update")
.input(apiUpdateNtfy)
.mutation(async ({ input, ctx }) => {
try {
@@ -693,7 +693,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateCustom: withPermission("notification", "create")
updateCustom: withPermission("notification", "update")
.input(apiUpdateCustom)
.mutation(async ({ input, ctx }) => {
try {
@@ -755,7 +755,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateLark: withPermission("notification", "create")
updateLark: withPermission("notification", "update")
.input(apiUpdateLark)
.mutation(async ({ input, ctx }) => {
try {
@@ -821,7 +821,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updateTeams: withPermission("notification", "create")
updateTeams: withPermission("notification", "update")
.input(apiUpdateTeams)
.mutation(async ({ input, ctx }) => {
try {
@@ -888,7 +888,7 @@ export const notificationRouter = createTRPCRouter({
});
}
}),
updatePushover: withPermission("notification", "create")
updatePushover: withPermission("notification", "update")
.input(apiUpdatePushover)
.mutation(async ({ input, ctx }) => {
try {

View File

@@ -11,11 +11,9 @@ export const auditLog = pgTable(
.primaryKey()
.$defaultFn(() => nanoid()),
organizationId: text("organization_id")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
.references(() => organization.id, { onDelete: "set null" }),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
.references(() => user.id, { onDelete: "set null" }),
userEmail: text("user_email").notNull(),
userRole: text("user_role").notNull(),
action: text("action").notNull(),

View File

@@ -38,12 +38,12 @@ export const statements = {
server: ["read", "create", "delete"],
registry: ["read", "create", "delete"],
certificate: ["read", "create", "delete"],
backup: ["read", "create", "delete", "restore"],
backup: ["read", "create", "update", "delete", "restore"],
volumeBackup: ["read", "create", "update", "delete", "restore"],
schedule: ["read", "create", "update", "delete"],
domain: ["read", "create", "delete"],
destination: ["read", "create", "delete"],
notification: ["read", "create", "delete"],
notification: ["read", "create", "update", "delete"],
logs: ["read"],
monitoring: ["read"],
auditLog: ["read"],
@@ -101,12 +101,12 @@ export const ownerRole = ac.newRole({
server: ["read", "create", "delete"],
registry: ["read", "create", "delete"],
certificate: ["read", "create", "delete"],
backup: ["read", "create", "delete", "restore"],
backup: ["read", "create", "update", "delete", "restore"],
volumeBackup: ["read", "create", "update", "delete", "restore"],
schedule: ["read", "create", "update", "delete"],
domain: ["read", "create", "delete"],
destination: ["read", "create", "delete"],
notification: ["read", "create", "delete"],
notification: ["read", "create", "update", "delete"],
logs: ["read"],
monitoring: ["read"],
auditLog: ["read"],
@@ -137,12 +137,12 @@ export const adminRole = ac.newRole({
server: ["read", "create", "delete"],
registry: ["read", "create", "delete"],
certificate: ["read", "create", "delete"],
backup: ["read", "create", "delete", "restore"],
backup: ["read", "create", "update", "delete", "restore"],
volumeBackup: ["read", "create", "update", "delete", "restore"],
schedule: ["read", "create", "update", "delete"],
domain: ["read", "create", "delete"],
destination: ["read", "create", "delete"],
notification: ["read", "create", "delete"],
notification: ["read", "create", "update", "delete"],
logs: ["read"],
monitoring: ["read"],
auditLog: ["read"],