diff --git a/.github/workflows/branch-deploys.yml b/.github/workflows/branch-deploys.yml index f249daa..ff4e8d6 100644 --- a/.github/workflows/branch-deploys.yml +++ b/.github/workflows/branch-deploys.yml @@ -51,4 +51,4 @@ jobs: - name: Migrate Database Schema run: pnpm migrate.staging - name: Build ⚙️ & Deploy ⬆️ - run: netlify deploy --alias="staging" --build --context deploy-preview --message="${{ github.event.head_commit.message }}" + run: netlify deploy --alias="staging" --build --context branch-deploy --message="${{ github.event.head_commit.message }}" diff --git a/db/migrations/0005_hard_overlord.sql b/db/migrations/0005_hard_overlord.sql new file mode 100644 index 0000000..234fd60 --- /dev/null +++ b/db/migrations/0005_hard_overlord.sql @@ -0,0 +1,12 @@ +CREATE TYPE "public"."theme_pref" AS ENUM('system', 'dark', 'light');--> statement-breakpoint +CREATE TABLE "user_prefs" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp DEFAULT now(), + "updated_at" timestamp DEFAULT now(), + "user" bigint NOT NULL, + "country" varchar(2) NOT NULL, + "theme" "theme_pref" DEFAULT 'light', + "currency" varchar(3) NOT NULL +); +--> statement-breakpoint +ALTER TABLE "user_prefs" ADD CONSTRAINT "user_prefs_user_users_id_fk" FOREIGN KEY ("user") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/0006_aromatic_captain_flint.sql b/db/migrations/0006_aromatic_captain_flint.sql new file mode 100644 index 0000000..a13de09 --- /dev/null +++ b/db/migrations/0006_aromatic_captain_flint.sql @@ -0,0 +1,2 @@ +ALTER TABLE "account_transactions" ADD COLUMN "notes" text;--> statement-breakpoint +ALTER TABLE "wallet_transactions" ADD COLUMN "notes" text; \ No newline at end of file diff --git a/db/migrations/0007_grey_centennial.sql b/db/migrations/0007_grey_centennial.sql new file mode 100644 index 0000000..b50323e --- /dev/null +++ b/db/migrations/0007_grey_centennial.sql @@ -0,0 +1 @@ +ALTER TABLE "user_prefs" ADD COLUMN "language" varchar(2) NOT NULL; \ No newline at end of file diff --git a/db/migrations/0008_soft_the_call.sql b/db/migrations/0008_soft_the_call.sql new file mode 100644 index 0000000..97ae694 --- /dev/null +++ b/db/migrations/0008_soft_the_call.sql @@ -0,0 +1 @@ +ALTER TABLE "user_prefs" ALTER COLUMN "theme" SET NOT NULL; \ No newline at end of file diff --git a/db/migrations/0009_sticky_zaran.sql b/db/migrations/0009_sticky_zaran.sql new file mode 100644 index 0000000..6803c7e --- /dev/null +++ b/db/migrations/0009_sticky_zaran.sql @@ -0,0 +1,13 @@ +CREATE TYPE "public"."account_connection_providers" AS ENUM('telegram');--> statement-breakpoint +CREATE TABLE "account_connections" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp DEFAULT now(), + "updated_at" timestamp DEFAULT now(), + "user" bigint NOT NULL, + "provider" "account_connection_providers" NOT NULL, + "params" jsonb NOT NULL, + "is_valid" boolean DEFAULT true +); +--> statement-breakpoint +ALTER TABLE "account_transactions" RENAME COLUMN "payment_method_extras" TO "params";--> statement-breakpoint +ALTER TABLE "account_connections" ADD CONSTRAINT "account_connections_user_users_id_fk" FOREIGN KEY ("user") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/0010_clever_ultimo.sql b/db/migrations/0010_clever_ultimo.sql new file mode 100644 index 0000000..ca3f3e9 --- /dev/null +++ b/db/migrations/0010_clever_ultimo.sql @@ -0,0 +1,7 @@ +CREATE TYPE "public"."account_connection_status" AS ENUM('active', 'inactive', 'reconnect_required');--> statement-breakpoint +ALTER TABLE "account_transactions" RENAME TO "payment_transactions";--> statement-breakpoint +ALTER TABLE "wallet_transactions" DROP CONSTRAINT "wallet_transactions_account_transaction_account_transactions_id_fk"; +--> statement-breakpoint +ALTER TABLE "account_connections" ADD COLUMN "status" "account_connection_status" DEFAULT 'active' NOT NULL;--> statement-breakpoint +ALTER TABLE "wallet_transactions" ADD CONSTRAINT "wallet_transactions_account_transaction_payment_transactions_id_fk" FOREIGN KEY ("account_transaction") REFERENCES "public"."payment_transactions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "account_connections" DROP COLUMN "is_valid"; \ No newline at end of file diff --git a/db/migrations/0011_worried_colonel_america.sql b/db/migrations/0011_worried_colonel_america.sql new file mode 100644 index 0000000..03c4ab0 --- /dev/null +++ b/db/migrations/0011_worried_colonel_america.sql @@ -0,0 +1,3 @@ +ALTER TABLE "account_connections" DROP CONSTRAINT "account_connections_user_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "account_connections" ADD CONSTRAINT "account_connections_user_users_id_fk" FOREIGN KEY ("user") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/0012_naive_silvermane.sql b/db/migrations/0012_naive_silvermane.sql new file mode 100644 index 0000000..c266662 --- /dev/null +++ b/db/migrations/0012_naive_silvermane.sql @@ -0,0 +1,18 @@ +CREATE TABLE "verification_codes" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "window" interval NOT NULL, + "code" varchar(6) NOT NULL, + "confirmed_at" timestamp, + "data" jsonb, + CONSTRAINT "verification_codes_code_unique" UNIQUE("code") +); +--> statement-breakpoint +CREATE VIEW "public"."vw_verification_codes" AS (select "code", "created_at", + ("created_at" + "window")::TIMESTAMP + as "expires_at", + (CASE + WHEN "confirmed_at" IS NOT NULL THEN true + ELSE NOW() > ("created_at" + "window") + END)::BOOlEAN + as "is_expired", "data" from "verification_codes"); \ No newline at end of file diff --git a/db/migrations/0013_orange_maverick.sql b/db/migrations/0013_orange_maverick.sql new file mode 100644 index 0000000..b218ea8 --- /dev/null +++ b/db/migrations/0013_orange_maverick.sql @@ -0,0 +1,13 @@ +DROP VIEW "public"."vw_verification_codes";--> statement-breakpoint +ALTER TABLE "verification_codes" DROP CONSTRAINT "verification_codes_code_unique";--> statement-breakpoint +ALTER TABLE "verification_codes" ADD COLUMN "hash" varchar(32) NOT NULL;--> statement-breakpoint +ALTER TABLE "verification_codes" DROP COLUMN "code";--> statement-breakpoint +ALTER TABLE "verification_codes" ADD CONSTRAINT "verification_codes_hash_unique" UNIQUE("hash");--> statement-breakpoint +CREATE VIEW "public"."vw_verification_codes" AS (select "hash", "created_at", + ("created_at" + "window")::TIMESTAMP + as "expires_at", + (CASE + WHEN "confirmed_at" IS NOT NULL THEN true + ELSE NOW() > ("created_at" + "window") + END)::BOOlEAN + as "is_expired", "data" from "verification_codes"); \ No newline at end of file diff --git a/db/migrations/0014_windy_masked_marvel.sql b/db/migrations/0014_windy_masked_marvel.sql new file mode 100644 index 0000000..2ec7f5b --- /dev/null +++ b/db/migrations/0014_windy_masked_marvel.sql @@ -0,0 +1,4 @@ +ALTER TABLE "account_connections" DROP CONSTRAINT "account_connections_user_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "user_prefs" ALTER COLUMN "theme" SET DEFAULT 'system';--> statement-breakpoint +ALTER TABLE "account_connections" ADD COLUMN "provider_id" varchar(255) NOT NULL; \ No newline at end of file diff --git a/db/migrations/0015_salty_aqueduct.sql b/db/migrations/0015_salty_aqueduct.sql new file mode 100644 index 0000000..e6f3fc4 --- /dev/null +++ b/db/migrations/0015_salty_aqueduct.sql @@ -0,0 +1 @@ +ALTER TABLE "account_connections" ADD CONSTRAINT "account_connections_user_users_id_fk" FOREIGN KEY ("user") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/0016_wakeful_sheva_callister.sql b/db/migrations/0016_wakeful_sheva_callister.sql new file mode 100644 index 0000000..15306d6 --- /dev/null +++ b/db/migrations/0016_wakeful_sheva_callister.sql @@ -0,0 +1 @@ +ALTER TABLE "user_prefs" ALTER COLUMN "theme" SET DEFAULT 'light'; \ No newline at end of file diff --git a/db/migrations/0017_careless_kingpin.sql b/db/migrations/0017_careless_kingpin.sql new file mode 100644 index 0000000..2f25f59 --- /dev/null +++ b/db/migrations/0017_careless_kingpin.sql @@ -0,0 +1 @@ +ALTER TYPE "public"."payment_methods" RENAME TO "payment_methods_types"; \ No newline at end of file diff --git a/db/migrations/0018_ordinary_amphibian.sql b/db/migrations/0018_ordinary_amphibian.sql new file mode 100644 index 0000000..24d3410 --- /dev/null +++ b/db/migrations/0018_ordinary_amphibian.sql @@ -0,0 +1,13 @@ +CREATE TYPE "public"."payment_method_status" AS ENUM('active', 'inactive', 're-connection required');--> statement-breakpoint +ALTER TYPE "public"."payment_methods_types" RENAME TO "payment_method_provider";--> statement-breakpoint +CREATE TABLE "payment_methods" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "provider" "payment_method_provider" NOT NULL, + "params" jsonb NOT NULL, + "status" "payment_method_status" DEFAULT 'active' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "owner" bigint NOT NULL +); +--> statement-breakpoint +ALTER TABLE "payment_methods" ADD CONSTRAINT "payment_methods_owner_users_id_fk" FOREIGN KEY ("owner") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/0019_chubby_cassandra_nova.sql b/db/migrations/0019_chubby_cassandra_nova.sql new file mode 100644 index 0000000..97d8833 --- /dev/null +++ b/db/migrations/0019_chubby_cassandra_nova.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX "payment_methods_provider_owner_index" ON "payment_methods" USING btree ("provider","owner"); \ No newline at end of file diff --git a/db/migrations/0020_steep_azazel.sql b/db/migrations/0020_steep_azazel.sql new file mode 100644 index 0000000..7bd897b --- /dev/null +++ b/db/migrations/0020_steep_azazel.sql @@ -0,0 +1,12 @@ +ALTER TABLE "campaign_publications" DROP CONSTRAINT "campaign_publications_campaign_campaigns_id_fk"; +--> statement-breakpoint +ALTER TABLE "campaigns" DROP CONSTRAINT "campaigns_created_by_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "payment_methods" DROP CONSTRAINT "payment_methods_owner_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "wallets" DROP CONSTRAINT "wallets_owned_by_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "campaign_publications" ADD CONSTRAINT "campaign_publications_campaign_campaigns_id_fk" FOREIGN KEY ("campaign") REFERENCES "public"."campaigns"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "campaigns" ADD CONSTRAINT "campaigns_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "payment_methods" ADD CONSTRAINT "payment_methods_owner_users_id_fk" FOREIGN KEY ("owner") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wallets" ADD CONSTRAINT "wallets_owned_by_users_id_fk" FOREIGN KEY ("owned_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/meta/0005_snapshot.json b/db/migrations/meta/0005_snapshot.json new file mode 100644 index 0000000..bda1546 --- /dev/null +++ b/db/migrations/meta/0005_snapshot.json @@ -0,0 +1,811 @@ +{ + "id": "4b21c235-9ca8-4ce0-aaf1-f8159a878a2e", + "prevId": "509173b9-84d5-45a3-8cfd-c5d45a439614", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_transactions": { + "name": "account_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "payment_method_extras": { + "name": "payment_method_extras", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_account_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_account_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "account_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0006_snapshot.json b/db/migrations/meta/0006_snapshot.json new file mode 100644 index 0000000..b97e193 --- /dev/null +++ b/db/migrations/meta/0006_snapshot.json @@ -0,0 +1,823 @@ +{ + "id": "c442c8a5-5d70-4dcb-91cd-7491e8920705", + "prevId": "4b21c235-9ca8-4ce0-aaf1-f8159a878a2e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_transactions": { + "name": "account_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "payment_method_extras": { + "name": "payment_method_extras", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_account_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_account_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "account_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0007_snapshot.json b/db/migrations/meta/0007_snapshot.json new file mode 100644 index 0000000..5c4e1e3 --- /dev/null +++ b/db/migrations/meta/0007_snapshot.json @@ -0,0 +1,829 @@ +{ + "id": "7afd57a9-9dc6-4be6-b42b-303959440a89", + "prevId": "c442c8a5-5d70-4dcb-91cd-7491e8920705", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_transactions": { + "name": "account_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "payment_method_extras": { + "name": "payment_method_extras", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_account_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_account_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "account_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0008_snapshot.json b/db/migrations/meta/0008_snapshot.json new file mode 100644 index 0000000..6c83ff7 --- /dev/null +++ b/db/migrations/meta/0008_snapshot.json @@ -0,0 +1,829 @@ +{ + "id": "5a9da89f-9040-4006-b7fc-61df8a2c41cd", + "prevId": "7afd57a9-9dc6-4be6-b42b-303959440a89", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_transactions": { + "name": "account_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "payment_method_extras": { + "name": "payment_method_extras", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_account_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_account_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "account_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0009_snapshot.json b/db/migrations/meta/0009_snapshot.json new file mode 100644 index 0000000..b2c650b --- /dev/null +++ b/db/migrations/meta/0009_snapshot.json @@ -0,0 +1,910 @@ +{ + "id": "d2a9b436-8328-46f4-9ae5-da6656a45b1a", + "prevId": "5a9da89f-9040-4006-b7fc-61df8a2c41cd", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_transactions": { + "name": "account_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_account_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_account_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "account_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "is_valid": { + "name": "is_valid", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0010_snapshot.json b/db/migrations/meta/0010_snapshot.json new file mode 100644 index 0000000..951a9b9 --- /dev/null +++ b/db/migrations/meta/0010_snapshot.json @@ -0,0 +1,920 @@ +{ + "id": "6af22a82-aee0-4ec4-b996-bf7125bbaba1", + "prevId": "d2a9b436-8328-46f4-9ae5-da6656a45b1a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0011_snapshot.json b/db/migrations/meta/0011_snapshot.json new file mode 100644 index 0000000..c145edb --- /dev/null +++ b/db/migrations/meta/0011_snapshot.json @@ -0,0 +1,920 @@ +{ + "id": "4c06dfbb-44d4-4e38-b51e-82619c550c4d", + "prevId": "6af22a82-aee0-4ec4-b996-bf7125bbaba1", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0012_snapshot.json b/db/migrations/meta/0012_snapshot.json new file mode 100644 index 0000000..e2550bd --- /dev/null +++ b/db/migrations/meta/0012_snapshot.json @@ -0,0 +1,1007 @@ +{ + "id": "bd0b6b98-a3b9-42b1-9f1d-efbfa3dfea99", + "prevId": "4c06dfbb-44d4-4e38-b51e-82619c550c4d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_code_unique": { + "name": "verification_codes_code_unique", + "nullsNotDistinct": false, + "columns": [ + "code" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "code": { + "name": "code", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"code\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0013_snapshot.json b/db/migrations/meta/0013_snapshot.json new file mode 100644 index 0000000..4392ce1 --- /dev/null +++ b/db/migrations/meta/0013_snapshot.json @@ -0,0 +1,1007 @@ +{ + "id": "b43b27d2-3c2a-445a-beeb-35affdf0f760", + "prevId": "bd0b6b98-a3b9-42b1-9f1d-efbfa3dfea99", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0014_snapshot.json b/db/migrations/meta/0014_snapshot.json new file mode 100644 index 0000000..c227a72 --- /dev/null +++ b/db/migrations/meta/0014_snapshot.json @@ -0,0 +1,999 @@ +{ + "id": "06992557-f53c-457c-b859-c624e076e338", + "prevId": "b43b27d2-3c2a-445a-beeb-35affdf0f760", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0015_snapshot.json b/db/migrations/meta/0015_snapshot.json new file mode 100644 index 0000000..7554d8a --- /dev/null +++ b/db/migrations/meta/0015_snapshot.json @@ -0,0 +1,1013 @@ +{ + "id": "0cb97517-5d69-4ff9-b150-8e2a21f48078", + "prevId": "06992557-f53c-457c-b859-c624e076e338", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0016_snapshot.json b/db/migrations/meta/0016_snapshot.json new file mode 100644 index 0000000..46fc5fd --- /dev/null +++ b/db/migrations/meta/0016_snapshot.json @@ -0,0 +1,1013 @@ +{ + "id": "431d6d58-1cfb-4476-9bd2-e3d31454a9f0", + "prevId": "0cb97517-5d69-4ff9-b150-8e2a21f48078", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods": { + "name": "payment_methods", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0017_snapshot.json b/db/migrations/meta/0017_snapshot.json new file mode 100644 index 0000000..3dd4816 --- /dev/null +++ b/db/migrations/meta/0017_snapshot.json @@ -0,0 +1,1013 @@ +{ + "id": "5e165dc1-1b10-47d7-978e-d8a984ef876e", + "prevId": "431d6d58-1cfb-4476-9bd2-e3d31454a9f0", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_methods_types", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_methods_types": { + "name": "payment_methods_types", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0018_snapshot.json b/db/migrations/meta/0018_snapshot.json new file mode 100644 index 0000000..5b4fa0c --- /dev/null +++ b/db/migrations/meta/0018_snapshot.json @@ -0,0 +1,1097 @@ +{ + "id": "a87054ab-076a-4101-8e02-17e0a4c1ebb6", + "prevId": "5e165dc1-1b10-47d7-978e-d8a984ef876e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "payment_method_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "owner": { + "name": "owner", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "payment_methods_owner_users_id_fk": { + "name": "payment_methods_owner_users_id_fk", + "tableFrom": "payment_methods", + "tableTo": "users", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_method_provider": { + "name": "payment_method_provider", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.payment_method_status": { + "name": "payment_method_status", + "schema": "public", + "values": [ + "active", + "inactive", + "re-connection required" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0019_snapshot.json b/db/migrations/meta/0019_snapshot.json new file mode 100644 index 0000000..1cbbdfb --- /dev/null +++ b/db/migrations/meta/0019_snapshot.json @@ -0,0 +1,1119 @@ +{ + "id": "6a9696a1-12f8-488d-b4ff-350d37099f1b", + "prevId": "a87054ab-076a-4101-8e02-17e0a4c1ebb6", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "payment_method_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "owner": { + "name": "owner", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "payment_methods_provider_owner_index": { + "name": "payment_methods_provider_owner_index", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payment_methods_owner_users_id_fk": { + "name": "payment_methods_owner_users_id_fk", + "tableFrom": "payment_methods", + "tableTo": "users", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_method_provider": { + "name": "payment_method_provider", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.payment_method_status": { + "name": "payment_method_status", + "schema": "public", + "values": [ + "active", + "inactive", + "re-connection required" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0020_snapshot.json b/db/migrations/meta/0020_snapshot.json new file mode 100644 index 0000000..77ff4da --- /dev/null +++ b/db/migrations/meta/0020_snapshot.json @@ -0,0 +1,1119 @@ +{ + "id": "ed875421-5fff-4903-b19d-8a5143a8eae7", + "prevId": "6a9696a1-12f8-488d-b4ff-350d37099f1b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.campaign_publications": { + "name": "campaign_publications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaign_publications_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "campaign": { + "name": "campaign", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "publish_after": { + "name": "publish_after", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "publish_before": { + "name": "publish_before", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "campaign_publications_campaign_campaigns_id_fk": { + "name": "campaign_publications_campaign_campaigns_id_fk", + "tableFrom": "campaign_publications", + "tableTo": "campaigns", + "columnsFrom": [ + "campaign" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.campaigns": { + "name": "campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "campaigns_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media": { + "name": "media", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "emails": { + "name": "emails", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "phones": { + "name": "phones", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "categories": { + "name": "categories", + "type": "bigint[]", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "campaigns_created_by_users_id_fk": { + "name": "campaigns_created_by_users_id_fk", + "tableFrom": "campaigns", + "tableTo": "users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "payment_method_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "owner": { + "name": "owner", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "payment_methods_provider_owner_index": { + "name": "payment_methods_provider_owner_index", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payment_methods_owner_users_id_fk": { + "name": "payment_methods_owner_users_id_fk", + "tableFrom": "payment_methods", + "tableTo": "users", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_transactions": { + "name": "payment_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_method_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "external_transaction_id": { + "name": "external_transaction_id", + "type": "varchar(400)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "inbound": { + "name": "inbound", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_transactions": { + "name": "wallet_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from": { + "name": "from", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to": { + "name": "to", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "type": { + "name": "type", + "type": "wallet_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_transaction": { + "name": "account_transaction", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wallet_transactions_from_wallets_id_fk": { + "name": "wallet_transactions_from_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "from" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_to_wallets_id_fk": { + "name": "wallet_transactions_to_wallets_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "wallets", + "columnsFrom": [ + "to" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wallet_transactions_account_transaction_payment_transactions_id_fk": { + "name": "wallet_transactions_account_transaction_payment_transactions_id_fk", + "tableFrom": "wallet_transactions", + "tableTo": "payment_transactions", + "columnsFrom": [ + "account_transaction" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owned_by": { + "name": "owned_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "starting_balance": { + "name": "starting_balance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "wallets_owned_by_users_id_fk": { + "name": "wallets_owned_by_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "owned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account_connections": { + "name": "account_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "account_connection_providers", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "account_connection_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_connections_user_users_id_fk": { + "name": "account_connections_user_users_id_fk", + "tableFrom": "account_connections", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.federated_credentials": { + "name": "federated_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "last_access_token": { + "name": "last_access_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_prefs": { + "name": "user_prefs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "theme_pref", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'light'" + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_prefs_user_users_id_fk": { + "name": "user_prefs_user_users_id_fk", + "tableFrom": "user_prefs", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "users_id_seq", + "schema": "public", + "increment": "1", + "startWith": "100", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "names": { + "name": "names", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "dob": { + "name": "dob", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_credentials_federated_credentials_id_fk": { + "name": "users_credentials_federated_credentials_id_fk", + "tableFrom": "users", + "tableTo": "federated_credentials", + "columnsFrom": [ + "credentials" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "verification_codes_hash_unique": { + "name": "verification_codes_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.payment_method_provider": { + "name": "payment_method_provider", + "schema": "public", + "values": [ + "momo" + ] + }, + "public.payment_method_status": { + "name": "payment_method_status", + "schema": "public", + "values": [ + "active", + "inactive", + "re-connection required" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "cancelled", + "complete" + ] + }, + "public.wallet_transaction_type": { + "name": "wallet_transaction_type", + "schema": "public", + "values": [ + "funding", + "reward", + "withdrawal" + ] + }, + "public.account_connection_providers": { + "name": "account_connection_providers", + "schema": "public", + "values": [ + "telegram" + ] + }, + "public.account_connection_status": { + "name": "account_connection_status", + "schema": "public", + "values": [ + "active", + "inactive", + "reconnect_required" + ] + }, + "public.theme_pref": { + "name": "theme_pref", + "schema": "public", + "values": [ + "system", + "dark", + "light" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.vw_funding_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'funding' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_funding_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_reward_balances": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ownedBy": { + "name": "ownedBy", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "definition": "select \"wallets\".\"id\", \"wallets\".\"starting_balance\" + SUM(\n CASE\n WHEN (\"wallet_transactions\".\"from\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN -\"wallet_transactions\".\"value\"\n WHEN (\"wallet_transactions\".\"to\" = \"wallets\".\"id\" and \"wallet_transactions\".\"type\" = 'reward' and \"wallet_transactions\".\"status\" = 'complete') THEN \"wallet_transactions\".\"value\"\n ELSE 0\n END\n )::BIGINT as \"balance\", \"wallets\".\"owned_by\" from \"wallets\" left join \"wallet_transactions\" on (\"wallets\".\"id\" = \"wallet_transactions\".\"from\" or \"wallets\".\"id\" = \"wallet_transactions\".\"to\") left join \"users\" on \"wallets\".\"owned_by\" = \"users\".\"id\" group by \"wallets\".\"id\", \"wallets\".\"owned_by\"", + "name": "vw_reward_balances", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_verification_codes": { + "columns": { + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "definition": "select \"hash\", \"created_at\", \n (\"created_at\" + \"window\")::TIMESTAMP\n as \"expires_at\", \n (CASE\n WHEN \"confirmed_at\" IS NOT NULL THEN true\n ELSE NOW() > (\"created_at\" + \"window\")\n END)::BOOlEAN\n as \"is_expired\", \"data\" from \"verification_codes\"", + "name": "vw_verification_codes", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/_journal.json b/db/migrations/meta/_journal.json index 805b1ad..da28f99 100644 --- a/db/migrations/meta/_journal.json +++ b/db/migrations/meta/_journal.json @@ -36,6 +36,118 @@ "when": 1735994501332, "tag": "0004_bright_abomination", "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1736081680386, + "tag": "0005_hard_overlord", + "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1736082913811, + "tag": "0006_aromatic_captain_flint", + "breakpoints": true + }, + { + "idx": 7, + "version": "7", + "when": 1736086612115, + "tag": "0007_grey_centennial", + "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1736114399152, + "tag": "0008_soft_the_call", + "breakpoints": true + }, + { + "idx": 9, + "version": "7", + "when": 1736124659462, + "tag": "0009_sticky_zaran", + "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1736127260142, + "tag": "0010_clever_ultimo", + "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1736160957442, + "tag": "0011_worried_colonel_america", + "breakpoints": true + }, + { + "idx": 12, + "version": "7", + "when": 1736200144418, + "tag": "0012_naive_silvermane", + "breakpoints": true + }, + { + "idx": 13, + "version": "7", + "when": 1736200563990, + "tag": "0013_orange_maverick", + "breakpoints": true + }, + { + "idx": 14, + "version": "7", + "when": 1736257860294, + "tag": "0014_windy_masked_marvel", + "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1736257983027, + "tag": "0015_salty_aqueduct", + "breakpoints": true + }, + { + "idx": 16, + "version": "7", + "when": 1736334264840, + "tag": "0016_wakeful_sheva_callister", + "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1736339034995, + "tag": "0017_careless_kingpin", + "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1736380990994, + "tag": "0018_ordinary_amphibian", + "breakpoints": true + }, + { + "idx": 19, + "version": "7", + "when": 1736386723508, + "tag": "0019_chubby_cassandra_nova", + "breakpoints": true + }, + { + "idx": 20, + "version": "7", + "when": 1736391217180, + "tag": "0020_steep_azazel", + "breakpoints": true } ] } \ No newline at end of file diff --git a/db/schema/campaigns.ts b/db/schema/campaigns.ts index 1151d52..6592ae2 100644 --- a/db/schema/campaigns.ts +++ b/db/schema/campaigns.ts @@ -14,7 +14,7 @@ export const campaigns = pgTable('campaigns', { createdAt: timestamp({ mode: 'date' }).defaultNow(), updatedAt: timestamp({ mode: 'date' }).defaultNow().$onUpdate(() => new Date()), categories: bigint({ mode: 'number' }).array().notNull(), - createdBy: bigint({ mode: 'number' }).notNull().references(() => users.id, { onDelete: 'cascade' }) + createdBy: bigint({ mode: 'number' }).notNull().references(() => users.id) }); export const newCampaignSchema = createInsertSchema(campaigns); @@ -23,7 +23,7 @@ export const campaignPublications = pgTable('campaign_publications', { id: bigint({ mode: 'number' }).generatedAlwaysAsIdentity().primaryKey(), createdAt: timestamp({ mode: 'date' }).defaultNow(), updatedAt: timestamp({ mode: 'date' }).defaultNow(), - campaign: bigint({ mode: 'number' }).notNull().references(() => campaigns.id, { onDelete: 'cascade' }), + campaign: bigint({ mode: 'number' }).notNull().references(() => campaigns.id), tokens: integer().notNull(), publishAfter: date({ mode: 'string' }).defaultNow(), publishBefore: date({ mode: 'string' }) diff --git a/db/schema/finance.ts b/db/schema/finance.ts index f83884a..c6fe919 100644 --- a/db/schema/finance.ts +++ b/db/schema/finance.ts @@ -1,17 +1,46 @@ -import { and, eq, or, sql } from "drizzle-orm"; -import { bigint, boolean, json, pgEnum, pgTable, pgView, real, timestamp, uuid, varchar } from "drizzle-orm/pg-core"; -import { users } from "./users"; +import { and, eq, or, sql } from 'drizzle-orm'; +import { + bigint, + boolean, + jsonb, + pgEnum, + pgTable, + pgView, + real, + text, + timestamp, + unique, + uniqueIndex, + uuid, + varchar +} from 'drizzle-orm/pg-core'; +import { users } from './users'; + +export const paymentMethodProviders = pgEnum('payment_method_provider', ['momo']); +export const paymentMethodStatus = pgEnum('payment_method_status', ['active', 'inactive', 're-connection required']); +export const paymentMethods = pgTable('payment_methods', { + id: uuid().primaryKey().defaultRandom(), + provider: paymentMethodProviders().notNull(), + params: jsonb().notNull(), + status: paymentMethodStatus().notNull().default('active'), + createdAt: timestamp({ mode: 'date' }).notNull().defaultNow(), + updatedAt: timestamp({ mode: 'date' }).notNull().defaultNow().$onUpdate(() => new Date()), + owner: bigint({ mode: 'number' }).notNull().references(() => users.id), +}, table => { + return { + idx: uniqueIndex().on(table.provider, table.owner) + } +}); export const wallets = pgTable('wallets', { id: uuid().primaryKey().defaultRandom(), - ownedBy: bigint({ mode: 'number' }).notNull().references(() => users.id, { onDelete: 'cascade' }), + ownedBy: bigint({ mode: 'number' }).notNull().references(() => users.id), createdAt: timestamp({ mode: 'date' }).defaultNow(), updatedAt: timestamp({ mode: 'date' }).defaultNow().$onUpdate(() => new Date()), startingBalance: bigint({ mode: 'number' }).default(0) }); export const transactionStatus = pgEnum('transaction_status', ['pending', 'cancelled', 'complete']); -export const paymentMethods = pgEnum('payment_methods', ['momo']); export const walletTransactionType = pgEnum('wallet_transaction_type', ['funding', 'reward', 'withdrawal']); export const walletTransactions = pgTable('wallet_transactions', { id: uuid().primaryKey().defaultRandom(), @@ -23,19 +52,22 @@ export const walletTransactions = pgTable('wallet_transactions', { cancelledAt: timestamp({ mode: 'date' }), status: transactionStatus().default('pending'), type: walletTransactionType().notNull(), + notes: text(), accountTransaction: uuid().references(() => paymentTransactions.id) }); -export const paymentTransactions = pgTable('account_transactions', { + +export const paymentTransactions = pgTable('payment_transactions', { id: uuid().primaryKey().defaultRandom(), - paymentMethod: paymentMethods().notNull(), + paymentMethod: paymentMethodProviders().notNull(), status: transactionStatus().notNull(), externalTransactionId: varchar({ length: 400 }), recordedAt: timestamp({ mode: 'date' }).defaultNow(), completedAt: timestamp({ mode: 'date' }), cancelledAt: timestamp({ mode: 'date' }), value: real().notNull(), + notes: text(), currency: varchar({ length: 10 }).notNull(), - paymentMethodExtras: json(), + params: jsonb(), inbound: boolean().notNull() }); @@ -57,18 +89,18 @@ export const fundingBalances = pgView('vw_funding_balances') ); export const rewardBalances = pgView('vw_reward_balances') -.as(qb => qb.select({ - id: wallets.id, - balance: sql`${wallets.startingBalance} + SUM( + .as(qb => qb.select({ + id: wallets.id, + balance: sql`${wallets.startingBalance} + SUM( CASE WHEN ${and(eq(walletTransactions.from, wallets.id), eq(walletTransactions.type, 'reward'), eq(walletTransactions.status, 'complete'))} THEN -${walletTransactions.value} WHEN ${and(eq(walletTransactions.to, wallets.id), eq(walletTransactions.type, 'reward'), eq(walletTransactions.status, 'complete'))} THEN ${walletTransactions.value} ELSE 0 END )::BIGINT`.as('balance'), - ownerId: wallets.ownedBy -}).from(wallets) - .leftJoin(walletTransactions, (wallet) => or(eq(wallet.id, walletTransactions.from), eq(wallet.id, walletTransactions.to))) - .leftJoin(users, (wallet) => eq(wallet.ownerId, users.id)) - .groupBy(wallets.id, wallets.ownedBy) -); + ownerId: wallets.ownedBy + }).from(wallets) + .leftJoin(walletTransactions, (wallet) => or(eq(wallet.id, walletTransactions.from), eq(wallet.id, walletTransactions.to))) + .leftJoin(users, (wallet) => eq(wallet.ownerId, users.id)) + .groupBy(wallets.id, wallets.ownedBy) + ); diff --git a/db/schema/users.ts b/db/schema/users.ts index 8ec3baa..f1759fe 100644 --- a/db/schema/users.ts +++ b/db/schema/users.ts @@ -1,5 +1,48 @@ -import { bigint, date, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'; -import { createSelectSchema } from 'drizzle-zod'; +import { sql } from 'drizzle-orm'; +import { bigint, date, interval, jsonb, pgEnum, pgTable, pgView, timestamp, uuid, varchar } from 'drizzle-orm/pg-core'; +import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-zod'; +import { z } from 'zod'; + +export const verificationCodes = pgTable('verification_codes', { + id: uuid().primaryKey().defaultRandom(), + created_at: timestamp({ mode: 'date' }).notNull().defaultNow(), + window: interval().notNull(), + hash: varchar({ length: 32 }).notNull().unique(), + confirmed_at: timestamp({ mode: 'date' }), + data: jsonb() +}); + +export const newVerificationSchema = createInsertSchema(verificationCodes); + +export const verificationCodesView = pgView('vw_verification_codes').as(qb => { + return qb.select({ + code: verificationCodes.hash, + createdAt: verificationCodes.created_at, + expiresAt: sql` + (${verificationCodes.created_at} + ${verificationCodes.window})::TIMESTAMP + `.as('expires_at'), + isExpired: sql` + (CASE + WHEN ${verificationCodes.confirmed_at} IS NOT NULL THEN true + ELSE NOW() > (${verificationCodes.created_at} + ${verificationCodes.window}) + END)::BOOlEAN + `.as('is_expired'), + data: verificationCodes.data + }).from(verificationCodes) +}) + +export const accountConnectionProviders = pgEnum('account_connection_providers', ['telegram']); +export const accountConnectionStatus = pgEnum('account_connection_status', ['active', 'inactive', 'reconnect_required']); +export const accountConnections = pgTable('account_connections', { + id: uuid().primaryKey().defaultRandom(), + createdAt: timestamp({ mode: 'date' }).defaultNow(), + updatedAt: timestamp({ mode: 'date' }).defaultNow().$onUpdate(() => new Date()), + user: bigint({ mode: 'number' }).notNull().references(() => users.id), + provider: accountConnectionProviders().notNull(), + params: jsonb().notNull(), + status: accountConnectionStatus().notNull().default('active'), + providerId: varchar({ length: 255 }).notNull() +}); export const federatedCredentials = pgTable('federated_credentials', { id: varchar({ length: 255 }).notNull().primaryKey(), @@ -21,4 +64,25 @@ export const users = pgTable('users', { credentials: varchar().references(() => federatedCredentials.id) }); +export const themePrefs = pgEnum('theme_pref', ['system', 'dark', 'light']); +export const userPrefs = pgTable('user_prefs', { + id: uuid().primaryKey().defaultRandom(), + createdAt: timestamp({ mode: 'date' }).defaultNow(), + updatedAt: timestamp({ mode: 'date' }).defaultNow().$onUpdate(() => new Date()), + user: bigint({ mode: 'number' }).notNull().references(() => users.id), + country: varchar({ length: 2 }).notNull(), + theme: themePrefs().notNull().default('light'), + currency: varchar({ length: 3 }).notNull(), + language: varchar({ length: 2 }).notNull() +}); + +export const updatePrefSchema = createUpdateSchema(userPrefs).pick({ + country: true, + theme: true, + currency: true, + language: true +}); + export const userSchema = createSelectSchema(users); +const connectionsSchema = createSelectSchema(accountConnections); +export type AccountConnection = z.infer; diff --git a/lib/campaign.ts b/lib/models/campaign.ts similarity index 100% rename from lib/campaign.ts rename to lib/models/campaign.ts diff --git a/lib/category.ts b/lib/models/category.ts similarity index 100% rename from lib/category.ts rename to lib/models/category.ts diff --git a/lib/country-data.ts b/lib/models/country-data.ts similarity index 100% rename from lib/country-data.ts rename to lib/models/country-data.ts diff --git a/lib/models/payment-method-lookup.ts b/lib/models/payment-method-lookup.ts new file mode 100644 index 0000000..cc1cbf9 --- /dev/null +++ b/lib/models/payment-method-lookup.ts @@ -0,0 +1,4 @@ +export type PaymentMethodLookup = { + provider: "momo"; + status: "active" | "inactive" | "re-connection required"; +}; diff --git a/lib/models/user.ts b/lib/models/user.ts new file mode 100644 index 0000000..c398eed --- /dev/null +++ b/lib/models/user.ts @@ -0,0 +1,19 @@ +export type UserPrefs = { + theme: 'dark' | 'light' | 'system'; + country: string; + currency: string; + id: string; + createdAt: Date | null; + language: string; + updatedAt: Date | null; + user: number; +} +export type DisplayPrefs = Pick; + +export type ConnectedAccount = { + id: string + createdAt: Date | null + updatedAt: Date | null + provider: 'telegram' + status: 'active' | 'inactive' | 'reconnect_required' +}; diff --git a/lib/wallet.ts b/lib/models/wallet.ts similarity index 100% rename from lib/wallet.ts rename to lib/models/wallet.ts diff --git a/netlify.toml b/netlify.toml index fe3877f..3c35122 100644 --- a/netlify.toml +++ b/netlify.toml @@ -6,7 +6,7 @@ directory = "server/functions" from = "/api/*" to = "/.netlify/functions/:splat" status = 200 -[context.deploy-preview] +[context.branch-deploy] command = "pnpm build --configuration staging" [[redirects]] from = "/*" diff --git a/package.json b/package.json index 42a183e..025b8a0 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "watch": "ng build --watch --configuration development", "test": "ng test", "release": "standard-version", - "db:seed": "npx tsx db/seed/index.ts" + "db:seed": "npx tsx scripts/seed.ts", + "tunnel": "npx tsx scripts/tunnel/index.ts 8888" }, "private": true, "dependencies": { @@ -63,6 +64,7 @@ "winston-transport": "^4.9.0", "ws": "^8.18.0", "zod": "^3.24.1", + "zod-validation-error": "^3.4.0", "zone.js": "~0.15.0" }, "devDependencies": { @@ -75,6 +77,7 @@ "@types/google-libphonenumber": "^7.4.30", "@types/jasmine": "~5.1.0", "@types/jsonwebtoken": "^9.0.7", + "@types/localtunnel": "^2.0.4", "@types/node": "^22.10.2", "@types/passport": "^1.0.17", "@types/passport-google-oauth20": "^2.0.16", @@ -89,6 +92,7 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "ngrok": "5.0.0-beta.2", "postcss": "^8.4.49", "standard-version": "^9.5.0", "tailwindcss": "^3.4.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f078cc3..7c500f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,6 +143,9 @@ importers: zod: specifier: ^3.24.1 version: 3.24.1 + zod-validation-error: + specifier: ^3.4.0 + version: 3.4.0(zod@3.24.1) zone.js: specifier: ~0.15.0 version: 0.15.0 @@ -174,6 +177,9 @@ importers: '@types/jsonwebtoken': specifier: ^9.0.7 version: 9.0.7 + '@types/localtunnel': + specifier: ^2.0.4 + version: 2.0.4 '@types/node': specifier: ^22.10.2 version: 22.10.2 @@ -216,6 +222,9 @@ importers: karma-jasmine-html-reporter: specifier: ~2.1.0 version: 2.1.0(jasmine-core@5.4.0)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) + ngrok: + specifier: 5.0.0-beta.2 + version: 5.0.0-beta.2 postcss: specifier: ^8.4.49 version: 8.4.49 @@ -3187,6 +3196,9 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/localtunnel@2.0.4': + resolution: {integrity: sha512-7WM5nlEfEKp8MpwthPa2utdy+f/7ZBxMPzu8qw6EijFFTcpzh5CXgt2YoncxWAZNOPNieMofXCKFudtDEY4bag==} + '@types/lodash@4.17.14': resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} @@ -3306,6 +3318,9 @@ packages: '@types/ws@8.5.13': resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@ungap/structured-clone@1.2.1': resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} @@ -3715,6 +3730,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -4912,6 +4930,11 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + fast-copy@2.1.7: resolution: {integrity: sha512-ozrGwyuCTAy7YgFCua8rmqmytECYk/JYAMXcswOcm0qvGoE3tPb7ivBeIHTOK2DiapBhDZgacIhzhQIKU5TCfA==} @@ -4948,6 +4971,9 @@ packages: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} @@ -5359,6 +5385,9 @@ packages: hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + hpagent@0.1.2: + resolution: {integrity: sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==} + html-encoding-sniffer@3.0.0: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} @@ -6605,6 +6634,11 @@ packages: next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + ngrok@5.0.0-beta.2: + resolution: {integrity: sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ==} + engines: {node: '>=14.2'} + hasBin: true + ngxtension@4.2.0: resolution: {integrity: sha512-cICPiNES4uKJaHASU5c1dkYYfgchIQ0TAZwcZ8NgGX9HbFzAClyS3TG8hSOHA8GIaT03eJU6CKCMSBrWyVjxZA==} engines: {node: '>=18'} @@ -7046,6 +7080,9 @@ packages: resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} engines: {node: '>=8'} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -8760,6 +8797,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -8800,6 +8840,12 @@ packages: peerDependencies: zod: ^3.18.0 + zod-validation-error@3.4.0: + resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + zod@3.24.1: resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} @@ -12211,6 +12257,10 @@ snapshots: dependencies: '@types/node': 22.10.2 + '@types/localtunnel@2.0.4': + dependencies: + '@types/node': 22.10.2 + '@types/lodash@4.17.14': {} '@types/mdast@4.0.4': @@ -12345,6 +12395,11 @@ snapshots: dependencies: '@types/node': 22.10.2 + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 22.10.2 + optional: true + '@ungap/structured-clone@1.2.1': {} '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.11(@types/node@22.10.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': @@ -12795,6 +12850,8 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.1(browserslist@4.24.3) + buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -14246,6 +14303,16 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + extract-zip@2.0.1: + dependencies: + debug: 4.4.0 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + fast-copy@2.1.7: {} fast-deep-equal@3.1.3: {} @@ -14279,6 +14346,10 @@ snapshots: dependencies: websocket-driver: 0.7.4 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fecha@4.2.3: {} figures@3.2.0: @@ -14824,6 +14895,9 @@ snapshots: readable-stream: 2.3.8 wbuf: 1.7.3 + hpagent@0.1.2: + optional: true + html-encoding-sniffer@3.0.0: dependencies: whatwg-encoding: 2.0.0 @@ -16247,6 +16321,18 @@ snapshots: next-tick@1.1.0: {} + ngrok@5.0.0-beta.2: + dependencies: + extract-zip: 2.0.1 + got: 11.8.6 + lodash.clonedeep: 4.5.0 + uuid: 8.3.2 + yaml: 2.6.1 + optionalDependencies: + hpagent: 0.1.2 + transitivePeerDependencies: + - supports-color + ngxtension@4.2.0(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1): dependencies: '@angular-eslint/bundled-angular-compiler': 18.4.3 @@ -16762,6 +16848,8 @@ snapshots: peek-readable@4.1.0: {} + pend@1.2.0: {} + pg-int8@1.0.1: {} pg-numeric@1.0.2: {} @@ -18585,6 +18673,11 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yn@3.1.1: optional: true @@ -18616,6 +18709,10 @@ snapshots: dependencies: zod: 3.24.1 + zod-validation-error@3.4.0(zod@3.24.1): + dependencies: + zod: 3.24.1 + zod@3.24.1: {} zone.js@0.15.0: {} diff --git a/db/seed/index.ts b/scripts/seed.ts similarity index 78% rename from db/seed/index.ts rename to scripts/seed.ts index b7b5275..7306434 100644 --- a/db/seed/index.ts +++ b/scripts/seed.ts @@ -1,10 +1,10 @@ import 'dotenv/config'; import { drizzle } from 'drizzle-orm/neon-serverless'; import { PgTransaction } from 'drizzle-orm/pg-core'; -import * as categories from './categories'; -import * as users from './users'; -import * as wallets from './wallets'; -import { DefaultWriter } from '../log-writer'; +import * as categories from '../db/seed/categories'; +import * as users from '../db/seed/users'; +import * as wallets from '../db/seed/wallets'; +import { DefaultWriter } from '../db/log-writer'; import { DefaultLogger } from 'drizzle-orm/logger'; type Seeder = { name: string, seed: (t: PgTransaction) => Promise } diff --git a/scripts/tunnel/index.ts b/scripts/tunnel/index.ts new file mode 100644 index 0000000..03e5e52 --- /dev/null +++ b/scripts/tunnel/index.ts @@ -0,0 +1,36 @@ +import 'dotenv/config'; +import { readdir } from 'node:fs/promises'; +import { join } from 'node:path'; +import ngrok from 'ngrok'; +import { pathToFileURL } from 'node:url'; + +export type TunnelInit = (url: string) => Promise; + +const port = Number(process.argv[2] ?? process.env['PORT'] ?? 8888); + +async function setupTunnel() { + try { + console.log('Starting Tunnel... on port ', port); + const entries = await readdir(__dirname); + if (entries.length <= 1) { + console.log('No tunnel scripts available. Shutting down...'); + return; + } + const tunnelUrl = await ngrok.connect({ authtoken: process.env['NGROK_TOKEN'], port }); + console.log(`Tunnelling port localhost:${port} -> ${tunnelUrl}`); + + for await (const entry of entries) { + if (entry == '.' || entry == '..' || entry == 'index.ts') continue; + const url = pathToFileURL(join(__dirname, entry)); + const file = url.toString(); + const init = await import(file).then(mod => mod.default as TunnelInit); + await init(tunnelUrl); + } + } catch (e) { + console.error(e); + process.exit(1); + } +} + +setupTunnel().then(() => { +}); diff --git a/scripts/tunnel/telegram-webhooks-local.ts b/scripts/tunnel/telegram-webhooks-local.ts new file mode 100644 index 0000000..932fc79 --- /dev/null +++ b/scripts/tunnel/telegram-webhooks-local.ts @@ -0,0 +1,25 @@ +import { TunnelInit } from './index'; + +type WebHookSetupResponse = { + ok: boolean; + result: boolean; + description: string; +} + +const init: TunnelInit = async (url) => { + console.log('Registering telegram webhooks tunnel'); + + // Remove existing integration + await fetch(`https://api.telegram.org/bot${process.env['TM_BOT_TOKEN']}/setWebhook?url=`, { method: 'GET' }).then(r => r.json()) + .then(console.log) + .catch(console.error); + + // Setup webhooks + const response: WebHookSetupResponse = await fetch(`https://api.telegram.org/bot${process.env['TM_BOT_TOKEN']}/setWebhook?secret_token=${process.env['TM_WEBHOOK_SECRET']}&url=${encodeURIComponent(`${url}/api/webhooks/tm`)}`, { method: 'GET' }) + .then(response => response.json()); + + if (!response.ok) throw new Error(response.description); + console.log(response); +} + +export default init; diff --git a/server/constants/messages/user.mts b/server/constants/messages/user.mts new file mode 100644 index 0000000..57d50ef --- /dev/null +++ b/server/constants/messages/user.mts @@ -0,0 +1,8 @@ +export const TM_USER_ACCOUNT_CONNECTION_VERIFIED_MSG = (userName: string) => { + return `Your account has been successfully linked ${userName}.` +} +export const TM_USER_ACCOUNT_DISCONNECTION_MSG = `Account disconnection successful. Goodbye`; +export const TM_UNKNOWN_COMMAND_MSG = 'Unknown command. Please try again with a command from the menu'; +export const TM_ACCOUNT_ALREADY_EXISTS_MD_MSG = (userName: string, email: string) => { + return `Your account is already linked to *${userName} - (${email})*. If you want to disconnect your Telegram account from the linked account please click the *Disconnect* button on your account's settings page.` +}; diff --git a/server/events/user.ts b/server/events/user.ts index 7298cf3..c9a2643 100644 --- a/server/events/user.ts +++ b/server/events/user.ts @@ -5,5 +5,15 @@ export interface SignedUpEvent extends CustomAsyncWorkloadEvent { eventData: { userId: number; email: string; + countryCode: string; + } +} + +export interface UserDeletedEvent extends CustomAsyncWorkloadEvent { + eventName: 'accounts.deleted'; + eventData: { + userId: number; + email: string; + credentials: string; } } diff --git a/server/functions/async-user-deletion/index.mts b/server/functions/async-user-deletion/index.mts new file mode 100644 index 0000000..8751878 --- /dev/null +++ b/server/functions/async-user-deletion/index.mts @@ -0,0 +1,41 @@ +import { UserDeletedEvent } from "@events/user"; +import { doRemovePaymentMethods } from "@handlers/payment.mjs"; +import { doRemoveAccountConnections } from "@handlers/user.mjs"; +import { doDeleteUserWallet } from "@handlers/wallet.mjs"; +import { useUsersDb } from "@helpers/db.mjs"; +import defaultLogger from "@logger/common"; +import { AsyncWorkloadConfig, asyncWorkloadFn } from "@netlify/async-workloads"; +import { federatedCredentials, users } from "@schemas/users"; +import { eq } from "drizzle-orm"; + +export default asyncWorkloadFn(async ({ step, eventData: { userId, email, credentials } }) => { + await step.run('delete-wallet', async () => { + await doDeleteUserWallet(userId); + defaultLogger.info(`Deleted wallet for user: ${email}`); + }); + + await step.run('remove-account-connections', async () => { + await doRemoveAccountConnections(userId); + defaultLogger.info(`Deleted connected accounts for user: ${email}`); + }); + + await step.run('remove-payment-methods', async () => { + const cnt = await doRemovePaymentMethods(userId); + defaultLogger.info(`Deleted ${cnt} payment methods for user: ${email}`); + }) + + await step.run('remove-user-account', async () => { + const db = useUsersDb(); + await db.transaction(t => { + return Promise.all([ + t.delete(users).where(eq(users.id, userId)), + t.delete(federatedCredentials).where(eq(federatedCredentials.id, credentials as string)) + ]); + }); + defaultLogger.info(`Deleted user account: ${email}`); + }) +}); + +export const asyncWorkloadConfig: AsyncWorkloadConfig = { + events: ['accounts.deleted'] +} diff --git a/server/functions/async-user-onboarding/index.mts b/server/functions/async-user-onboarding/index.mts index e4b25c0..9816a37 100644 --- a/server/functions/async-user-onboarding/index.mts +++ b/server/functions/async-user-onboarding/index.mts @@ -1,6 +1,7 @@ import { SignedUpEvent } from '@events/user'; import { doCreateUserWallet } from '@handlers/wallet.mjs'; import { AsyncWorkloadConfig, asyncWorkloadFn } from '@netlify/async-workloads'; +import { doCreateUserPreferences } from '@handlers/user.mjs'; import defaultLogger from '@logger/common'; // Performs user onboarding tasks on sign-up @@ -9,6 +10,10 @@ export default asyncWorkloadFn(async ({ step, eventData }) => { await doCreateUserWallet(eventData.userId); defaultLogger.info(`Wallet created for ${eventData.email}`); }); + await step.run('setup-preferences', async () => { + await doCreateUserPreferences(eventData.userId, eventData.countryCode); + defaultLogger.info(`Preferences setup for ${eventData.email}`); + }); }); export const asyncWorkloadConfig: AsyncWorkloadConfig = { diff --git a/server/functions/auth/index.mts b/server/functions/auth/index.mts index 8d53382..7baf21b 100644 --- a/server/functions/auth/index.mts +++ b/server/functions/auth/index.mts @@ -1,10 +1,12 @@ -import { handleGoogleOauthCallback, handleGoogleSignIn, handleUserSignIn } from '@handlers/auth.mjs'; +import { handleGoogleOauthCallback, handleGoogleSignIn, handleUserSignIn, removeUserAccount } from '@handlers/auth.mjs'; import { prepareHandler } from '@helpers/handler.mjs'; +import { jwtAuth } from '@middleware/auth.mjs'; import { Router } from 'express'; const router = Router(); router.get('/google', handleGoogleSignIn); router.get('/google/callback', handleGoogleOauthCallback({ failureRedirect: '/auth/login' }), handleUserSignIn); +router.delete('/', jwtAuth, removeUserAccount); export const handler = prepareHandler('auth', router); diff --git a/server/functions/campaigns/index.mts b/server/functions/campaigns/index.mts index 1ef56f0..28ba35c 100644 --- a/server/functions/campaigns/index.mts +++ b/server/functions/campaigns/index.mts @@ -5,13 +5,13 @@ import { findUserCampaigns } from '@handlers/campaign.mjs'; import { prepareHandler } from '@helpers/handler.mjs'; -import { auth } from '@middleware/auth.mjs'; +import { jwtAuth } from '@middleware/auth.mjs'; import { Router } from 'express'; const router = Router(); -router.get('/', auth, findUserCampaigns); -router.post('/', auth, createCampaign); -router.get('/:campaign/publications', auth, findCampaignPublications); -router.post('/:campaign/publications', auth, createCampaignPublication); +router.get('/', jwtAuth, findUserCampaigns); +router.post('/', jwtAuth, createCampaign); +router.get('/:campaign/publications', jwtAuth, findCampaignPublications); +router.post('/:campaign/publications', jwtAuth, createCampaignPublication); export const handler = prepareHandler('campaigns', router); diff --git a/server/functions/countries/index.mts b/server/functions/countries/index.mts index f3fc638..5ad9029 100644 --- a/server/functions/countries/index.mts +++ b/server/functions/countries/index.mts @@ -1,6 +1,6 @@ -import { findCountryByIso2Code, findExchangeRates, getAllCountries } from "@handlers/countries.mjs"; -import { Config, Context } from "@netlify/functions"; -import { ZodError } from "zod"; +import { handleFindCountryByIso2Code, findExchangeRates, getAllCountries, getUserCountry } from '@handlers/countries.mjs'; +import { Config, Context } from '@netlify/functions'; +import { ZodError } from 'zod'; export default async function (req: Request, ctx: Context) { const method = req.method.toLowerCase(); @@ -11,13 +11,22 @@ export default async function (req: Request, ctx: Context) { else if (method == 'get' && ctx.url.pathname == '/api/countries/exchange_rates') response = await findExchangeRates(req, ctx); else if (method == 'get' && ctx.url.pathname == '/api/countries/find') - response = findCountryByIso2Code(req, ctx); + response = handleFindCountryByIso2Code(req, ctx); + else if (method == 'get' && ctx.url.pathname == '/api/countries/mine') + response = getUserCountry(req, ctx); } catch (e) { if (e instanceof ZodError) { - return new Response(JSON.stringify(e.errors.map(({ code, message, path }) => ({ path, message, code }))), { status: 400, headers: { 'content-type': 'application/json' } }); + return new Response(JSON.stringify(e.errors.map(({ code, message, path }) => ({ + path, + message, + code + }))), { status: 400, headers: { 'content-type': 'application/json' } }); } console.error(e); - response = new Response(JSON.stringify({ message: 'Internal server error' }), { status: 500, headers: { 'content-type': 'application/json' } }) + response = new Response(JSON.stringify({ message: 'Internal server error' }), { + status: 500, + headers: { 'content-type': 'application/json' } + }) } return response; } @@ -25,9 +34,8 @@ export default async function (req: Request, ctx: Context) { export const config: Config = { path: [ '/api/countries', - // '/.netlify/functions/countries/all', '/api/countries/exchange_rates', - // '/.netlify/functions/countries/exchange_rates', - '/api/countries/find' + '/api/countries/find', + '/api/countries/mine' ] } diff --git a/server/functions/payment/index.mts b/server/functions/payment/index.mts new file mode 100644 index 0000000..ba558d5 --- /dev/null +++ b/server/functions/payment/index.mts @@ -0,0 +1,11 @@ +import { updatePaymentMethod, findUserPaymentMethods, handleRemovePaymentMethod } from "@handlers/payment.mjs"; +import { prepareHandler } from "@helpers/handler.mjs"; +import { jwtAuth } from "@middleware/auth.mjs"; +import { Router } from "express"; + +const router = Router(); +router.get('/methods', jwtAuth, findUserPaymentMethods); +router.put('/methods', jwtAuth, updatePaymentMethod); +router.delete('/methods', jwtAuth, handleRemovePaymentMethod); + +export const handler = prepareHandler('payment', router); diff --git a/server/functions/users/index.mts b/server/functions/users/index.mts index d143677..9845ec6 100644 --- a/server/functions/users/index.mts +++ b/server/functions/users/index.mts @@ -1,9 +1,13 @@ -import { findUserCampaigns } from '@handlers/campaign.mjs'; +import { findUserConnections, getUserPreferences, handleTelegramAccountConnectionRemoval, updateUserPreferences, verifyTelegramVerificationCode } from '@handlers/user.mjs'; import { prepareHandler } from '@helpers/handler.mjs'; -import { auth } from '@middleware/auth.mjs'; +import { jwtAuth } from '@middleware/auth.mjs'; import { Router } from 'express'; const router = Router(); -router.get('/', auth, findUserCampaigns); +router.get('/prefs', jwtAuth, getUserPreferences); +router.put('/prefs', jwtAuth, updateUserPreferences); +router.get('/connections', jwtAuth, findUserConnections); +router.get('/connections/verify/tm', jwtAuth, verifyTelegramVerificationCode); +router.get('/connections/disconnect/tm', jwtAuth, handleTelegramAccountConnectionRemoval); export const handler = prepareHandler('users', router); diff --git a/server/functions/wallet/index.mts b/server/functions/wallet/index.mts index d19a5f7..fde7197 100644 --- a/server/functions/wallet/index.mts +++ b/server/functions/wallet/index.mts @@ -1,9 +1,9 @@ import { getUserWalletBalances } from "@handlers/wallet.mjs"; import { prepareHandler } from "@helpers/handler.mjs"; -import { auth } from "@middleware/auth.mjs"; +import { jwtAuth } from "@middleware/auth.mjs"; import { Router } from "express"; const router = Router(); -router.get('/balances', auth, getUserWalletBalances); +router.get('/balances', jwtAuth, getUserWalletBalances); export const handler = prepareHandler('wallet', router); diff --git a/server/functions/webhooks/index.ts b/server/functions/webhooks/index.ts new file mode 100644 index 0000000..4331d47 --- /dev/null +++ b/server/functions/webhooks/index.ts @@ -0,0 +1,12 @@ +import { Router } from 'express'; +import { prepareHandler } from '@helpers/handler.mjs'; +import { onTelegramUpdate } from '@handlers/webhooks/telegram'; +import { telegramWebhookAuth } from '@middleware/auth.mjs'; + +const tmRouter = Router(); +tmRouter.post('/', telegramWebhookAuth, onTelegramUpdate); + +const router = Router(); +router.use('/tm', tmRouter); + +export const handler = prepareHandler('webhooks', router); diff --git a/server/handlers/auth.mts b/server/handlers/auth.mts index b573657..12d892e 100644 --- a/server/handlers/auth.mts +++ b/server/handlers/auth.mts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; -import { SignedUpEvent } from '@events/user'; -import { useAwlClient } from '@helpers/awl-client'; +import { SignedUpEvent, UserDeletedEvent } from '@events/user'; +import { useAwlClient } from '@helpers/awl-client.mjs'; import { useUsersDb } from '@helpers/db.mjs'; import * as users from '@schemas/users'; import { userSchema } from '@schemas/users'; @@ -8,12 +8,16 @@ import { eq } from 'drizzle-orm'; import passport from 'passport'; import { Strategy as GoogleStrategy, Profile, VerifyCallback } from 'passport-google-oauth20'; import { sign } from 'jsonwebtoken'; +import { extractUser } from '@helpers/auth.mjs'; +import { handleError } from '@helpers/error.mjs'; +import defaultLogger from '@logger/common'; passport.use(new GoogleStrategy({ clientID: String(process.env['OAUTH2_CLIENT_ID']), clientSecret: String(process.env['OAUTH2_CLIENT_SECRET']), callbackURL: `${process.env['ORIGIN']}/api/auth/google/callback`, -}, async (accessToken: string, __: string, profile: Profile, done: VerifyCallback) => { + passReqToCallback: true +}, async (request, accessToken: string, __: string, profile: Profile, done: VerifyCallback) => { const db = useUsersDb(); let existingUser = await db.query.users.findFirst({ // where: eq(users.users.credentials, profile.id) @@ -35,7 +39,14 @@ passport.use(new GoogleStrategy({ }); existingUser = await db.query.users.findFirst({ where: eq(users.users.credentials, profile.id) }); const client = useAwlClient(); - client.send('accounts.sign-up', { data: { userId, email } }); + const eventData = { + userId, + email, + countryCode: request.header('accept-language')?.split(',')?.[0]?.split('-')?.[1] ?? 'CM' + }; + client.send('accounts.sign-up', { + data: eventData + }); } else { await db.transaction(async tx => { await tx.update(users.federatedCredentials).set({ lastAccessToken: accessToken }).where(eq(users.federatedCredentials.id, profile.id)) @@ -55,10 +66,28 @@ passport.deserializeUser((id, done) => { .then(user => done(null, user ?? null), (err) => done(err, null)); }); +export async function removeUserAccount(req: Request, res: Response) { + const user = extractUser(req); + const db = useUsersDb(); + try { + await db.transaction(async t => { + await t.delete(users.userPrefs).where(eq(users.userPrefs.user, user.id)); + }); + defaultLogger.info('user account deleted', 'user', user.id); + const client = useAwlClient(); + client.send('accounts.deleted', { data: { email: user.email, userId: user.id, credentials: user.credentials as string } }); + res.status(202).send({}); + } catch (e) { + handleError(e as Error, res); + } +} + export const handleGoogleSignIn = passport.authenticate('google', { session: false, scope: ['profile', 'email'] }); + export function handleGoogleOauthCallback({ failureRedirect }: { failureRedirect: string }) { return passport.authenticate('google', { failureRedirect, session: false }) } + export function handleUserSignIn(req: Request, res: Response) { if (!req.user) { res.status(401).json({ message: 'Unauthorized' }); diff --git a/server/handlers/campaign.mts b/server/handlers/campaign.mts index 3e94c9f..338e6ff 100644 --- a/server/handlers/campaign.mts +++ b/server/handlers/campaign.mts @@ -1,7 +1,7 @@ import { extractUser } from '@helpers/auth.mjs'; import { useCampaignsDb } from '@helpers/db.mjs'; import { handleError } from '@helpers/error.mjs'; -import { LookupCampaignResponse } from '@lib/campaign'; +import { LookupCampaignResponse } from '@lib/models/campaign'; import { campaignPublications, campaigns, newCampaignSchema, newPublicationSchema } from '@schemas/campaigns'; import { count, eq } from 'drizzle-orm'; import express from 'express'; diff --git a/server/handlers/countries.mts b/server/handlers/countries.mts index 2d59927..99bbd85 100644 --- a/server/handlers/countries.mts +++ b/server/handlers/countries.mts @@ -1,9 +1,10 @@ -import { ExchangeRateResponse } from "@lib/country-data"; -import { getStore } from "@netlify/blobs"; -import { Context } from "@netlify/functions"; +import { ExchangeRateResponse } from '@lib/models/country-data'; +import { getStore } from '@netlify/blobs'; +import { Context } from '@netlify/functions'; import _ from 'lodash'; -import { z } from "zod"; +import { z } from 'zod'; import AllCountries from '../../assets/countries.json'; +import defaultLogger from '@logger/common'; const exchangeRateQuerySchema = z.object({ src: z.string().length(3), @@ -13,21 +14,33 @@ const exchangeRateQuerySchema = z.object({ }); const getCountryByIso2CodeSchema = z.object({ - alpha2Code: z.string().length(2) + alpha2Code: z.string() + .length(2) + .transform(val => val.toUpperCase()) + .or( + z.string() + .transform(val => val.toUpperCase().split(',')) + .pipe(z.string().length(2).array()) + ) }); export async function getAllCountries(_: Request, ctx: Context) { return ctx.json(AllCountries); } -export function findCountryByIso2Code(req: Request, ctx: Context) { +export function handleFindCountryByIso2Code(_: Request, ctx: Context) { const params = [...ctx.url.searchParams.entries()].reduce((acc, [k, v]) => { acc[k] = v; return acc }, {} as Record); - const { alpha2Code: code } = getCountryByIso2CodeSchema.parse(params); - const country = AllCountries.find(({ alpha2Code }) => alpha2Code == code) - return ctx.json(country); + const { alpha2Code } = getCountryByIso2CodeSchema.parse(params); + return ctx.json(findCountryByIso2Code(alpha2Code)); +} + +export function findCountryByIso2Code(countryCode: string | string[]) { + if (Array.isArray(countryCode)) + return AllCountries.filter(({ alpha2Code }) => countryCode.includes(alpha2Code)); + return AllCountries.find(({ alpha2Code }) => alpha2Code == countryCode); } export async function findExchangeRates(req: Request, ctx: Context) { @@ -41,18 +54,22 @@ export async function findExchangeRates(req: Request, ctx: Context) { consistency: 'strong' }); + defaultLogger.debug(`looking up exchange rate from cache for ${src} -> ${dest.join(',')}`); const result = await ratesStore.getWithMetadata(src, { type: 'json' }) if (!result) { - const { rates, date } = await getLatestExchangeRates(src, dest); + defaultLogger.debug(`cache miss for ${src} -> ${dest.join(',')}`); + const { rates } = await getLatestExchangeRates(src, dest); await ratesStore.setJSON(src, rates, { metadata: { fetchDate: new Date().valueOf() } }); return ctx.json(pickFields(rates, ...dest)); } else { + defaultLogger.debug(`cache hit for ${src} -> ${dest.join(',')}`); const { data, metadata: { fetchDate } } = result; const now = new Date().valueOf(); const then = new Date(fetchDate as number).valueOf(); if (now > then + 6 * 3_600_000) { // Data is stale - const { rates, date } = await getLatestExchangeRates(src, dest); + defaultLogger.debug(`updating stale exchange rate for ${src} -> ${dest.join(',')}`); + const { rates } = await getLatestExchangeRates(src, dest); await ratesStore.setJSON(src, rates, { metadata: { fetchDate: new Date().valueOf() } }); return ctx.json(pickFields(rates, ...dest)); } @@ -84,5 +101,14 @@ async function getLatestExchangeRates(src: string, dest: string[]) { url.searchParams.set('symbols', dest.join(',')); url.searchParams.set('base', src); - return fetch(url, { headers: { apikey: String(process.env['API_LAYER_KEY']) } }).then(res => res.json()).then(d => d as ExchangeRateResponse) + defaultLogger.debug(`Looking up exchange rates for ${src} -> ${dest.join(',')} from APILayer`); + return fetch(url, { headers: { apikey: String(process.env['API_LAYER_KEY']) } }) + .then(res => res.json()) + .then(d => d as ExchangeRateResponse); +} + +export function getUserCountry(req: Request, ctx: Context) { + const { country } = ctx.geo; + if (!country) return new Response(undefined, { status: 404 }); + return ctx.json(AllCountries.find(({ alpha2Code }) => country.code == alpha2Code)); } diff --git a/server/handlers/payment.mts b/server/handlers/payment.mts new file mode 100644 index 0000000..8522747 --- /dev/null +++ b/server/handlers/payment.mts @@ -0,0 +1,81 @@ +import { extractUser } from "@helpers/auth.mjs"; +import { useFinanceDb } from "@helpers/db.mjs"; +import { handleError } from "@helpers/error.mjs"; +import defaultLogger from "@logger/common"; +import { paymentMethods } from "@schemas/finance"; +import { RemoveMomoPaymentMethodSchema, UpdatePaymentMethodSchema } from "@zod-schemas/payment-method.mjs"; +import { and, eq, sql } from "drizzle-orm"; +import { Request, Response } from "express"; +import { fromError } from "zod-validation-error"; + +export async function handleRemovePaymentMethod(req: Request, res: Response) { + const { success, data, error } = RemoveMomoPaymentMethodSchema.safeParse(req.query); + if (!success) { + res.status(400).json({ message: fromError(error).message }); + return; + } + + const user = extractUser(req); + const { provider } = data; + try { + const result = await removePaymentMethod(user.id, provider); + res.status(202).json({}); + if (result.rowCount) + defaultLogger.info('removed payment method', 'user', user.id, 'provider', provider); + } catch (e) { + handleError(e as Error, res); + } +} + +export async function updatePaymentMethod(req: Request, res: Response) { + const { success, data, error } = UpdatePaymentMethodSchema.safeParse(req.body); + if (!success) { + res.status(400).json({ message: fromError(error).message }); + return; + } + + const { provider, data: params } = data; + const user = extractUser(req); + const db = useFinanceDb(); + try { + await db.transaction(t => { + return t.insert(paymentMethods).values({ + owner: user.id, + params, + provider, + status: 'active' + }).onConflictDoUpdate({ + target: [paymentMethods.provider, paymentMethods.owner], + set: { params: sql.raw(`excluded.${paymentMethods.params.name}`), status: sql.raw(`excluded.${paymentMethods.status.name}`) } + }); + }); + res.status(202).json({}); + defaultLogger.info('upserted payment methods', 'user', user.id, 'provider', provider); + } catch (e) { + handleError(e as Error, res); + } +} + +export async function findUserPaymentMethods(req: Request, res: Response) { + const user = extractUser(req); + const db = useFinanceDb(); + const paymentMethods = await db.query.paymentMethods.findMany({ + columns: { + provider: true, + status: true + }, + where: (method, { eq }) => eq(method.owner, user.id) + }); + + res.json(paymentMethods); +} + +export async function doRemovePaymentMethods(userId: number) { + const db = useFinanceDb(); + return await db.transaction(t => t.delete(paymentMethods).where(eq(paymentMethods.owner, userId))).then(r => r.rowCount); +} + +async function removePaymentMethod(userId: number, provider: 'momo') { + const db = useFinanceDb(); + return await db.transaction(t => t.delete(paymentMethods).where(and(eq(paymentMethods.owner, userId), eq(paymentMethods.provider, provider)))); +} diff --git a/server/handlers/user.mts b/server/handlers/user.mts index 6dd4754..b6cefdd 100644 --- a/server/handlers/user.mts +++ b/server/handlers/user.mts @@ -1,5 +1,139 @@ +import { TM_USER_ACCOUNT_CONNECTION_VERIFIED_MSG, TM_USER_ACCOUNT_DISCONNECTION_MSG } from '@constants/messages/user.mjs'; +import { extractUser } from '@helpers/auth.mjs'; +import { useUsersDb } from '@helpers/db.mjs'; +import { sendTelegramBotMessage } from '@helpers/telegram.mjs'; +import defaultLogger from '@logger/common'; +import { AccountConnection, accountConnections, updatePrefSchema, userPrefs, verificationCodes, verificationCodesView } from '@schemas/users'; +import { TelegramAccountConnectionDataSchema } from '@zod-schemas/telegram.mjs'; +import { and, eq } from 'drizzle-orm'; import { Request, Response } from 'express'; +import { hashThese } from 'server/util'; +import { z } from 'zod'; +import { findCountryByIso2Code } from './countries.mjs'; +import { CountryData } from '@lib/models/country-data'; -export async function findUsers(req: Request, res: Response) { - res.json([{ user: 'foo' }]); +const codeVerificationSchema = z.object({ + code: z.string().length(6).transform(arg => hashThese(arg)) +}); + +export async function handleTelegramAccountConnectionRemoval(req: Request, res: Response) { + const user = extractUser(req); + const ans = await removeTelegramAccountConnection(user.id); + + res.status(ans ? 202 : 404).json(ans ? {} : { message: 'Telegram connection not found' }); +} + +export async function verifyTelegramVerificationCode(req: Request, res: Response) { + const params = { ...req.query }; + const { code } = codeVerificationSchema.parse(params); + const db = useUsersDb(); + const results = await db.select().from(verificationCodesView).where((verificationCode) => and(eq(verificationCode.code, code), eq(verificationCodesView.isExpired, false))).limit(1); + if (results.length == 0) { + res.status(404).json({ message: 'Code not found or is expired' }); + return; + } + + defaultLogger.debug('verification code confirmed', 'hash', code); + const user = extractUser(req); + const [{ data }] = results; + const telegramData = TelegramAccountConnectionDataSchema.parse(data); + await db.transaction(async t => { + await t.insert(accountConnections).values({ + user: user.id, + provider: 'telegram', + params: data, + status: 'active', + providerId: String(telegramData.userInfo.id) + }); + + await t.update(verificationCodes).set({ + confirmed_at: new Date() + }).where(eq(verificationCodes.hash, code)); + }); + defaultLogger.info('account connection created', 'user', user, 'platform', 'telegram'); + + const { chatId } = TelegramAccountConnectionDataSchema.parse(data); + await sendTelegramBotMessage(chatId, TM_USER_ACCOUNT_CONNECTION_VERIFIED_MSG(user.names)); + + res.status(202).json({}); +} + +export async function findUserConnections(req: Request, res: Response) { + const db = useUsersDb(); + const user = extractUser(req); + const connections = await db.query.accountConnections.findMany({ + columns: { + id: true, + createdAt: true, + updatedAt: true, + provider: true, + status: true + }, + where: (connections, { eq }) => eq(connections.user, user.id) + }); + res.json(connections); +} + +export async function updateUserPreferences(req: Request, res: Response) { + const db = useUsersDb(); + const user = extractUser(req); + const input = updatePrefSchema.parse(req.body); + const [{ id }] = await db.transaction(async t => { + return t.update(userPrefs).set(input).where(eq(userPrefs.user, user.id)).returning({ id: userPrefs.id }); + }); + res.status(202).json({}); +} + +export async function getUserPreferences(req: Request, res: Response) { + const db = useUsersDb(); + const user = extractUser(req); + const prefs = await db.query.userPrefs.findFirst({ + where: (prefs, { eq }) => eq(prefs.user, user.id) + }); + + res.json(prefs); +} + +export async function doRemoveAccountConnections(userId: number) { + const db = useUsersDb(); + const connections = await db.query.accountConnections.findMany({ + where: (connection, { eq }) => eq(connection.user, userId) + }); + + if (connections.length == 0) return; + for await (const connection of connections) { + switch (connection.provider) { + case 'telegram': + await removeTelegramAccountConnection(userId, connection as any); + break; + } + } +} + +async function removeTelegramAccountConnection(userId: number, conn?: AccountConnection) { + const db = useUsersDb(); + const connection = conn ?? await db.query.accountConnections.findFirst({ + where: (connection => and(eq(connection.provider, 'telegram'), eq(connection.user, userId))) + }); + if (!connection) { + return false; + } + + await db.transaction(t => t.delete(accountConnections).where(eq(accountConnections.id, connection.id))); + + const { chatId } = TelegramAccountConnectionDataSchema.parse(connection.params); + await sendTelegramBotMessage(chatId, TM_USER_ACCOUNT_DISCONNECTION_MSG); + return true; +} + + +export async function doCreateUserPreferences(userId: number, countryCode: string) { + const db = useUsersDb(); + const country = findCountryByIso2Code(countryCode) as CountryData | undefined; + await db.insert(userPrefs).values({ + country: countryCode, + user: userId, + currency: country?.currencies?.[0]?.code ?? 'XAF', + language: country?.languages?.[0]?.iso639_1 ?? 'en' + }); } diff --git a/server/handlers/wallet.mts b/server/handlers/wallet.mts index 914bdac..88389f8 100644 --- a/server/handlers/wallet.mts +++ b/server/handlers/wallet.mts @@ -17,9 +17,16 @@ export async function getUserWalletBalances(req: Request, res: Response) { } } +export async function doDeleteUserWallet(userId: number) { + const db = useFinanceDb(); + await db.transaction(t => { + return t.delete(wallets).where(eq(wallets.ownedBy, userId)); + }); +} + export async function doCreateUserWallet(userId: number) { const db = useFinanceDb(); - await db.insert(wallets).values({ + await db.transaction(t => t.insert(wallets).values({ ownedBy: userId - }); + })); } diff --git a/server/handlers/webhooks/telegram.ts b/server/handlers/webhooks/telegram.ts new file mode 100644 index 0000000..0cd30d0 --- /dev/null +++ b/server/handlers/webhooks/telegram.ts @@ -0,0 +1,79 @@ +import { TM_ACCOUNT_ALREADY_EXISTS_MD_MSG, TM_UNKNOWN_COMMAND_MSG } from '@constants/messages/user.mjs'; +import { useUsersDb } from '@helpers/db.mjs'; +import { handleError } from '@helpers/error.mjs'; +import { sendTelegramBotMessage } from '@helpers/telegram.mjs'; +import defaultLogger from '@logger/common'; +import { accountConnections, users, verificationCodes } from '@schemas/users'; +import { TelegramBotCommandUpdateSchema, TelegramChatInfo, TelegramUserInfo } from '@zod-schemas/telegram.mjs'; +import { eq, and } from 'drizzle-orm'; +import { Request, Response } from 'express'; +import { randomBytes } from 'node:crypto'; +import { hashThese } from 'server/util'; + +export async function onTelegramUpdate(req: Request, res: Response) { + try { + const { success, data, error } = TelegramBotCommandUpdateSchema.safeParse(req.body); + if (!success) { + console.error(error); + await sendTelegramBotMessage(Number(req.body.message.chat.id), TM_UNKNOWN_COMMAND_MSG); + res.status(204).send(); + return; + } + + const { message } = data; + const { text, chat, from } = message; + switch (text.substring(1)) { + case 'start': + await handleStartCommand(chat, from); + break; + default: + res.status(404).send('Not found'); + return; + } + + res.status(204).send(); + } catch (e) { + handleError(e as Error, res); + } +} + +async function handleStartCommand({ first_name, id }: TelegramChatInfo, tmUser: TelegramUserInfo) { + const db = useUsersDb(); + const existingConnection = await db.select({ + user: users + }) + .from(accountConnections) + .innerJoin(users, () => eq(accountConnections.user, users.id)).where(and(eq(accountConnections.providerId, String(tmUser.id)), eq(accountConnections.provider, 'telegram'), eq(accountConnections.status, 'active'))) + .limit(1); + + if (existingConnection.length > 0) { + const [{ user: { email, names } }] = existingConnection; + await sendTelegramBotMessage(id, TM_ACCOUNT_ALREADY_EXISTS_MD_MSG(names, email)); + return; + } + + const code = randomBytes(3).toString('hex').toUpperCase(); + const hash = hashThese(code); + + await db.transaction(t => t.insert(verificationCodes).values({ + window: '15m', + hash, + data: { + chatId: id, + userInfo: tmUser + } + })); + defaultLogger.debug('verification code created', 'plaintext', code, 'hash', hash); + + const settingsPageLink = process.env['NODE_ENV'] == 'development' ? `Go to *${process.env['ORIGIN']}/settings*` : `[Click here](${process.env['ORIGIN']}/settings)`; + const message = ` +Hi [${first_name}](tg://user?id=${tmUser.id}), thanks for connecting! + +${settingsPageLink} and enter the code below, to completely link your account and start earning your rewards. The code will expire in 15 minutes. + +*${code}* + `; + await sendTelegramBotMessage(id, message); +} + + diff --git a/server/helpers/awl-client.ts b/server/helpers/awl-client.mts similarity index 100% rename from server/helpers/awl-client.ts rename to server/helpers/awl-client.mts diff --git a/server/helpers/error.mts b/server/helpers/error.mts index 5c45609..7fa094e 100644 --- a/server/helpers/error.mts +++ b/server/helpers/error.mts @@ -1,11 +1,14 @@ +import defaultLogger from '@logger/common'; import { Response } from 'express'; import { ZodError } from 'zod'; +import { fromError } from 'zod-validation-error'; export function handleError(err: Error, res: Response) { if (err instanceof ZodError) { - res.status(400).json({ error: err.errors.map(({ code, message, path }) => ({ path, message, code })) }); + res.status(400).json({ message: fromError(err).message }); } else { - console.error(err); + console.log(err); + defaultLogger.error('handled error', 'error', err); res.status(500).json({ message: 'Internal server error' }); } } diff --git a/server/helpers/handler.mts b/server/helpers/handler.mts index 2d7779d..96c29f0 100644 --- a/server/helpers/handler.mts +++ b/server/helpers/handler.mts @@ -1,19 +1,21 @@ +import { errorHandler } from '@middleware/error.mjs'; +import logger from '@middleware/logger.mjs'; import cookieParser from 'cookie-parser'; import express, { json, Router, urlencoded } from 'express'; import context from 'express-http-context'; -import pino from 'pino-http'; import passport from 'passport'; import serverless from 'serverless-http'; export function prepareHandler(prefix: string, router: Router) { const app = express(); - // app.use(pino()); app.use( + logger, context.middleware as unknown as express.Handler, json(), cookieParser(), urlencoded({ extended: true }), passport.initialize(), + errorHandler ); app.use(`/api/${prefix}`, router); return serverless(app); diff --git a/server/helpers/logging.mts b/server/helpers/logging.mts deleted file mode 100644 index ddbc010..0000000 --- a/server/helpers/logging.mts +++ /dev/null @@ -1,5 +0,0 @@ -import pino from "pino"; - -export function useLogger(name?: string) { - return pino(); -} diff --git a/server/helpers/telegram.mts b/server/helpers/telegram.mts new file mode 100644 index 0000000..c254849 --- /dev/null +++ b/server/helpers/telegram.mts @@ -0,0 +1,11 @@ +import defaultLogger from "@logger/common"; + +export async function sendTelegramBotMessage(chatId: number, message: string) { + const url = new URL(`/bot${process.env['TM_BOT_TOKEN']}/sendMessage`, 'https://api.telegram.org'); + url.searchParams.set('chat_id', chatId.toString()); + url.searchParams.set('text', message); + url.searchParams.set('parse_mode', 'Markdown'); + + const response = await fetch(url, { method: 'GET' }).then(res => res.json()); + defaultLogger.info('telegram message sent', 'chat_id', chatId, 'response', response); +} diff --git a/server/middleware/auth.mts b/server/middleware/auth.mts index 4563582..cac5923 100644 --- a/server/middleware/auth.mts +++ b/server/middleware/auth.mts @@ -1,5 +1,5 @@ import passport from 'passport'; -import express from 'express'; +import express, { NextFunction } from 'express'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { useUsersDb } from '../helpers/db.mjs'; import { Context } from '@netlify/functions'; @@ -7,6 +7,15 @@ import jwt from 'jsonwebtoken'; const { verify } = jwt; +export function telegramWebhookAuth(req: express.Request, res: express.Response, next: NextFunction) { + const authHeaderValue = req.header('x-telegram-bot-api-secret-token'); + if (!authHeaderValue || authHeaderValue != String(process.env['TM_WEBHOOK_SECRET'])) { + res.status(401).send(); + return; + } + next(); +} + passport.use(new Strategy( { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), @@ -27,7 +36,7 @@ passport.use(new Strategy( } )); -export const auth = passport.authenticate('jwt', { session: false }) as express.Handler; +export const jwtAuth = passport.authenticate('jwt', { session: false }) as express.Handler; export const rawAuth = async (req: Request, ctx: Context, next: (req: Request, ctx: Context) => Promise) => { const headerValue = req.headers.get('authorization')?.split(' '); diff --git a/server/middleware/logger.mts b/server/middleware/logger.mts new file mode 100644 index 0000000..6cdd1dc --- /dev/null +++ b/server/middleware/logger.mts @@ -0,0 +1,19 @@ +import { NextFunction, Request, Response } from 'express'; +import defaultLogger from '@logger/common'; + +export default function logger(req: Request, res: Response, next: NextFunction) { + const start = Date.now(); + res.on('finish', () => { + const now = Date.now(); + const diff = (now - start) / 1000 + const message = `${req.method} ${req.originalUrl} -> ${res.statusCode} | ${diff}s`; + if (res.statusCode < 400) { + defaultLogger.verbose(message, ['duration', diff]); + } else if (res.statusCode >= 400 && res.statusCode < 500) { + defaultLogger.warn(message, ['duration', diff]); + } else { + defaultLogger.error(message, ['duration', diff]); + } + }); + next(); +} diff --git a/server/schemas/payment-method.mts b/server/schemas/payment-method.mts new file mode 100644 index 0000000..7f2efc4 --- /dev/null +++ b/server/schemas/payment-method.mts @@ -0,0 +1,21 @@ +import pkg from "google-libphonenumber"; +import { z } from "zod"; +const { PhoneNumberUtil } = pkg; + +export const UpdateMomoPaymentMethodSchema = z.object({ + phoneNumber: z.string() + .refine(phone => { + const util = PhoneNumberUtil.getInstance(); + const p = util.parseAndKeepRawInput(phone); + return util.isValidNumber(p); + }) +}); + +export const UpdatePaymentMethodSchema = z.object({ + provider: z.enum(['momo']), + data: UpdateMomoPaymentMethodSchema +}); + +export const RemoveMomoPaymentMethodSchema = z.object({ + provider: UpdatePaymentMethodSchema.shape.provider +}) diff --git a/server/schemas/telegram.mts b/server/schemas/telegram.mts new file mode 100644 index 0000000..f1a27fe --- /dev/null +++ b/server/schemas/telegram.mts @@ -0,0 +1,61 @@ +import { z } from 'zod'; + +export const TelegramUpdateType = z.enum(['text_message', 'bot_command']); +export const TelegramBotCommandUpdateSchema = z.object({ + update_id: z.number(), + message: z.object({ + message_id: z.number(), + from: z.object({ + id: z.number(), + is_bot: z.boolean(), + first_name: z.string(), + username: z.string(), + language_code: z.string() + }), + chat: z.object({ + id: z.number(), + first_name: z.string(), + username: z.string(), + type: z.string() + }), + date: z.number(), + text: z.string(), + entities: z.array( + z.object({ offset: z.number(), length: z.number(), type: z.enum(['bot_command']) }) + ).length(1) + }), +}); +export const TelegramBotMessageSchema = z.object({ + update_id: z.number(), + message: z.object({ + message_id: z.number(), + from: z.object({ + id: z.number(), + is_bot: z.literal(false), + first_name: z.string(), + username: z.string(), + language_code: z.string() + }), + chat: z.object({ + id: z.number(), + first_name: z.string(), + username: z.string(), + type: z.string() + }), + date: z.number(), + text: z.string() + }), + updateType: z.string().optional().default('text_message') +}); + + +export type TelegramUpdate = z.infer['message']; +export type TelegramChatInfo = TelegramUpdate['chat']; +export type TelegramUserInfo = TelegramUpdate['from']; + +export const TelegramAccountConnectionDataSchema = z.object({ + chatId: TelegramBotCommandUpdateSchema.shape.message.shape.chat.shape.id.and(TelegramBotMessageSchema.shape.message.shape.chat.shape.id), + userInfo: TelegramBotCommandUpdateSchema.shape.message.shape.from.or(TelegramBotMessageSchema.shape.message.shape.from) +}); + +export type TelegramAccountConnectionData = z.infer; diff --git a/src/app/app.component.html b/src/app/app.component.html index d78a0b0..602ea72 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -3,7 +3,7 @@ } -
+
+
- diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8acf962..e4c364b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,13 +1,15 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { AsyncPipe } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; -import { select } from '@ngxs/store'; -import { MessageService } from 'primeng/api'; -import { Toast } from 'primeng/toast'; -import { map } from 'rxjs'; -import { TopBarComponent } from './components/top-bar/top-bar.component'; -import { isUserSignedIn } from './state/user'; +import { AsyncPipe } from '@angular/common'; +import { Component, effect, inject } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { select } from '@ngxs/store'; +import { updatePreset, usePreset } from '@primeng/themes'; +import { MessageService } from 'primeng/api'; +import { Toast } from 'primeng/toast'; +import { map } from 'rxjs'; +import { AutoThemePreset, ManualThemePreset } from './app.config'; +import { TopBarComponent } from './components/top-bar/top-bar.component'; +import { isUserSignedIn, preferences } from './state/user'; @Component({ selector: 'tm-root', @@ -19,4 +21,28 @@ import { isUserSignedIn } from './state/user'; export class AppComponent { readonly isSmallDisplay = inject(BreakpointObserver).observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]).pipe(map(({ matches }) => matches)) readonly isSignedIn = select(isUserSignedIn); + private readonly prefs = select(preferences); + constructor() { + effect(() => { + const doc = document.querySelector('html') as HTMLHtmlElement; + const { theme } = this.prefs(); + let colorMode: '.app-dark' | 'system' | 'none' = '.app-dark'; + if (theme == 'system') { + usePreset(AutoThemePreset); + } else { + updatePreset(ManualThemePreset); + } + + switch (theme) { + case 'dark': + doc.classList.add('app-dark'); + colorMode = '.app-dark'; + break; + case 'light': + doc.classList.remove('app-dark'); + break; + default: + } + }) + } } diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 51674ec..95a16e0 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,17 +1,21 @@ import { ApplicationConfig, isDevMode, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter, withViewTransitions } from '@angular/router'; +import { provideRouter, withViewTransitions } from '@angular/router'; -import { appRoutes } from './app.routes'; -import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; -import { providePrimeNG } from 'primeng/config'; -import Aura from '@primeng/themes/aura'; -import { provideStore } from '@ngxs/store' -import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin'; +import { appRoutes } from './app.routes'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { providePrimeNG } from 'primeng/config'; +import Aura from '@primeng/themes/aura'; +import { provideStore } from '@ngxs/store' +import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin'; import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin'; -import { withNgxsStoragePlugin } from '@ngxs/storage-plugin' -import { withNgxsRouterPlugin, } from '@ngxs/router-plugin' +import { withNgxsStoragePlugin } from '@ngxs/storage-plugin' +import { withNgxsRouterPlugin, } from '@ngxs/router-plugin' import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { accessTokenInterceptor } from './interceptors/access-token.interceptor'; +import { definePreset } from '@primeng/themes'; +export const AutoThemePreset = definePreset(Aura); + +export const ManualThemePreset = definePreset(Aura,) export const appConfig: ApplicationConfig = { providers: [ @@ -26,8 +30,9 @@ export const appConfig: ApplicationConfig = { withNgxsReduxDevtoolsPlugin({ disabled: !isDevMode() })), providePrimeNG({ theme: { - preset: Aura, + preset: AutoThemePreset, options: { + darkModeSelector: '.app-dark', cssLayer: { name: 'primeng', order: 'tailwind-base, primeng, tailwind-utilities' diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 288128a..07c73e4 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,8 +1,8 @@ -import { Routes } from '@angular/router'; -import { authGuard } from './guards/auth.guard'; -import { provideUserState, USER } from './state/user'; +import { Routes } from '@angular/router'; +import { authGuard } from './guards/auth.guard'; +import { provideUserState, USER } from './state/user'; import { SESSION_STORAGE_ENGINE, withStorageFeature } from '@ngxs/storage-plugin'; -import { NotFoundComponent } from './pages/not-found/not-found.component'; +import { NotFoundComponent } from './pages/not-found/not-found.component'; const userState = provideUserState(withStorageFeature([ { key: USER, engine: SESSION_STORAGE_ENGINE } @@ -27,16 +27,25 @@ export const appRoutes: Routes = [ providers: [userState], canActivate: [signedInGuard], title: 'Dashboard', - path: '', - pathMatch: 'full', + path: 'dashboard', loadComponent: () => import('./pages/dashboard/dashboard.component').then(m => m.DashboardComponent) }, { providers: [userState], canActivate: [signedInGuard], - title: 'Wallet', + title: 'Wallets', path: 'wallet', loadComponent: () => import('./pages/wallet/wallet.component').then(m => m.WalletComponent), }, + { + providers: [userState], + canActivate: [signedInGuard], + title: 'Settings', + path: 'settings', + loadComponent: () => import('./pages/settings/settings.component').then(m => m.SettingsComponent) + }, + { + path: '', pathMatch: 'full', redirectTo: 'campaigns' + }, { path: '**', component: NotFoundComponent, title: '404 - Resource not found' } ]; diff --git a/src/app/components/campaign-form/campaign-form.component.html b/src/app/components/campaign-form/campaign-form.component.html index 25a5525..6d06a8c 100644 --- a/src/app/components/campaign-form/campaign-form.component.html +++ b/src/app/components/campaign-form/campaign-form.component.html @@ -17,36 +17,38 @@
- @if (newCampaignForm.controls.basic.controls.title.invalid && newCampaignForm.controls.basic.controls.title.dirty) { - - @if (newCampaignForm.controls.basic.controls.title.hasError('required')) { - This field is required - } @else if (newCampaignForm.controls.basic.controls.title.hasError('maxlength')) { - - } - + @if (newCampaignForm.controls.basic.controls.title.invalid && + newCampaignForm.controls.basic.controls.title.dirty) { + + @if (newCampaignForm.controls.basic.controls.title.hasError('required')) { + This field is required + } @else if (newCampaignForm.controls.basic.controls.title.hasError('maxlength')) { + + } + }
+ class="text-red-500">*
- @if (newCampaignForm.controls.basic.controls.description.invalid && newCampaignForm.controls.basic.controls.description.dirty) { - - @if (newCampaignForm.controls.basic.controls.description.hasError('required')) { - This field is required - } @else if (newCampaignForm.controls.basic.controls.description.hasError('maxlength')) { - - } - + @if (newCampaignForm.controls.basic.controls.description.invalid && + newCampaignForm.controls.basic.controls.description.dirty) { + + @if (newCampaignForm.controls.basic.controls.description.hasError('required')) { + This field is required + } @else if (newCampaignForm.controls.basic.controls.description.hasError('maxlength')) { + + } + }
@@ -55,15 +57,15 @@
- +
Next + (onClick)="advanceTo(2)">Next
@@ -77,32 +79,31 @@
@for (control of newCampaignForm.controls.contactsAndLinks.controls.emails.controls; track control) { - -
-
- - - - @if (control.invalid && control.dirty) { -
- - @if (control.hasError('required')) { - This field is required - } @else if (control.hasError('email')) { - Invalid email address - } - -
- } + +
+
+ + + + @if (control.invalid && control.dirty) { +
+ + @if (control.hasError('required')) { + This field is required + } @else if (control.hasError('email')) { + Invalid email address + } +
- @if ($count > 1 && !$last) { - }
- + @if ($count > 1 && !$last) { + + } +
+
}
@@ -112,54 +113,53 @@
@for (group of newCampaignForm.controls.contactsAndLinks.controls.phones.controls; track group) { - -
-
-
-
- - -
- - +{{ item.callingCodes[0] }} -
-
-
-
- + +
+
+
+
+ + +
+ + +{{ item.callingCodes[0] }} +
+
+ +
+ + {{ country.nativeName }} +
+
+
- @if (group.invalid && group.dirty) { -
- - @if (group.controls.code.hasError('required')) { - The country code is required - } @else if (group.controls.number.hasError('number')) { - Invalid national number value - } @else if (group.hasError('phoneInvalid')) { - Invalid phone number - } - -
- } + +
+ @if (group.invalid && group.dirty) { +
+ + @if (group.controls.code.hasError('required')) { + The country code is required + } @else if (group.controls.number.hasError('number')) { + Invalid national number value + } @else if (group.hasError('phoneInvalid')) { + Invalid phone number + } +
- @if ($count > 1 && !$last) { - }
- + @if ($count > 1 && !$last) { + + } +
+
}
@@ -169,27 +169,26 @@
@for (control of newCampaignForm.controls.contactsAndLinks.controls.links.controls; track control) { -
-
- - - - @if (control.invalid && control.dirty) { -
- - @if (control.hasError('pattern')) { - Invalid URL - } - -
- } +
+
+ + + + @if (control.invalid && control.dirty) { +
+ + @if (control.hasError('pattern')) { + Invalid URL + } +
- @if (!$last) { - }
+ @if (!$last) { + + } +
}
@@ -198,7 +197,7 @@ Back Next + [outlined]="true" (onClick)="advanceTo(3)">Next
@@ -208,60 +207,53 @@
+ accept="image/*,video/*" (onSelect)="onMediaFileSelected($event)" [multiple]="true" + [maxFileSize]="maxFileSize()" [fileLimit]="maxUploadCount()" (onClear)="onMediaFilesCleared()" + (onRemove)="onMediaFileRemoved($event)" mode="advanced">

Drag and drop a file here to upload.

+ let-doUpload="uploadCallback">
- +
+ severity="secondary" label="Add media file" (onClick)="pickFile()" /> + icon="pi pi-upload" label="upload" /> + icon="pi pi-trash" severity="danger" label="Clear" />
- +
@if (newCampaignForm.controls.media.length > 0) { -

Uploaded files

-
- @for (url of newCampaignForm.controls.media.value; track url) { -
-
- @if (url.endsWith('.mp4')) { - - } @else { - media-{{$index}} - } -
- -
-
+

Uploaded files

+
+ @for (url of newCampaignForm.controls.media.value; track url) { +
+
+ @if (url.endsWith('.mp4')) { + + } @else { + media-{{$index}} + } +
+
- } +
+ } +
} diff --git a/src/app/components/campaign-form/campaign-form.component.ts b/src/app/components/campaign-form/campaign-form.component.ts index 9339f1e..7f7aa2a 100644 --- a/src/app/components/campaign-form/campaign-form.component.ts +++ b/src/app/components/campaign-form/campaign-form.component.ts @@ -1,22 +1,23 @@ -import { Component, computed, inject, input, model, output, signal } from '@angular/core'; +import { Component, computed, inject, input, model, output, signal } from '@angular/core'; import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { Step, StepList, StepPanel, StepPanels, Stepper } from 'primeng/stepper'; -import { Fluid } from 'primeng/fluid'; -import { InputText } from 'primeng/inputtext'; -import { Message } from 'primeng/message'; -import { MultiSelect } from 'primeng/multiselect'; -import { Button } from 'primeng/button'; -import { Select } from 'primeng/select'; -import { PhoneDirective } from '@app/directives/phone.directive'; -import { FileRemoveEvent, FileSelectEvent, FileUpload, FileUploadEvent } from 'primeng/fileupload'; -import { MeterGroup } from 'primeng/metergroup'; -import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { PhoneNumberFormat, PhoneNumberUtil } from 'google-libphonenumber'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Category } from '@lib/category'; -import { Divider } from 'primeng/divider'; -import { CountryData } from '@lib/country-data'; -import { Textarea } from 'primeng/textarea'; +import { Step, StepList, StepPanel, StepPanels, Stepper } from 'primeng/stepper'; +import { Fluid } from 'primeng/fluid'; +import { InputText } from 'primeng/inputtext'; +import { Message } from 'primeng/message'; +import { MultiSelect } from 'primeng/multiselect'; +import { Button } from 'primeng/button'; +import { Select } from 'primeng/select'; +import { PhoneDirective } from '@app/directives/phone.directive'; +import { FileRemoveEvent, FileSelectEvent, FileUpload, FileUploadEvent } from 'primeng/fileupload'; +import { MeterGroup } from 'primeng/metergroup'; +import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { PhoneNumberFormat, PhoneNumberUtil } from 'google-libphonenumber'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Category } from '@lib/models/category'; +import { Divider } from 'primeng/divider'; +import { CountryData } from '@lib/models/country-data'; +import { Textarea } from 'primeng/textarea'; +import { phoneValidator } from '@app/util/phone-valiator'; function newMediaControl(url: string) { return new FormControl(url, { nonNullable: true }); @@ -35,19 +36,7 @@ function newPhoneControl(defaultCode = 'CM') { nonNullable: true, }), number: new FormControl('', { nonNullable: false }) - }, [(group) => { - try { - const codeControl = group.get('code'); - const numberControl = group.get('number'); - if (codeControl?.value && !numberControl?.value) return null; - else if (!codeControl?.value || !numberControl?.value) return { phoneInvalid: true }; - const phoneUtil = PhoneNumberUtil.getInstance(); - const parsed = phoneUtil.parseAndKeepRawInput(numberControl.value, codeControl.value); - return phoneUtil.isValidNumberForRegion(parsed, codeControl.value) ? null : { phoneInvalid: true }; - } catch (e) { - return { phoneInvalid: true }; - } - }]) + }, [phoneValidator()]) } function newLinkControl() { diff --git a/src/app/components/campaign-publications/campaign-publications.component.ts b/src/app/components/campaign-publications/campaign-publications.component.ts index 4f50fb5..a86dcf4 100644 --- a/src/app/components/campaign-publications/campaign-publications.component.ts +++ b/src/app/components/campaign-publications/campaign-publications.component.ts @@ -2,7 +2,7 @@ import { Component, inject, input, ResourceRef } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { DataView } from 'primeng/dataview'; import { HttpClient } from '@angular/common/http'; -import { CampaignPublication } from '@lib/campaign'; +import { CampaignPublication } from '@lib/models/campaign'; import { of } from 'rxjs'; @Component({ diff --git a/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.html b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.html new file mode 100644 index 0000000..55a4ba9 --- /dev/null +++ b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.html @@ -0,0 +1,44 @@ +
+ telegram logo +
+

Telegram™ + +

+ @if(showVerificationCodeInput() && !isTelegramAccountConnected()) { +
+ + + + + + + + + +
+ +

Don't have the code? Go to the telegram bot and obtain a code by sending the /start + command

+ } @else { +

Receive promotion requests for your Telegram status.

+ } +
+
+@defer (when !acountLoading()) { +@if (!isTelegramAccountConnected()) { +@if(!showVerificationCodeInput()) { + +} +} @else { + +} +} @placeholder { + +} diff --git a/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.scss b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.scss new file mode 100644 index 0000000..2c1f104 --- /dev/null +++ b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.scss @@ -0,0 +1,3 @@ +:host { + @apply inline-flex items-center gap-3 p-2 +} diff --git a/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.ts b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.ts new file mode 100644 index 0000000..0fb127b --- /dev/null +++ b/src/app/components/connection-forms/telegram-connection-form/telegram-connection-form.component.ts @@ -0,0 +1,73 @@ +import { Component, computed, input, model, output, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ConnectedAccount } from '@lib/models/user'; +import { Button } from 'primeng/button'; +import { InputText } from 'primeng/inputtext'; +import { ProgressSpinner } from 'primeng/progressspinner'; +import { Tag } from 'primeng/tag'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; + +export type VerificationSubmission = { + code: string; +}; + +@Component({ + selector: 'tm-telegram-connection-form', + imports: [ + Button, + Tag, InputGroupModule, InputGroupAddonModule, + ProgressSpinner, + InputText, + FormsModule + ], + templateUrl: './telegram-connection-form.component.html', + styleUrl: './telegram-connection-form.component.scss' +}) +export class TelegramConnectionFormComponent { + readonly showVerificationCodeInput = signal(false); + readonly telegramBotUrl = input.required(); + readonly acountLoading = input(false); + readonly telegramConnection = input(); + readonly submitting = input(); + readonly disconnecting = input(); + readonly telegramConnectionStatusText = computed(() => { + const conn = this.telegramConnection(); + switch (conn?.status) { + case 'active': + return 'connected'; + case 'inactive': + return 'inactive'; + case 'reconnect_required': + return 'attention required'; + default: + return 'not connected'; + } + }); + readonly telegramConnectionSeverityText = computed(() => { + const conn = this.telegramConnection(); + switch (conn?.status) { + case 'active': return 'success'; + case 'reconnect_required': return 'warn'; + default: return 'secondary'; + } + }) + readonly isTelegramAccountConnected = computed(() => { + return this.telegramConnection()?.status === 'active'; + }); + readonly verificationCode = model(); + readonly submission = output(); + readonly disconnect = output(); + + onVerifyCodeButtonClicked() { + this.submission.emit({ code: String(this.verificationCode()) }); + } + onCancelButtonClicked() { + this.verificationCode.set(undefined); + this.showVerificationCodeInput.set(false); + } + + onDisconnectButtonClicked() { + this.disconnect.emit(); + } +} diff --git a/src/app/components/connections-form/connections-form.component.html b/src/app/components/connections-form/connections-form.component.html new file mode 100644 index 0000000..5537ead --- /dev/null +++ b/src/app/components/connections-form/connections-form.component.html @@ -0,0 +1,9 @@ +

In order to promote campaigns, you must connect at least one of your social + media accounts from the provider + platforms from below

+
+ +
diff --git a/src/app/components/connections-form/connections-form.component.scss b/src/app/components/connections-form/connections-form.component.scss new file mode 100644 index 0000000..cf7cef7 --- /dev/null +++ b/src/app/components/connections-form/connections-form.component.scss @@ -0,0 +1,3 @@ +:host { + @apply block space-y-3 +} diff --git a/src/app/components/connections-form/connections-form.component.ts b/src/app/components/connections-form/connections-form.component.ts new file mode 100644 index 0000000..cbebaf8 --- /dev/null +++ b/src/app/components/connections-form/connections-form.component.ts @@ -0,0 +1,79 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, inject, signal } from '@angular/core'; +import { rxResource } from '@angular/core/rxjs-interop'; +import { environment } from '@env/environment.development'; +import { ConnectedAccount } from '@lib/models/user'; +import { TelegramConnectionFormComponent, VerificationSubmission } from '../connection-forms/telegram-connection-form/telegram-connection-form.component'; +import { MessageService } from 'primeng/api'; + +@Component({ + selector: 'tm-connections-form', + imports: [ + TelegramConnectionFormComponent + ], + templateUrl: './connections-form.component.html', + styleUrl: './connections-form.component.scss' +}) +export class ConnectionsFormComponent { + private messageService = inject(MessageService); + private http = inject(HttpClient); + readonly verifyingTelegramCode = signal(false); + readonly disconnectingTelegram = signal(false); + readonly connections = rxResource({ + loader: () => this.http.get('/api/users/connections') + }); + readonly telegramConnection = computed(() => { + return this.connections.value()?.find(({ provider }) => provider == 'telegram'); + }); + readonly telegramBotLink = environment.telegramBot; + + onTelegramVerificationCodeSubmitted({ code }: VerificationSubmission) { + this.verifyingTelegramCode.set(true); + this.http.get('/api/users/connections/verify/tm', { + params: { + code + } + }).subscribe({ + complete: () => { + this.verifyingTelegramCode.set(false); + this.connections.reload(); + this.messageService.add({ + severity: 'info', + summary: 'Notification', + detail: 'Your telegram account has been successfully linked.' + }) + }, + error: (error: HttpErrorResponse) => { + this.verifyingTelegramCode.set(false); + this.messageService.add({ + severity: 'error', + summary: 'Error', + detail: error.error?.message ?? error.message + }); + } + }); + } + + onDisconnectTelegramConnectionRequested() { + this.disconnectingTelegram.set(true); + this.http.get('/api/users/connections/disconnect/tm').subscribe({ + error: (error: HttpErrorResponse) => { + this.disconnectingTelegram.set(false); + this.messageService.add({ + summary: 'Error', + severity: 'error', + detail: error.error?.message ?? error.message + }); + }, + complete: () => { + this.disconnectingTelegram.set(false); + this.connections.reload(); + this.messageService.add({ + summary: 'Info', + severity: 'info', + detail: 'Your Telegram account was disconnected successfully.' + }); + } + }) + } +} diff --git a/src/app/components/payment-method-registration/momo/momo.component.html b/src/app/components/payment-method-registration/momo/momo.component.html new file mode 100644 index 0000000..9635e2a --- /dev/null +++ b/src/app/components/payment-method-registration/momo/momo.component.html @@ -0,0 +1,69 @@ +
+
+ momo logo +
+
+

MTN Mobile Money ™

+
+
+ @if(!showMomoNumberInput()) { +

Connect your MTN Mobile Money number. Not your + PIN

+ }@else { +
+ + +
+ + +{{ item.callingCodes[0] }} +
+
+ +
+ + {{ country.nativeName }} +
+
+
+
+ + + + + + + + + + @if(form.hasError('phoneInvalid') && form.dirty) { +
+ + Invalid phone number + +
+ } +
+
+ } +
+
+@if(!showMomoNumberInput()) { +
+ @if(!momoPaymentMethodConnected()) { + + }@else { + + + + } +
+} diff --git a/src/app/components/payment-method-registration/momo/momo.component.scss b/src/app/components/payment-method-registration/momo/momo.component.scss new file mode 100644 index 0000000..3e6c888 --- /dev/null +++ b/src/app/components/payment-method-registration/momo/momo.component.scss @@ -0,0 +1,3 @@ +:host { + @apply flex gap-3 items-center; +} diff --git a/src/app/components/payment-method-registration/momo/momo.component.ts b/src/app/components/payment-method-registration/momo/momo.component.ts new file mode 100644 index 0000000..ed27791 --- /dev/null +++ b/src/app/components/payment-method-registration/momo/momo.component.ts @@ -0,0 +1,183 @@ +import { AsyncPipe } from '@angular/common'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, effect, inject, output, signal } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { PhoneDirective } from '@app/directives/phone.directive'; +import { pmMomo } from '@app/state/user'; +import { phoneValidator } from '@app/util/phone-valiator'; +import { CountryData } from '@lib/models/country-data'; +import { select } from '@ngxs/store'; +import { PhoneNumberFormat, PhoneNumberUtil } from 'google-libphonenumber'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { Button } from 'primeng/button'; +import { InputGroup } from 'primeng/inputgroup'; +import { InputGroupAddon } from 'primeng/inputgroupaddon'; +import { InputText } from 'primeng/inputtext'; +import { Message } from 'primeng/message'; +import { Select } from 'primeng/select'; +import { Tag } from 'primeng/tag'; +import { ConfirmPopupModule } from 'primeng/confirmpopup'; + +import { filter, map, startWith } from 'rxjs'; +const momoSupportedCountries = ['BJ', 'BW', 'BF', 'BI', 'CM', 'TD', 'CG', 'CD', 'CI', 'GH', 'GN', 'GW', 'KE', 'MW', 'ML', 'MZ', 'NE', 'NG', 'RW', 'SN', 'SC', 'SL', 'SO', 'SS', 'SZ', 'TZ', 'TG', 'UG', 'ZM', 'ZW', 'CN', 'IN', 'PH', 'TR', 'CA', 'ET']; +const util = PhoneNumberUtil.getInstance(); + +@Component({ + selector: 'tm-momo', + imports: [ + Tag, + Select, + ReactiveFormsModule, + Button, + PhoneDirective, + InputText, + ConfirmPopupModule, + InputGroupAddon, + InputGroup, + AsyncPipe, + Message, + ], + providers: [ConfirmationService], + templateUrl: './momo.component.html', + styleUrl: './momo.component.scss' +}) +export class MomoComponent { + private http = inject(HttpClient); + private messageService = inject(MessageService); + private confirmationService = inject(ConfirmationService); + readonly updated = output(); + readonly momoPaymentMethod = select(pmMomo); + readonly momoPaymentMethodConnected = computed(() => { + return this.momoPaymentMethod()?.status === 'active'; + }); + readonly momoCountries = signal([]); + readonly loadingMomoCountries = signal(false); + readonly showMomoNumberInput = signal(false); + readonly form = new FormGroup({ + code: new FormControl('CM', [Validators.required]), + number: new FormControl('', [Validators.required]) + }, [phoneValidator()]); + readonly samplePhoneNumber$ = this.form.controls.code.valueChanges.pipe( + startWith(this.form.value.code), + takeUntilDestroyed(), + map(code => util.getExampleNumber(code!)), + filter(p => !!p), + map(p => util.format(p, PhoneNumberFormat.NATIONAL)) + ); + readonly connectionStatusText = computed(() => { + const method = this.momoPaymentMethod(); + const submitting = this.submitting(); + if (submitting) return 'connecting'; + + switch (method?.status) { + default: return 'not connected'; + case 'active': return 'connected'; + case 'inactive': + case 're-connection required': + return 'attention required' + } + }); + readonly connectionStatusSeverityText = computed(() => { + const method = this.momoPaymentMethod(); + const submitting = this.submitting(); + if (submitting) return 'info'; + + switch (method?.status) { + default: return 'secondary'; + case 'active': return 'success'; + case 'inactive': + case 're-connection required': + return 'warn' + } + }); + readonly submitting = signal(false); + readonly disconnecting = signal(false); + + constructor() { + effect(() => { + const showingMomoNumberInput = this.showMomoNumberInput(); + const momoCountries = this.momoCountries(); + if (!showingMomoNumberInput || momoCountries.length == momoSupportedCountries.length) return; + + this.loadingMomoCountries.set(true); + this.http.get('/api/countries/find', { + params: { + alpha2Code: momoSupportedCountries.join(',') + } + }).subscribe({ + next: countries => this.momoCountries.set(countries), + complete: () => { + this.loadingMomoCountries.set(false) + }, + error: (error: Error) => { + console.error(error); + this.loadingMomoCountries.set(false); + } + }); + }) + } + + onFormSubmit() { + const { code, number } = this.form.value; + const p = util.parse(String(number), code!); + const phoneNumber = util.format(p, PhoneNumberFormat.E164); + + this.submitting.set(true); + this.http.put('/api/payment/methods', { data: { phoneNumber }, provider: 'momo' }).subscribe({ + error: (error: HttpErrorResponse) => { + this.messageService.add({ + summary: 'Error', + severity: 'error', + detail: error.error?.message ?? error.message + }); + this.submitting.set(false); + }, + complete: () => { + this.submitting.set(false); + this.form.reset(); + this.form.patchValue({ code }); + this.form.markAsUntouched(); + this.form.markAsPristine(); + this.showMomoNumberInput.set(false); + this.updated.emit(); + } + }); + } + + onDisconnectButtonClicked(event: MouseEvent) { + this.confirmationService.confirm({ + target: event.target as EventTarget, + message: 'Are you sure you want to proceed?', + icon: 'pi pi-exclamation-triangle', + rejectButtonProps: { + label: 'Cancel', + severity: 'secondary', + size: 'small', + outlined: true + }, + acceptButtonProps: { + label: 'Disconnect', + size: 'small', + severity: 'danger' + }, + accept: () => { + this.disconnecting.set(true); + this.http.delete('/api/payment/methods', { params: { provider: 'momo' } }).subscribe({ + error: (error: HttpErrorResponse) => { + this.messageService.add({ + summary: 'Error', + severity: 'error', + detail: error.error?.message ?? error.message + }); + this.disconnecting.set(false); + }, + complete: () => { + this.disconnecting.set(false); + this.updated.emit(); + } + }); + } + }); + } +} diff --git a/src/app/components/payment-method-registration/payment-method-registration.component.html b/src/app/components/payment-method-registration/payment-method-registration.component.html new file mode 100644 index 0000000..36beda5 --- /dev/null +++ b/src/app/components/payment-method-registration/payment-method-registration.component.html @@ -0,0 +1,4 @@ + +

Setup payment methods to top-up your funding wallets as well as to make withdrawals + from your rewards wallet.

+ diff --git a/src/app/components/payment-method-registration/payment-method-registration.component.scss b/src/app/components/payment-method-registration/payment-method-registration.component.scss new file mode 100644 index 0000000..da26eae --- /dev/null +++ b/src/app/components/payment-method-registration/payment-method-registration.component.scss @@ -0,0 +1,3 @@ +:host{ + @apply space-y-2 block py-3; +} diff --git a/src/app/components/payment-method-registration/payment-method-registration.component.ts b/src/app/components/payment-method-registration/payment-method-registration.component.ts new file mode 100644 index 0000000..0dc8b1d --- /dev/null +++ b/src/app/components/payment-method-registration/payment-method-registration.component.ts @@ -0,0 +1,24 @@ +import { Component } from '@angular/core'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; +import { MomoComponent } from './momo/momo.component'; +import { dispatch } from '@ngxs/store'; +import { RefreshPaymentMethod } from '@app/state/user'; + +@Component({ + selector: 'tm-payment-method-registration', + imports: [ + InputGroupModule, + InputGroupAddonModule, + MomoComponent + ], + templateUrl: './payment-method-registration.component.html', + styleUrl: './payment-method-registration.component.scss' +}) +export class PaymentMethodRegistrationComponent { + private refreshPaymentMethods = dispatch(RefreshPaymentMethod); + + onMomoNumberPaymentMethodConnected() { + this.refreshPaymentMethods(); + } +} diff --git a/src/app/components/prefs-form/prefs-form.component.html b/src/app/components/prefs-form/prefs-form.component.html new file mode 100644 index 0000000..2edc464 --- /dev/null +++ b/src/app/components/prefs-form/prefs-form.component.html @@ -0,0 +1,64 @@ +
+
+ +
+
+ +
+
+ +
+
+ + +
+ + {{ country.nativeName }} +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ + +
+ {{ currency.code }} ({{ currency.symbol }}) + {{ currency.name }} +
+
+
+
+ +
diff --git a/src/app/components/prefs-form/prefs-form.component.scss b/src/app/components/prefs-form/prefs-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/prefs-form/prefs-form.component.ts b/src/app/components/prefs-form/prefs-form.component.ts new file mode 100644 index 0000000..26229c6 --- /dev/null +++ b/src/app/components/prefs-form/prefs-form.component.ts @@ -0,0 +1,107 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, inject, OnInit, output, signal } from '@angular/core'; +import { rxResource, takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { preferences, PrefsUpdated, UpdatePrefs } from '@app/state/user'; +import { CountryData, Currency, Language } from '@lib/models/country-data'; +import { Actions, dispatch, ofActionCompleted, ofActionDispatched, select } from '@ngxs/store'; +import { Select } from 'primeng/select'; +import { distinct, filter, from, map, merge, mergeMap, of, tap, toArray } from 'rxjs'; + +@Component({ + selector: 'tm-prefs-form', + imports: [ + Select, + ReactiveFormsModule + ], + templateUrl: './prefs-form.component.html', + styleUrl: './prefs-form.component.scss' +}) +export class PrefsFormComponent implements OnInit { + private http = inject(HttpClient); + private prefsUpdated = dispatch(PrefsUpdated); + private updatePrefs = dispatch(UpdatePrefs); + readonly submitting = signal(false); + readonly loadingPrefs = signal(false); + readonly countries = rxResource({ + loader: () => this.http.get('/api/countries') + }); + readonly languages = rxResource({ + request: () => ({ countries: this.countries.value() }), + loader: ({ request: { countries } }) => { + if (!countries) return of(Array()); + return from(countries).pipe( + mergeMap(country => country.languages), + distinct(l => l.iso639_2), + toArray() + ) + } + }); + readonly currencies = rxResource({ + request: () => ({ countries: this.countries.value() }), + loader: ({ request: { countries } }) => { + if (!countries) return of(Array()); + return from(countries).pipe( + mergeMap(country => country.currencies ?? []), + distinct(c => c.code), + toArray() + ) + } + }); + readonly themeOptions = [ + { label: 'Dark', value: 'dark' }, + { label: 'Light', value: 'light' }, + ]; + readonly form = new FormGroup({ + theme: new FormControl<'light' | 'dark' | 'system'>('light', { + nonNullable: true, + validators: [Validators.required] + }), + country: new FormControl('US', { nonNullable: true, validators: [Validators.required] }), + language: new FormControl('en', { nonNullable: true, validators: [Validators.required] }), + currency: new FormControl('USD', { nonNullable: true, validators: [Validators.required] }), + }); + readonly error = output(); + readonly currentPrefs = select(preferences); + + ngOnInit() { + this.setConfiguredPreferences(); + this.form.markAsUntouched(); + this.form.markAsPristine(); + } + + private setConfiguredPreferences() { + const { language, country, currency, theme } = this.currentPrefs(); + this.form.patchValue({ + language, currency, theme, country + }); + } + + constructor(actions: Actions) { + merge( + actions.pipe(ofActionDispatched(UpdatePrefs), map(() => true)), + actions.pipe(ofActionCompleted(UpdatePrefs), map(() => false)) + ).pipe( + takeUntilDestroyed(), + tap(() => { + this.ngOnInit(); + }) + ).subscribe(v => this.submitting.set(v)); + + this.form.valueChanges.pipe( + takeUntilDestroyed(), + filter(() => this.form.dirty), + mergeMap(({ country, currency, language, theme }) => this.updatePrefs(theme!, country!, currency!, language!)) + ).subscribe({ + error: (error: Error) => { + this.submitting.set(false); + this.error.emit(error); + this.setConfiguredPreferences(); + }, + complete: () => { + this.submitting.set(false); + this.prefsUpdated(); + } + }) + } +} diff --git a/src/app/components/publication-form/publication-form.component.html b/src/app/components/publication-form/publication-form.component.html index 2f6a1a2..9933093 100644 --- a/src/app/components/publication-form/publication-form.component.html +++ b/src/app/components/publication-form/publication-form.component.html @@ -52,8 +52,8 @@

Allocating {{ form.value.tokens | number }} of your {{ totalTokens() | number }} tokens

} @else { -

Your out of tokens. Top up account +

Your out of tokens. Top up account

}
diff --git a/src/app/components/publication-form/publication-form.component.ts b/src/app/components/publication-form/publication-form.component.ts index 367b9b3..97eb46f 100644 --- a/src/app/components/publication-form/publication-form.component.ts +++ b/src/app/components/publication-form/publication-form.component.ts @@ -1,5 +1,5 @@ import { Component, computed, effect, inject, input, output, signal } from '@angular/core'; -import { Campaign } from '@lib/campaign'; +import { Campaign } from '@lib/models/campaign'; import { Fluid } from 'primeng/fluid'; import { Select } from 'primeng/select'; import { InputNumberModule } from 'primeng/inputnumber'; diff --git a/src/app/components/top-bar/top-bar.component.html b/src/app/components/top-bar/top-bar.component.html index f5763f3..be018a4 100644 --- a/src/app/components/top-bar/top-bar.component.html +++ b/src/app/components/top-bar/top-bar.component.html @@ -5,7 +5,7 @@
- +

{{ principal()?.name }}

@@ -14,8 +14,22 @@
- +
+
+ + + + + +
+
+ +
+
diff --git a/src/app/components/top-bar/top-bar.component.ts b/src/app/components/top-bar/top-bar.component.ts index a7d54bf..56878e8 100644 --- a/src/app/components/top-bar/top-bar.component.ts +++ b/src/app/components/top-bar/top-bar.component.ts @@ -1,30 +1,40 @@ -import { Component } from '@angular/core'; -import { Menubar } from 'primeng/menubar'; -import { MenuItem } from 'primeng/api'; -import { dispatch, select } from '@ngxs/store'; -import { principal, SignOut } from '../../state/user'; -import { Avatar } from 'primeng/avatar'; -import { Menu } from 'primeng/menu'; +import { NgClass } from '@angular/common'; +import { Component, inject, linkedSignal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { dispatch, select } from '@ngxs/store'; +import { MenuItem, MessageService } from 'primeng/api'; +import { Avatar } from 'primeng/avatar'; +import { Menu } from 'primeng/menu'; +import { Menubar } from 'primeng/menubar'; +import { ToggleSwitchChangeEvent, ToggleSwitchModule } from 'primeng/toggleswitch'; +import { preferences, principal, SetColorMode, SignOut } from '../../state/user'; @Component({ selector: 'tm-top-bar', imports: [ Menubar, Avatar, - Menu + ToggleSwitchModule, + Menu, + NgClass, + FormsModule ], templateUrl: './top-bar.component.html', styleUrl: './top-bar.component.scss' }) export class TopBarComponent { + private messageService = inject(MessageService); private readonly signOut = dispatch(SignOut); readonly principal = select(principal); + readonly preferences = select(preferences); + private updateMode = dispatch(SetColorMode); + readonly darkMode = linkedSignal(() => this.preferences().theme == 'dark'); readonly menuItems: MenuItem[] = [ - { label: 'Dashboard', routerLink: '/', icon: 'pi pi-gauge', routerLinkActiveOptions: { match: true } }, + // { label: 'Dashboard', routerLink: '/', icon: 'pi pi-gauge', routerLinkActiveOptions: { match: true } }, { label: 'Campaigns', icon: 'pi pi-list', routerLink: '/campaigns', routerLinkActiveOptions: { match: true } }, - { label: 'Wallet', icon: 'pi pi-wallet', routerLink: '/wallet', routerLinkActiveOptions: { match: true } }, + { label: 'Wallets', icon: 'pi pi-wallet', routerLink: '/wallet', routerLinkActiveOptions: { match: true } }, ]; readonly userMenuItems: MenuItem[] = [ { label: 'Settings', icon: 'pi pi-cog', routerLink: '/settings' }, @@ -32,5 +42,18 @@ export class TopBarComponent { { label: 'Sign out', icon: 'pi pi-sign-out', command: () => this.signOut() }, - ] + ]; + + onDarkModeChanged({ checked }: ToggleSwitchChangeEvent) { + this.darkMode.set(checked); + this.updateMode(checked ? 'dark' : 'light').subscribe({ + error: (error: Error) => { + this.messageService.add({ + detail: error.message, + summary: 'Error', + severity: 'error' + }); + } + }); + } } diff --git a/src/app/directives/phone.directive.ts b/src/app/directives/phone.directive.ts index 807bbe7..5c8d36a 100644 --- a/src/app/directives/phone.directive.ts +++ b/src/app/directives/phone.directive.ts @@ -1,15 +1,11 @@ -import { computed, Directive, ElementRef, HostListener, inject, input } from '@angular/core'; -import { AsYouTypeFormatter, PhoneNumberUtil } from 'google-libphonenumber'; -import { DefaultValueAccessor } from '@angular/forms'; +import { computed, Directive, HostListener, input } from '@angular/core'; +import { AsYouTypeFormatter } from 'google-libphonenumber'; @Directive({ selector: '[tmPhone]' }) export class PhoneDirective { - private phoneUtil = PhoneNumberUtil.getInstance(); readonly code = input(); - private readonly el: ElementRef = inject(ElementRef); - readonly renderer = inject(DefaultValueAccessor); private formatter = computed(() => new AsYouTypeFormatter(this.code() ?? 'CM')); @HostListener('input', ['$event']) @@ -30,7 +26,7 @@ export class PhoneDirective { @HostListener('keydown', ['$event']) onKeyDown(event: KeyboardEvent) { - if (event.key != 'Delete' && event.key != 'Backspace' && !/[0-9]/.test(event.key) && event.key != 'Tab') { + if (event.key != 'Delete' && event.key != 'Backspace' && !/[0-9]/.test(event.key) && event.key != 'Tab' && event.key != 'Shift' && event.key != 'End' && event.key != 'Home' && event.key != 'Alt') { event.preventDefault(); return; } diff --git a/src/app/pages/auth-callback/auth-callback.component.scss b/src/app/pages/auth-callback/auth-callback.component.scss index e69de29..a5697ae 100644 --- a/src/app/pages/auth-callback/auth-callback.component.scss +++ b/src/app/pages/auth-callback/auth-callback.component.scss @@ -0,0 +1,3 @@ +:host { + @apply flex h-full items-center justify-center; +} diff --git a/src/app/pages/campaigns/campaigns.component.html b/src/app/pages/campaigns/campaigns.component.html index f93a967..ce6b919 100644 --- a/src/app/pages/campaigns/campaigns.component.html +++ b/src/app/pages/campaigns/campaigns.component.html @@ -1,60 +1,62 @@ -
- - - -
-

Campaigns

-
- - - - - - - -
-
-
- - - Title - Categories - Created - - - - - -

{{ campaign.title }}

- {{ campaign.categories.length }} categories - {{ campaign.createdAt | date }} - -
- - +
+

Campaigns

+
+ + + +
+
+ + + + + + +
- - - - - - @if (selectedCampaign()) { -
- +
+ -

Publications

+ + Title + Categories + Created + +
- - -
- } + + +

{{ campaign.title }}

+ {{ campaign.categories.length }} categories + {{ campaign.createdAt | date }} + +
+ + +
+ + +
+
+
+ @if (selectedCampaign()) { +
+ + +

Publications

+
+ +
+
+ } +
@@ -65,7 +67,6 @@

New Campaign

[categoriesLoading]="categories.isLoading()" [countriesLoading]="countries.isLoading()" [categories]="categories.value() ?? []"/>
-

Publish Campaign

diff --git a/src/app/pages/campaigns/campaigns.component.ts b/src/app/pages/campaigns/campaigns.component.ts index e8ae75e..4ef50ff 100644 --- a/src/app/pages/campaigns/campaigns.component.ts +++ b/src/app/pages/campaigns/campaigns.component.ts @@ -11,10 +11,10 @@ import { MenuItem, MessageService } import { rxResource } from '@angular/core/rxjs-interop'; -import { CountryData } from '@lib/country-data'; -import { Category } from '@lib/category'; +import { CountryData } from '@lib/models/country-data'; +import { Category } from '@lib/models/category'; import { HttpClient } from '@angular/common/http'; -import { Campaign, LookupCampaignResponse } from '@lib/campaign'; +import { Campaign, LookupCampaignResponse } from '@lib/models/campaign'; import { Panel } from 'primeng/panel'; import { Menu } from 'primeng/menu'; import { DataViewModule } from 'primeng/dataview'; @@ -27,6 +27,7 @@ import { import { PublicationFormComponent } from '@app/components/publication-form/publication-form.component'; +import { Ripple } from 'primeng/ripple'; @Component({ selector: 'tm-campaigns', @@ -44,7 +45,8 @@ import { DataViewModule, CampaignFormComponent, CampaignPublicationsComponent, - PublicationFormComponent + PublicationFormComponent, + Ripple ], templateUrl: './campaigns.component.html', styleUrl: './campaigns.component.scss' @@ -58,7 +60,7 @@ export class CampaignsComponent { showPublicationModal = model(false); currentPage = model(0); currentPageSize = model(20); - readonly tokens = signal(500); + readonly tokens = signal(0); readonly categories = rxResource({ loader: () => this.http.get('/api/categories') diff --git a/src/app/pages/settings/settings.component.html b/src/app/pages/settings/settings.component.html index 4ab2a41..aaee385 100644 --- a/src/app/pages/settings/settings.component.html +++ b/src/app/pages/settings/settings.component.html @@ -1 +1,45 @@ -

settings works!

+
+

Settings

+ +
+

General

+
+ +
+
+ +
+

Connected Accounts

+
+ +
+
+ +
+

Payment Methods

+
+ +
+
+ +
+ + +

Account Deletion - Danger zone

+
+
+

Deleting your account would:

+
    +
  1. - Make all your campaigns unavailable to posters
  2. +
  3. - Make you lose all accumulated rewards
  4. +
+ +
+ + +
+
+
+
+
diff --git a/src/app/pages/settings/settings.component.scss b/src/app/pages/settings/settings.component.scss index e69de29..249c71a 100644 --- a/src/app/pages/settings/settings.component.scss +++ b/src/app/pages/settings/settings.component.scss @@ -0,0 +1,3 @@ +:host { + @apply block; +} diff --git a/src/app/pages/settings/settings.component.ts b/src/app/pages/settings/settings.component.ts index 7043eda..a50cc95 100644 --- a/src/app/pages/settings/settings.component.ts +++ b/src/app/pages/settings/settings.component.ts @@ -1,11 +1,72 @@ -import { Component } from '@angular/core'; - +import { Component, inject, signal } from '@angular/core'; +import { Panel } from 'primeng/panel'; +import { Divider } from 'primeng/divider'; +import { Button } from 'primeng/button'; +import { PrefsFormComponent } from '@app/components/prefs-form/prefs-form.component'; +import { ConnectionsFormComponent } from '@app/components/connections-form/connections-form.component'; +import { PaymentMethodRegistrationComponent } from "@app/components/payment-method-registration/payment-method-registration.component"; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { dispatch } from '@ngxs/store'; +import { SignOut } from '@app/state/user'; @Component({ selector: 'tm-settings', - imports: [], + imports: [ + Panel, ConfirmDialogModule, + Divider, + Button, + PrefsFormComponent, + ConnectionsFormComponent, + PaymentMethodRegistrationComponent + ], templateUrl: './settings.component.html', - styleUrl: './settings.component.scss' + styleUrl: './settings.component.scss', + providers: [ConfirmationService] }) export class SettingsComponent { + private http = inject(HttpClient); + private messageService = inject(MessageService); + private confirmService = inject(ConfirmationService); + private deletingAccount = signal(false); + private signOut = dispatch(SignOut); + onDeleteAccountButtonClicked(event: MouseEvent) { + this.confirmService.confirm({ + target: event.target as EventTarget, + message: 'Are you sure you want to proceed?', + header: 'Confirmation', + closable: true, + closeOnEscape: true, + icon: 'pi pi-exclamation-triangle', + rejectButtonProps: { + label: 'Cancel', + severity: 'secondary', + outlined: true, + size: 'small' + }, + acceptButtonProps: { + severity: 'danger', + label: 'Yes, delete my account', + size: 'small' + }, + accept: () => { + this.deletingAccount.set(true); + this.http.delete('/api/auth').subscribe({ + complete: () => { + this.deletingAccount.set(false); + this.signOut('/'); + }, + error: (error: HttpErrorResponse) => { + this.deletingAccount.set(false); + this.messageService.add({ + summary: 'Error', + severity: 'error', + detail: error.error?.message ?? error.message + }); + } + }) + } + }) + } } diff --git a/src/app/pages/wallet/wallet.component.html b/src/app/pages/wallet/wallet.component.html index 2b99ece..0300850 100644 --- a/src/app/pages/wallet/wallet.component.html +++ b/src/app/pages/wallet/wallet.component.html @@ -1,72 +1,76 @@ -

Wallet

-
- -

Funding

-

- - @if(balances.isLoading()){ - - }@else{ - {{ balances.value()?.funding?.balance | number }} - } credits - -

-
- -

Rewards

-

- - @if(balances.isLoading()){ - - }@else{ - {{ balances.value()?.rewards?.balance | number }} - } credits - -

-
-
-
- - - - - Wallet - Amount - Status - Type - Date - - - -
-

Transactions

-
- - @if((balances.value()?.rewards?.balance ?? 0) > 0) { - - } +
+

Wallet

+
+ +

Funding

+

+ + @if (balances.isLoading()) { + + } @else { + {{ balances.value()?.funding?.balance | number }} + } credits + +

+
+ +

Rewards

+

+ + @if (balances.isLoading()) { + + } @else { + {{ balances.value()?.rewards?.balance | number }} + } credits + +

+
+
+
+ + + + + Wallet + Amount + Status + Type + Date + + + +
+

Transactions

+
+ + @if ((balances.value()?.rewards?.balance ?? 0) > 0) { + + } +
-
- - - - - - - - - - - - -
+
+ + + + + + + + + + + + - - -

Top up your Account

-
- -
+ + +

Top up your Account

+
+ +
+ +
diff --git a/src/app/pages/wallet/wallet.component.ts b/src/app/pages/wallet/wallet.component.ts index d2bebfa..56ca63a 100644 --- a/src/app/pages/wallet/wallet.component.ts +++ b/src/app/pages/wallet/wallet.component.ts @@ -1,15 +1,17 @@ import { DecimalPipe } from '@angular/common'; import { HttpClient } from '@angular/common/http'; -import { Component, inject, model } from '@angular/core'; +import { Component, effect, inject, model } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; -import { WalletBalanceResponse } from '@lib/wallet'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TopUpFormComponent } from '@app/components/top-up-form/top-up-form.component'; +import { WalletBalanceResponse } from '@lib/models/wallet'; +import { injectQueryParams } from 'ngxtension/inject-query-params'; import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; +import { Drawer } from 'primeng/drawer'; import { Panel } from 'primeng/panel'; -import { TableModule } from 'primeng/table'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; -import { Drawer } from 'primeng/drawer'; -import { TopUpFormComponent } from '@app/components/top-up-form/top-up-form.component'; +import { TableModule } from 'primeng/table'; @Component({ selector: 'tm-wallet', @@ -31,6 +33,17 @@ export class WalletComponent { readonly balances = rxResource({ loader: () => this.http.get('/api/wallet/balances') }); - + readonly topUpQuery = injectQueryParams('top-up'); readonly showTopupFormModal = model(false); + constructor(private route: ActivatedRoute, private router: Router) { + effect(() => { + const topUpQueryAvailable = this.topUpQuery() == 'true'; + if (!topUpQueryAvailable) return; + this.showTopupFormModal.set(true); + }) + } + + publicationModalHidden() { + this.router.navigate([], { queryParamsHandling: 'replace', queryParams: {} }) + } } diff --git a/src/app/state/user/actions.ts b/src/app/state/user/actions.ts index 5d20ff6..d121291 100644 --- a/src/app/state/user/actions.ts +++ b/src/app/state/user/actions.ts @@ -1,3 +1,5 @@ +import { DisplayPrefs } from "@lib/models/user"; + const prefix = '[user]'; export class GoogleSignInFlow { @@ -16,4 +18,25 @@ export class FinishGoogleSignInFlow { export class SignOut { static type = `${prefix} sign out` + constructor(readonly redirect?: string) { } +} +export class SignedIn { + static type = `${prefix} signed in` +} + +export class PrefsUpdated { + static type = `${prefix} prefs updated` +} + +export class UpdatePrefs implements DisplayPrefs { + constructor(readonly theme: 'dark' | 'light' | 'system', readonly country: string, readonly currency: string, readonly language: string) { } + static type = `${prefix} upate prefs`; +} +export class SetColorMode { + static type = `${prefix} set color mode`; + constructor(readonly mode: 'dark' | 'light') { } +} + +export class RefreshPaymentMethod { + static type = `${prefix} refresh payment methods`; } diff --git a/src/app/state/user/index.ts b/src/app/state/user/index.ts index df88475..29ae4f3 100644 --- a/src/app/state/user/index.ts +++ b/src/app/state/user/index.ts @@ -1,3 +1,8 @@ +import { HttpClient } from '@angular/common/http'; +import { EnvironmentProviders, inject, Injectable } from '@angular/core'; +import { PaymentMethodLookup } from '@lib/models/payment-method-lookup'; +import { DisplayPrefs, UserPrefs } from '@lib/models/user'; +import { Navigate } from '@ngxs/router-plugin'; import { Action, createPropertySelectors, @@ -6,12 +11,11 @@ import { State, StateContext, StateToken -} from '@ngxs/store'; -import { EnvironmentProviders, Injectable } from '@angular/core'; -import { FinishGoogleSignInFlow, GoogleSignInFlow, SignOut } from './actions'; -import { jwtDecode } from 'jwt-decode'; -import { patch } from '@ngxs/store/operators'; -import { Navigate } from '@ngxs/router-plugin'; +} from '@ngxs/store'; +import { patch } from '@ngxs/store/operators'; +import { jwtDecode } from 'jwt-decode'; +import { catchError, tap, throwError } from 'rxjs'; +import { FinishGoogleSignInFlow, GoogleSignInFlow, PrefsUpdated, RefreshPaymentMethod, SetColorMode, SignedIn, SignOut, UpdatePrefs } from './actions'; export * from './actions'; @@ -26,10 +30,18 @@ export type UserStateModel = { token?: string; signedIn: boolean; principal?: Principal; + prefs?: UserPrefs; + paymentMethods: PaymentMethodLookup[] } export const USER = new StateToken('user'); -const defaultState: UserStateModel = { signedIn: false } +const defaultDisplayPrefs = { + country: 'CM', + currency: 'XAF', + language: 'en', + theme: 'light' +} as DisplayPrefs; +const defaultState: UserStateModel = { signedIn: false, paymentMethods: [] }; type Context = StateContext; @@ -39,16 +51,69 @@ type Context = StateContext; }) @Injectable() class UserState implements NgxsOnInit { + private http = inject(HttpClient); + + @Action(RefreshPaymentMethod) + onRefreshPaymentMethod(ctx: Context) { + return this.http.get('/api/payment/methods').pipe( + tap(paymentMethods => ctx.setState(patch({ + paymentMethods + }))) + ); + } + + @Action(SignedIn) + updatePaymentMethodsOnSignIn(ctx: Context) { + ctx.dispatch(RefreshPaymentMethod); + } + + @Action(SetColorMode, { cancelUncompleted: true }) + onSetColorMode(ctx: Context, { mode }: SetColorMode) { + const { prefs } = ctx.getState(); + ctx.dispatch(new UpdatePrefs(mode, prefs?.country ?? '', prefs?.currency ?? '', prefs?.language ?? '')); + } + + @Action(UpdatePrefs, { cancelUncompleted: true }) + onUpdatePrefs(ctx: Context, action: UpdatePrefs) { + const { prefs: backup } = ctx.getState(); + ctx.setState(patch({ + prefs: patch({ + country: action.country || defaultDisplayPrefs.country, + theme: action.theme || defaultDisplayPrefs.theme, + language: action.language || defaultDisplayPrefs.language, + currency: action.currency || defaultDisplayPrefs.currency + }) + })); + return this.http.put('/api/users/prefs', action).pipe( + tap(() => ctx.dispatch(PrefsUpdated)), + catchError((e: Error) => { + ctx.setState(patch({ + prefs: backup + })); + return throwError(() => e); + }) + ); + } + + @Action(SignedIn) + @Action(PrefsUpdated) + onUserSignedIn(ctx: Context) { + return this.http.get('/api/users/prefs').pipe( + tap(prefs => { + ctx.setState(patch({ prefs })); + }) + ) + } @Action(SignOut) - signOut(ctx: Context) { + signOut(ctx: Context, { redirect }: SignOut) { ctx.setState(defaultState); - ctx.dispatch(new Navigate(['/'])); + ctx.dispatch(new Navigate([redirect ?? '/'])); location.reload(); } @Action(GoogleSignInFlow) - googleSignInFlow(ctx: Context, { redirect, apiBase }: GoogleSignInFlow) { + googleSignInFlow(_: Context, { redirect, apiBase }: GoogleSignInFlow) { localStorage.setItem('auth-redirect', redirect); location.href = `${apiBase}/auth/google`; } @@ -64,7 +129,7 @@ class UserState implements NgxsOnInit { })); const redirect = localStorage.getItem('auth-redirect'); localStorage.removeItem('auth-redirect'); - ctx.dispatch(new Navigate([redirect ?? '/'])); + ctx.dispatch([SignedIn, new Navigate([redirect ?? '/'])]); } catch (error) { ctx.setState(defaultState); console.error(error); @@ -77,7 +142,7 @@ class UserState implements NgxsOnInit { if (!token) return; const { exp } = jwtDecode(token); const now = Date.now(); - if (now > Number(exp)*1000) { + if (now > Number(exp) * 1000) { ctx.setState(defaultState); location.reload(); } @@ -93,3 +158,19 @@ const slices = createPropertySelectors(USER); export const isUserSignedIn = createSelector([USER], state => state?.signedIn); export const principal = slices.principal; export const accessToken = slices.token; +export const preferences = createSelector([slices.prefs], prefs => { + if (!prefs) { + return defaultDisplayPrefs; + } + + const { country, currency, language, theme } = prefs; + return { country, currency, language, theme }; +}); +export const preferredLanguage = createSelector([preferences], ({ language }) => { + return language; +}) +export const darkMode = createSelector([preferences], ({ theme }) => theme == 'dark') +export const paymentMethods = slices.paymentMethods; +export const pmMomo = createSelector([paymentMethods], (methods) => { + return methods.find(({ provider }) => provider == 'momo'); +}) diff --git a/src/app/util/phone-valiator.ts b/src/app/util/phone-valiator.ts new file mode 100644 index 0000000..cac44fc --- /dev/null +++ b/src/app/util/phone-valiator.ts @@ -0,0 +1,18 @@ +import { AbstractControl } from "@angular/forms"; +import { PhoneNumberUtil } from "google-libphonenumber"; + +export function phoneValidator(codeControlName = 'code', numberControlName = 'number') { + return (group: AbstractControl) => { + try { + const codeControl = group.get(codeControlName); + const numberControl = group.get(numberControlName); + if (codeControl?.value && !numberControl?.value) return null; + else if (!codeControl?.value || !numberControl?.value) return { phoneInvalid: true }; + const phoneUtil = PhoneNumberUtil.getInstance(); + const parsed = phoneUtil.parseAndKeepRawInput(numberControl.value, codeControl.value); + return phoneUtil.isValidNumberForRegion(parsed, codeControl.value) ? null : { phoneInvalid: true }; + } catch (e) { + return { phoneInvalid: true }; + } + } +} diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 3c0af98..7321e4e 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -1,3 +1,4 @@ export const environment = { - apiOrigin: 'http://localhost:8888' -}; + apiOrigin: 'http://localhost:8888', + telegramBot: 'https://t.me/TellThem672Devbot' +} as const; diff --git a/src/environments/environment.staging.ts b/src/environments/environment.staging.ts index 3807ef0..7ab57ee 100644 --- a/src/environments/environment.staging.ts +++ b/src/environments/environment.staging.ts @@ -1,3 +1,4 @@ export const environment = { - apiOrigin: 'https://next--tellthem.netlify.app' + apiOrigin: 'https://staging--tellthem.netlify.app', + telegramBot: 'https://t.me/TellThem672Stagingbot' }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 78a9e54..5a9c7b5 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,3 +1,4 @@ export const environment = { - apiOrigin: 'https://tellthem.netlify.app' + apiOrigin: 'https://tellthem.netlify.app', + telegramBot: 'https://t.me/Tellthem672bot' }; diff --git a/src/index.html b/src/index.html index 6682f1a..d279d13 100644 --- a/src/index.html +++ b/src/index.html @@ -6,8 +6,13 @@ + + + - + diff --git a/src/styles.scss b/src/styles.scss index 00fde8b..10407a7 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -13,3 +13,7 @@ html,body,app-root { @apply w-full h-full block; } + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} diff --git a/tsconfig.json b/tsconfig.json index d8185a3..f959a18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -43,6 +43,15 @@ ], "@logger/*": [ "logging/*" + ], + "@env/*": [ + "src/environments/*" + ], + "@zod-schemas/*": [ + "server/schemas/*" + ], + "@constants/*": [ + "server/constants/*" ] } },