diff --git a/drizzle/0017_rainy_adam_warlock.sql b/drizzle/0017_rainy_adam_warlock.sql
new file mode 100644
index 0000000..02f091b
--- /dev/null
+++ b/drizzle/0017_rainy_adam_warlock.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "email_email_templates" (
+ "id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "email_email_templates_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
+ "subject" text NOT NULL,
+ "body" text NOT NULL,
+ "created_by_id" bigint NOT NULL,
+ "modified_by_id" bigint,
+ "updated_at" timestamp (0) with time zone,
+ "created_at" timestamp (0) with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "email_email_templates" ADD CONSTRAINT "email_email_templates_created_by_id_tg_permissions_user_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."tg_permissions"("user_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "email_email_templates" ADD CONSTRAINT "email_email_templates_modified_by_id_tg_permissions_user_id_fk" FOREIGN KEY ("modified_by_id") REFERENCES "public"."tg_permissions"("user_id") ON DELETE no action ON UPDATE no action;
\ No newline at end of file
diff --git a/drizzle/meta/0017_snapshot.json b/drizzle/meta/0017_snapshot.json
new file mode 100644
index 0000000..cefe1f4
--- /dev/null
+++ b/drizzle/meta/0017_snapshot.json
@@ -0,0 +1,2302 @@
+{
+ "id": "c45cd745-9e86-4f02-987d-8094cd889b5f",
+ "prevId": "b0ac1c7c-a42f-43c1-a703-3aabafad59f4",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.auth_accounts": {
+ "name": "auth_accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "account_userId_idx": {
+ "name": "account_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auth_accounts_user_id_auth_users_id_fk": {
+ "name": "auth_accounts_user_id_auth_users_id_fk",
+ "tableFrom": "auth_accounts",
+ "tableTo": "auth_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_passkey": {
+ "name": "auth_passkey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_key": {
+ "name": "public_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backed_up": {
+ "name": "backed_up",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aaguid": {
+ "name": "aaguid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "passkey_userId_idx": {
+ "name": "passkey_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "passkey_credentialID_idx": {
+ "name": "passkey_credentialID_idx",
+ "columns": [
+ {
+ "expression": "credential_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auth_passkey_user_id_auth_users_id_fk": {
+ "name": "auth_passkey_user_id_auth_users_id_fk",
+ "tableFrom": "auth_passkey",
+ "tableTo": "auth_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_sessions": {
+ "name": "auth_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "session_userId_idx": {
+ "name": "session_userId_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auth_sessions_user_id_auth_users_id_fk": {
+ "name": "auth_sessions_user_id_auth_users_id_fk",
+ "tableFrom": "auth_sessions",
+ "tableTo": "auth_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "auth_sessions_token_unique": {
+ "name": "auth_sessions_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_users": {
+ "name": "auth_users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tg_id": {
+ "name": "tg_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tg_username": {
+ "name": "tg_username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "auth_users_email_unique": {
+ "name": "auth_users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_verifications": {
+ "name": "auth_verifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.common_group_labels": {
+ "name": "common_group_labels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "common_group_labels_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "color": {
+ "name": "color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#ffffff'"
+ },
+ "granted_by_id": {
+ "name": "granted_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "common_group_labels_granted_by_id_tg_permissions_user_id_fk": {
+ "name": "common_group_labels_granted_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "common_group_labels",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "granted_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "common_group_labels_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "common_group_labels_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "common_group_labels",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "common_group_labels_label_unique": {
+ "name": "common_group_labels_label_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "label"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_email_templates": {
+ "name": "email_email_templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "email_email_templates_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "9223372036854775807",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "email_email_templates_created_by_id_tg_permissions_user_id_fk": {
+ "name": "email_email_templates_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "email_email_templates",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "email_email_templates_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "email_email_templates_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "email_email_templates",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_audit_log": {
+ "name": "tg_audit_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tg_audit_log_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "admin_id": {
+ "name": "admin_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "until": {
+ "name": "until",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reason": {
+ "name": "reason",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "auditlog_adminid_idx": {
+ "name": "auditlog_adminid_idx",
+ "columns": [
+ {
+ "expression": "admin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_grants": {
+ "name": "tg_grants",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tg_grants_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_by_id": {
+ "name": "granted_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "valid_since": {
+ "name": "valid_since",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "valid_until": {
+ "name": "valid_until",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "interrupted_by_id": {
+ "name": "interrupted_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tg_grants_user_id_idx": {
+ "name": "tg_grants_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tg_grants_granted_by_id_tg_permissions_user_id_fk": {
+ "name": "tg_grants_granted_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "tg_grants",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "granted_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tg_grants_interrupted_by_id_tg_permissions_user_id_fk": {
+ "name": "tg_grants_interrupted_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "tg_grants",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "interrupted_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_groups": {
+ "name": "tg_groups",
+ "schema": "",
+ "columns": {
+ "telegram_id": {
+ "name": "telegram_id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag": {
+ "name": "tag",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "link": {
+ "name": "link",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hide": {
+ "name": "hide",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tg_groups_link_unique": {
+ "name": "tg_groups_link_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "link"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_group_label_relations": {
+ "name": "tg_group_label_relations",
+ "schema": "",
+ "columns": {
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label_id": {
+ "name": "label_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "tg_group_label_relations_group_id_tg_groups_telegram_id_fk": {
+ "name": "tg_group_label_relations_group_id_tg_groups_telegram_id_fk",
+ "tableFrom": "tg_group_label_relations",
+ "tableTo": "tg_groups",
+ "columnsFrom": [
+ "group_id"
+ ],
+ "columnsTo": [
+ "telegram_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tg_group_label_relations_label_id_common_group_labels_id_fk": {
+ "name": "tg_group_label_relations_label_id_common_group_labels_id_fk",
+ "tableFrom": "tg_group_label_relations",
+ "tableTo": "common_group_labels",
+ "columnsFrom": [
+ "label_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "tg_group_label_relations_group_id_label_id_pk": {
+ "name": "tg_group_label_relations_group_id_label_id_pk",
+ "columns": [
+ "group_id",
+ "label_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_link": {
+ "name": "tg_link",
+ "schema": "",
+ "columns": {
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "ttl": {
+ "name": "ttl",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tg_username": {
+ "name": "tg_username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tg_id": {
+ "name": "tg_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "tg_link_user_id_auth_users_id_fk": {
+ "name": "tg_link_user_id_auth_users_id_fk",
+ "tableFrom": "tg_link",
+ "tableTo": "auth_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tg_link_tg_id_unique": {
+ "name": "tg_link_tg_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "tg_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_messages": {
+ "name": "tg_messages",
+ "schema": "",
+ "columns": {
+ "chat_id": {
+ "name": "chat_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "varchar(8704)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tg_messages_chat_author_timestamp_idx": {
+ "name": "tg_messages_chat_author_timestamp_idx",
+ "columns": [
+ {
+ "expression": "chat_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "timestamp",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "tg_messages_chat_id_message_id_pk": {
+ "name": "tg_messages_chat_id_message_id_pk",
+ "columns": [
+ "chat_id",
+ "message_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_group_admins": {
+ "name": "tg_group_admins",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "added_by_id": {
+ "name": "added_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "user_id_idx": {
+ "name": "user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "tg_group_admins_user_id_group_id_pk": {
+ "name": "tg_group_admins_user_id_group_id_pk",
+ "columns": [
+ "user_id",
+ "group_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_permissions": {
+ "name": "tg_permissions",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "roles": {
+ "name": "roles",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"admin\"}'"
+ },
+ "added_by_id": {
+ "name": "added_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_test": {
+ "name": "tg_test",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tg_test_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "text": {
+ "name": "text",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tg_users": {
+ "name": "tg_users",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(192)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(192)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_bot": {
+ "name": "is_bot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "lang_code": {
+ "name": "lang_code",
+ "type": "varchar(35)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.wa_group_label_relations": {
+ "name": "wa_group_label_relations",
+ "schema": "",
+ "columns": {
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label_id": {
+ "name": "label_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "wa_group_label_relations_group_id_wa_groups_group_id_fk": {
+ "name": "wa_group_label_relations_group_id_wa_groups_group_id_fk",
+ "tableFrom": "wa_group_label_relations",
+ "tableTo": "wa_groups",
+ "columnsFrom": [
+ "group_id"
+ ],
+ "columnsTo": [
+ "group_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "wa_group_label_relations_label_id_common_group_labels_id_fk": {
+ "name": "wa_group_label_relations_label_id_common_group_labels_id_fk",
+ "tableFrom": "wa_group_label_relations",
+ "tableTo": "common_group_labels",
+ "columnsFrom": [
+ "label_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "wa_group_label_relations_group_id_label_id_pk": {
+ "name": "wa_group_label_relations_group_id_label_id_pk",
+ "columns": [
+ "group_id",
+ "label_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.wa_groups": {
+ "name": "wa_groups",
+ "schema": "",
+ "columns": {
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "wa_groups_group_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "9223372036854775807",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hide": {
+ "name": "hide",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "wa_groups_link_unique": {
+ "name": "wa_groups_link_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "link"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_associations": {
+ "name": "web_associations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_associations_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_it": {
+ "name": "description_it",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_en": {
+ "name": "description_en",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website": {
+ "name": "website",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "facebook": {
+ "name": "facebook",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "instagram": {
+ "name": "instagram",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tiktok": {
+ "name": "tiktok",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "x": {
+ "name": "x",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "youtube": {
+ "name": "youtube",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "telegram": {
+ "name": "telegram",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin": {
+ "name": "linkedin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "spotify": {
+ "name": "spotify",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "web_associations_created_by_id_tg_permissions_user_id_fk": {
+ "name": "web_associations_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_associations",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "web_associations_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "web_associations_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_associations",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_faq_categories": {
+ "name": "web_faq_categories",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_faq_categories_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "title_it": {
+ "name": "title_it",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title_en": {
+ "name": "title_en",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "web_faq_categories_created_by_id_tg_permissions_user_id_fk": {
+ "name": "web_faq_categories_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_faq_categories",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "web_faq_categories_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "web_faq_categories_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_faq_categories",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_faqs": {
+ "name": "web_faqs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_faqs_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "title_it": {
+ "name": "title_it",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title_en": {
+ "name": "title_en",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_it": {
+ "name": "description_it",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_en": {
+ "name": "description_en",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "category_id": {
+ "name": "category_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "web_faqs_category_id_web_faq_categories_id_fk": {
+ "name": "web_faqs_category_id_web_faq_categories_id_fk",
+ "tableFrom": "web_faqs",
+ "tableTo": "web_faq_categories",
+ "columnsFrom": [
+ "category_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "web_faqs_created_by_id_tg_permissions_user_id_fk": {
+ "name": "web_faqs_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_faqs",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "web_faqs_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "web_faqs_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_faqs",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_guides_matricole": {
+ "name": "web_guides_matricole",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_guides_matricole_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "date": {
+ "name": "date",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file": {
+ "name": "file",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "web_guides_matricole_created_by_id_tg_permissions_user_id_fk": {
+ "name": "web_guides_matricole_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_guides_matricole",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "web_guides_matricole_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "web_guides_matricole_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_guides_matricole",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "web_guides_matricole_version_unique": {
+ "name": "web_guides_matricole_version_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "version"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_projects": {
+ "name": "web_projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_projects_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_it": {
+ "name": "description_it",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description_en": {
+ "name": "description_en",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "link": {
+ "name": "link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'general'"
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "modified_by_id": {
+ "name": "modified_by_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "web_projects_created_by_id_tg_permissions_user_id_fk": {
+ "name": "web_projects_created_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_projects",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "web_projects_modified_by_id_tg_permissions_user_id_fk": {
+ "name": "web_projects_modified_by_id_tg_permissions_user_id_fk",
+ "tableFrom": "web_projects",
+ "tableTo": "tg_permissions",
+ "columnsFrom": [
+ "modified_by_id"
+ ],
+ "columnsTo": [
+ "user_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.web_test": {
+ "name": "web_test",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "web_test_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "text": {
+ "name": "text",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {
+ "public.groups_view": {
+ "columns": {
+ "telegram_id": {
+ "name": "telegram_id",
+ "type": "bigint",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hide": {
+ "name": "hide",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "definition": "(select \"telegram_id\", \"title\", \"link\", \"hide\", 'tg' as \"type\" from \"tg_groups\") union all (select \"group_id\", \"title\", \"link\", \"hide\", 'wa' as \"type\" from \"wa_groups\")",
+ "name": "groups_view",
+ "schema": "public",
+ "isExisting": false,
+ "materialized": false
+ },
+ "public.group_label_relations_view": {
+ "columns": {
+ "group_id": {
+ "name": "group_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label_id": {
+ "name": "label_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "definition": "(select \"group_id\", \"label_id\", 'tg' as \"type\" from \"tg_group_label_relations\") union all (select \"group_id\", \"label_id\", 'wa' as \"type\" from \"wa_group_label_relations\")",
+ "name": "group_label_relations_view",
+ "schema": "public",
+ "isExisting": false,
+ "materialized": false
+ }
+ },
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index c44814e..37a5a89 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -120,6 +120,13 @@
"when": 1788360706113,
"tag": "0016_telegram_message_lookup",
"breakpoints": true
+ },
+ {
+ "idx": 17,
+ "version": "7",
+ "when": 1788784396506,
+ "tag": "0017_rainy_adam_warlock",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/src/db/index.ts b/src/db/index.ts
index 63b0e43..ea3e004 100644
--- a/src/db/index.ts
+++ b/src/db/index.ts
@@ -1,6 +1,7 @@
import { db } from "./db"
import { schema as authSchema } from "./schema/auth"
import { schema as commonSchema } from "./schema/common"
+import { schema as emailSchema } from "./schema/email"
import { schema as tgSchema } from "./schema/tg"
import { views } from "./schema/views"
import { schema as waSchema } from "./schema/wa"
@@ -13,6 +14,7 @@ export const SCHEMA = {
TG: tgSchema,
WA: waSchema,
WEB: webSchema,
+ EMAIL: emailSchema,
}
export const VIEWS = {
GROUPS: views,
diff --git a/src/db/schema/create-table.ts b/src/db/schema/create-table.ts
index 14a25fc..b087eb5 100644
--- a/src/db/schema/create-table.ts
+++ b/src/db/schema/create-table.ts
@@ -6,4 +6,5 @@ export const createTable = {
tg: pgTableCreator((tableName) => `tg_${tableName}`),
wa: pgTableCreator((tableName) => `wa_${tableName}`),
web: pgTableCreator((tableName) => `web_${tableName}`),
+ email: pgTableCreator((tableName) => `email_${tableName}`),
}
diff --git a/src/db/schema/email/email_template.ts b/src/db/schema/email/email_template.ts
new file mode 100644
index 0000000..72f7c61
--- /dev/null
+++ b/src/db/schema/email/email_template.ts
@@ -0,0 +1,15 @@
+import { bigint, text } from "drizzle-orm/pg-core"
+import { timeColumns } from "@/db/columns"
+import { createTable } from "../create-table"
+import { permissions } from "../tg/permissions"
+
+export const emailTemplateSchema = createTable.email("email_templates", {
+ id: bigint("id", { mode: "number" }).primaryKey().generatedAlwaysAsIdentity(),
+ subject: text("subject").notNull(),
+ body: text("body").notNull(),
+ createdBy: bigint("created_by_id", { mode: "number" })
+ .references(() => permissions.userId)
+ .notNull(),
+ modifiedBy: bigint("modified_by_id", { mode: "number" }).references(() => permissions.userId),
+ ...timeColumns,
+})
diff --git a/src/db/schema/email/index.tsx b/src/db/schema/email/index.tsx
new file mode 100644
index 0000000..387fc9d
--- /dev/null
+++ b/src/db/schema/email/index.tsx
@@ -0,0 +1,3 @@
+import * as email_template from "./email_template";
+
+export const schema = { ...email_template }
diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts
index 96d41cd..6528e6b 100644
--- a/src/db/schema/index.ts
+++ b/src/db/schema/index.ts
@@ -1,5 +1,6 @@
import { schema as authSchema } from "./auth"
import { schema as commonSchema } from "./common"
+import { schema as emailSchema } from "./email"
import { schema as tgSchema } from "./tg"
import { views } from "./views"
import { schema as waSchema } from "./wa"
@@ -11,5 +12,6 @@ export const schema = {
...tgSchema,
...waSchema,
...webSchema,
+ ...emailSchema,
...views,
}
diff --git a/src/emails/mailer.tsx b/src/emails/mailer.tsx
index 2dfb4f7..8f485ec 100644
--- a/src/emails/mailer.tsx
+++ b/src/emails/mailer.tsx
@@ -4,6 +4,7 @@ import type { JSX } from "react"
import { sendEmail as azureSendEmail } from "@/azure/functions/emails"
import { env } from "@/env"
import { logger } from "@/logger"
+import CustomMessageEmail from "./templates/custom"
import OtpEmail from "./templates/otp"
import Welcome from "./templates/welcome"
@@ -51,3 +52,16 @@ export async function sendWelcomeEmail(
/>
)
}
+
+export async function sendCustomEmail(
+ toAddress: string,
+ content: { subject: string; body: string },
+ firstName: string
+) {
+ const customSubject = makeSubject(content.subject)
+ return sendEmail(
+ toAddress,
+ customSubject,
+
+ )
+}
diff --git a/src/emails/templates/custom.tsx b/src/emails/templates/custom.tsx
new file mode 100644
index 0000000..8049af3
--- /dev/null
+++ b/src/emails/templates/custom.tsx
@@ -0,0 +1,49 @@
+import {
+ Body,
+ Container,
+ Head,
+ Heading,
+ Html,
+ Preview,
+ pixelBasedPreset,
+ Section,
+ Tailwind,
+ Text,
+} from "@react-email/components"
+import { Footer } from "@/emails/components/footer"
+import { Logo } from "@/emails/components/logo"
+import type { EmailTemplate } from "@/emails/types"
+
+type Props = { firstName: string; subject: string; body: string }
+
+const CustomMessageEmail: EmailTemplate = (props) => {
+ return (
+
+
+
+ {props.subject}
+
+
+
+ {props.subject}
+
+
+ Dear {props.firstName},
+
+ {props.body}
+
+
+
+
+
+
+ )
+}
+
+CustomMessageEmail.PreviewProps = {
+ firstName: "Mario",
+ subject: "Renew your PoliNetwork membership",
+ body: "Dear Mario,\n\n please renew your membership.",
+}
+
+export default CustomMessageEmail
diff --git a/src/routers/azure/members.ts b/src/routers/azure/members.ts
index 95a853e..2faeca4 100644
--- a/src/routers/azure/members.ts
+++ b/src/routers/azure/members.ts
@@ -1,6 +1,6 @@
import z from "zod"
import { azureDirectory } from "@/azure/directory"
-import { sendWelcomeEmail } from "@/emails/mailer"
+import { sendCustomEmail, sendWelcomeEmail } from "@/emails/mailer"
import { logger } from "@/logger"
import { createTRPCRouter, publicProcedure } from "@/trpc"
@@ -68,4 +68,62 @@ export default createTRPCRouter({
return { error: error instanceof Error ? error.message : "Unknown error, see backend logs" }
}
}),
+ sendCustomEmail: publicProcedure
+ .input(
+ z.object({
+ userIds: z.array(z.string().min(1)),
+ subject: z.string().min(1),
+ body: z.string().min(1),
+ })
+ )
+ .output(
+ z.object({
+ error: z.nullable(z.string()),
+ total: z.number(),
+ sent: z.number(),
+ failed: z.array(z.object({ userId: z.string(), error: z.nullable(z.string()) })),
+ })
+ )
+ .mutation(async ({ input }) => {
+ try {
+ const members = await azureDirectory.getMembers()
+ const targetMembers = members.filter((m) => input.userIds.includes(m.id))
+
+ if (targetMembers.length === 0) {
+ return { error: "No valid members found to send email", total: 0, sent: 0, failed: [] }
+ }
+
+ let sent = 0
+ const failed = []
+
+ for (const member of targetMembers) {
+ if (!member.mail) {
+ failed.push({ userId: member.id, error: "Member does not have an email address" })
+ continue
+ }
+
+ const mailOk = await sendCustomEmail(
+ member.mail,
+ { subject: input.subject, body: input.body },
+ member.givenName || "Member"
+ )
+
+ if (!mailOk) {
+ failed.push({ userId: member.id, error: "Failed to send email" })
+ } else {
+ sent++
+ }
+ }
+
+ return { error: null, total: targetMembers.length, sent, failed }
+ } catch (error) {
+ logger.error({ error }, "[trpc:azure:members] error in sendCustomEmail procedure")
+ return {
+ error: error instanceof Error ? error.message : "Unknown error, see backend logs",
+ total: 0,
+ sent: 0,
+ failed: [],
+ }
+ }
+ }),
})
diff --git a/src/routers/email/email_templates.ts b/src/routers/email/email_templates.ts
new file mode 100644
index 0000000..31c685d
--- /dev/null
+++ b/src/routers/email/email_templates.ts
@@ -0,0 +1,83 @@
+import { asc, eq } from "drizzle-orm"
+import z from "zod"
+import { DB, SCHEMA } from "@/db"
+import { createTRPCRouter, publicProcedure } from "@/trpc"
+
+const EMAIL_TEMPLATES = SCHEMA.EMAIL.emailTemplateSchema
+
+export default createTRPCRouter({
+ getAll: publicProcedure
+ .output(
+ z.array(
+ z.object({
+ id: z.number(),
+ subject: z.string(),
+ body: z.string(),
+ })
+ )
+ )
+ .query(async () => {
+ const results = await DB.select().from(EMAIL_TEMPLATES).orderBy(asc(EMAIL_TEMPLATES.id))
+
+ return results
+ }),
+
+ add: publicProcedure
+ .input(
+ z.object({
+ subject: z.string().min(1),
+ body: z.string().min(1),
+ createdBy: z.number(),
+ })
+ )
+ .mutation(async ({ input }) => {
+ const { subject, body, createdBy } = input
+
+ const [res] = await DB.insert(EMAIL_TEMPLATES)
+ .values({
+ subject,
+ body,
+ createdBy,
+ })
+ .returning()
+
+ return res
+ }),
+ edit: publicProcedure
+ .input(
+ z.object({
+ id: z.number(),
+ subject: z.string().min(1),
+ body: z.string().min(1),
+ modifiedBy: z.number(),
+ })
+ )
+ .mutation(async ({ input }) => {
+ const { id, subject, body, modifiedBy } = input
+
+ const [res] = await DB.update(EMAIL_TEMPLATES)
+ .set({
+ subject,
+ body,
+ modifiedBy,
+ })
+ .where(eq(EMAIL_TEMPLATES.id, id))
+ .returning()
+
+ if (!res) return { error: "NOT_FOUND" }
+ return res
+ }),
+ delete: publicProcedure
+ .input(
+ z.object({
+ id: z.number(),
+ })
+ )
+ .mutation(async ({ input }) => {
+ const { id } = input
+ const deleted = await DB.delete(EMAIL_TEMPLATES).where(eq(EMAIL_TEMPLATES.id, id)).returning()
+
+ if (deleted.length === 0) return { error: "NOT_FOUND" }
+ return { error: null }
+ }),
+})
diff --git a/src/routers/email/index.ts b/src/routers/email/index.ts
new file mode 100644
index 0000000..324e466
--- /dev/null
+++ b/src/routers/email/index.ts
@@ -0,0 +1,6 @@
+import { createTRPCRouter } from "@/trpc"
+import emailTemplates from "./email_templates"
+
+export const emailRouter = createTRPCRouter({
+ templates: emailTemplates,
+})
diff --git a/src/routers/index.ts b/src/routers/index.ts
index 38e6584..8f2bf34 100644
--- a/src/routers/index.ts
+++ b/src/routers/index.ts
@@ -1,3 +1,4 @@
+import { emailRouter } from "@/routers/email"
import { createTRPCRouter } from "@/trpc"
import { authRouter } from "./auth"
import { azureRouter } from "./azure"
@@ -20,6 +21,7 @@ export const appRouter = createTRPCRouter({
tg: tgRouter,
wa: waRouter,
web: webRouter,
+ email: emailRouter,
})
// export type definition of API