From 53bb3bc610f4e42ba3b7c8c45806bcd3c536c028 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Tue, 12 Dec 2023 20:59:24 +0530 Subject: [PATCH] feat(infisical-pg): added database schema for project, its membership role and env --- backend-pg/package.json | 2 +- backend-pg/src/@types/knex.d.ts | 27 ++++++++++++ .../20231204092747_org-membership.ts | 1 - .../db/migrations/20231212110939_project.ts | 33 +++++++++++++++ .../20231212110946_project-membership.ts | 42 +++++++++++++++++++ backend-pg/src/db/schemas/index.ts | 4 ++ backend-pg/src/db/schemas/models.ts | 14 ++++++- .../src/db/schemas/project-environments.ts | 21 ++++++++++ .../src/db/schemas/project-memberships.ts | 22 ++++++++++ backend-pg/src/db/schemas/project-roles.ts | 23 ++++++++++ backend-pg/src/db/schemas/projects.ts | 21 ++++++++++ backend-pg/src/db/utils.ts | 4 +- 12 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 backend-pg/src/db/migrations/20231212110939_project.ts create mode 100644 backend-pg/src/db/migrations/20231212110946_project-membership.ts create mode 100644 backend-pg/src/db/schemas/project-environments.ts create mode 100644 backend-pg/src/db/schemas/project-memberships.ts create mode 100644 backend-pg/src/db/schemas/project-roles.ts create mode 100644 backend-pg/src/db/schemas/projects.ts diff --git a/backend-pg/package.json b/backend-pg/package.json index cebaa3546..4c77f294b 100644 --- a/backend-pg/package.json +++ b/backend-pg/package.json @@ -8,7 +8,7 @@ "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev:docker": "nodemon", "build": "rimraf dist && tsup src --out-dir dist --loader '.handlebars=copy' --loader '.md=copy'", - "start": "node dist/server/app.js", + "start": "node dist/server/main.js", "type:check": "tsc --noEmit", "lint:fix": "eslint --fix --ext js,ts ./src", "lint": "eslint 'src/**/*.ts'", diff --git a/backend-pg/src/@types/knex.d.ts b/backend-pg/src/@types/knex.d.ts index 8827f4b7a..e2f7b13cf 100644 --- a/backend-pg/src/@types/knex.d.ts +++ b/backend-pg/src/@types/knex.d.ts @@ -25,6 +25,17 @@ import { TOrgRoles, TOrgRolesInsert, TOrgRolesUpdate, + TProjectEnvironments, + TProjectEnvironmentsInsert, + TProjectMemberships, + TProjectMembershipsInsert, + TProjectMembershipsUpdate, + TProjectRoles, + TProjectRolesInsert, + TProjectRolesUpdate, + TProjects, + TProjectsInsert, + TProjectsUpdate, TSuperAdmin, TSuperAdminInsert, TSuperAdminUpdate, @@ -89,5 +100,21 @@ declare module "knex/types/tables" { TSuperAdminUpdate >; [TableName.ApiKey]: Knex.CompositeTableType; + [TableName.Project]: Knex.CompositeTableType; + [TableName.ProjectMembership]: Knex.CompositeTableType< + TProjectMemberships, + TProjectMembershipsInsert, + TProjectMembershipsUpdate + >; + [TableName.Environment]: Knex.CompositeTableType< + TProjectEnvironments, + TProjectEnvironmentsInsert, + TPRo + >; + [TableName.ProjectRoles]: Knex.CompositeTableType< + TProjectRoles, + TProjectRolesInsert, + TProjectRolesUpdate + >; } } diff --git a/backend-pg/src/db/migrations/20231204092747_org-membership.ts b/backend-pg/src/db/migrations/20231204092747_org-membership.ts index ea8a42ac7..2b166d3e5 100644 --- a/backend-pg/src/db/migrations/20231204092747_org-membership.ts +++ b/backend-pg/src/db/migrations/20231204092747_org-membership.ts @@ -33,7 +33,6 @@ export async function up(knex: Knex): Promise { t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE"); t.uuid("orgId").notNullable(); t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); - // until role is changed/removed the role should not deleted t.uuid("roleId"); t.foreign("roleId").references("id").inTable(TableName.OrgRoles); }); diff --git a/backend-pg/src/db/migrations/20231212110939_project.ts b/backend-pg/src/db/migrations/20231212110939_project.ts new file mode 100644 index 000000000..fdc7dff4b --- /dev/null +++ b/backend-pg/src/db/migrations/20231212110939_project.ts @@ -0,0 +1,33 @@ +import { Knex } from "knex"; +import { TableName } from "../schemas"; +import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; + +export async function up(knex: Knex): Promise { + if (!(await knex.schema.hasTable(TableName.Project))) { + await knex.schema.createTable(TableName.Project, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("name").notNullable(); + t.boolean("autoCapitalization").defaultTo(true); + t.uuid("orgId").notNullable(); + t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); + t.timestamps(true, true, true); + }); + } + await createOnUpdateTrigger(knex, TableName.Project); + if (!(await knex.schema.hasTable(TableName.Environment))) { + await knex.schema.createTable(TableName.Environment, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("name").notNullable(); + t.string("slug").notNullable(); + t.uuid("projectId").notNullable(); + t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); + t.timestamps(true, true, true); + }); + } +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists(TableName.Environment); + await knex.schema.dropTableIfExists(TableName.Project); + await dropOnUpdateTrigger(knex, TableName.Project); +} diff --git a/backend-pg/src/db/migrations/20231212110946_project-membership.ts b/backend-pg/src/db/migrations/20231212110946_project-membership.ts new file mode 100644 index 000000000..8218eb2a4 --- /dev/null +++ b/backend-pg/src/db/migrations/20231212110946_project-membership.ts @@ -0,0 +1,42 @@ +import { Knex } from "knex"; +import { TableName } from "../schemas"; +import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; + +export async function up(knex: Knex): Promise { + if (!(await knex.schema.hasTable(TableName.ProjectRoles))) { + await knex.schema.createTable(TableName.ProjectRoles, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("name").notNullable(); + t.string("description"); + t.string("slug").notNullable(); + t.jsonb("permissions").notNullable(); + // does not need update trigger we will do it manually + t.timestamps(true, true, true); + t.uuid("projectId").notNullable(); + t.foreign("projectId").references("id").inTable(TableName.ProjectRoles).onDelete("CASCADE"); + }); + } + + if (!(await knex.schema.hasTable(TableName.ProjectMembership))) { + await knex.schema.createTable(TableName.ProjectMembership, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("role").notNullable(); + // does not need update trigger we will do it manually + t.timestamps(true, true, true); + t.uuid("userId").notNullable(); + t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE"); + t.uuid("projectId").notNullable(); + t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); + // until role is changed/removed the role should not deleted + t.uuid("roleId"); + t.foreign("roleId").references("id").inTable(TableName.ProjectRoles); + }); + } + await createOnUpdateTrigger(knex, TableName.ProjectMembership); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists(TableName.ProjectMembership); + await knex.schema.dropTableIfExists(TableName.ProjectRoles); + await dropOnUpdateTrigger(knex, TableName.ProjectMembership); +} diff --git a/backend-pg/src/db/schemas/index.ts b/backend-pg/src/db/schemas/index.ts index 8bb6a169b..ec8e272eb 100644 --- a/backend-pg/src/db/schemas/index.ts +++ b/backend-pg/src/db/schemas/index.ts @@ -7,6 +7,10 @@ export * from "./models"; export * from "./org-memberships"; export * from "./org-roles"; export * from "./organizations"; +export * from "./project-environments"; +export * from "./project-memberships"; +export * from "./project-roles"; +export * from "./projects"; export * from "./super-admin"; export * from "./user-actions"; export * from "./user-encryption-keys"; diff --git a/backend-pg/src/db/schemas/models.ts b/backend-pg/src/db/schemas/models.ts index 85bb06c59..438fca26a 100644 --- a/backend-pg/src/db/schemas/models.ts +++ b/backend-pg/src/db/schemas/models.ts @@ -12,7 +12,11 @@ export enum TableName { IncidentContact = "incident_contacts", UserAction = "user_actions", SuperAdmin = "super_admin", - ApiKey = "api_keys" + ApiKey = "api_keys", + Project = "projects", + Environment = "project_environments", + ProjectMembership = "project_memberships", + ProjectRoles = "project_roles" } export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt"; @@ -36,6 +40,14 @@ export enum OrgMembershipStatus { Accepted = "accepted" } +export enum ProjectMembershipRole { + Admin = "admin", + Member = "member", + Custom = "custom", + Viewer = "viewer", + NoAccess = "no-access" +} + export enum SecretEncryptionAlgo { AES_256_GCM = "aes-256-gcm" } diff --git a/backend-pg/src/db/schemas/project-environments.ts b/backend-pg/src/db/schemas/project-environments.ts new file mode 100644 index 000000000..1521393b8 --- /dev/null +++ b/backend-pg/src/db/schemas/project-environments.ts @@ -0,0 +1,21 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const ProjectEnvironmentsSchema = z.object({ + id: z.string().uuid(), + name: z.string(), + slug: z.string(), + projectId: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), +}); + +export type TProjectEnvironments = z.infer; +export type TProjectEnvironmentsInsert = Omit; +export type TProjectEnvironmentsUpdate = Partial>; diff --git a/backend-pg/src/db/schemas/project-memberships.ts b/backend-pg/src/db/schemas/project-memberships.ts new file mode 100644 index 000000000..b8156ab9d --- /dev/null +++ b/backend-pg/src/db/schemas/project-memberships.ts @@ -0,0 +1,22 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const ProjectMembershipsSchema = z.object({ + id: z.string().uuid(), + role: z.string(), + createdAt: z.date(), + updatedAt: z.date(), + userId: z.string().uuid(), + projectId: z.string().uuid(), + roleId: z.string().uuid().nullable().optional(), +}); + +export type TProjectMemberships = z.infer; +export type TProjectMembershipsInsert = Omit; +export type TProjectMembershipsUpdate = Partial>; diff --git a/backend-pg/src/db/schemas/project-roles.ts b/backend-pg/src/db/schemas/project-roles.ts new file mode 100644 index 000000000..1cd763173 --- /dev/null +++ b/backend-pg/src/db/schemas/project-roles.ts @@ -0,0 +1,23 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const ProjectRolesSchema = z.object({ + id: z.string().uuid(), + name: z.string(), + description: z.string().nullable().optional(), + slug: z.string(), + permissions: z.unknown(), + createdAt: z.date(), + updatedAt: z.date(), + projectId: z.string().uuid(), +}); + +export type TProjectRoles = z.infer; +export type TProjectRolesInsert = Omit; +export type TProjectRolesUpdate = Partial>; diff --git a/backend-pg/src/db/schemas/projects.ts b/backend-pg/src/db/schemas/projects.ts new file mode 100644 index 000000000..007e7d018 --- /dev/null +++ b/backend-pg/src/db/schemas/projects.ts @@ -0,0 +1,21 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const ProjectsSchema = z.object({ + id: z.string().uuid(), + name: z.string(), + autoCapitalization: z.boolean().default(true).nullable().optional(), + orgId: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), +}); + +export type TProjects = z.infer; +export type TProjectsInsert = Omit; +export type TProjectsUpdate = Partial>; diff --git a/backend-pg/src/db/utils.ts b/backend-pg/src/db/utils.ts index 9c537f5b5..fdcb0fb19 100644 --- a/backend-pg/src/db/utils.ts +++ b/backend-pg/src/db/utils.ts @@ -33,11 +33,11 @@ DROP FUNCTION IF EXISTS on_update_timestamp() CASCADE; // remember to set `timestamps(true,true,true)` before this on schema export const createOnUpdateTrigger = (knex: Knex, tableName: string) => knex.raw(` -CREATE TRIGGER ${tableName}_updatedAt +CREATE TRIGGER "${tableName}_updatedAt" BEFORE UPDATE ON ${tableName} FOR EACH ROW EXECUTE PROCEDURE on_update_timestamp(); `); export const dropOnUpdateTrigger = (knex: Knex, tableName: string) => - knex.raw(`DROP TRIGGER IF EXISTS ${tableName}_updatedAt ON ${tableName}`); + knex.raw(`DROP TRIGGER IF EXISTS "${tableName}_updatedAt" ON ${tableName}`);