initialize git, basic setup for crm
This commit is contained in:
32
src/db/migrate.js
Normal file
32
src/db/migrate.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
||||
import pkg from 'pg';
|
||||
const { Pool } = pkg;
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const pool = new Pool({
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT || '5432'),
|
||||
user: process.env.DB_USER || 'admin',
|
||||
password: process.env.DB_PASSWORD || 'heslo123',
|
||||
database: process.env.DB_NAME || 'crm',
|
||||
});
|
||||
|
||||
const db = drizzle(pool);
|
||||
|
||||
async function runMigrations() {
|
||||
console.log('⏳ Running migrations...');
|
||||
|
||||
try {
|
||||
await migrate(db, { migrationsFolder: './src/db/migrations' });
|
||||
console.log('✅ Migrations completed successfully');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runMigrations();
|
||||
36
src/db/migrations/0000_legal_karnak.sql
Normal file
36
src/db/migrations/0000_legal_karnak.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
CREATE TYPE "public"."role" AS ENUM('admin', 'member');--> statement-breakpoint
|
||||
CREATE TABLE "audit_logs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid,
|
||||
"action" text NOT NULL,
|
||||
"resource" text NOT NULL,
|
||||
"resource_id" text,
|
||||
"old_value" text,
|
||||
"new_value" text,
|
||||
"ip_address" text,
|
||||
"user_agent" text,
|
||||
"success" boolean DEFAULT true NOT NULL,
|
||||
"error_message" text,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"username" text NOT NULL,
|
||||
"email" text,
|
||||
"email_password" text,
|
||||
"jmap_account_id" text,
|
||||
"first_name" text,
|
||||
"last_name" text,
|
||||
"password" text,
|
||||
"temp_password" text,
|
||||
"changed_password" boolean DEFAULT false,
|
||||
"role" "role" DEFAULT 'member' NOT NULL,
|
||||
"last_login" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "users_username_unique" UNIQUE("username"),
|
||||
CONSTRAINT "users_email_unique" UNIQUE("email")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;
|
||||
34
src/db/migrations/0001_slow_drax.sql
Normal file
34
src/db/migrations/0001_slow_drax.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE "contacts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"name" text,
|
||||
"notes" text,
|
||||
"added_at" timestamp DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emails" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"contact_id" uuid,
|
||||
"jmap_id" text,
|
||||
"message_id" text,
|
||||
"thread_id" text,
|
||||
"in_reply_to" text,
|
||||
"from" text,
|
||||
"to" text,
|
||||
"subject" text,
|
||||
"body" text,
|
||||
"is_read" boolean DEFAULT false NOT NULL,
|
||||
"date" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "emails_jmap_id_unique" UNIQUE("jmap_id"),
|
||||
CONSTRAINT "emails_message_id_unique" UNIQUE("message_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "contacts" ADD CONSTRAINT "contacts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emails" ADD CONSTRAINT "emails_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emails" ADD CONSTRAINT "emails_contact_id_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."contacts"("id") ON DELETE cascade ON UPDATE no action;
|
||||
248
src/db/migrations/meta/0000_snapshot.json
Normal file
248
src/db/migrations/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"id": "d81153f7-e0c6-4843-bee9-21a7129f7d01",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.audit_logs": {
|
||||
"name": "audit_logs",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"resource_id": {
|
||||
"name": "resource_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"old_value": {
|
||||
"name": "old_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"new_value": {
|
||||
"name": "new_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ip_address": {
|
||||
"name": "ip_address",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_agent": {
|
||||
"name": "user_agent",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"success": {
|
||||
"name": "success",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"error_message": {
|
||||
"name": "error_message",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"audit_logs_user_id_users_id_fk": {
|
||||
"name": "audit_logs_user_id_users_id_fk",
|
||||
"tableFrom": "audit_logs",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email_password": {
|
||||
"name": "email_password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"jmap_account_id": {
|
||||
"name": "jmap_account_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"first_name": {
|
||||
"name": "first_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_name": {
|
||||
"name": "last_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"password": {
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"temp_password": {
|
||||
"name": "temp_password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"changed_password": {
|
||||
"name": "changed_password",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "role",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'member'"
|
||||
},
|
||||
"last_login": {
|
||||
"name": "last_login",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"users_username_unique": {
|
||||
"name": "users_username_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"username"
|
||||
]
|
||||
},
|
||||
"users_email_unique": {
|
||||
"name": "users_email_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"public.role": {
|
||||
"name": "role",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"admin",
|
||||
"member"
|
||||
]
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
476
src/db/migrations/meta/0001_snapshot.json
Normal file
476
src/db/migrations/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,476 @@
|
||||
{
|
||||
"id": "1b8c1e0f-8476-470c-a641-b3c350a2c1a4",
|
||||
"prevId": "d81153f7-e0c6-4843-bee9-21a7129f7d01",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.audit_logs": {
|
||||
"name": "audit_logs",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"resource_id": {
|
||||
"name": "resource_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"old_value": {
|
||||
"name": "old_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"new_value": {
|
||||
"name": "new_value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ip_address": {
|
||||
"name": "ip_address",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_agent": {
|
||||
"name": "user_agent",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"success": {
|
||||
"name": "success",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"error_message": {
|
||||
"name": "error_message",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"audit_logs_user_id_users_id_fk": {
|
||||
"name": "audit_logs_user_id_users_id_fk",
|
||||
"tableFrom": "audit_logs",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.contacts": {
|
||||
"name": "contacts",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"notes": {
|
||||
"name": "notes",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"added_at": {
|
||||
"name": "added_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"contacts_user_id_users_id_fk": {
|
||||
"name": "contacts_user_id_users_id_fk",
|
||||
"tableFrom": "contacts",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.emails": {
|
||||
"name": "emails",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"contact_id": {
|
||||
"name": "contact_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"jmap_id": {
|
||||
"name": "jmap_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"message_id": {
|
||||
"name": "message_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"thread_id": {
|
||||
"name": "thread_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"in_reply_to": {
|
||||
"name": "in_reply_to",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"from": {
|
||||
"name": "from",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"to": {
|
||||
"name": "to",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"subject": {
|
||||
"name": "subject",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"body": {
|
||||
"name": "body",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"is_read": {
|
||||
"name": "is_read",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"date": {
|
||||
"name": "date",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"emails_user_id_users_id_fk": {
|
||||
"name": "emails_user_id_users_id_fk",
|
||||
"tableFrom": "emails",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"emails_contact_id_contacts_id_fk": {
|
||||
"name": "emails_contact_id_contacts_id_fk",
|
||||
"tableFrom": "emails",
|
||||
"tableTo": "contacts",
|
||||
"columnsFrom": [
|
||||
"contact_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"emails_jmap_id_unique": {
|
||||
"name": "emails_jmap_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"jmap_id"
|
||||
]
|
||||
},
|
||||
"emails_message_id_unique": {
|
||||
"name": "emails_message_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"message_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email_password": {
|
||||
"name": "email_password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"jmap_account_id": {
|
||||
"name": "jmap_account_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"first_name": {
|
||||
"name": "first_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_name": {
|
||||
"name": "last_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"password": {
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"temp_password": {
|
||||
"name": "temp_password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"changed_password": {
|
||||
"name": "changed_password",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "role",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'member'"
|
||||
},
|
||||
"last_login": {
|
||||
"name": "last_login",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"users_username_unique": {
|
||||
"name": "users_username_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"username"
|
||||
]
|
||||
},
|
||||
"users_email_unique": {
|
||||
"name": "users_email_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"public.role": {
|
||||
"name": "role",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"admin",
|
||||
"member"
|
||||
]
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
20
src/db/migrations/meta/_journal.json
Normal file
20
src/db/migrations/meta/_journal.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1763450484405,
|
||||
"tag": "0000_legal_karnak",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1763457837858,
|
||||
"tag": "0001_slow_drax",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
69
src/db/schema.js
Normal file
69
src/db/schema.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { pgTable, text, timestamp, boolean, uuid, pgEnum } from 'drizzle-orm/pg-core';
|
||||
|
||||
// Role enum
|
||||
export const roleEnum = pgEnum('role', ['admin', 'member']);
|
||||
|
||||
// Users table - hlavná tabuľka používateľov
|
||||
export const users = pgTable('users', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
username: text('username').notNull().unique(),
|
||||
email: text('email').unique(),
|
||||
emailPassword: text('email_password'), // Heslo k emailovému účtu (encrypted)
|
||||
jmapAccountId: text('jmap_account_id'), // JMAP account ID z truemail
|
||||
firstName: text('first_name'),
|
||||
lastName: text('last_name'),
|
||||
password: text('password'), // bcrypt hash (null ak ešte nenastavené)
|
||||
tempPassword: text('temp_password'), // dočasné heslo (bcrypt hash)
|
||||
changedPassword: boolean('changed_password').default(false), // či si užívateľ zmenil heslo
|
||||
role: roleEnum('role').default('member').notNull(),
|
||||
lastLogin: timestamp('last_login'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Audit logs - kompletný audit trail všetkých akcií
|
||||
export const auditLogs = pgTable('audit_logs', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'set null' }),
|
||||
action: text('action').notNull(), // 'login', 'password_change', 'email_linked', 'role_change', atď.
|
||||
resource: text('resource').notNull(), // 'user', 'auth', atď.
|
||||
resourceId: text('resource_id'), // ID ovplyvneného zdroja
|
||||
oldValue: text('old_value'), // JSON string starých hodnôt
|
||||
newValue: text('new_value'), // JSON string nových hodnôt
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
success: boolean('success').default(true).notNull(),
|
||||
errorMessage: text('error_message'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Contacts table - ľudia s ktorými komunikujeme cez email
|
||||
export const contacts = pgTable('contacts', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
||||
email: text('email').notNull(),
|
||||
name: text('name'),
|
||||
notes: text('notes'),
|
||||
addedAt: timestamp('added_at').defaultNow().notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Emails table - uložené emaily z JMAP (iba pre pridané kontakty)
|
||||
export const emails = pgTable('emails', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
||||
contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }),
|
||||
jmapId: text('jmap_id').unique(),
|
||||
messageId: text('message_id').unique(),
|
||||
threadId: text('thread_id'),
|
||||
inReplyTo: text('in_reply_to'),
|
||||
from: text('from'),
|
||||
to: text('to'),
|
||||
subject: text('subject'),
|
||||
body: text('body'),
|
||||
isRead: boolean('is_read').default(false).notNull(),
|
||||
date: timestamp('date'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
});
|
||||
67
src/db/seeds/admin.seed.js
Normal file
67
src/db/seeds/admin.seed.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { db } from '../../config/database.js';
|
||||
import { users } from '../schema.js';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { hashPassword, generateTempPassword } from '../../utils/password.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* Seed script pre vytvorenie admin účtu
|
||||
*/
|
||||
async function seedAdmin() {
|
||||
try {
|
||||
logger.info('Starting admin user seed...');
|
||||
|
||||
// Skontroluj či admin už existuje
|
||||
const [existingAdmin] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, 'admin'))
|
||||
.limit(1);
|
||||
|
||||
if (existingAdmin) {
|
||||
logger.warn('Admin user already exists. Skipping seed.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Vygeneruj temporary password
|
||||
const tempPassword = generateTempPassword(16);
|
||||
|
||||
// Hash temporary password
|
||||
const hashedTempPassword = await hashPassword(tempPassword);
|
||||
|
||||
// Vytvor admin usera
|
||||
const [newAdmin] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username: 'admin',
|
||||
tempPassword: hashedTempPassword,
|
||||
role: 'admin',
|
||||
changedPassword: false,
|
||||
firstName: 'System',
|
||||
lastName: 'Administrator',
|
||||
})
|
||||
.returning();
|
||||
|
||||
logger.success('Admin user created successfully!');
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
logger.info(' ADMIN CREDENTIALS');
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
logger.info(` Username: admin`);
|
||||
logger.info(` Temporary Password: ${tempPassword}`);
|
||||
logger.info(` User ID: ${newAdmin.id}`);
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
logger.warn(' ⚠️ IMPORTANT: Save this password securely!');
|
||||
logger.warn(' ⚠️ You will need to change it after first login.');
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
logger.error('Failed to seed admin user', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
seedAdmin();
|
||||
62
src/db/seeds/testuser.seed.js
Normal file
62
src/db/seeds/testuser.seed.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { db } from '../../config/database.js';
|
||||
import { users } from '../schema.js';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { hashPassword } from '../../utils/password.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* Seed script pre vytvorenie test usera
|
||||
*/
|
||||
async function seedTestUser() {
|
||||
try {
|
||||
logger.info('Starting test user seed...');
|
||||
|
||||
// Skontroluj či testuser už existuje
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, 'testuser'))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser) {
|
||||
logger.warn('Test user already exists. Deleting old one...');
|
||||
await db.delete(users).where(eq(users.username, 'testuser'));
|
||||
}
|
||||
|
||||
const tempPassword = 'testuser123!';
|
||||
const hashedTempPassword = await hashPassword(tempPassword);
|
||||
|
||||
// Vytvor test usera
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username: 'testuser',
|
||||
tempPassword: hashedTempPassword,
|
||||
role: 'member',
|
||||
changedPassword: false,
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
})
|
||||
.returning();
|
||||
|
||||
logger.success('Test user created successfully!');
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
logger.info(' TEST USER CREDENTIALS');
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
logger.info(` Username: testuser`);
|
||||
logger.info(` Temporary Password: ${tempPassword}`);
|
||||
logger.info(` User ID: ${newUser.id}`);
|
||||
logger.info(` Role: ${newUser.role}`);
|
||||
logger.info('═══════════════════════════════════════════════════════');
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
logger.error('Failed to seed test user', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
seedTestUser();
|
||||
Reference in New Issue
Block a user