diff --git a/.env.example b/.env.example index 480bf3d..780efb3 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,40 @@ REDIS_URL="redis://localhost" DATABASE_URL="postgresql://user:password@host/database" SENTRY_URL="" -DISCORD_SERVER="discord id" +DISCORD_SERVER="discord server id" +GENERAL_CHANNEL="discord channel id" BOT_TOKEN="xxxxx" SXC_TOKEN="xxxxx" BBB_TOKEN="xxxxx" +PROXMOX_HOST="https://proxmox.example.com" +PROXMOX_NODE="pve" +PROXMOX_TEMPLATE="local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.gz" +PROXMOX_BRIDGE="vmbr0" +PROXMOX_PASSWORD="yourproxmoxpassword" +PROXMOX_STORAGE="local-lvm" +PROXMOX_NET_GATEWAY="192.168.1.1" +PROXMOX_NET_IP="192.168.1.0/24" +PROXMOX_NET_MAC="00:00:00:00:00:01" + PTERO_URL="https://pterodactyl.example.com" +PTERO_THEME_URLS="Nova=https://nova.example.com/,Arix=https://arix.example.com/" PTERO_DEMO_SERVERS="id1,id2,id3" PTERO_ADMIN_TOKEN="ptla_xxxxx" PTERO_CLIENT_TOKEN="ptlc_xxxxx" +S3_URL="https://s3.example.com" +S3_SSL="true" +S3_BUCKET="mybucket" +S3_REGION="us-east-1" +S3_HOST="s3.example.com" +S3_PORT="443" +S3_ACCESS_KEY="youraccesskey" +S3_SECRET_KEY="yoursecretkey" + TICKET_CATEGORY="discord category id" +TICKET_LOG_CHANNEL="discord channel id" DEMO_CHANNEL="discord channel id" DEMO_ROLE="discord role id" CUSTOMER_ROLE="discord role id" diff --git a/drizzle/0023_empty_quasimodo.sql b/drizzle/0023_empty_quasimodo.sql new file mode 100644 index 0000000..0c6d2e4 --- /dev/null +++ b/drizzle/0023_empty_quasimodo.sql @@ -0,0 +1,2 @@ +ALTER TABLE "send_messages" ADD COLUMN "sticky" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "send_messages" ADD COLUMN "updated" timestamp DEFAULT now() NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0023_snapshot.json b/drizzle/meta/0023_snapshot.json new file mode 100644 index 0000000..8ed27f1 --- /dev/null +++ b/drizzle/meta/0023_snapshot.json @@ -0,0 +1,1190 @@ +{ + "id": "fbfcd35a-9a39-4ae6-9edb-819d58401359", + "prevId": "22757b68-2b40-40c4-a1bd-b5cd00e574af", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.advent_calendar_days": { + "name": "advent_calendar_days", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "smallserial", + "primaryKey": true, + "notNull": true + }, + "year": { + "name": "year", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maxRedeems": { + "name": "maxRedeems", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "after": { + "name": "after", + "type": "time", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "adventCalendarDays_year_day_idx": { + "name": "adventCalendarDays_year_day_idx", + "columns": [ + { + "expression": "year", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "day", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adventCalendarDays_created_idx": { + "name": "adventCalendarDays_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.advent_calendar_redeems": { + "name": "advent_calendar_redeems", + "schema": "", + "columns": { + "calendarDayId": { + "name": "calendarDayId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "discordId": { + "name": "discordId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "adventCalendarRedeems_calendarDayId_discordId_idx": { + "name": "adventCalendarRedeems_calendarDayId_discordId_idx", + "columns": [ + { + "expression": "calendarDayId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "adventCalendarRedeems_created_idx": { + "name": "adventCalendarRedeems_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "advent_calendar_redeems_calendarDayId_advent_calendar_days_id_fk": { + "name": "advent_calendar_redeems_calendarDayId_advent_calendar_days_id_fk", + "tableFrom": "advent_calendar_redeems", + "tableTo": "advent_calendar_days", + "columnsFrom": [ + "calendarDayId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.automatic_errors": { + "name": "automatic_errors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "allowed_regex": { + "name": "allowed_regex", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "disallowed_regex": { + "name": "disallowed_regex", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "automaticErrors_allowedRegex_idx": { + "name": "automaticErrors_allowedRegex_idx", + "columns": [ + { + "expression": "allowed_regex", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "automaticErrors_created_idx": { + "name": "automaticErrors_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.demo_accesses": { + "name": "demo_accesses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "expired": { + "name": "expired", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "password": { + "name": "password", + "type": "char(16)", + "primaryKey": false, + "notNull": true + }, + "discordId": { + "name": "discordId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "demoAccesses_discordId_idx": { + "name": "demoAccesses_discordId_idx", + "columns": [ + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "demoAccesses_expired_idx": { + "name": "demoAccesses_expired_idx", + "columns": [ + { + "expression": "expired", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "demoAccesses_created_idx": { + "name": "demoAccesses_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.faqs": { + "name": "faqs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(31)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "faqs_title_idx": { + "name": "faqs_title_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "faqs_created_idx": { + "name": "faqs_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "faqs_updated_idx": { + "name": "faqs_updated_idx", + "columns": [ + { + "expression": "updated", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_changelogs": { + "name": "product_changelogs", + "schema": "", + "columns": { + "productId": { + "name": "productId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "varchar(51)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "productChangelogs_productId_version_idx": { + "name": "productChangelogs_productId_version_idx", + "columns": [ + { + "expression": "productId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "productChangelogs_productId_idx": { + "name": "productChangelogs_productId_idx", + "columns": [ + { + "expression": "productId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "productChangelogs_created_idx": { + "name": "productChangelogs_created_idx", + "columns": [ + { + "expression": "created", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_changelogs_productId_products_id_fk": { + "name": "product_changelogs_productId_products_id_fk", + "tableFrom": "product_changelogs", + "tableTo": "products", + "columnsFrom": [ + "productId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_links": { + "name": "product_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "productId": { + "name": "productId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "providerId": { + "name": "providerId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "discordId": { + "name": "discordId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "paymentId": { + "name": "paymentId", + "type": "varchar(51)", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "productLinks_paymentId_idx": { + "name": "productLinks_paymentId_idx", + "columns": [ + { + "expression": "paymentId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "productLinks_discordId_productId_providerId_idx": { + "name": "productLinks_discordId_productId_providerId_idx", + "columns": [ + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "productId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "providerId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_links_productId_products_id_fk": { + "name": "product_links_productId_products_id_fk", + "tableFrom": "product_links", + "tableTo": "products", + "columnsFrom": [ + "productId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "product_links_providerId_product_providers_id_fk": { + "name": "product_links_providerId_product_providers_id_fk", + "tableFrom": "product_links", + "tableTo": "product_providers", + "columnsFrom": [ + "providerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_providers": { + "name": "product_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "productId": { + "name": "productId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "productProvider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "productProviderId": { + "name": "productProviderId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "link": { + "name": "link", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "currency", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "productProviders_productId_provider_idx": { + "name": "productProviders_productId_provider_idx", + "columns": [ + { + "expression": "productId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_providers_productId_products_id_fk": { + "name": "product_providers_productId_products_id_fk", + "tableFrom": "product_providers", + "tableTo": "products", + "columnsFrom": [ + "productId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(51)", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "banner": { + "name": "banner", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "varchar(51)", + "primaryKey": false, + "notNull": true, + "default": "'1.0.0'" + }, + "identifier": { + "name": "identifier", + "type": "varchar(51)", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role": { + "name": "role", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "products_name_idx": { + "name": "products_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_identifier_idx": { + "name": "products_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_role_idx": { + "name": "products_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.send_messages": { + "name": "send_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sticky": { + "name": "sticky", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "discordId": { + "name": "discordId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": false + }, + "discordChannelId": { + "name": "discordChannelId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "ticket": { + "name": "ticket", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated": { + "name": "updated", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sendMessages_discordChannelId_discordId_idx": { + "name": "sendMessages_discordChannelId_discordId_idx", + "columns": [ + { + "expression": "discordChannelId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.support_data_points": { + "name": "support_data_points", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "possible_values": { + "name": "possible_values", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "support_data_points_key_unique": { + "name": "support_data_points_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.support_matchers": { + "name": "support_matchers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "conditions": { + "name": "conditions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "solution": { + "name": "solution", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tickets": { + "name": "tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "discordId": { + "name": "discordId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "channelId": { + "name": "channelId", + "type": "varchar(22)", + "primaryKey": false, + "notNull": true + }, + "transcript": { + "name": "transcript", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "users": { + "name": "users", + "type": "varchar(22)[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "closed": { + "name": "closed", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created": { + "name": "created", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tickets_channelId_idx": { + "name": "tickets_channelId_idx", + "columns": [ + { + "expression": "channelId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tickets_discordId_idx": { + "name": "tickets_discordId_idx", + "columns": [ + { + "expression": "discordId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.currency": { + "name": "currency", + "schema": "public", + "values": [ + "EUR", + "USD" + ] + }, + "public.productProvider": { + "name": "productProvider", + "schema": "public", + "values": [ + "SOURCEXCHANGE", + "BUILTBYBIT" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 874dfbe..7e376e9 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -162,6 +162,13 @@ "when": 1745950085094, "tag": "0022_noisy_captain_marvel", "breakpoints": true + }, + { + "idx": 23, + "version": "7", + "when": 1758538446686, + "tag": "0023_empty_quasimodo", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index 385688f..033ab54 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.15.0", + "version": "1.15.1", "license": "UNLICENSED", "scripts": { "build": "rm -rf lib && esbuild `find src \\( -name '*.ts' -o -name '*.tsx' \\)` --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' && esbuild src/index.ts --platform='node' --sourcemap --ignore-annotations --format='cjs' --target='es2022' --outdir='lib' --banner:js='require(\"module-alias\").addAlias(\"@\", __dirname);'", @@ -11,22 +11,22 @@ "@aws-sdk/client-s3": "^3.821.0", "@rjweb/runtime-node": "^1.1.1", "@rjweb/sentry": "^2.1.5", - "@rjweb/utils": "^1.12.28", + "@rjweb/utils": "^1.12.29", "@sentry/node": "^7.120.3", "ansi-colors": "^4.1.3", - "axios": "^1.9.0", + "axios": "^1.12.2", "discord-html-transcripts": "github:0x7d8/discord-html-transcripts#b81d826", - "discord.js": "^14.19.3", + "discord.js": "^14.22.1", "drizzle-orm": "^0.42.0", - "ioredis": "^5.6.1", + "ioredis": "^5.7.0", "module-alias": "^2.2.3", "node-cron": "^3.0.3", - "pg": "^8.16.0", + "pg": "^8.16.3", "proxmox-api": "^1.1.1", "rjweb-server": "^9.8.6", "tesseract.js": "^6.0.1", "undici": "6.21.3", - "zod": "^3.25.48" + "zod": "^3.25.76" }, "devDependencies": { "@types/node": "^22.15.29", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17a018c..e1ecc81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ importers: specifier: ^2.1.5 version: 2.1.5 '@rjweb/utils': - specifier: ^1.12.28 - version: 1.12.28 + specifier: ^1.12.29 + version: 1.12.29 '@sentry/node': specifier: ^7.120.3 version: 7.120.3 @@ -27,20 +27,20 @@ importers: specifier: ^4.1.3 version: 4.1.3 axios: - specifier: ^1.9.0 - version: 1.9.0 + specifier: ^1.12.2 + version: 1.12.2 discord-html-transcripts: specifier: github:0x7d8/discord-html-transcripts#b81d826 - version: https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826(discord.js@14.19.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826(discord.js@14.22.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)) discord.js: - specifier: ^14.19.3 - version: 14.19.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: ^14.22.1 + version: 14.22.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) drizzle-orm: specifier: ^0.42.0 - version: 0.42.0(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(gel@2.0.2)(pg@8.16.0) + version: 0.42.0(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(gel@2.0.2)(pg@8.16.3) ioredis: - specifier: ^5.6.1 - version: 5.6.1 + specifier: ^5.7.0 + version: 5.7.0 module-alias: specifier: ^2.2.3 version: 2.2.3 @@ -48,8 +48,8 @@ importers: specifier: ^3.0.3 version: 3.0.3 pg: - specifier: ^8.16.0 - version: 8.16.0 + specifier: ^8.16.3 + version: 8.16.3 proxmox-api: specifier: ^1.1.1 version: 1.1.1 @@ -63,8 +63,18 @@ importers: specifier: 6.21.3 version: 6.21.3 zod: - specifier: ^3.25.48 - version: 3.25.48 + specifier: ^3.25.76 + version: 3.25.76 + optionalDependencies: + bufferutil: + specifier: ^4.0.9 + version: 4.0.9 + utf-8-validate: + specifier: ^6.0.5 + version: 6.0.5 + zlib-sync: + specifier: ^0.1.10 + version: 0.1.10 devDependencies: '@types/node': specifier: ^22.15.29 @@ -84,16 +94,6 @@ importers: typescript: specifier: ^5.8.3 version: 5.8.3 - optionalDependencies: - bufferutil: - specifier: ^4.0.9 - version: 4.0.9 - utf-8-validate: - specifier: ^6.0.5 - version: 6.0.5 - zlib-sync: - specifier: ^0.1.10 - version: 0.1.10 packages: @@ -275,16 +275,16 @@ packages: resolution: {integrity: sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==} engines: {node: '>=16.11.0'} - '@discordjs/rest@2.5.0': - resolution: {integrity: sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ==} + '@discordjs/rest@2.6.0': + resolution: {integrity: sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==} engines: {node: '>=18'} '@discordjs/util@1.1.1': resolution: {integrity: sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==} engines: {node: '>=18'} - '@discordjs/ws@1.2.2': - resolution: {integrity: sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w==} + '@discordjs/ws@1.2.3': + resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==} engines: {node: '>=16.11.0'} '@drizzle-team/brocli@0.10.2': @@ -738,8 +738,8 @@ packages: resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} engines: {node: '>=18'} - '@ioredis/commands@1.2.0': - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@ioredis/commands@1.4.0': + resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} '@opentelemetry/api-logs@0.53.0': resolution: {integrity: sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw==} @@ -977,8 +977,8 @@ packages: '@rjweb/sentry@2.1.5': resolution: {integrity: sha512-kurfaPZJw40butfjGrqIHU/omcHIpK/IPLE/dNug55r07UE+b/Oz14UP6FzFN2Olunm7job8B2JimBaRC5NOlA==} - '@rjweb/utils@1.12.28': - resolution: {integrity: sha512-XULx5cU/aKuukK82tP6ku5UTTyf0IQ/CMY2CJYUIut1ChTzHHlksvJCjCSYabL2J4VvMYW6eKQqzC97xBV3dsw==} + '@rjweb/utils@1.12.29': + resolution: {integrity: sha512-iBE0VN4FKYKpMtgrT0KgfscRfIyC6xPnxLF2v/05rSet+bukfqEbfceLfFh5V6U9sH3ncTlZ56kzUsCOvoJsSA==} engines: {node: '>=18.0.0'} '@sapphire/async-queue@1.5.5': @@ -1319,8 +1319,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.9.0: - resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1413,6 +1413,15 @@ packages: supports-color: optional: true + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -1424,8 +1433,8 @@ packages: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} - discord-api-types@0.38.9: - resolution: {integrity: sha512-LaVbou3fYrRbploSUNAWnyXBmUmVfH4FrQ8W2AVA7f/hUSPcUtkc1bDjELt2wrbIDBbSI4uMo/d/HxeIz7JvoQ==} + discord-api-types@0.38.26: + resolution: {integrity: sha512-xpmPviHjIJ6dFu1eNwNDIGQ3N6qmPUUYFVAx/YZ64h7ZgPkTcKjnciD8bZe8Vbeji7yS5uYljyciunpq0J5NSw==} discord-html-transcripts@https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826: resolution: {tarball: https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826} @@ -1436,8 +1445,8 @@ packages: discord-markdown-parser@1.1.0: resolution: {integrity: sha512-o2+iFgt5qer6UYY5hVTPGq2mGzleKRGYKcvymg67FdKg4AMJ061KbebKunCERWKjx79dmNHMDnGV2F0DRGCNkw==} - discord.js@14.19.3: - resolution: {integrity: sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA==} + discord.js@14.22.1: + resolution: {integrity: sha512-3k+Kisd/v570Jr68A1kNs7qVhNehDwDJAPe4DZ2Syt+/zobf9zEcuYFvsfIaAOgCa0BiHMfOOKQY4eYINl0z7w==} engines: {node: '>=18'} drizzle-kit@0.31.1: @@ -1604,8 +1613,8 @@ packages: debug: optional: true - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} forwarded-parse@2.1.2: @@ -1691,8 +1700,8 @@ packages: resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} engines: {node: '>=18'} - ioredis@5.6.1: - resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} + ioredis@5.7.0: + resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==} engines: {node: '>=12.22.0'} is-core-module@2.16.1: @@ -1827,31 +1836,34 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - pg-cloudflare@1.2.5: - resolution: {integrity: sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==} + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} - pg-connection-string@2.9.0: - resolution: {integrity: sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==} + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} - pg-pool@3.10.0: - resolution: {integrity: sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==} + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} peerDependencies: pg: '>=8.0' pg-protocol@1.10.0: resolution: {integrity: sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==} + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.16.0: - resolution: {integrity: sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==} - engines: {node: '>= 8.0.0'} + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' peerDependenciesMeta: @@ -2046,10 +2058,6 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} - undici@6.21.1: - resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} - engines: {node: '>=18.17'} - undici@6.21.3: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} engines: {node: '>=18.17'} @@ -2110,6 +2118,18 @@ packages: utf-8-validate: optional: true + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -2141,8 +2161,8 @@ packages: zlibjs@0.3.1: resolution: {integrity: sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==} - zod@3.25.48: - resolution: {integrity: sha512-0X1mz8FtgEIvaxGjdIImYpZEaZMrund9pGXm3M6vM7Reba0e2eI71KPjSCGXBfwKDPwPoywf6waUKc3/tFvX2Q==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: @@ -2624,7 +2644,7 @@ snapshots: '@discordjs/formatters': 0.6.1 '@discordjs/util': 1.1.1 '@sapphire/shapeshift': 4.0.0 - discord-api-types: 0.38.9 + discord-api-types: 0.38.26 fast-deep-equal: 3.1.3 ts-mixer: 6.0.4 tslib: 2.8.1 @@ -2635,33 +2655,33 @@ snapshots: '@discordjs/formatters@0.6.1': dependencies: - discord-api-types: 0.38.9 + discord-api-types: 0.38.26 - '@discordjs/rest@2.5.0': + '@discordjs/rest@2.6.0': dependencies: '@discordjs/collection': 2.1.1 '@discordjs/util': 1.1.1 '@sapphire/async-queue': 1.5.5 '@sapphire/snowflake': 3.5.3 '@vladfrangu/async_event_emitter': 2.4.6 - discord-api-types: 0.38.9 + discord-api-types: 0.38.26 magic-bytes.js: 1.12.1 tslib: 2.8.1 - undici: 6.21.1 + undici: 6.21.3 '@discordjs/util@1.1.1': {} - '@discordjs/ws@1.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)': + '@discordjs/ws@1.2.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)': dependencies: '@discordjs/collection': 2.1.1 - '@discordjs/rest': 2.5.0 + '@discordjs/rest': 2.6.0 '@discordjs/util': 1.1.1 '@sapphire/async-queue': 1.5.5 '@types/ws': 8.18.1 '@vladfrangu/async_event_emitter': 2.4.6 - discord-api-types: 0.38.9 + discord-api-types: 0.38.26 tslib: 2.8.1 - ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -2898,7 +2918,7 @@ snapshots: '@inquirer/figures@1.0.12': {} - '@ioredis/commands@1.2.0': {} + '@ioredis/commands@1.4.0': {} '@opentelemetry/api-logs@0.53.0': dependencies: @@ -3206,7 +3226,7 @@ snapshots: '@rjweb/runtime-node@1.1.1(utf-8-validate@6.0.5)': dependencies: - '@rjweb/utils': 1.12.28 + '@rjweb/utils': 1.12.29 bufferutil: 4.0.9 ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: @@ -3219,7 +3239,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@rjweb/utils@1.12.28': + '@rjweb/utils@1.12.29': dependencies: ts-arithmetic: 0.1.1 @@ -3678,7 +3698,7 @@ snapshots: '@types/pg@8.6.1': dependencies: '@types/node': 22.15.29 - pg-protocol: 1.10.0 + pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/react@19.1.6': @@ -3717,10 +3737,10 @@ snapshots: asynckit@0.4.0: {} - axios@1.9.0: + axios@1.12.2: dependencies: follow-redirects: 1.15.9 - form-data: 4.0.2 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -3802,6 +3822,11 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.3: + dependencies: + ms: 2.1.3 + optional: true + defaults@1.0.4: dependencies: clone: 1.0.4 @@ -3810,14 +3835,14 @@ snapshots: denque@2.1.0: {} - discord-api-types@0.38.9: {} + discord-api-types@0.38.26: {} - discord-html-transcripts@https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826(discord.js@14.19.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)): + discord-html-transcripts@https://codeload.github.com/0x7d8/discord-html-transcripts/tar.gz/b81d826(discord.js@14.22.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)): dependencies: '@derockdev/discord-components-core': 3.6.1 '@derockdev/discord-components-react': 3.6.1(react-dom@0.0.0-experimental-6639ed3b3-20240111(react@0.0.0-experimental-6639ed3b3-20240111))(react@0.0.0-experimental-6639ed3b3-20240111) discord-markdown-parser: 1.1.0 - discord.js: 14.19.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + discord.js: 14.22.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) react: 0.0.0-experimental-6639ed3b3-20240111 react-dom: 0.0.0-experimental-6639ed3b3-20240111(react@0.0.0-experimental-6639ed3b3-20240111) simple-markdown: 0.7.3 @@ -3828,21 +3853,21 @@ snapshots: dependencies: simple-markdown: 0.7.3 - discord.js@14.19.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): + discord.js@14.22.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@discordjs/builders': 1.11.2 '@discordjs/collection': 1.5.3 '@discordjs/formatters': 0.6.1 - '@discordjs/rest': 2.5.0 + '@discordjs/rest': 2.6.0 '@discordjs/util': 1.1.1 - '@discordjs/ws': 1.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) + '@discordjs/ws': 1.2.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) '@sapphire/snowflake': 3.5.3 - discord-api-types: 0.38.9 + discord-api-types: 0.38.26 fast-deep-equal: 3.1.3 lodash.snakecase: 4.1.1 magic-bytes.js: 1.12.1 tslib: 2.8.1 - undici: 6.21.1 + undici: 6.21.3 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -3856,12 +3881,12 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.42.0(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(gel@2.0.2)(pg@8.16.0): + drizzle-orm@0.42.0(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(gel@2.0.2)(pg@8.16.3): optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/pg': 8.15.4 gel: 2.0.2 - pg: 8.16.0 + pg: 8.16.3 dunder-proto@1.0.1: dependencies: @@ -3993,11 +4018,12 @@ snapshots: follow-redirects@1.15.9: {} - form-data@4.0.2: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 forwarded-parse@2.1.2: {} @@ -4013,7 +4039,7 @@ snapshots: gel@2.0.2: dependencies: '@petamoriken/float16': 3.9.2 - debug: 4.4.1 + debug: 4.4.3 env-paths: 3.0.0 semver: 7.7.2 shell-quote: 1.8.3 @@ -4100,9 +4126,9 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - ioredis@5.6.1: + ioredis@5.7.0: dependencies: - '@ioredis/commands': 1.2.0 + '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 debug: 4.4.1 denque: 2.1.0 @@ -4225,19 +4251,21 @@ snapshots: path-parse@1.0.7: {} - pg-cloudflare@1.2.5: + pg-cloudflare@1.2.7: optional: true - pg-connection-string@2.9.0: {} + pg-connection-string@2.9.1: {} pg-int8@1.0.1: {} - pg-pool@3.10.0(pg@8.16.0): + pg-pool@3.10.1(pg@8.16.3): dependencies: - pg: 8.16.0 + pg: 8.16.3 pg-protocol@1.10.0: {} + pg-protocol@1.10.3: {} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 @@ -4246,15 +4274,15 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.16.0: + pg@8.16.3: dependencies: - pg-connection-string: 2.9.0 - pg-pool: 3.10.0(pg@8.16.0) - pg-protocol: 1.10.0 + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.2.5 + pg-cloudflare: 1.2.7 pgpass@1.0.5: dependencies: @@ -4325,12 +4353,12 @@ snapshots: rjweb-server@9.8.6: dependencies: - '@rjweb/utils': 1.12.28 + '@rjweb/utils': 1.12.29 content-disposition: 0.5.4 inquirer: 9.3.7 openapi3-ts: 4.4.0 yargs: 17.7.2 - zod: 3.25.48 + zod: 3.25.76 run-async@3.0.0: {} @@ -4439,8 +4467,6 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 - undici@6.21.1: {} - undici@6.21.3: {} universalify@0.1.2: {} @@ -4491,6 +4517,11 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 6.0.5 + ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 6.0.5 + xtend@4.0.2: {} y18n@5.0.8: {} @@ -4518,4 +4549,4 @@ snapshots: zlibjs@0.3.1: {} - zod@3.25.48: {} + zod@3.25.76: {} diff --git a/src/bot/buttons/products.ts b/src/bot/buttons/products.ts index 4a57491..f55b172 100644 --- a/src/bot/buttons/products.ts +++ b/src/bot/buttons/products.ts @@ -3,10 +3,10 @@ import { count, eq } from "drizzle-orm" const button: Exported<[user: string | null, current: number, type: PaginateType]> = new Button() .setName('products') - .listen(async(ctx, user: string | null, current: number, type: PaginateType) => { + .listen(async (ctx, user: string | null, current: number, type: PaginateType) => { switch (user) { case null: { - const [ page, total, data ] = await ctx.paginate(current, type, ctx.database.select({ + const [page, total, data] = await ctx.paginate(current, type, ctx.database.select({ count: count(ctx.database.schema.products.id) }).from(ctx.database.schema.products).then((r) => r[0].count), ({ skip, take }) => ctx.database.select({ @@ -26,7 +26,7 @@ const button: Exported<[user: string | null, current: number, type: PaginateType .limit(take * ctx.database.schema.productProvider.enumValues.length), 1 ) - + return ctx.interaction.update({ embeds: [ ctx.Embed() @@ -48,28 +48,28 @@ const button: Exported<[user: string | null, current: number, type: PaginateType } default: { - const [ page, total, data ] = await ctx.paginate(current, type, ctx.database.selectDistinct({ + const [page, total, data] = await ctx.paginate(current, type, ctx.database.selectDistinct({ count: count(ctx.database.schema.products.id) }).from(ctx.database.schema.products) .leftJoin(ctx.database.schema.productLinks, eq(ctx.database.schema.products.id, ctx.database.schema.productLinks.productId)) .where(eq(ctx.database.schema.productLinks.discordId, user)) .then((r) => r[0].count), - ({ skip, take }) => ctx.database.selectDistinct({ - id: ctx.database.schema.products.id, - name: ctx.database.schema.products.name, - icon: ctx.database.schema.products.icon, - banner: ctx.database.schema.products.banner, - summary: ctx.database.schema.products.summary, - version: ctx.database.schema.products.version - }).from(ctx.database.schema.products) - .leftJoin(ctx.database.schema.productLinks, eq(ctx.database.schema.products.id, ctx.database.schema.productLinks.productId)) - .where(eq(ctx.database.schema.productLinks.discordId, user)) - .orderBy(ctx.database.schema.products.id) - .offset(skip) - .limit(take), + ({ skip, take }) => ctx.database.selectDistinct({ + id: ctx.database.schema.products.id, + name: ctx.database.schema.products.name, + icon: ctx.database.schema.products.icon, + banner: ctx.database.schema.products.banner, + summary: ctx.database.schema.products.summary, + version: ctx.database.schema.products.version + }).from(ctx.database.schema.products) + .leftJoin(ctx.database.schema.productLinks, eq(ctx.database.schema.products.id, ctx.database.schema.productLinks.productId)) + .where(eq(ctx.database.schema.productLinks.discordId, user)) + .orderBy(ctx.database.schema.products.id) + .offset(skip) + .limit(take), 1 ) - + return ctx.interaction.update({ embeds: [ ctx.Embed() diff --git a/src/bot/buttons/tickets/close.ts b/src/bot/buttons/tickets/close.ts index c20121e..faf56a9 100644 --- a/src/bot/buttons/tickets/close.ts +++ b/src/bot/buttons/tickets/close.ts @@ -7,7 +7,7 @@ import { size, string } from "@rjweb/utils" export default new Button() .setName('ticket-close') - .listen(async(ctx) => { + .listen(async (ctx) => { const ticket = await ctx.database.select({ id: ctx.database.schema.tickets.id, discordId: ctx.database.schema.tickets.discordId diff --git a/src/bot/buttons/tickets/diagnosis.ts b/src/bot/buttons/tickets/diagnosis.ts index 8e1fb87..e5f08e6 100644 --- a/src/bot/buttons/tickets/diagnosis.ts +++ b/src/bot/buttons/tickets/diagnosis.ts @@ -4,7 +4,7 @@ import { eq } from "drizzle-orm" export default new Button() .setName('ticket-diagnosis') - .listen(async(ctx, addon: number | null) => { + .listen(async (ctx, addon: number | null) => { const product = !addon ? null : await ctx.database.select({ id: ctx.database.schema.products.id, name: ctx.database.schema.products.name }) .from(ctx.database.schema.products) .where(eq(ctx.database.schema.products.id, addon)) diff --git a/src/bot/buttons/tickets/force-open.ts b/src/bot/buttons/tickets/force-open.ts index af85e1a..d22f6ef 100644 --- a/src/bot/buttons/tickets/force-open.ts +++ b/src/bot/buttons/tickets/force-open.ts @@ -5,7 +5,7 @@ import closeTicket from "@/bot/buttons/tickets/close" export default new Button() .setName('ticket-force-open') - .listen(async(ctx, rawData: number[], logs: string) => { + .listen(async (ctx, rawData: number[], logs: string) => { const data = ctx.support.expandData(rawData) const existingTicket = await ctx.database.select({ diff --git a/src/bot/buttons/tickets/open.ts b/src/bot/buttons/tickets/open.ts index 4fe3758..a7aa642 100644 --- a/src/bot/buttons/tickets/open.ts +++ b/src/bot/buttons/tickets/open.ts @@ -4,7 +4,7 @@ import productSelect from "@/bot/selects/tickets/product" export default new Button() .setName('ticket-open') - .listen(async(ctx) => { + .listen(async (ctx) => { const products = await ctx.database.select({ name: ctx.database.schema.products.name, identifier: ctx.database.schema.products.identifier diff --git a/src/bot/context.ts b/src/bot/context.ts index 91ecb75..fd106e1 100644 --- a/src/bot/context.ts +++ b/src/bot/context.ts @@ -55,14 +55,14 @@ export default class Context { : direction === 'back' ? page - 1 : direction === 'next' - ? page + 1 - : page + ? page + 1 + : page const c = await Promise.resolve(count) if (direction === 'last') page = Math.ceil((c || 1) / itemsPerPage) else page = page > Math.ceil((c || 1) / itemsPerPage) ? Math.ceil((c || 1) / itemsPerPage) : page - return [ page, c, await Promise.resolve(data({ take: itemsPerPage, skip: (page - 1) * itemsPerPage })) ] + return [page, c, await Promise.resolve(data({ take: itemsPerPage, skip: (page - 1) * itemsPerPage }))] } /** diff --git a/src/bot/index.ts b/src/bot/index.ts index 9a27907..866abfe 100644 --- a/src/bot/index.ts +++ b/src/bot/index.ts @@ -4,7 +4,6 @@ import * as Sentry from "@sentry/node" import * as customid from "@/globals/customid" import logger from "@/globals/logger" import database from "@/globals/database" -import openTicketButton from "@/bot/buttons/tickets/open" import { eq } from "drizzle-orm" import env from "@/globals/env" @@ -44,7 +43,7 @@ function parseOptions(options: (AutocompleteInteraction | ChatInputCommandIntera return output } -client.on('interactionCreate', async(interaction) => { +client.on('interactionCreate', async (interaction) => { if (!interaction.guild) return const startTime = performance.now() @@ -153,7 +152,7 @@ client.on('interactionCreate', async(interaction) => { let decoded = await customid.decode(interaction.client.user.id.concat(interaction.guildId), interaction.customId) if (!decoded) decoded = interaction.customId - const [ name, ...args ] = decoded.split('°').filter(Boolean) + const [name, ...args] = decoded.split('°').filter(Boolean) const button = buttons.find((button) => button['m_name'] === name) if (!button) return @@ -207,8 +206,8 @@ client.on('interactionCreate', async(interaction) => { const decoded = await customid.decode(interaction.client.user.id.concat(interaction.guildId), interaction.customId) if (!decoded) return - const [ modalRawArgs, listenerRawArgs ] = decoded.split('^') - const [ name, ...modalArgs ] = modalRawArgs.split('°').filter(Boolean) + const [modalRawArgs, listenerRawArgs] = decoded.split('^') + const [name, ...modalArgs] = modalRawArgs.split('°').filter(Boolean) const listenerArgs = listenerRawArgs.split('°').filter(Boolean) const modal = modals.find((modal) => modal['m_name'] === name) @@ -263,7 +262,7 @@ client.on('interactionCreate', async(interaction) => { const decoded = await customid.decode(interaction.client.user.id, interaction.customId) if (!decoded) return - const [ name, ...listenerArgs ] = decoded.split('°').filter(Boolean) + const [name, ...listenerArgs] = decoded.split('°').filter(Boolean) const select = selects.find((select) => select['m_name'] === name) if (!select) return @@ -314,15 +313,15 @@ client.on('interactionCreate', async(interaction) => { async function main() { await Promise.all([ - Promise.all([ ...filesystem.getFiles(`${__dirname}/events`, { recursive: true }).filter((file) => file.endsWith('js')).map(async(file) => events.push((await import(file)).default.default)) ]), - Promise.all([ ...filesystem.getFiles(`${__dirname}/commands`, { recursive: true }).filter((file) => file.endsWith('js')).map(async(file) => commands.push((await import(file)).default.default)) ]), - Promise.all([ ...filesystem.getFiles(`${__dirname}/buttons`, { recursive: true }).filter((file) => file.endsWith('js')).map(async(file) => buttons.push((await import(file)).default.default)) ]), - Promise.all([ ...filesystem.getFiles(`${__dirname}/modals`, { recursive: true }).filter((file) => file.endsWith('js')).map(async(file) => modals.push((await import(file)).default.default)) ]), - Promise.all([ ...filesystem.getFiles(`${__dirname}/selects`, { recursive: true }).filter((file) => file.endsWith('js')).map(async(file) => selects.push((await import(file)).default.default)) ]) + Promise.all([...filesystem.getFiles(`${__dirname}/events`, { recursive: true }).filter((file) => file.endsWith('js')).map(async (file) => events.push((await import(file)).default.default))]), + Promise.all([...filesystem.getFiles(`${__dirname}/commands`, { recursive: true }).filter((file) => file.endsWith('js')).map(async (file) => commands.push((await import(file)).default.default))]), + Promise.all([...filesystem.getFiles(`${__dirname}/buttons`, { recursive: true }).filter((file) => file.endsWith('js')).map(async (file) => buttons.push((await import(file)).default.default))]), + Promise.all([...filesystem.getFiles(`${__dirname}/modals`, { recursive: true }).filter((file) => file.endsWith('js')).map(async (file) => modals.push((await import(file)).default.default))]), + Promise.all([...filesystem.getFiles(`${__dirname}/selects`, { recursive: true }).filter((file) => file.endsWith('js')).map(async (file) => selects.push((await import(file)).default.default))]) ]) for (const event of events) { - client.on(event['event'] as any, async(interaction, ...rest) => { + client.on(event['event'] as any, async (interaction, ...rest) => { const scope = new Sentry.Scope() scope @@ -359,7 +358,7 @@ async function main() { }) } - await client.login(env.BOT_TOKEN).then(async() => { + await client.login(env.BOT_TOKEN).then(async () => { await client.application?.commands.set(commands.map((command) => command['builder'].toJSON())) logger() diff --git a/src/crontab.ts b/src/crontab.ts index a1a8aa8..e1c146f 100644 --- a/src/crontab.ts +++ b/src/crontab.ts @@ -3,6 +3,7 @@ import database from "@/globals/database" import * as proxmox from "@/globals/proxmox" import getVersion from "@/index" import * as pterodactyl from "@/globals/pterodactyl" +import { client } from "@/bot" export const runContext = { /** @@ -17,6 +18,10 @@ export const runContext = { * The Pterodactyl API Client * @since 1.1.0 */ pterodactyl, + /** + * The Discord Client + * @since 1.15.1 + */ client, /** * The Proxmox API Client * @since 1.15.0 diff --git a/src/crontabs/check-demo-accesses.ts b/src/crontabs/check-demo-accesses.ts index 78c9739..49151bb 100644 --- a/src/crontabs/check-demo-accesses.ts +++ b/src/crontabs/check-demo-accesses.ts @@ -1,4 +1,3 @@ -import { client } from "@/bot" import Crontab from "@/crontab" import env from "@/globals/env" import logger from "@/globals/logger" @@ -7,7 +6,7 @@ import { and, eq, sql } from "drizzle-orm" export default new Crontab() .cron('* * * * *') - .listen(async(ctx) => { + .listen(async (ctx) => { const expiredDemoAccesses = await ctx.database.select({ id: ctx.database.schema.demoAccesses.id, discordId: ctx.database.schema.demoAccesses.discordId @@ -26,7 +25,7 @@ export default new Crontab() .info() for (const expiredDemoAccess of expiredDemoAccesses) { - const member = await client.guilds.fetch(env.DISCORD_SERVER) + const member = await ctx.client.guilds.fetch(env.DISCORD_SERVER) .then((guild) => guild .members.fetch(expiredDemoAccess.discordId) ) @@ -50,7 +49,7 @@ export default new Crontab() await Promise.allSettled([ member.roles.remove(env.DEMO_ROLE), member.send('`🔍` Your **1 hour** demo acccess has expired.'), - client.guilds.cache.get(env.DISCORD_SERVER)!.channels.fetch(env.DEMO_CHANNEL) + ctx.client.guilds.cache.get(env.DISCORD_SERVER)!.channels.fetch(env.DEMO_CHANNEL) .then((channel) => 'send' in channel! ? channel.send({ content: `\`🔍\` <@${member.id}>'s demo acccess has expired.`, allowedMentions: { users: [] } }) : null diff --git a/src/crontabs/security/lock-dms.ts b/src/crontabs/security/lock-dms.ts new file mode 100644 index 0000000..e4cdd74 --- /dev/null +++ b/src/crontabs/security/lock-dms.ts @@ -0,0 +1,26 @@ +import Crontab from "@/crontab" + +import { Guild } from "discord.js" + +import logger from "@/globals/logger" +import env from "@/globals/env" + +export default new Crontab() + .cron("0 0 * * *") + .listen(async (ctx) => { + const guild = await ctx.client.guilds.fetch(env.DISCORD_SERVER).catch(() => null) + if (!guild) { + logger().text(`Unable to fetch guild ${env.DISCORD_SERVER}`).info() + return + } + try { + const until = new Date(Date.now() + 24 * 60 * 60 * 1000), + options: any = { + dmsDisabledUntil: until, + } + await (guild as Guild).setIncidentActions(options) + logger().text(`Updated incident actions (dmsDisabledUntil) via guild.setIncidentActions for ${env.DISCORD_SERVER}`).info() + } catch (err) { + logger().text(`Failed to update incident actions for ${env.DISCORD_SERVER}: ${String(err)}`).error() + } + }) \ No newline at end of file diff --git a/src/globals/env.ts b/src/globals/env.ts index e2c3404..a579af3 100644 --- a/src/globals/env.ts +++ b/src/globals/env.ts @@ -17,6 +17,7 @@ const infos = z.object({ DATABASE_URL: z.string(), SENTRY_URL: z.string().optional(), DISCORD_SERVER: z.string(), + DISCORD_CHANNEL: z.string(), BOT_TOKEN: z.string(), SXC_TOKEN: z.string(), @@ -39,7 +40,7 @@ const infos = z.object({ PTERO_CLIENT_TOKEN: z.string(), S3_URL: z.string().optional(), - S3_SSL: z.union([ z.literal('true'), z.literal('false') ]).transform((str) => str === 'true').default('true'), + S3_SSL: z.union([z.literal('true'), z.literal('false')]).transform((str) => str === 'true').default('true'), S3_BUCKET: z.string().optional(), S3_REGION: z.string().optional(), S3_HOST: z.string().optional(), @@ -56,7 +57,7 @@ const infos = z.object({ PORT: z.string().transform((v) => parseInt(v)).optional(), ENCODING_SEQUENCE: z.string(), - LOG_LEVEL: z.union([ z.literal('none'), z.literal('info'), z.literal('debug') ]) + LOG_LEVEL: z.union([z.literal('none'), z.literal('info'), z.literal('debug')]) }) export type Environment = z.infer diff --git a/src/schema.ts b/src/schema.ts index eb647ff..56e5d87 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -5,7 +5,7 @@ export const productProvider = pgEnum('productProvider', ['SOURCEXCHANGE', 'BUIL export const currency = pgEnum('currency', ['EUR', 'USD']) export const products = pgTable('products', { - id: serial('id').primaryKey(), + id: serial('id').primaryKey(), name: varchar('name', { length: 51 }).notNull(), icon: varchar('icon', { length: 255 }).notNull(), @@ -80,6 +80,8 @@ export const sendMessages = pgTable('send_messages', { id: serial('id').primaryKey(), enabled: boolean('enabled').default(true).notNull(), + sticky: boolean('sticky').default(false).notNull(), + discordId: varchar('discordId', { length: 22 }), discordChannelId: varchar('discordChannelId', { length: 22 }).notNull(), message: text('message').notNull(), @@ -87,8 +89,11 @@ export const sendMessages = pgTable('send_messages', { image: varchar('image', { length: 255 }), ticket: boolean('ticket').default(false).notNull(), + + updated: timestamp('updated').default(sql`now()`).notNull(), }, (sendMessages) => [ - uniqueIndex('sendMessages_discordChannelId_discordId_idx').on(sendMessages.discordChannelId, sendMessages.discordId) + uniqueIndex('sendMessages_discordChannelId_discordId_idx') + .on(sendMessages.discordChannelId, sendMessages.discordId) ]) export const faqs = pgTable('faqs', { @@ -147,19 +152,19 @@ export const automaticErrors = pgTable('automatic_errors', { ]) export const supportDataPoints = pgTable('support_data_points', { - id: serial('id').primaryKey(), - key: varchar('key', { length: 100 }).notNull().unique(), - question: text('question').notNull(), - priority: integer('priority').notNull(), - possibleValues: jsonb('possible_values').$type().notNull() + id: serial('id').primaryKey(), + key: varchar('key', { length: 100 }).notNull().unique(), + question: text('question').notNull(), + priority: integer('priority').notNull(), + possibleValues: jsonb('possible_values').$type().notNull() }) export const supportMatchers = pgTable('support_matchers', { - id: serial('id').primaryKey(), - name: varchar('name', { length: 200 }).notNull(), - conditions: jsonb('conditions').$type>().notNull(), - solution: text('solution').notNull(), - priority: integer('priority').default(100).notNull() + id: serial('id').primaryKey(), + name: varchar('name', { length: 200 }).notNull(), + conditions: jsonb('conditions').$type>().notNull(), + solution: text('solution').notNull(), + priority: integer('priority').default(100).notNull() }) export const tickets = pgTable('tickets', {