diff --git a/db/mysql/migration_v2.sql b/db/mysql/migration_v2.sql deleted file mode 100644 index b59878fd..00000000 --- a/db/mysql/migration_v2.sql +++ /dev/null @@ -1,85 +0,0 @@ --- account -DELETE FROM `user` -WHERE username = 'admin'; - -INSERT INTO `user` -(user_id, username, password, role, created_at, updated_at, deleted_at) -SELECT account_uuid, - username, - password, - CASE WHEN is_admin = true THEN 'admin' ELSE 'user' END, - created_at, - updated_at, - NULL -FROM v1_account -WHERE NOT EXISTS (SELECT 1 FROM `user`); - --- website -INSERT INTO website -(website_id, name, domain, share_id, rev_id, user_id, team_id, created_at) -SELECT website_uuid, - name, - domain, - share_id, - 0 rev_id, - a.account_uuid, - NULL team_id, - a.created_at -FROM v1_website w -JOIN v1_account a -ON a.user_id = w.user_id -WHERE NOT EXISTS (SELECT 1 FROM website); - --- session -INSERT INTO session -(session_id, website_id, hostname, browser, os, device, screen, language, country) -SELECT session_uuid, - w.website_uuid, - hostname, - browser, - os, - device, - screen, - language, - country -FROM v1_session s -JOIN v1_website w -ON w.website_id = s.website_id -WHERE NOT EXISTS (SELECT 1 FROM session); - --- pageview -INSERT INTO website_event -(event_id, website_id, session_id, created_at, url, referrer, event_type) -SELECT uuid() event_id, - w.website_uuid, - s.session_uuid, - p.created_at, - p.url, - p.referrer, - 1 event_type -FROM v1_pageview p -JOIN v1_session s -ON s.session_id = p.session_id -JOIN v1_website w -ON w.website_id = s.website_id -WHERE NOT EXISTS (SELECT 1 FROM website_event WHERE event_type = 1); - --- event / event_data -INSERT INTO website_event -(event_id, website_id, session_id, created_at, url, event_type, event_name, event_data) -SELECT e.event_uuid, - w.website_uuid, - s.session_uuid, - e.created_at, - e.url, - 2 event_type, - e.event_name, - ed.event_data -FROM v1_event e -JOIN v1_session s -ON s.session_id = e.session_id -JOIN v1_website w -ON w.website_id = s.website_id -LEFT JOIN v1_event_data ed -ON ed.event_id = e.event_id -WHERE NOT EXISTS (SELECT 1 FROM website_event WHERE event_type = 2); \ No newline at end of file diff --git a/db/postgresql/migration_v2.sql b/db/postgresql/migration_v2.sql deleted file mode 100644 index 3b8c1356..00000000 --- a/db/postgresql/migration_v2.sql +++ /dev/null @@ -1,85 +0,0 @@ --- account -DELETE FROM "user" -WHERE username = 'admin'; - -INSERT INTO "user" -(user_id, username, password, role, created_at, updated_at, deleted_at) -SELECT account_uuid, - username, - password, - CASE WHEN is_admin = true THEN 'admin' ELSE 'user' END, - created_at, - updated_at, - NULL -FROM v1_account -WHERE NOT EXISTS (SELECT 1 FROM "user"); - --- website -INSERT INTO website -(website_id, name, domain, share_id, rev_id, user_id, team_id, created_at) -SELECT website_uuid, - name, - domain, - share_id, - 0 rev_id, - a.account_uuid, - NULL team_id, - a.created_at -FROM v1_website w -JOIN v1_account a -ON a.user_id = w.user_id -WHERE NOT EXISTS (SELECT 1 FROM website); - --- session -INSERT INTO session -(session_id, website_id, hostname, browser, os, device, screen, language, country) -SELECT session_uuid, - w.website_uuid, - hostname, - browser, - os, - device, - screen, - language, - country -FROM v1_session s -JOIN v1_website w -ON w.website_id = s.website_id -WHERE NOT EXISTS (SELECT 1 FROM session); - --- pageview -INSERT INTO website_event -(event_id, website_id, session_id, created_at, url, referrer, event_type) -SELECT gen_random_uuid() event_id, - w.website_uuid, - s.session_uuid, - p.created_at, - p.url, - p.referrer, - 1 event_type -FROM v1_pageview p -JOIN v1_session s -ON s.session_id = p.session_id -JOIN v1_website w -ON w.website_id = s.website_id -WHERE NOT EXISTS (SELECT 1 FROM website_event WHERE event_type = 1); - --- event / event_data -INSERT INTO website_event -(event_id, website_id, session_id, created_at, url, event_type, event_name, event_data) -SELECT e.event_uuid, - w.website_uuid, - s.session_uuid, - e.created_at, - e.url, - 2 event_type, - e.event_name, - ed.event_data -FROM v1_event e -JOIN v1_session s -ON s.session_id = e.session_id -JOIN v1_website w -ON w.website_id = s.website_id -LEFT JOIN v1_event_data ed -ON ed.event_id = e.event_id -WHERE NOT EXISTS (SELECT 1 FROM website_event WHERE event_type = 2); \ No newline at end of file diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma index 5248ec79..ed1c4fe9 100644 --- a/db/postgresql/schema.prisma +++ b/db/postgresql/schema.prisma @@ -1,13 +1,11 @@ generator client { provider = "prisma-client-js" - previewFeatures = ["postgresqlExtensions"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") relationMode = "prisma" - extensions = [pgcrypto] } model User { diff --git a/package.json b/package.json index f7528d3b..334327f8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "update-tracker": "node scripts/update-tracker.js", "update-db": "prisma migrate deploy", "check-db": "node scripts/check-db.js", - "migrate-db": "node scripts/migrate-db.js", "copy-db-files": "node scripts/copy-db-files.js", "generate-lang": "npm-run-all extract-lang merge-lang", "extract-lang": "formatjs extract \"{pages,components}/**/*.js\" --out-file build/messages.json", diff --git a/scripts/migrate-db.js b/scripts/migrate-db.js deleted file mode 100644 index a41b4941..00000000 --- a/scripts/migrate-db.js +++ /dev/null @@ -1,277 +0,0 @@ -/* eslint-disable no-console */ -require('dotenv').config(); -const fs = require('fs'); -const path = require('path'); -const { PrismaClient } = require('@prisma/client'); -const chalk = require('chalk'); -const { execSync } = require('child_process'); -const prompts = require('prompts'); - -const prisma = new PrismaClient(); - -function getDatabaseType(url = process.env.DATABASE_URL) { - const type = process.env.DATABASE_TYPE || (url && url.split(':')[0]); - - if (type === 'postgres') { - return 'postgresql'; - } - - return type; -} - -const databaseType = getDatabaseType(); - -function success(msg) { - console.log(chalk.greenBright(`✓ ${msg}`)); -} - -function error(msg) { - console.log(chalk.redBright(`✗ ${msg}`)); -} - -async function checkEnv() { - if (!process.env.DATABASE_URL) { - throw new Error('DATABASE_URL is not defined.'); - } else { - success('DATABASE_URL is defined.'); - } -} - -async function checkConnection() { - try { - await prisma.$connect(); - - success('Database connection successful.'); - } catch (e) { - throw new Error('Unable to connect to the database.'); - } -} - -async function checkV1Tables(databaseType) { - try { - await prisma.$queryRaw`select * from _prisma_migrations where migration_name = '04_add_uuid' and finished_at IS NOT NULL`; - - console.log('Preparing v1 tables for migration'); - - // alter v1 tables - await dropV1Keys(databaseType); - await dropV1Indexes(databaseType); - await renameV1Tables(databaseType); - - success('Database v1 tables ready for migration.'); - } catch (e) { - error('Database v1 tables is not up to date.'); - } -} - -async function checkV2Tables() { - try { - await prisma.$queryRaw`select * from website_event limit 1`; - - success('Database v2 tables found.'); - } catch (e) { - console.log('Database v2 tables not found.'); - console.log('Adding v2 tables...'); - - // run v2 prisma migration steps - await runSqlFile('../prisma/migrations/01_init/migration.sql'); - console.log(execSync('prisma migrate resolve --applied 01_init').toString()); - } -} - -async function checkMigrationReady() { - try { - await prisma.$queryRaw`select * from website_event limit 1`; - await prisma.$queryRaw`select * from v1_account limit 1`; - - success('Database is ready for migration.'); - } catch (e) { - throw new Error('Database is not ready for migration.'); - } -} - -async function migrateData() { - const filePath = `../prisma/migration_v2.sql`; - console.log('Starting v2 data migration. Please do no cancel this process, it may take a while.'); - await runSqlFile(filePath); - - success('Data migration from V1 to V2 tables completed.'); -} - -async function dropV1Keys(databaseType) { - try { - // drop keys - if (databaseType === 'postgresql') { - await prisma.$transaction([ - prisma.$executeRaw`ALTER TABLE IF EXISTS _prisma_migrations DROP CONSTRAINT IF EXISTS _prisma_migrations_pkey CASCADE;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS account DROP CONSTRAINT IF EXISTS account_pkey CASCADE;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS event DROP CONSTRAINT IF EXISTS event_pkey CASCADE;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS session DROP CONSTRAINT IF EXISTS session_pkey CASCADE;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS website DROP CONSTRAINT IF EXISTS website_pkey CASCADE;`, - ]); - } else { - await prisma.$transaction([ - prisma.$executeRaw`ALTER TABLE session DROP FOREIGN KEY session_website_id_fkey;`, - prisma.$executeRaw`ALTER TABLE website DROP FOREIGN KEY website_user_id_fkey;`, - ]); - } - - success('Dropped v1 database keys.'); - } catch (e) { - console.log(e); - error('Failed to drop v1 database keys.'); - process.exit(1); - } -} - -async function dropV1Indexes(databaseType) { - try { - // drop indexes - if (databaseType === 'postgresql') { - await prisma.$transaction([ - prisma.$executeRaw`DROP INDEX IF EXISTS session_session_id_key;`, - prisma.$executeRaw`DROP INDEX IF EXISTS session_created_at_idx;`, - prisma.$executeRaw`DROP INDEX IF EXISTS session_website_id_idx;`, - prisma.$executeRaw`DROP INDEX IF EXISTS website_website_id_key;`, - prisma.$executeRaw`DROP INDEX IF EXISTS website_share_id_key;`, - prisma.$executeRaw`DROP INDEX IF EXISTS website_created_at_idx;`, - prisma.$executeRaw`DROP INDEX IF EXISTS website_share_id_idx;`, - prisma.$executeRaw`DROP INDEX IF EXISTS website_user_id_idx;`, - ]); - } else { - await prisma.$transaction([ - prisma.$executeRaw`DROP INDEX session_created_at_idx ON session;`, - prisma.$executeRaw`DROP INDEX session_website_id_idx ON session;`, - prisma.$executeRaw`DROP INDEX website_user_id_idx ON website;`, - prisma.$executeRaw`DROP INDEX website_share_id_key ON website;`, - ]); - } - - success('Dropped v1 database indexes.'); - } catch (e) { - console.log(e); - error('Failed to drop v1 database indexes.'); - process.exit(1); - } -} - -async function renameV1Tables(databaseType) { - try { - // rename tables - if (databaseType === 'postgresql') { - await prisma.$transaction([ - prisma.$executeRaw`ALTER TABLE IF EXISTS _prisma_migrations RENAME TO v1_prisma_migrations;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS account RENAME TO v1_account;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS event RENAME TO v1_event;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS event_data RENAME TO v1_event_data;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS pageview RENAME TO v1_pageview;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS session RENAME TO v1_session;`, - prisma.$executeRaw`ALTER TABLE IF EXISTS website RENAME TO v1_website;`, - ]); - } else { - await prisma.$transaction([ - prisma.$executeRaw`RENAME TABLE _prisma_migrations TO v1_prisma_migrations;`, - prisma.$executeRaw`RENAME TABLE account TO v1_account;`, - prisma.$executeRaw`RENAME TABLE event TO v1_event;`, - prisma.$executeRaw`RENAME TABLE event_data TO v1_event_data;`, - prisma.$executeRaw`RENAME TABLE pageview TO v1_pageview;`, - prisma.$executeRaw`RENAME TABLE session TO v1_session;`, - prisma.$executeRaw`RENAME TABLE website TO v1_website;`, - ]); - } - - success('Renamed v1 database tables.'); - } catch (e) { - console.log(e); - error('Failed to rename v1 database tables.'); - process.exit(1); - } -} - -async function deleteV1TablesPrompt() { - const response = await prompts({ - type: 'text', - name: 'value', - message: 'Do you want to delete V1 database tables? (Y/N)', - validate: value => - value.toUpperCase() !== 'Y' && value.toUpperCase() !== 'N' ? `Please enter Y or N.` : true, - }); - - if (response.value.toUpperCase() == 'Y') { - await deleteV1Tables(); - } - - success('Migration successfully completed.'); -} - -async function deleteV1Tables() { - try { - // drop tables - await prisma.$transaction([ - prisma.$executeRaw`DROP TABLE IF EXISTS v1_prisma_migrations;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_event_data;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_event;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_pageview;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_session;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_website;`, - prisma.$executeRaw`DROP TABLE IF EXISTS v1_account;`, - ]); - - success('Dropped v1 database tables.'); - } catch (e) { - console.log(e); - throw new Error('Failed to drop v1 database tables.'); - } -} - -async function runSqlFile(filePath) { - try { - const rawSql = await fs.promises.readFile(path.join(__dirname, filePath)); - - const sqlStatements = rawSql - .toString() - .split('\n') - .filter(line => !line.startsWith('--')) // remove comments-only lines - .join('\n') - .replace(/\r\n|\n|\r/g, ' ') // remove newlines - .replace(/\s+/g, ' ') // excess white space - .split(';'); - - for (const sql of sqlStatements) { - if (sql.length > 0) { - await prisma.$executeRawUnsafe(sql); - } - } - filePath; - - success(`Ran sql file ${filePath}.`); - } catch (e) { - console.log(e); - throw new Error(`Failed to run sql file ${filePath}.`); - } -} - -(async () => { - let err = false; - for (let fn of [ - checkEnv, - checkConnection, - checkV1Tables, - checkV2Tables, - checkMigrationReady, - migrateData, - deleteV1TablesPrompt, - ]) { - try { - fn.name === 'checkV1Tables' ? await fn(databaseType) : await fn(); - } catch (e) { - error(e.message); - err = true; - } finally { - await prisma.$disconnect(); - if (err) { - process.exit(1); - } - } - } -})();