diff --git a/apps/backend/src/drizzle/schema/answerRevisions.ts b/apps/backend/src/drizzle/schema/answerRevisions.ts new file mode 100644 index 0000000..f0f80ec --- /dev/null +++ b/apps/backend/src/drizzle/schema/answerRevisions.ts @@ -0,0 +1,24 @@ +import { createId } from "@paralleldrive/cuid2"; + +import { + boolean, + pgTable, + text, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; +import { options } from "./options"; +import { answers } from "./answers"; + +export const answerRevisions = pgTable("answer_revisions", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + answerId: varchar("answerId", { length: 50 }) + .references(() => answers.id), + newOptionId: varchar("newOptionId", { length: 50 }) + .references(() => options.id), + revisedBy: varchar("revisedBy").notNull(), + newValidationInformation: text("newValidationInformation").notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), +}); diff --git a/apps/backend/src/drizzle/schema/answers.ts b/apps/backend/src/drizzle/schema/answers.ts new file mode 100644 index 0000000..6f8bf5c --- /dev/null +++ b/apps/backend/src/drizzle/schema/answers.ts @@ -0,0 +1,26 @@ +import { createId } from "@paralleldrive/cuid2"; +import { relations } from "drizzle-orm"; +import { + boolean, + pgTable, + text, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; +import { options } from "./options"; +import { assessments } from "./assessments"; + +export const answers = pgTable("answers", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + optionId: varchar("optionId", { length: 50 }) + .references(() => options.id), + assessmentId: varchar("assessmentId", { length: 50 }) + .references(() => assessments.id), + isFlagged: boolean("isFlagged").default(false), + filename: varchar("filename"), + validationInformation: text("validationInformation").notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), +}); diff --git a/apps/backend/src/drizzle/schema/aspects.ts b/apps/backend/src/drizzle/schema/aspects.ts new file mode 100644 index 0000000..f5df23c --- /dev/null +++ b/apps/backend/src/drizzle/schema/aspects.ts @@ -0,0 +1,16 @@ +import { createId } from "@paralleldrive/cuid2"; +import { + pgTable, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; + +export const aspects = pgTable("aspects", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + name: varchar("name", { length: 255 }).notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletedAt", { mode: "date" }), +}); diff --git a/apps/backend/src/drizzle/schema/assessments.ts b/apps/backend/src/drizzle/schema/assessments.ts new file mode 100644 index 0000000..44eb4ed --- /dev/null +++ b/apps/backend/src/drizzle/schema/assessments.ts @@ -0,0 +1,23 @@ +import { createId } from "@paralleldrive/cuid2"; +import { pgEnum, pgTable, timestamp, varchar } from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { respondents } from "./respondents"; +import { users } from "./users"; + +const statusEnum = pgEnum("status", ["tertunda", "disetujui", "ditolak", "selesai"]); + +export const assessments = pgTable("assessments", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + respondentId: varchar("respondentId").references(() => respondents.id), + status: statusEnum("status"), + reviewedBy: varchar("reviewedBy"), + reviewedAt: timestamp("reviewedAt", { mode: "date" }), + validatedBy: varchar("validatedBy").notNull(), + validatedAt: timestamp("validatedAt", { mode: "date" }), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + +}); +// Query Tools in PosgreSQL +// CREATE TYPE status AS ENUM ('tertunda', 'disetujui', 'ditolak', 'selesai'); \ No newline at end of file diff --git a/apps/backend/src/drizzle/schema/options.ts b/apps/backend/src/drizzle/schema/options.ts new file mode 100644 index 0000000..59df2b1 --- /dev/null +++ b/apps/backend/src/drizzle/schema/options.ts @@ -0,0 +1,23 @@ +import { createId } from "@paralleldrive/cuid2"; +import { relations } from "drizzle-orm"; +import { + integer, + pgTable, + text, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; +import { questions } from "./questions"; + +export const options = pgTable("options", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + questionId: varchar("questionId", { length: 50 }) + .references(() => questions.id), + text: text("text").notNull(), + score: integer("score").notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletedAt", { mode: "date" }), +}); diff --git a/apps/backend/src/drizzle/schema/permissions.ts b/apps/backend/src/drizzle/schema/permissions.ts index f4a2461..90b13ba 100644 --- a/apps/backend/src/drizzle/schema/permissions.ts +++ b/apps/backend/src/drizzle/schema/permissions.ts @@ -5,13 +5,13 @@ import { permissionsToUsers } from "./permissionsToUsers"; import { permissionsToRoles } from "./permissionsToRoles"; export const permissionsSchema = pgTable("permissions", { - id: text("id") + id: varchar("id", { length: 50 }) .primaryKey() .$defaultFn(() => createId()), code: varchar("code", { length: 50 }).notNull().unique(), description: varchar("description", { length: 255 }), - createdAt: timestamp("created_at").defaultNow(), - updatedAt: timestamp("updated_at").defaultNow(), + createdAt: timestamp("createdAt").defaultNow(), + updatedAt: timestamp("updatedAt").defaultNow(), }); export const permissionsRelations = relations( diff --git a/apps/backend/src/drizzle/schema/permissionsToRoles.ts b/apps/backend/src/drizzle/schema/permissionsToRoles.ts index ddce625..72ead8c 100644 --- a/apps/backend/src/drizzle/schema/permissionsToRoles.ts +++ b/apps/backend/src/drizzle/schema/permissionsToRoles.ts @@ -1,4 +1,4 @@ -import { pgTable, primaryKey, text } from "drizzle-orm/pg-core"; +import { pgTable, primaryKey, varchar } from "drizzle-orm/pg-core"; import { permissionsSchema } from "./permissions"; import { relations } from "drizzle-orm"; import { rolesSchema } from "./roles"; @@ -6,10 +6,10 @@ import { rolesSchema } from "./roles"; export const permissionsToRoles = pgTable( "permissions_to_roles", { - roleId: text("roleId") + roleId: varchar("roleId", { length: 50 }) .notNull() .references(() => rolesSchema.id), - permissionId: text("permissionId") + permissionId: varchar("permissionId", { length: 50 }) .notNull() .references(() => permissionsSchema.id), }, diff --git a/apps/backend/src/drizzle/schema/permissionsToUsers.ts b/apps/backend/src/drizzle/schema/permissionsToUsers.ts index 110b433..117c030 100644 --- a/apps/backend/src/drizzle/schema/permissionsToUsers.ts +++ b/apps/backend/src/drizzle/schema/permissionsToUsers.ts @@ -1,4 +1,4 @@ -import { pgTable, primaryKey, text } from "drizzle-orm/pg-core"; +import { pgTable, primaryKey, varchar } from "drizzle-orm/pg-core"; import { users } from "./users"; import { permissionsSchema } from "./permissions"; import { relations } from "drizzle-orm"; @@ -6,10 +6,10 @@ import { relations } from "drizzle-orm"; export const permissionsToUsers = pgTable( "permissions_to_users", { - userId: text("userId") + userId: varchar("userId", { length: 50 }) .notNull() .references(() => users.id), - permissionId: text("permissionId") + permissionId: varchar("permissionId", { length: 50 }) .notNull() .references(() => permissionsSchema.id), }, diff --git a/apps/backend/src/drizzle/schema/questions.ts b/apps/backend/src/drizzle/schema/questions.ts new file mode 100644 index 0000000..a84ba99 --- /dev/null +++ b/apps/backend/src/drizzle/schema/questions.ts @@ -0,0 +1,21 @@ +import { createId } from "@paralleldrive/cuid2"; +import { + boolean, + pgTable, + text, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; +import { subAspects } from "./subAspects" + +export const questions = pgTable("questions", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + subAspectId: varchar("subAspectId").references(() => subAspects.id), + question: text("question"), + needFile: boolean("needFile").default(false), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletedAt", { mode: "date" }), +}); diff --git a/apps/backend/src/drizzle/schema/respondents.ts b/apps/backend/src/drizzle/schema/respondents.ts new file mode 100644 index 0000000..744ec4f --- /dev/null +++ b/apps/backend/src/drizzle/schema/respondents.ts @@ -0,0 +1,26 @@ +import { createId } from "@paralleldrive/cuid2"; +import { pgTable, text, timestamp, varchar } from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { users } from "./users"; + +export const respondents = pgTable("respondents", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + userId: varchar('userId').references(() => users.id).unique(), + companyName: varchar("companyName").notNull(), + position: varchar("position").notNull(), + workExperience: varchar("workExperience").notNull(), + address: text("address").notNull(), + phoneNumber: varchar("phoneNumber", { length: 13 }).notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletetAt", { mode: "date" }), +}); + +export const respondentsRelations = relations(respondents, ({ one }) => ({ + user: one(users, { + fields: [respondents.userId], + references: [users.id], + }), +})); \ No newline at end of file diff --git a/apps/backend/src/drizzle/schema/roles.ts b/apps/backend/src/drizzle/schema/roles.ts index 6b8bfe5..61e585d 100644 --- a/apps/backend/src/drizzle/schema/roles.ts +++ b/apps/backend/src/drizzle/schema/roles.ts @@ -4,14 +4,14 @@ import { pgTable, text, timestamp, varchar } from "drizzle-orm/pg-core"; import { permissionsToRoles } from "./permissionsToRoles"; export const rolesSchema = pgTable("roles", { - id: text("id") + id: varchar("id", { length: 50 }) .primaryKey() .$defaultFn(() => createId()), code: varchar("code", { length: 50 }).notNull().unique(), name: varchar("name", { length: 255 }).notNull(), description: varchar("description", { length: 255 }), - createdAt: timestamp("created_at").defaultNow(), - updatedAt: timestamp("updated_at").defaultNow(), + createdAt: timestamp("createdAt").defaultNow(), + updatedAt: timestamp("updatedAt").defaultNow(), }); export const rolesRelations = relations(rolesSchema, ({ many }) => ({ diff --git a/apps/backend/src/drizzle/schema/rolesToUsers.ts b/apps/backend/src/drizzle/schema/rolesToUsers.ts index dcf422c..396cda4 100644 --- a/apps/backend/src/drizzle/schema/rolesToUsers.ts +++ b/apps/backend/src/drizzle/schema/rolesToUsers.ts @@ -1,4 +1,4 @@ -import { pgTable, primaryKey, text } from "drizzle-orm/pg-core"; +import { pgTable, primaryKey, varchar } from "drizzle-orm/pg-core"; import { users } from "./users"; import { relations } from "drizzle-orm"; import { rolesSchema } from "./roles"; @@ -6,10 +6,10 @@ import { rolesSchema } from "./roles"; export const rolesToUsers = pgTable( "roles_to_users", { - userId: text("userId") + userId: varchar("userId", { length: 50 }) .notNull() .references(() => users.id), - roleId: text("roleId") + roleId: varchar("roleId", { length: 50 }) .notNull() .references(() => rolesSchema.id), }, diff --git a/apps/backend/src/drizzle/schema/subAspects.ts b/apps/backend/src/drizzle/schema/subAspects.ts new file mode 100644 index 0000000..e1df105 --- /dev/null +++ b/apps/backend/src/drizzle/schema/subAspects.ts @@ -0,0 +1,14 @@ +import { createId } from "@paralleldrive/cuid2"; +import { pgTable, timestamp, varchar } from "drizzle-orm/pg-core"; +import { aspects } from "./aspects"; + +export const subAspects = pgTable("sub_aspects", { + id: varchar("id", { length: 50 }) + .primaryKey() + .$defaultFn(() => createId()), + aspectId: varchar("aspectId").references(() => aspects.id), + name: varchar("name", { length: 255 }).notNull(), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletedAt", { mode: "date" }), +}); diff --git a/apps/backend/src/drizzle/schema/users.ts b/apps/backend/src/drizzle/schema/users.ts index af91337..0574af0 100644 --- a/apps/backend/src/drizzle/schema/users.ts +++ b/apps/backend/src/drizzle/schema/users.ts @@ -9,22 +9,27 @@ import { } from "drizzle-orm/pg-core"; import { permissionsToUsers } from "./permissionsToUsers"; import { rolesToUsers } from "./rolesToUsers"; +import { respondents } from "./respondents"; export const users = pgTable("users", { - id: text("id") + id: varchar("id", { length: 50 }) .primaryKey() .$defaultFn(() => createId()), name: varchar("name", { length: 255 }).notNull(), username: varchar("username").notNull().unique(), email: varchar("email"), password: text("password").notNull(), - isEnabled: boolean("is_enable").default(true), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow(), - deletedAt: timestamp("deleted_at", { mode: "date" }), + isEnabled: boolean("isEnabled").default(true), + createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(), + updatedAt: timestamp("updatedAt", { mode: "date" }).defaultNow(), + deletedAt: timestamp("deletedAt", { mode: "date" }), }); -export const usersRelations = relations(users, ({ many }) => ({ +export const usersRelations = relations(users, ({ many, one}) => ({ permissionsToUsers: many(permissionsToUsers), rolesToUsers: many(rolesToUsers), + respondent: one(respondents, { + fields: [users.id], + references: [respondents.userId], + }), })); diff --git a/apps/backend/src/drizzle/seed.ts b/apps/backend/src/drizzle/seed.ts index 305be1b..bb98014 100644 --- a/apps/backend/src/drizzle/seed.ts +++ b/apps/backend/src/drizzle/seed.ts @@ -2,6 +2,8 @@ import db from "."; import permissionSeeder from "./seeds/permissionSeeder"; import roleSeeder from "./seeds/rolesSeeder"; import userSeeder from "./seeds/userSeeder"; +import aspectsSeeder from "./seeds/aspectsSeeder"; +import subAspectsSeeder from "./seeds/subAspectsSeeder"; (async () => { console.time("Done seeding"); @@ -9,6 +11,8 @@ import userSeeder from "./seeds/userSeeder"; await permissionSeeder(); await roleSeeder(); await userSeeder(); + await aspectsSeeder(); + await subAspectsSeeder(); })().then(() => { console.log("\n"); console.timeEnd("Done seeding"); diff --git a/apps/backend/src/drizzle/seeds/aspectsSeeder.ts b/apps/backend/src/drizzle/seeds/aspectsSeeder.ts new file mode 100644 index 0000000..0438d9d --- /dev/null +++ b/apps/backend/src/drizzle/seeds/aspectsSeeder.ts @@ -0,0 +1,69 @@ +import { aspects } from "../schema/aspects"; +import db from ".."; +import { eq } from "drizzle-orm"; + + +const aspectsSeeder = async () => { + const aspectsData: (typeof aspects.$inferInsert)[] = [ + { + name: "Tata Kelola", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + }, + { + name: "Identifikasi", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + }, + { + name: "Proteksi", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + }, + { + name: "Deteksi", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + }, + { + name: "Respon", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + }, + ]; + + console.log("Seeding aspects..."); + + for (let aspect of aspectsData) { + // Check if the aspect already exists + const existingAspect = await db + .select() + .from(aspects) + .where(eq(aspects.name, aspect.name)) + .limit(1); + + if (existingAspect.length === 0) { + // If the aspect does not exist, insert it + const insertedAspect = ( + await db + .insert(aspects) + .values(aspect) + .onConflictDoNothing() + .returning() + )[0]; + + if (insertedAspect) { + console.log(`Aspect ${aspect.name} created`); + } + } else { + console.log(`Aspect ${aspect.name} already exists`); + } + } +}; + +export default aspectsSeeder; diff --git a/apps/backend/src/drizzle/seeds/subAspectsSeeder.ts b/apps/backend/src/drizzle/seeds/subAspectsSeeder.ts new file mode 100644 index 0000000..7c082d2 --- /dev/null +++ b/apps/backend/src/drizzle/seeds/subAspectsSeeder.ts @@ -0,0 +1,273 @@ +import { subAspects } from "../schema/subAspects"; +import db from ".."; +import { aspects } from "../schema/aspects"; +import { eq, and } from "drizzle-orm"; + +const subAspectSeeder = async () => { + const subAspectsData: (typeof subAspects.$inferInsert & { aspectName: string })[] = [ + /////// Aspect 1 + { + name: "Kesadaran", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + { + name: "Audit", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + { + name: "Kontrol", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + { + name: "Pemenuhan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + { + name: "Kebijakan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + { + name: "Proses", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Tata Kelola", + }, + /////// Aspect 2 + { + name: "Manajemen Aset", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + { + name: "Inventaris", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + { + name: "Manajemen Risiko", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + { + name: "Prioritas", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + { + name: "Pelaporan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + { + name: "Klasifikasi", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Identifikasi", + }, + /////// Aspect 3 + { + name: "Jaringan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + { + name: "Aplikasi", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + { + name: "Pengguna", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + { + name: "Manajemen Identitas dan Aset", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + { + name: "Cloud", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + { + name: "Data", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Proteksi", + }, + /////// Aspect 4 + { + name: "Perubahan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + { + name: "Monitor", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + { + name: "Peringatan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + { + name: "Pemberitahuan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + { + name: "Intelijen", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + { + name: "Pelaporan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Deteksi", + }, + /////// Aspect 5 + { + name: "Penahanan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Respon", + }, + { + name: "Penanggulangan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Respon", + }, + { + name: "Pemulihan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Respon", + }, + { + name: "Kegiatan Paska Insiden", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Respon", + }, + { + name: "Pelaporan", + createdAt: new Date(), + updatedAt: new Date(), + deletedAt: null, + aspectName: "Respon", + }, + ]; + + console.log("Seeding subAspects..."); + + const memoizedAspectIds: Map = new Map(); + + for (let subAspect of subAspectsData) { + // Check if aspect ID is already memoized + if (!memoizedAspectIds.has(subAspect.aspectName)) { + const aspect = ( + await db + .select({ id: aspects.id }) + .from(aspects) + .where(eq(aspects.name, subAspect.aspectName)) + )[0]; + + if (!aspect) { + throw new Error(`Aspect ${subAspect.aspectName} does not exist in the database`); + } + + memoizedAspectIds.set(subAspect.aspectName, aspect.id); + } + + const aspectId = memoizedAspectIds.get(subAspect.aspectName)!; + + // Check if the subAspect already exists + const existingSubAspect = await db + .select() + .from(subAspects) + .where(and(eq(subAspects.name, subAspect.name), eq(subAspects.aspectId, aspectId))) + .limit(1); + + if (existingSubAspect.length === 0) { + // If the subAspect does not exist, insert it + const insertedSubAspect = ( + await db + .insert(subAspects) + .values({ + name: subAspect.name, + createdAt: subAspect.createdAt, + updatedAt: subAspect.updatedAt, + deletedAt: subAspect.deletedAt, + aspectId: aspectId + }) + .onConflictDoNothing() + .returning() + )[0]; + + if (insertedSubAspect) { + console.log(`SubAspect ${subAspect.name} created and linked to aspect ${subAspect.aspectName}`); + } + } else { + console.log(`SubAspect ${subAspect.name} already exists`); + } + } +}; + +export default subAspectSeeder;