From 03f3944d31ec69041d406e12151f9b3e1c8fe48a Mon Sep 17 00:00:00 2001 From: Isaac Date: Fri, 10 Jan 2025 01:41:24 +0000 Subject: [PATCH] feat(scripts): data export and import scripts (#575) * wip: data import/export scripts * feat: data exporter * feat: importer * fix: that was stupid * fix: importer * style: clean up comments --- .eslintrc.json | 2 +- package.json | 4 +- pnpm-lock.yaml | 125 ++++++++++++++++++++++++++++ scripts/export.mjs | 112 +++++++++++++++++++++++++ scripts/import.mjs | 199 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 scripts/export.mjs create mode 100644 scripts/import.mjs diff --git a/.eslintrc.json b/.eslintrc.json index f2aff2d..5493d67 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -9,7 +9,7 @@ "eslint:recommended" ], "parserOptions": { - "ecmaVersion": 12, + "ecmaVersion": 2024, "sourceType": "module" }, "plugins": [ diff --git a/package.json b/package.json index c618598..80bfe8d 100644 --- a/package.json +++ b/package.json @@ -80,13 +80,15 @@ "@commitlint/cli": "^17.8.1", "@commitlint/config-conventional": "^17.8.1", "all-contributors-cli": "^6.26.1", + "commander": "^12.1.0", "conventional-changelog-cli": "^2.2.2", "eslint": "^8.57.1", "eslint-plugin-unused-imports": "^2.0.0", "husky": "^8.0.3", "lint-staged": "^13.3.0", "markdown-table": "^3.0.4", - "nodemon": "^2.0.22" + "nodemon": "^2.0.22", + "ora": "^8.1.1" }, "optionalDependencies": { "bufferutil": "^4.0.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea7accb..3ebebc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ importers: all-contributors-cli: specifier: ^6.26.1 version: 6.26.1 + commander: + specifier: ^12.1.0 + version: 12.1.0 conventional-changelog-cli: specifier: ^2.2.2 version: 2.2.2 @@ -139,6 +142,9 @@ importers: nodemon: specifier: ^2.0.22 version: 2.0.22 + ora: + specifier: ^8.1.1 + version: 8.1.1 packages: @@ -623,6 +629,14 @@ packages: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + cli-truncate@3.1.0: resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -655,6 +669,10 @@ packages: resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} engines: {node: '>=16'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} @@ -862,6 +880,9 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1074,6 +1095,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + get-pkg-repo@4.2.1: resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} engines: {node: '>=6.9.0'} @@ -1235,6 +1260,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1263,6 +1292,14 @@ packages: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} engines: {node: '>=0.10.0'} + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -1408,6 +1445,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + log-update@5.0.1: resolution: {integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1452,6 +1493,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -1563,10 +1608,18 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@8.1.1: + resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} + engines: {node: '>=18'} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -1810,6 +1863,10 @@ packages: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + ret@0.4.3: resolution: {integrity: sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==} engines: {node: '>=10'} @@ -1899,6 +1956,10 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-oauth2@5.1.0: resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==} @@ -1942,6 +2003,10 @@ packages: split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + steed@1.1.3: resolution: {integrity: sha512-EUkci0FAUiE4IvGTSKcDJIQ/eRUP2JJb56+fvZ4sdnguLTqIdKjSxUe138poW8mkvKWXW2sFPrgTsxqoISnmoA==} @@ -1957,6 +2022,10 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -2828,6 +2897,12 @@ snapshots: dependencies: restore-cursor: 4.0.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + cli-truncate@3.1.0: dependencies: slice-ansi: 5.0.0 @@ -2863,6 +2938,8 @@ snapshots: commander@11.0.0: {} + commander@12.1.0: {} + compare-func@2.0.0: dependencies: array-ify: 1.0.0 @@ -3095,6 +3172,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + emoji-regex@10.4.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3366,6 +3445,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.3.0: {} + get-pkg-repo@4.2.1: dependencies: '@hutson/parse-repository-url': 3.0.2 @@ -3521,6 +3602,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-interactive@2.0.0: {} + is-number@7.0.0: {} is-obj@2.0.0: {} @@ -3537,6 +3620,10 @@ snapshots: dependencies: text-extensions: 1.9.0 + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + isarray@1.0.0: {} isexe@2.0.0: {} @@ -3685,6 +3772,11 @@ snapshots: lodash@4.17.21: {} + log-symbols@6.0.0: + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + log-update@5.0.1: dependencies: ansi-escapes: 5.0.0 @@ -3732,6 +3824,8 @@ snapshots: mimic-fn@4.0.0: {} + mimic-function@5.0.1: {} + min-indent@1.0.1: {} minimalistic-assert@1.0.1: {} @@ -3839,6 +3933,10 @@ snapshots: dependencies: mimic-fn: 4.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3848,6 +3946,18 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@8.1.1: + dependencies: + chalk: 5.3.0 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + os-tmpdir@1.0.2: {} p-limit@1.3.0: @@ -4061,6 +4171,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + ret@0.4.3: {} reusify@1.0.4: {} @@ -4121,6 +4236,8 @@ snapshots: signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + simple-oauth2@5.1.0: dependencies: '@hapi/hoek': 11.0.6 @@ -4171,6 +4288,8 @@ snapshots: dependencies: through: 2.3.8 + stdin-discarder@0.2.2: {} + steed@1.1.3: dependencies: fastfall: 1.5.1 @@ -4193,6 +4312,12 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 diff --git a/scripts/export.mjs b/scripts/export.mjs new file mode 100644 index 0000000..f3bfaf9 --- /dev/null +++ b/scripts/export.mjs @@ -0,0 +1,112 @@ +import { config } from 'dotenv'; +import { program } from 'commander'; +import fse from 'fs-extra'; +import { join } from 'path'; +import ora from 'ora'; +import { PrismaClient } from '@prisma/client'; +import { createHash } from 'crypto'; +import Cryptr from 'cryptr'; + +config(); + +program + .requiredOption('-g, --guild ', 'the ID of the guild to export'); + +program.parse(); + +const options = program.opts(); + +const hash = createHash('sha256').update(options.guild).digest('hex'); +const file_path = join(process.cwd(), './user/dumps', `${hash}.dump`); +const file_cryptr = new Cryptr(options.guild); +const db_cryptr = new Cryptr(process.env.ENCRYPTION_KEY); + +fse.ensureDirSync(join(process.cwd(), './user/dumps')); + +let spinner = ora('Connecting').start(); + +const prisma_options = {}; + +if (process.env.DB_PROVIDER === 'sqlite' && !process.env.DB_CONNECTION_URL) { + prisma_options.datasources = { db: { url: 'file:' + join(process.cwd(), './user/database.db') } }; +} + +const prisma = new PrismaClient(prisma_options); + +if (process.env.DB_PROVIDER === 'sqlite') { + const { default: sqliteMiddleware } = await import('../src/lib/middleware/prisma-sqlite.js'); + prisma.$use(sqliteMiddleware); + await prisma.$queryRaw`PRAGMA journal_mode=WAL;`; + await prisma.$queryRaw`PRAGMA synchronous=normal;`; +} + +spinner.succeed('Connected'); + + +const dump = {}; + +spinner = ora('Exporting settings').start(); +dump.settings = await prisma.guild.findFirst({ where: { id: options.guild } }); +spinner.succeed('Exported settings'); + +spinner = ora('Exporting categories').start(); +dump.categories = await prisma.category.findMany({ + include: { questions: true }, + where: { guildId: options.guild }, +}); +spinner.succeed(`Exported ${dump.categories.length} categories`); + +spinner = ora('Exporting tags').start(); +dump.tags = await prisma.tag.findMany({ where: { guildId: options.guild } }); +spinner.succeed(`Exported ${dump.tags.length} tags`); + +spinner = ora('Exporting tickets').start(); +dump.tickets = await prisma.ticket.findMany({ + include: { + archivedChannels: true, + archivedMessages: true, + archivedRoles: true, + archivedUsers: true, + feedback: true, + questionAnswers: true, + }, + where: { guildId: options.guild }, +}); +dump.tickets = dump.tickets.map(ticket => { + if (ticket.topic) ticket.topic = db_cryptr.decrypt(ticket.topic); + + ticket.archivedChannels = ticket.archivedChannels.map(channel => { + channel.name = db_cryptr.decrypt(channel.name); + return channel; + }); + + ticket.archivedMessages = ticket.archivedMessages.map(message => { + message.content = db_cryptr.decrypt(message.content); + return message; + }); + + ticket.archivedUsers = ticket.archivedUsers.map(user => { + user.displayName = db_cryptr.decrypt(user.displayName); + user.username = db_cryptr.decrypt(user.username); + return user; + }); + + if (ticket.feedback?.comment) { + ticket.feedback.comment = db_cryptr.decrypt(ticket.feedback.comment); + } + + ticket.questionAnswers = ticket.questionAnswers.map(answer => { + if (answer.value) answer.value = db_cryptr.decrypt(answer.value); + return answer; + }); + + return ticket; +}); +spinner.succeed(`Exported ${dump.tickets.length} tickets`); + +spinner = ora(`Writing to "${file_path}"`).start(); + +// async to not freeze the spinner +await fse.promises.writeFile(file_path, file_cryptr.encrypt(JSON.stringify(dump))); + +spinner.succeed(`Written to "${file_path}"`); diff --git a/scripts/import.mjs b/scripts/import.mjs new file mode 100644 index 0000000..e9abf10 --- /dev/null +++ b/scripts/import.mjs @@ -0,0 +1,199 @@ +import { config } from 'dotenv'; +import { program } from 'commander'; +import fse from 'fs-extra'; +import { join } from 'path'; +import ora from 'ora'; +import { PrismaClient } from '@prisma/client'; +import { createHash } from 'crypto'; +import Cryptr from 'cryptr'; + +config(); + +program + .requiredOption('-g, --guild ', 'the ID of the guild to export') + .option('-f, --force', 'DELETE all data if the guild already exists', false); + +program.parse(); + +const options = program.opts(); + +const hash = createHash('sha256').update(options.guild).digest('hex'); +const file_cryptr = new Cryptr(options.guild); +const db_cryptr = new Cryptr(process.env.ENCRYPTION_KEY); + + +let spinner = ora('Connecting').start(); + +const prisma_options = {}; + +if (process.env.DB_PROVIDER === 'sqlite' && !process.env.DB_CONNECTION_URL) { + prisma_options.datasources = { db: { url: 'file:' + join(process.cwd(), './user/database.db') } }; +} + +const prisma = new PrismaClient(prisma_options); + +if (process.env.DB_PROVIDER === 'sqlite') { + const { default: sqliteMiddleware } = await import('../src/lib/middleware/prisma-sqlite.js'); + prisma.$use(sqliteMiddleware); + await prisma.$queryRaw`PRAGMA journal_mode=WAL;`; + await prisma.$queryRaw`PRAGMA synchronous=normal;`; +} + +spinner.succeed('Connected'); + +spinner = ora('Reading dump file').start(); +const file_path = join(process.cwd(), './user/dumps', `${hash}.dump`); +const dump = JSON.parse(file_cryptr.decrypt(await fse.promises.readFile(file_path, 'utf8'))); +spinner.succeed('Read dump file'); + +spinner = ora('Checking if guild exists').start(); +const exists = await prisma.guild.count({ where: { id: options.guild } }); +if (exists === 0) { + spinner.succeed('Guild doesn\'t exist'); +} else { + if (options.force) { + await prisma.guild.delete({ where: { id: options.guild } }); + spinner.succeed('Deleted guild'); + } else { + spinner.fail('Guild already exists; run again with --force to delete it'); + process.exit(1); + } +} + +spinner = ora('Importing settings & tags').start(); +await prisma.guild.create({ + data: { + ...dump.settings, + id: options.guild, + tags: { + create: dump.tags.map(tag => { + delete tag.guildId; + return tag; + }), + }, + }, +}); +spinner.succeed(`Imported settings & ${dump.tags.length} tags`); + +const category_map = {}; +spinner = ora('Importing categories').start(); +for (const category of dump.categories) { + const original_id = category.id; + delete category.id; + delete category.guildId; + category.questions = { create: category.questions }; + const { id: new_id } = await prisma.category.create({ + data: { + ...category, + guild: { connect: { id: options.guild } }, + }, + }); + category_map[original_id] = new_id; +} +spinner.succeed(`Imported ${dump.categories.length} categories`); + +spinner = ora('Importing tickets').start(); +for (const i in dump.tickets) { + spinner.text = `Importing tickets (${i}/${dump.tickets.length})`; + const ticket = dump.tickets[i]; + ticket.category = { connect: { id: category_map[ticket.categoryId] } }; + + if (ticket.topic) ticket.topic = db_cryptr.encrypt(ticket.topic); + + ticket.archivedChannels = { + create: ticket.archivedChannels.map(channel => { + delete channel.ticketId; + channel.name = db_cryptr.encrypt(channel.name); + return channel; + }), + }; + + ticket.archivedUsers = { + create: ticket.archivedUsers.map(user => { + delete user.ticketId; + user.displayName = db_cryptr.encrypt(user.displayName); + user.username = db_cryptr.encrypt(user.username); + return user; + }), + }; + + ticket.archivedRoles = { + create: ticket.archivedRoles.map(role => { + delete role.ticketId; + return role; + }), + }; + + const archivedMessages = ticket.archivedMessages.map(message => { + message.content = db_cryptr.encrypt(message.content); + return message; + }); + ticket.archivedMessages = undefined; + + if (ticket.feedback) { + delete ticket.feedback.ticketId; + delete ticket.feedback.guildId; + ticket.feedback.guild = { connect: { id: options.guild } }; + if (ticket.feedback.comment) { + ticket.feedback.comment = db_cryptr.encrypt(ticket.feedback.comment); + } + ticket.feedback = { create: ticket.feedback }; + } else { + ticket.feedback = undefined; + } + + if (ticket.questionAnswers?.length) { + ticket.questionAnswers = { + createMany: ticket.questionAnswers.map(answer => { + delete answer.ticketId; + if (answer.value) answer.value = db_cryptr.encrypt(answer.value); + return answer; + }), + }; + } else { + ticket.questionAnswers = undefined; + } + + if (ticket.claimedById) { + ticket.claimedBy = { + connectOrCreate: { + create: { id: ticket.claimedById }, + where: { id: ticket.claimedById }, + }, + }; + } + if (ticket.closedById) { + ticket.closedBy = { + connectOrCreate: { + create: { id: ticket.closedById }, + where: { id: ticket.closedById }, + }, + }; + } + if (ticket.createdById) { + ticket.createdBy = { + connectOrCreate: { + create: { id: ticket.createdById }, + where: { id: ticket.createdById }, + }, + }; + } + + + + if (ticket.referencesTicketId) { + ticket.referencesTicket = { connect: { id: ticket.referencedTicketId } }; + } + ticket.guild = { connect: { id: options.guild } }; + + delete ticket.categoryId; + delete ticket.guildId; + delete ticket.claimedById; + delete ticket.closedById; + delete ticket.createdById; + delete ticket.referencesTicketId; + + await prisma.ticket.create({ data: ticket }); + await prisma.archivedMessage.createMany({ data: archivedMessages }); +} +spinner.succeed(`Imported ${dump.tickets.length} tickets`);