From d9da36e5c7ccea920d4ac30b7086b9bb29f94cd1 Mon Sep 17 00:00:00 2001 From: Conrad Bekondo Date: Thu, 16 Jan 2025 10:06:26 +0100 Subject: [PATCH] feat: implement campaign redirect url --- .github/workflows/qodana_code_quality.yml | 5 +- db/migrations/0023_dusty_stephen_strange.sql | 1 + db/migrations/0024_blue_nick_fury.sql | 2 + db/migrations/0025_redundant_swarm.sql | 2 + db/migrations/meta/0023_snapshot.json | 1455 +++++++++++++++++ db/migrations/meta/0024_snapshot.json | 1455 +++++++++++++++++ db/migrations/meta/0025_snapshot.json | 1455 +++++++++++++++++ db/migrations/meta/_journal.json | 21 + db/schema/campaigns.ts | 24 +- db/schema/users.ts | 14 +- server/functions/auth/index.mts | 13 +- server/handlers/auth.mts | 39 +- server/handlers/campaign.mts | 28 +- server/helpers/handler.mts | 12 +- server/helpers/ip-extractor.ts | 5 + server/middleware/auth.mts | 13 +- server/schemas/campaigns.mts | 0 server/schemas/user.mts | 2 +- src/app/app.component.html | 1 + .../campaign-form.component.html | 40 +- .../campaign-form/campaign-form.component.ts | 48 +- .../interceptors/access-token.interceptor.ts | 23 +- .../pages/campaigns/campaigns.component.html | 6 +- .../pages/campaigns/campaigns.component.ts | 20 +- src/app/state/user/index.ts | 1 + 25 files changed, 4592 insertions(+), 93 deletions(-) create mode 100644 db/migrations/0023_dusty_stephen_strange.sql create mode 100644 db/migrations/0024_blue_nick_fury.sql create mode 100644 db/migrations/0025_redundant_swarm.sql create mode 100644 db/migrations/meta/0023_snapshot.json create mode 100644 db/migrations/meta/0024_snapshot.json create mode 100644 db/migrations/meta/0025_snapshot.json create mode 100644 server/helpers/ip-extractor.ts create mode 100644 server/schemas/campaigns.mts diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml index 9aa36c9..f49bfc6 100644 --- a/.github/workflows/qodana_code_quality.yml +++ b/.github/workflows/qodana_code_quality.yml @@ -5,7 +5,10 @@ on: push: branches: # Specify your branches here - master # The 'main' branch - - 'releases/*' # The release branches + - f/* + - b/* + - next + - 'r/*' # The release branches jobs: qodana: diff --git a/db/migrations/0023_dusty_stephen_strange.sql b/db/migrations/0023_dusty_stephen_strange.sql new file mode 100644 index 0000000..ff31599 --- /dev/null +++ b/db/migrations/0023_dusty_stephen_strange.sql @@ -0,0 +1 @@ +ALTER TABLE "campaigns" ADD COLUMN "redirect_url" varchar(500) NOT NULL; \ No newline at end of file diff --git a/db/migrations/0024_blue_nick_fury.sql b/db/migrations/0024_blue_nick_fury.sql new file mode 100644 index 0000000..c049714 --- /dev/null +++ b/db/migrations/0024_blue_nick_fury.sql @@ -0,0 +1,2 @@ +DROP VIEW "public"."vw_refresh_tokens";--> statement-breakpoint +CREATE VIEW "public"."vw_refresh_tokens" AS (select (now()::TIMESTAMP > ("created_at" + "window")::TIMESTAMP)::BOOLEAN OR "revoked_by" IS NOT NULL as "is_expired", ("created_at" + "window")::TIMESTAMP as "expires", "revoked_by", "replaced_by", "created_at", "access_token", "ip", "user", "token", "id" from "refresh_tokens"); \ No newline at end of file diff --git a/db/migrations/0025_redundant_swarm.sql b/db/migrations/0025_redundant_swarm.sql new file mode 100644 index 0000000..34ed761 --- /dev/null +++ b/db/migrations/0025_redundant_swarm.sql @@ -0,0 +1,2 @@ +DROP VIEW "public"."vw_access_tokens";--> statement-breakpoint +CREATE VIEW "public"."vw_access_tokens" AS (select "user", (now() > ("created_at" + "window")::TIMESTAMP)::BOOLEAN OR revoked_at IS NOT NULL OR replaced_by IS NOT NULL as "is_expired", (created_at + "window")::TIMESTAMP as "expires_at", "created_at", "ip", "id" from "access_tokens"); \ No newline at end of file diff --git a/db/migrations/meta/0023_snapshot.json b/db/migrations/meta/0023_snapshot.json new file mode 100644 index 0000000..31ae73e --- /dev/null +++ b/db/migrations/meta/0023_snapshot.json @@ -0,0 +1,1455 @@ +{ + "id": "42ebe4a9-270b-4752-8f9d-446f1e9a3976", + "prevId": "66e6117c-0980-456e-9261-f1f2d3b1701a", + "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 + }, + "redirect_url": { + "name": "redirect_url", + "type": "varchar(500)", + "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.access_tokens": { + "name": "access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "access_tokens_user_users_id_fk": { + "name": "access_tokens_user_users_id_fk", + "tableFrom": "access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "access_tokens_replaced_by_access_tokens_id_fk": { + "name": "access_tokens_replaced_by_access_tokens_id_fk", + "tableFrom": "access_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "replaced_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.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "refresh_tokens_token_user_index": { + "name": "refresh_tokens_token_user_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_users_id_fk": { + "name": "refresh_tokens_user_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "refresh_tokens_replaced_by_refresh_tokens_id_fk": { + "name": "refresh_tokens_replaced_by_refresh_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "refresh_tokens", + "columnsFrom": [ + "replaced_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_revoked_by_users_id_fk": { + "name": "refresh_tokens_revoked_by_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "revoked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_access_token_access_tokens_id_fk": { + "name": "refresh_tokens_access_token_access_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "access_token" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "refresh_tokens_token_unique": { + "name": "refresh_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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 + }, + "public.vw_access_tokens": { + "columns": { + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select \"user\", (now() > (created_at + \"window\")::TIMESTAMP)::BOOLEAN OR replaced_by IS NOT NULL as \"is_expired\", (created_at + \"window\")::TIMESTAMP as \"expires_at\", \"created_at\", \"ip\", \"id\" from \"access_tokens\"", + "name": "vw_access_tokens", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_refresh_tokens": { + "columns": { + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select (now()::TIMESTAMP > (created_at + \"window\")::TIMESTAMP)::BOOLEAN as \"is_expired\", (created_at + \"window\")::TIMESTAMP as \"expires\", \"revoked_by\", \"replaced_by\", \"created_at\", \"access_token\", \"ip\", \"user\", \"token\", \"id\" from \"refresh_tokens\"", + "name": "vw_refresh_tokens", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0024_snapshot.json b/db/migrations/meta/0024_snapshot.json new file mode 100644 index 0000000..5d795c5 --- /dev/null +++ b/db/migrations/meta/0024_snapshot.json @@ -0,0 +1,1455 @@ +{ + "id": "5302b668-2601-47cd-b689-92c5468d8d06", + "prevId": "42ebe4a9-270b-4752-8f9d-446f1e9a3976", + "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 + }, + "redirect_url": { + "name": "redirect_url", + "type": "varchar(500)", + "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.access_tokens": { + "name": "access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "access_tokens_user_users_id_fk": { + "name": "access_tokens_user_users_id_fk", + "tableFrom": "access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "access_tokens_replaced_by_access_tokens_id_fk": { + "name": "access_tokens_replaced_by_access_tokens_id_fk", + "tableFrom": "access_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "replaced_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.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "refresh_tokens_token_user_index": { + "name": "refresh_tokens_token_user_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_users_id_fk": { + "name": "refresh_tokens_user_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "refresh_tokens_replaced_by_refresh_tokens_id_fk": { + "name": "refresh_tokens_replaced_by_refresh_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "refresh_tokens", + "columnsFrom": [ + "replaced_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_revoked_by_users_id_fk": { + "name": "refresh_tokens_revoked_by_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "revoked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_access_token_access_tokens_id_fk": { + "name": "refresh_tokens_access_token_access_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "access_token" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "refresh_tokens_token_unique": { + "name": "refresh_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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 + }, + "public.vw_access_tokens": { + "columns": { + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select \"user\", (now() > (created_at + \"window\")::TIMESTAMP)::BOOLEAN OR replaced_by IS NOT NULL as \"is_expired\", (created_at + \"window\")::TIMESTAMP as \"expires_at\", \"created_at\", \"ip\", \"id\" from \"access_tokens\"", + "name": "vw_access_tokens", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_refresh_tokens": { + "columns": { + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select (now()::TIMESTAMP > (\"created_at\" + \"window\")::TIMESTAMP)::BOOLEAN OR \"revoked_by\" IS NOT NULL as \"is_expired\", (\"created_at\" + \"window\")::TIMESTAMP as \"expires\", \"revoked_by\", \"replaced_by\", \"created_at\", \"access_token\", \"ip\", \"user\", \"token\", \"id\" from \"refresh_tokens\"", + "name": "vw_refresh_tokens", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0025_snapshot.json b/db/migrations/meta/0025_snapshot.json new file mode 100644 index 0000000..af1c687 --- /dev/null +++ b/db/migrations/meta/0025_snapshot.json @@ -0,0 +1,1455 @@ +{ + "id": "6387c845-a763-4de3-a1b8-db6ad7fd1066", + "prevId": "5302b668-2601-47cd-b689-92c5468d8d06", + "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 + }, + "redirect_url": { + "name": "redirect_url", + "type": "varchar(500)", + "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.access_tokens": { + "name": "access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "access_tokens_user_users_id_fk": { + "name": "access_tokens_user_users_id_fk", + "tableFrom": "access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "access_tokens_replaced_by_access_tokens_id_fk": { + "name": "access_tokens_replaced_by_access_tokens_id_fk", + "tableFrom": "access_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "replaced_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.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "window": { + "name": "window", + "type": "interval", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "refresh_tokens_token_user_index": { + "name": "refresh_tokens_token_user_index", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_users_id_fk": { + "name": "refresh_tokens_user_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "refresh_tokens_replaced_by_refresh_tokens_id_fk": { + "name": "refresh_tokens_replaced_by_refresh_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "refresh_tokens", + "columnsFrom": [ + "replaced_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_revoked_by_users_id_fk": { + "name": "refresh_tokens_revoked_by_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "revoked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "refresh_tokens_access_token_access_tokens_id_fk": { + "name": "refresh_tokens_access_token_access_tokens_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "access_tokens", + "columnsFrom": [ + "access_token" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "refresh_tokens_token_unique": { + "name": "refresh_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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 + }, + "public.vw_access_tokens": { + "columns": { + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select \"user\", (now() > (\"created_at\" + \"window\")::TIMESTAMP)::BOOLEAN OR revoked_at IS NOT NULL OR replaced_by IS NOT NULL as \"is_expired\", (created_at + \"window\")::TIMESTAMP as \"expires_at\", \"created_at\", \"ip\", \"id\" from \"access_tokens\"", + "name": "vw_access_tokens", + "schema": "public", + "isExisting": false, + "materialized": false + }, + "public.vw_refresh_tokens": { + "columns": { + "revoked_by": { + "name": "revoked_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "replaced_by": { + "name": "replaced_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "access_token": { + "name": "access_token", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(39)", + "primaryKey": false, + "notNull": true + }, + "user": { + "name": "user", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "definition": "select (now()::TIMESTAMP > (\"created_at\" + \"window\")::TIMESTAMP)::BOOLEAN OR \"revoked_by\" IS NOT NULL as \"is_expired\", (\"created_at\" + \"window\")::TIMESTAMP as \"expires\", \"revoked_by\", \"replaced_by\", \"created_at\", \"access_token\", \"ip\", \"user\", \"token\", \"id\" from \"refresh_tokens\"", + "name": "vw_refresh_tokens", + "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 3100c94..d16e13f 100644 --- a/db/migrations/meta/_journal.json +++ b/db/migrations/meta/_journal.json @@ -162,6 +162,27 @@ "when": 1736694971000, "tag": "0022_famous_ozymandias", "breakpoints": true + }, + { + "idx": 23, + "version": "7", + "when": 1736890486682, + "tag": "0023_dusty_stephen_strange", + "breakpoints": true + }, + { + "idx": 24, + "version": "7", + "when": 1737017515199, + "tag": "0024_blue_nick_fury", + "breakpoints": true + }, + { + "idx": 25, + "version": "7", + "when": 1737017991125, + "tag": "0025_redundant_swarm", + "breakpoints": true } ] } \ No newline at end of file diff --git a/db/schema/campaigns.ts b/db/schema/campaigns.ts index 6592ae2..114beee 100644 --- a/db/schema/campaigns.ts +++ b/db/schema/campaigns.ts @@ -1,7 +1,8 @@ import { bigint, check, date, integer, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'; -import { sql } from 'drizzle-orm'; -import { createInsertSchema } from 'drizzle-zod'; +import { sql } from 'drizzle-orm'; +import { createInsertSchema } from 'drizzle-zod'; import { users } from './users'; +import { z } from 'zod'; export const campaigns = pgTable('campaigns', { id: bigint({ mode: 'number' }).generatedAlwaysAsIdentity().primaryKey(), @@ -14,20 +15,21 @@ 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) + createdBy: bigint({ mode: 'number' }).notNull().references(() => users.id), + redirectUrl: varchar({ length: 500 }).notNull() }); export const newCampaignSchema = createInsertSchema(campaigns); 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), - tokens: integer().notNull(), - publishAfter: date({ mode: 'string' }).defaultNow(), - publishBefore: date({ mode: 'string' }) - }, + id: bigint({ mode: 'number' }).generatedAlwaysAsIdentity().primaryKey(), + createdAt: timestamp({ mode: 'date' }).defaultNow(), + updatedAt: timestamp({ mode: 'date' }).defaultNow(), + campaign: bigint({ mode: 'number' }).notNull().references(() => campaigns.id), + tokens: integer().notNull(), + publishAfter: date({ mode: 'string' }).defaultNow(), + publishBefore: date({ mode: 'string' }) +}, table => [{ checkConstraint: check('tokens_check', sql`${table.tokens} > 0`) diff --git a/db/schema/users.ts b/db/schema/users.ts index 0bfcf82..d154a9a 100644 --- a/db/schema/users.ts +++ b/db/schema/users.ts @@ -1,4 +1,4 @@ -import { sql } from 'drizzle-orm'; +import { sql } from 'drizzle-orm'; import { AnyPgColumn, bigint, @@ -12,9 +12,9 @@ import { uniqueIndex, uuid, varchar -} from 'drizzle-orm/pg-core'; +} from 'drizzle-orm/pg-core'; import { createSelectSchema, createUpdateSchema } from 'drizzle-zod'; -import { z } from 'zod'; +import { z } from 'zod'; export const accessTokens = pgTable('access_tokens', { id: uuid().defaultRandom().primaryKey(), @@ -23,13 +23,13 @@ export const accessTokens = pgTable('access_tokens', { revoked_at: timestamp({ mode: 'date' }), window: interval().notNull(), user: bigint({ mode: 'number' }).notNull().references(() => users.id, { onDelete: 'cascade' }), - replacedBy: uuid().references((): AnyPgColumn => accessTokens.id) + replaced_by: uuid().references((): AnyPgColumn => accessTokens.id) }); export const vwAccessTokens = pgView('vw_access_tokens').as(qb => { return qb.select({ user: accessTokens.user, - is_expired: sql.raw(`(now() > (created_at + "window")::TIMESTAMP)::BOOLEAN OR replaced_by IS NOT NULL`).as('is_expired'), + is_expired: sql.raw(`(now() > ("${accessTokens.created_at.name}" + "${accessTokens.window.name}")::TIMESTAMP)::BOOLEAN OR ${accessTokens.revoked_at.name} IS NOT NULL OR ${accessTokens.replaced_by.name} IS NOT NULL`).as('is_expired'), expires_at: sql.raw(`(created_at + "window")::TIMESTAMP`).as('expires_at'), created_at: accessTokens.created_at, ip: accessTokens.ip, @@ -55,8 +55,8 @@ export const refreshTokens = pgTable('refresh_tokens', { export const vwRefreshTokens = pgView('vw_refresh_tokens').as(qb => { return qb.select({ - isExpired: sql.raw(`(now()::TIMESTAMP > (created_at + "${refreshTokens.window.name}")::TIMESTAMP)::BOOLEAN`).as('is_expired'), - expires: sql.raw(`(created_at + "${refreshTokens.window.name}")::TIMESTAMP`).as('expires'), + isExpired: sql.raw(`(now()::TIMESTAMP > ("${refreshTokens.created_at.name}" + "${refreshTokens.window.name}")::TIMESTAMP)::BOOLEAN OR "${refreshTokens.revoked_by.name}" IS NOT NULL`).as('is_expired'), + expires: sql.raw(`("${refreshTokens.created_at.name}" + "${refreshTokens.window.name}")::TIMESTAMP`).as('expires'), revoked_by: refreshTokens.revoked_by, replaced_by: refreshTokens.replaced_by, created_at: refreshTokens.created_at, diff --git a/server/functions/auth/index.mts b/server/functions/auth/index.mts index 41aef7d..f2449fa 100644 --- a/server/functions/auth/index.mts +++ b/server/functions/auth/index.mts @@ -1,7 +1,14 @@ -import { handleGoogleOauthCallback, handleGoogleSignIn, handleRevokeAccessToken, handleTokenRefresh, handleUserSignIn, removeUserAccount } from '@handlers/auth.mjs'; +import { + handleGoogleOauthCallback, + handleGoogleSignIn, + handleRevokeAccessToken, + handleTokenRefresh, + handleUserSignIn, + removeUserAccount +} from '@handlers/auth.mjs'; import { prepareHandler } from '@helpers/handler.mjs'; -import { jwtAuth } from '@middleware/auth.mjs'; -import { Router } from 'express'; +import { jwtAuth } from '@middleware/auth.mjs'; +import { Router } from 'express'; const router = Router(); diff --git a/server/handlers/auth.mts b/server/handlers/auth.mts index 4a0778d..cef63f0 100644 --- a/server/handlers/auth.mts +++ b/server/handlers/auth.mts @@ -1,21 +1,22 @@ import '@netlify/functions'; -import { extractUser } from '@helpers/auth.mjs'; -import { useUsersDb } from '@helpers/db.mjs'; -import { handleError } from '@helpers/error.mjs'; -import { AccessTokenClaims } from '@lib/models/user'; -import { useLogger } from '@logger/common'; -import * as users from '@schemas/users'; -import { UserSchema } from '@schemas/users'; -import { RefreshTokenValidationSchema } from '@zod-schemas/user.mjs'; -import { and, eq, isNull } from 'drizzle-orm'; -import { Request, Response } from 'express'; -import { sign } from 'jsonwebtoken'; -import { randomBytes } from 'node:crypto'; -import passport from 'passport'; -import { Strategy as GoogleStrategy, Profile, VerifyCallback } from 'passport-google-oauth20'; -import { doRemovePaymentMethods } from './payment.mjs'; -import { doRemoveAccountConnections } from './user.mjs'; -import { doCreateUserWallet, doDeleteUserWallet } from './wallet.mjs'; +import { extractUser } from '@helpers/auth.mjs'; +import { useUsersDb } from '@helpers/db.mjs'; +import { handleError } from '@helpers/error.mjs'; +import { AccessTokenClaims } from '@lib/models/user'; +import { useLogger } from '@logger/common'; +import * as users from '@schemas/users'; +import { UserSchema } from '@schemas/users'; +import { RefreshTokenValidationSchema } from '@zod-schemas/user.mjs'; +import { and, eq, isNull } from 'drizzle-orm'; +import { Request, Response } from 'express'; +import { sign } from 'jsonwebtoken'; +import { randomBytes } from 'node:crypto'; +import passport from 'passport'; +import { Profile, Strategy as GoogleStrategy, VerifyCallback } from 'passport-google-oauth20'; +import { doRemovePaymentMethods } from './payment.mjs'; +import { doRemoveAccountConnections } from './user.mjs'; +import { doCreateUserWallet, doDeleteUserWallet } from './wallet.mjs'; +import { extractIp } from '@helpers/ip-extractor'; const logger = useLogger({ service: 'auth' }); passport.use(new GoogleStrategy({ @@ -120,7 +121,7 @@ export function handleGoogleOauthCallback({ failureRedirect }: { failureRedirect export async function handleUserSignIn(req: Request, res: Response) { logger.info('signing in user'); logger.info('request headers', req.headers); - const ip = req.ip ?? String(req.header('client-ip')); + const ip = extractIp(req); try { const { success, data } = UserSchema.safeParse(req.user) @@ -180,7 +181,7 @@ export async function handleUserSignIn(req: Request, res: Response) { } export async function handleTokenRefresh(req: Request, res: Response) { - const ip = String(req.header('client-ip')); + const ip = extractIp(req); try { logger.info('refreshing tokens'); const { success, data, error } = RefreshTokenValidationSchema.safeParse(req.query); diff --git a/server/handlers/campaign.mts b/server/handlers/campaign.mts index f32f2f9..ba449c4 100644 --- a/server/handlers/campaign.mts +++ b/server/handlers/campaign.mts @@ -1,13 +1,15 @@ -import { extractUser } from '@helpers/auth.mjs'; -import { useCampaignsDb } from '@helpers/db.mjs'; -import { handleError } from '@helpers/error.mjs'; -import { LookupCampaignResponse } from '@lib/models/campaign'; -import { useLogger } from '@logger/common'; +import { extractUser } from '@helpers/auth.mjs'; +import { useCampaignsDb } from '@helpers/db.mjs'; +import { handleError } from '@helpers/error.mjs'; +import { LookupCampaignResponse } from '@lib/models/campaign'; +import { useLogger } from '@logger/common'; import { campaignPublications, campaigns, newCampaignSchema, newPublicationSchema } from '@schemas/campaigns'; -import { count, eq } from 'drizzle-orm'; -import express from 'express'; +import { count, eq } from 'drizzle-orm'; +import express from 'express'; +import { fromZodError } from 'zod-validation-error'; const logger = useLogger({ service: 'campaign' }); + export async function createCampaignPublication(req: express.Request, res: express.Response) { const db = useCampaignsDb(); const { campaign: campaignId } = req.params; @@ -45,12 +47,18 @@ export async function findCampaignPublications(req: express.Request, res: expres } export async function createCampaign(req: express.Request, res: express.Response) { - const dto = req.body; - const db = useCampaignsDb(); const user = extractUser(req); + const { success, data, error } = newCampaignSchema.safeParse({ ...req.body, createdBy: user.id}); + if (!success) { + const msg = fromZodError(error).message; + res.status(400).json({ message: msg }); + return; + + } + const db = useCampaignsDb(); try { logger.info('creating new campaign'); - await db.transaction(t => t.insert(campaigns).values(newCampaignSchema.parse({ ...dto, createdBy: user.id }))); + await db.transaction(t => t.insert(campaigns).values(data)); res.status(201).json({}); } catch (e) { handleError(e as Error, res); diff --git a/server/helpers/handler.mts b/server/helpers/handler.mts index 96c29f0..2fab5d8 100644 --- a/server/helpers/handler.mts +++ b/server/helpers/handler.mts @@ -1,10 +1,10 @@ -import { errorHandler } from '@middleware/error.mjs'; -import logger from '@middleware/logger.mjs'; -import cookieParser from 'cookie-parser'; +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 passport from 'passport'; -import serverless from 'serverless-http'; +import context from 'express-http-context'; +import passport from 'passport'; +import serverless from 'serverless-http'; export function prepareHandler(prefix: string, router: Router) { const app = express(); diff --git a/server/helpers/ip-extractor.ts b/server/helpers/ip-extractor.ts new file mode 100644 index 0000000..d0e3b94 --- /dev/null +++ b/server/helpers/ip-extractor.ts @@ -0,0 +1,5 @@ +import { Request } from 'express'; + +export function extractIp(req: Request) { + return String(req.header('x-nf-client-connection-ip') ?? req.header('client-ip') ?? req.header('x-forwarded-for')) +} diff --git a/server/middleware/auth.mts b/server/middleware/auth.mts index a2f5fbf..29290c9 100644 --- a/server/middleware/auth.mts +++ b/server/middleware/auth.mts @@ -2,13 +2,14 @@ import { Context } from '@netlify/functions'; import { vwAccessTokens } from '@schemas/users'; import { AccessTokenValidationSchema } from '@zod-schemas/user.mjs'; import express, { NextFunction } from 'express'; -import jwt from 'jsonwebtoken'; import passport from 'passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { useUsersDb } from '../helpers/db.mjs'; import { and, eq } from 'drizzle-orm'; +import { extractIp } from '@helpers/ip-extractor'; +import { useLogger } from '@logger/common'; -const { verify } = jwt; +const logger = useLogger({ service: 'auth-middleware' }); export function telegramWebhookAuth(req: express.Request, res: express.Response, next: NextFunction) { const authHeaderValue = req.header('x-telegram-bot-api-secret-token'); @@ -27,9 +28,10 @@ passport.use(new Strategy( passReqToCallback: true }, async (req: express.Request, payload, done) => { + logger.debug('validating authentication token') try { const { sub, tokenId } = payload; - const ip = String(req.header('client-ip')); + const ip = extractIp(req); const db = useUsersDb(); const user = await db.query.users.findFirst({ where: (users, { eq }) => eq(sub, users.id), @@ -56,19 +58,22 @@ passport.use(new Strategy( 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) => { + logger.debug('validating authentication token'); const headerValue = req.headers.get('authorization')?.split(' '); const unauthorizedResponse = new Response(JSON.stringify({ message: 'Unauthorized' }), { status: 401, headers: { 'content-type': 'application/json' } }); if (!headerValue) return unauthorizedResponse; + console.log(headerValue); const [scheme, token] = headerValue; if (scheme !== 'Bearer') return unauthorizedResponse; try { - const { success, data } = AccessTokenValidationSchema.safeParse(token); + const { success, data, error } = AccessTokenValidationSchema.safeParse(token); if (!success) { + console.log(error); return unauthorizedResponse; } diff --git a/server/schemas/campaigns.mts b/server/schemas/campaigns.mts new file mode 100644 index 0000000..e69de29 diff --git a/server/schemas/user.mts b/server/schemas/user.mts index c207bb2..238dc6a 100644 --- a/server/schemas/user.mts +++ b/server/schemas/user.mts @@ -16,7 +16,7 @@ export const RefreshTokenValidationSchema = z.object({ }); export const AccessTokenValidationSchema = z.string() - .refine(t => verify(t, String(process.env['JWT_SECRET']))) + .transform(t => verify(t, String(process.env['JWT_SECRET']))) .pipe( AccessTokenClaimsSchema ); diff --git a/src/app/app.component.html b/src/app/app.component.html index 602ea72..6ea170d 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -6,4 +6,5 @@
+
diff --git a/src/app/components/campaign-form/campaign-form.component.html b/src/app/components/campaign-form/campaign-form.component.html index 37ca17b..2d612cc 100644 --- a/src/app/components/campaign-form/campaign-form.component.html +++ b/src/app/components/campaign-form/campaign-form.component.html @@ -61,11 +61,28 @@ optionValue="id" optionLabel="title" /> +
+ +
+
+ + + + @if(basicControls.redirectUrl.dirty && basicControls.redirectUrl.invalid) { + + @if(basicControls.redirectUrl.hasError('required')) { + This field is required + }@else if (basicControls.redirectUrl.hasError('pattern')) { + Invalid URL + } + + } +
Next + (onClick)="onAdvanceButtonClicked(2, advanceTo, $event)">Next
@@ -116,7 +133,7 @@
-
+
- Back + Back - Next + Next
@@ -206,10 +224,11 @@
- +

Drag and drop a file here to upload.

@@ -262,7 +281,8 @@

Uploaded files

Back - Finish + Finish
diff --git a/src/app/components/campaign-form/campaign-form.component.ts b/src/app/components/campaign-form/campaign-form.component.ts index 7f7aa2a..605a1c0 100644 --- a/src/app/components/campaign-form/campaign-form.component.ts +++ b/src/app/components/campaign-form/campaign-form.component.ts @@ -1,4 +1,4 @@ -import { Component, computed, inject, input, model, output, signal } from '@angular/core'; +import { Component, computed, effect, inject, input, model, output, signal, viewChild } 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'; @@ -71,6 +71,7 @@ const maxUploadSize = 6291456; }) export class CampaignFormComponent { private http = inject(HttpClient); + private uploadComponent = viewChild(FileUpload); newCampaignFormStep = model(1); selectedFiles = signal([]); maxFileSize = computed(() => { @@ -91,13 +92,17 @@ export class CampaignFormComponent { readonly countries = input.required(); readonly categoriesLoading = input(false); readonly countriesLoading = input(false); + readonly submitting = signal(false); readonly error = output(); readonly onSubmit = output(); + readonly submissionPending = signal(false); + readonly uploading = signal(false); readonly newCampaignForm = new FormGroup({ basic: new FormGroup({ title: new FormControl('', [Validators.required, Validators.maxLength(255)]), description: new FormControl('', [Validators.required, Validators.maxLength(2000)]), - categories: new FormControl([], [Validators.required, Validators.minLength(1)]) + categories: new FormControl([], [Validators.required, Validators.minLength(1)]), + redirectUrl: new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.pattern(/^https?:\/\/[^\s/$.?#].[^\s]*$/)] }) }), contactsAndLinks: new FormGroup({ emails: new FormArray>([newEmailControl()]), @@ -109,6 +114,9 @@ export class CampaignFormComponent { }), media: new FormArray>([]) }); + get basicControls() { + return this.newCampaignForm.controls.basic.controls; + } onMediaFileSelected(event: FileSelectEvent) { this.selectedFiles.update(() => [...event.currentFiles]); @@ -118,7 +126,12 @@ export class CampaignFormComponent { this.selectedFiles.update(files => [...files.filter(f => f.name != event.file.name)]); } + onBeforeUpload() { + this.uploading.set(true); + } + onMediaFilesUploaded({ originalEvent, files }: FileUploadEvent) { + this.uploading.set(false); const { body } = originalEvent as HttpResponse; if (!body) return; @@ -172,11 +185,27 @@ export class CampaignFormComponent { event.preventDefault(); } + onAdvanceButtonClicked(step: number, callback: (n: number) => void, event: Event) { + event.preventDefault(); + callback(step); + } + onFinishButtonClicked() { - console.log(this.newCampaignForm.value); + this.submitting.set(true); + if (this.uploadComponent()?.hasFiles()) { + this.uploadComponent()?.uploader(); + this.submissionPending.set(true); + return + } + this.doSubmission(); + } + + private doSubmission() { + this.submissionPending.set(false); const { basic, media, contactsAndLinks } = this.newCampaignForm.value; const phoneUtil = PhoneNumberUtil.getInstance(); this.http.post('/api/campaigns', { + redirectUrl: String(basic?.redirectUrl), title: String(basic?.title), description: String(basic?.description), media: media ?? [], @@ -190,7 +219,10 @@ export class CampaignFormComponent { }), categories: basic?.categories ?? [] }).subscribe({ - error: (error: HttpErrorResponse) => this.error.emit(error), + error: (error: HttpErrorResponse) => { + this.error.emit(error); + this.submitting.set(false); + }, complete: () => { this.newCampaignForm.controls.contactsAndLinks.controls.links.clear(); this.addLinkControl(); @@ -205,6 +237,7 @@ export class CampaignFormComponent { this.newCampaignForm.reset(); this.newCampaignFormStep.set(1); this.onSubmit.emit(); + this.submitting.set(false); } }) } @@ -228,5 +261,12 @@ export class CampaignFormComponent { if (!values.every(v => v.length > 0)) return; this.addEmailControl(); }); + effect(() => { + const submissionPending = this.submissionPending(); + const uploading = this.uploading(); + if (submissionPending && !uploading) { + this.doSubmission(); + } + }) } } diff --git a/src/app/interceptors/access-token.interceptor.ts b/src/app/interceptors/access-token.interceptor.ts index e7c9716..1cfaa27 100644 --- a/src/app/interceptors/access-token.interceptor.ts +++ b/src/app/interceptors/access-token.interceptor.ts @@ -12,17 +12,18 @@ export const accessTokenInterceptor: HttpInterceptorFn = (req, next) => { if (req.url.startsWith('/api') && token) { return next(req.clone({ setHeaders: { 'Authorization': `Bearer ${token}` } })).pipe( catchError((e: HttpErrorResponse) => { - if (e.status != 401 && req.url == '/api/auth/revoke-token') return throwError(() => e); - return store.dispatch(RefreshAccessToken).pipe( - concatMap(() => next(req.clone({ setHeaders: { authorization: `Bearer ${store.selectSnapshot(accessToken)}` } }))), - catchError((e: HttpErrorResponse) => { - if (e.status == 403) { - store.dispatch(new Navigate(['/auth/login'], undefined, { queryParamsHandling: 'preserve' })) - return EMPTY; - } - return throwError(() => e); - }) - ); + if (e.status == 401 && req.url != '/api/auth/revoke-token') + return store.dispatch(RefreshAccessToken).pipe( + concatMap(() => next(req.clone({ setHeaders: { authorization: `Bearer ${store.selectSnapshot(accessToken)}` } }))), + catchError((e: HttpErrorResponse) => { + if (e.status == 403) { + store.dispatch(new Navigate(['/auth/login'], undefined, { queryParamsHandling: 'preserve' })) + return EMPTY; + } + return throwError(() => e); + }) + ); + return throwError(() => e); }) ); } diff --git a/src/app/pages/campaigns/campaigns.component.html b/src/app/pages/campaigns/campaigns.component.html index ecf441c..989171f 100644 --- a/src/app/pages/campaigns/campaigns.component.html +++ b/src/app/pages/campaigns/campaigns.component.html @@ -23,7 +23,7 @@

Campaigns

Title Categories - Created + Last Updated @@ -33,7 +33,7 @@

Campaigns

{{ campaign.title }}

{{ campaign.categories.length }} categories - {{ campaign.createdAt | date }} + {{ campaign.updatedAt | date }}
@@ -62,7 +62,7 @@

Publications

New Campaign

- diff --git a/src/app/pages/campaigns/campaigns.component.ts b/src/app/pages/campaigns/campaigns.component.ts index 4ef50ff..b39dc16 100644 --- a/src/app/pages/campaigns/campaigns.component.ts +++ b/src/app/pages/campaigns/campaigns.component.ts @@ -7,11 +7,13 @@ import { InputIcon } import { Drawer } from 'primeng/drawer'; import { ReactiveFormsModule } from '@angular/forms'; import { DatePipe } from '@angular/common'; -import { MenuItem, MessageService } from 'primeng/api'; +import { MenuItem, MessageService, ToastMessageOptions } from 'primeng/api'; import { rxResource } from '@angular/core/rxjs-interop'; -import { CountryData } from '@lib/models/country-data'; +import { + CountryData +} from '@lib/models/country-data'; import { Category } from '@lib/models/category'; import { HttpClient } from '@angular/common/http'; import { Campaign, LookupCampaignResponse } from '@lib/models/campaign'; @@ -27,7 +29,7 @@ import { import { PublicationFormComponent } from '@app/components/publication-form/publication-form.component'; -import { Ripple } from 'primeng/ripple'; +import { Ripple } from 'primeng/ripple'; @Component({ selector: 'tm-campaigns', @@ -102,6 +104,18 @@ export class CampaignsComponent { this.showCampaignModal.set(false); } + onCampaignFormErrored(error: Error) { + const message: ToastMessageOptions = { + summary: 'Error', + detail: error.message, + severity: 'error', + }; + if (this.showCampaignModal()) { + message.key = 'under-modal'; + } + this.messageService.add(message); + } + constructor() { effect(() => { const fetchError = this.categories.error(); diff --git a/src/app/state/user/index.ts b/src/app/state/user/index.ts index 898df7e..f8c2246 100644 --- a/src/app/state/user/index.ts +++ b/src/app/state/user/index.ts @@ -71,6 +71,7 @@ export class UserState implements NgxsOnInit { @Action(SignedOut) refreshOnSignedOut(_: Context, { redirect }: SignedOut) { this.location.go(redirect ?? '/'); + window.location.reload(); } @Action(RefreshAccessToken)