diff --git a/drizzle/0002_peaceful_tag.sql b/drizzle/0002_peaceful_tag.sql new file mode 100644 index 0000000..8e29874 --- /dev/null +++ b/drizzle/0002_peaceful_tag.sql @@ -0,0 +1 @@ +ALTER TABLE "gifts" ADD COLUMN "recipient_phone" text; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..114fb91 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,1540 @@ +{ + "id": "7620db3a-751b-41a5-8588-f1f74960d630", + "prevId": "2a631ec6-e53c-4173-8dc0-aba2463f2297", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.bank_accounts": { + "name": "bank_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "swift_bic": { + "name": "swift_bic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_number": { + "name": "account_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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()" + } + }, + "indexes": { + "bank_accounts_user_id_idx": { + "name": "bank_accounts_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bank_accounts_user_id_users_id_fk": { + "name": "bank_accounts_user_id_users_id_fk", + "tableFrom": "bank_accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_verifications": { + "name": "email_verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "otp_hash": { + "name": "otp_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_used": { + "name": "is_used", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "ev_user_id_idx": { + "name": "ev_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ev_expires_at_idx": { + "name": "ev_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_verifications_user_id_users_id_fk": { + "name": "email_verifications_user_id_users_id_fk", + "tableFrom": "email_verifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gifts": { + "name": "gifts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sender_id": { + "name": "sender_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recipient_id": { + "name": "recipient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "fee": { + "name": "fee", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_amount": { + "name": "total_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "template": { + "name": "template", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "gift_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending_otp'" + }, + "otp_hash": { + "name": "otp_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "otp_expires_at": { + "name": "otp_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "otp_attempts": { + "name": "otp_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "transaction_id": { + "name": "transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blockchain_tx_hash": { + "name": "blockchain_tx_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_reference": { + "name": "payment_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_verified_at": { + "name": "payment_verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "hide_amount": { + "name": "hide_amount", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hide_sender": { + "name": "hide_sender", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_anonymous": { + "name": "is_anonymous", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "unlock_datetime": { + "name": "unlock_datetime", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "sender_name": { + "name": "sender_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sender_email": { + "name": "sender_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sender_avatar": { + "name": "sender_avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recipient_phone": { + "name": "recipient_phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_link": { + "name": "share_link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_link_token": { + "name": "share_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short_code": { + "name": "short_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cover_image_id": { + "name": "cover_image_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "link_expires_at": { + "name": "link_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "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()" + } + }, + "indexes": { + "gift_sender_id_idx": { + "name": "gift_sender_id_idx", + "columns": [ + { + "expression": "sender_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_recipient_id_idx": { + "name": "gift_recipient_id_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_status_idx": { + "name": "gift_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_sender_email_recipient_idx": { + "name": "gift_sender_email_recipient_idx", + "columns": [ + { + "expression": "sender_email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_share_link_token_idx": { + "name": "gift_share_link_token_idx", + "columns": [ + { + "expression": "share_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_slug_idx": { + "name": "gift_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_short_code_idx": { + "name": "gift_short_code_idx", + "columns": [ + { + "expression": "short_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "gift_blockchain_tx_hash_idx": { + "name": "gift_blockchain_tx_hash_idx", + "columns": [ + { + "expression": "blockchain_tx_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "gifts_sender_id_users_id_fk": { + "name": "gifts_sender_id_users_id_fk", + "tableFrom": "gifts", + "tableTo": "users", + "columnsFrom": [ + "sender_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "gifts_recipient_id_users_id_fk": { + "name": "gifts_recipient_id_users_id_fk", + "tableFrom": "gifts", + "tableTo": "users", + "columnsFrom": [ + "recipient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "gifts_transaction_id_unique": { + "name": "gifts_transaction_id_unique", + "nullsNotDistinct": false, + "columns": [ + "transaction_id" + ] + }, + "gifts_share_link_unique": { + "name": "gifts_share_link_unique", + "nullsNotDistinct": false, + "columns": [ + "share_link" + ] + }, + "gifts_share_link_token_unique": { + "name": "gifts_share_link_token_unique", + "nullsNotDistinct": false, + "columns": [ + "share_link_token" + ] + }, + "gifts_slug_unique": { + "name": "gifts_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + }, + "gifts_short_code_unique": { + "name": "gifts_short_code_unique", + "nullsNotDistinct": false, + "columns": [ + "short_code" + ] + }, + "gift_payment_reference_unique": { + "name": "gift_payment_reference_unique", + "nullsNotDistinct": false, + "columns": [ + "payment_reference" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "read": { + "name": "read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notif_user_id_idx": { + "name": "notif_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notif_created_at_idx": { + "name": "notif_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_resets": { + "name": "password_resets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "used_at": { + "name": "used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pr_user_id_idx": { + "name": "pr_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pr_expires_at_idx": { + "name": "pr_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_resets_user_id_users_id_fk": { + "name": "password_resets_user_id_users_id_fk", + "tableFrom": "password_resets", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "password_resets_token_unique": { + "name": "password_resets_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "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 + }, + "device_info": { + "name": "device_info", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "device_id": { + "name": "device_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "rt_user_id_idx": { + "name": "rt_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_id_users_id_fk": { + "name": "refresh_tokens_user_id_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "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.transactions": { + "name": "transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "wallet_id": { + "name": "wallet_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "transaction_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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()" + } + }, + "indexes": { + "tx_user_id_idx": { + "name": "tx_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tx_wallet_id_idx": { + "name": "tx_wallet_id_idx", + "columns": [ + { + "expression": "wallet_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tx_created_at_idx": { + "name": "tx_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "transactions_user_id_users_id_fk": { + "name": "transactions_user_id_users_id_fk", + "tableFrom": "transactions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "transactions_wallet_id_wallets_id_fk": { + "name": "transactions_wallet_id_wallets_id_fk", + "tableFrom": "transactions", + "tableTo": "wallets", + "columnsFrom": [ + "wallet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone_number": { + "name": "phone_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "status": { + "name": "status", + "type": "user_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "login_attempts": { + "name": "login_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "otp_failed_attempts": { + "name": "otp_failed_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "otp_attempts_window_start": { + "name": "otp_attempts_window_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "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()" + }, + "last_login": { + "name": "last_login", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_otp_sent_at": { + "name": "last_otp_sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_phone_verified": { + "name": "is_phone_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "phone_last_4": { + "name": "phone_last_4", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_phone_number_idx": { + "name": "users_phone_number_idx", + "columns": [ + { + "expression": "phone_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_status_idx": { + "name": "users_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_created_at_idx": { + "name": "users_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_phone_number_unique": { + "name": "users_phone_number_unique", + "nullsNotDistinct": false, + "columns": [ + "phone_number" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallets": { + "name": "wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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()" + } + }, + "indexes": { + "wallet_user_id_idx": { + "name": "wallet_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "wallets_user_id_users_id_fk": { + "name": "wallets_user_id_users_id_fk", + "tableFrom": "wallets", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "wallet_user_currency_key": { + "name": "wallet_user_currency_key", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "currency" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.WebhookRetryQueue": { + "name": "WebhookRetryQueue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_retries": { + "name": "max_retries", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.gift_status": { + "name": "gift_status", + "schema": "public", + "values": [ + "pending_otp", + "otp_verified", + "pending_review", + "confirmed", + "completed", + "sent", + "failed" + ] + }, + "public.transaction_status": { + "name": "transaction_status", + "schema": "public", + "values": [ + "pending", + "completed", + "failed" + ] + }, + "public.transaction_type": { + "name": "transaction_type", + "schema": "public", + "values": [ + "deposit", + "withdrawal", + "transfer" + ] + }, + "public.user_status": { + "name": "user_status", + "schema": "public", + "values": [ + "unverified", + "active", + "suspended" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 26df85b..1d5bddf 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1780262061192, "tag": "0001_dapper_darkhawk", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1780557193754, + "tag": "0002_peaceful_tag", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/api/gifts/route.ts b/src/app/api/gifts/route.ts index ddfc80b..57c330b 100644 --- a/src/app/api/gifts/route.ts +++ b/src/app/api/gifts/route.ts @@ -10,6 +10,7 @@ import { convertToUTCDate, formatAsUTCISO, CreateGiftSchema, + sanitizePhoneNumber, } from "@/lib/validation"; import { generateOTP, storeGiftOTP } from "@/server/services/otpService"; import { sendGiftConfirmationOTP } from "@/server/services/emailService"; @@ -76,6 +77,8 @@ export async function POST(request: NextRequest) { template, coverImageId, unlock_at, + senderAvatar, + recipientPhone, } = validationResult.data; @@ -108,6 +111,8 @@ export async function POST(request: NextRequest) { const sanitizedCoverImageId = coverImageId ? sanitizeInput(String(coverImageId)) : null; + const sanitizedSenderAvatar = senderAvatar ? sanitizeInput(senderAvatar) : null; + const sanitizedRecipientPhone = recipientPhone ? sanitizePhoneNumber(recipientPhone) : null; if (!validateMessage(sanitizedMessage)) { @@ -149,6 +154,8 @@ export async function POST(request: NextRequest) { message: sanitizedMessage, template: sanitizedTemplate, coverImageId: sanitizedCoverImageId, + senderAvatar: sanitizedSenderAvatar, + recipientPhone: sanitizedRecipientPhone, unlockDatetime: unlock_at ? convertToUTCDate(unlock_at) : null, status: "pending_otp" as "pending_otp", slug, diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index ac4e6f4..7e084e5 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -167,6 +167,7 @@ export const gifts = pgTable( senderName: text("sender_name"), senderEmail: text("sender_email"), senderAvatar: text("sender_avatar"), + recipientPhone: text("recipient_phone"), shareLink: text("share_link").unique(), shareLinkToken: text("share_link_token").unique(), slug: text("slug").unique(), diff --git a/src/lib/validation.ts b/src/lib/validation.ts index ea64edc..67a198a 100644 --- a/src/lib/validation.ts +++ b/src/lib/validation.ts @@ -186,4 +186,6 @@ export const CreateGiftSchema = z.object({ template: z.string().optional().nullable(), coverImageId: z.union([z.string(), z.number()]).optional().nullable(), unlock_at: z.union([z.string(), z.date()]).optional().nullable(), + senderAvatar: z.string().refine((val) => !val || val.startsWith('http'), { message: "Invalid image URL" }).optional().nullable(), + recipientPhone: z.string().refine((val) => !val || validateE164PhoneNumber(val), { message: "Invalid phone number format" }).optional().nullable(), });