From 37d76b243b90ce21b5bece699c817046051f8a05 Mon Sep 17 00:00:00 2001
From: Face <69168154+face-hh@users.noreply.github.com>
Date: Mon, 26 May 2025 13:05:47 +0300
Subject: [PATCH] feat: implement daily rewards system & streak
---
website/drizzle/0005_acoustic_santa_claus.sql | 2 +
website/drizzle/0006_happy_katie_power.sql | 1 +
website/drizzle/meta/0005_snapshot.json | 906 +++++++++++++++++
website/drizzle/meta/0006_snapshot.json | 913 ++++++++++++++++++
website/drizzle/meta/_journal.json | 14 +
.../src/lib/components/self/AppSidebar.svelte | 16 +-
.../lib/components/self/DailyRewards.svelte | 166 ++++
.../lib/components/self/ProfileBadges.svelte | 7 +-
website/src/lib/server/db/schema.ts | 7 +
website/src/lib/types/user-profile.ts | 1 +
website/src/routes/+error.svelte | 47 +-
.../routes/api/coin/[coinSymbol]/+server.ts | 3 +-
.../src/routes/api/rewards/claim/+server.ts | 155 +++
.../src/routes/api/user/[userId]/+server.ts | 1 +
.../src/routes/coin/[coinSymbol]/+page.svelte | 11 +-
15 files changed, 2229 insertions(+), 21 deletions(-)
create mode 100644 website/drizzle/0005_acoustic_santa_claus.sql
create mode 100644 website/drizzle/0006_happy_katie_power.sql
create mode 100644 website/drizzle/meta/0005_snapshot.json
create mode 100644 website/drizzle/meta/0006_snapshot.json
create mode 100644 website/src/lib/components/self/DailyRewards.svelte
create mode 100644 website/src/routes/api/rewards/claim/+server.ts
diff --git a/website/drizzle/0005_acoustic_santa_claus.sql b/website/drizzle/0005_acoustic_santa_claus.sql
new file mode 100644
index 0000000..d95171b
--- /dev/null
+++ b/website/drizzle/0005_acoustic_santa_claus.sql
@@ -0,0 +1,2 @@
+ALTER TABLE "user" ADD COLUMN "last_reward_claim" timestamp with time zone;--> statement-breakpoint
+ALTER TABLE "user" ADD COLUMN "total_rewards_claimed" numeric(20, 8) DEFAULT '0.00000000' NOT NULL;
\ No newline at end of file
diff --git a/website/drizzle/0006_happy_katie_power.sql b/website/drizzle/0006_happy_katie_power.sql
new file mode 100644
index 0000000..169c69c
--- /dev/null
+++ b/website/drizzle/0006_happy_katie_power.sql
@@ -0,0 +1 @@
+ALTER TABLE "user" ADD COLUMN "login_streak" integer DEFAULT 0 NOT NULL;
\ No newline at end of file
diff --git a/website/drizzle/meta/0005_snapshot.json b/website/drizzle/meta/0005_snapshot.json
new file mode 100644
index 0000000..b56d6cd
--- /dev/null
+++ b/website/drizzle/meta/0005_snapshot.json
@@ -0,0 +1,906 @@
+{
+ "id": "a69a9696-6223-476a-ae09-79fb06baf0c0",
+ "prevId": "dc6eb2ec-7c88-48f3-96b3-7485786fffd2",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "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": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.coin": {
+ "name": "coin",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "symbol": {
+ "name": "symbol",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initial_supply": {
+ "name": "initial_supply",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "circulating_supply": {
+ "name": "circulating_supply",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "current_price": {
+ "name": "current_price",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "market_cap": {
+ "name": "market_cap",
+ "type": "numeric(30, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "volume_24h": {
+ "name": "volume_24h",
+ "type": "numeric(30, 2)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0.00'"
+ },
+ "change_24h": {
+ "name": "change_24h",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0.0000'"
+ },
+ "pool_coin_amount": {
+ "name": "pool_coin_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ },
+ "pool_base_currency_amount": {
+ "name": "pool_base_currency_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ },
+ "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()"
+ },
+ "is_listed": {
+ "name": "is_listed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "coin_creator_id_user_id_fk": {
+ "name": "coin_creator_id_user_id_fk",
+ "tableFrom": "coin",
+ "tableTo": "user",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "coin_symbol_unique": {
+ "name": "coin_symbol_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "symbol"
+ ]
+ }
+ }
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "likes_count": {
+ "name": "likes_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "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()"
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "comment_user_id_idx": {
+ "name": "comment_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_coin_id_idx": {
+ "name": "comment_coin_id_idx",
+ "columns": [
+ {
+ "expression": "coin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_user_id_user_id_fk": {
+ "name": "comment_user_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_coin_id_coin_id_fk": {
+ "name": "comment_coin_id_coin_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.comment_like": {
+ "name": "comment_like",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "comment_like_user_id_user_id_fk": {
+ "name": "comment_like_user_id_user_id_fk",
+ "tableFrom": "comment_like",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_like_comment_id_comment_id_fk": {
+ "name": "comment_like_comment_id_comment_id_fk",
+ "tableFrom": "comment_like",
+ "tableTo": "comment",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "comment_like_user_id_comment_id_pk": {
+ "name": "comment_like_user_id_comment_id_pk",
+ "columns": [
+ "user_id",
+ "comment_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.price_history": {
+ "name": "price_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price": {
+ "name": "price",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "price_history_coin_id_coin_id_fk": {
+ "name": "price_history_coin_id_coin_id_fk",
+ "tableFrom": "price_history",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "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()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ }
+ },
+ "public.transaction": {
+ "name": "transaction",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "transaction_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price_per_coin": {
+ "name": "price_per_coin",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "total_base_currency_amount": {
+ "name": "total_base_currency_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "transaction_user_id_user_id_fk": {
+ "name": "transaction_user_id_user_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transaction_coin_id_coin_id_fk": {
+ "name": "transaction_coin_id_coin_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "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()"
+ },
+ "is_admin": {
+ "name": "is_admin",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "is_banned": {
+ "name": "is_banned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "ban_reason": {
+ "name": "ban_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "base_currency_balance": {
+ "name": "base_currency_balance",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'10000.00000000'"
+ },
+ "bio": {
+ "name": "bio",
+ "type": "varchar(160)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'Hello am 48 year old man from somalia. Sorry for my bed england. I selled my wife for internet connection for play “conter stirk”'"
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(30)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_reward_claim": {
+ "name": "last_reward_claim",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_rewards_claimed": {
+ "name": "total_rewards_claimed",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ },
+ "user_username_unique": {
+ "name": "user_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ }
+ },
+ "public.user_portfolio": {
+ "name": "user_portfolio",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_portfolio_user_id_user_id_fk": {
+ "name": "user_portfolio_user_id_user_id_fk",
+ "tableFrom": "user_portfolio",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_portfolio_coin_id_coin_id_fk": {
+ "name": "user_portfolio_coin_id_coin_id_fk",
+ "tableFrom": "user_portfolio",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_portfolio_user_id_coin_id_pk": {
+ "name": "user_portfolio_user_id_coin_id_pk",
+ "columns": [
+ "user_id",
+ "coin_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "public.transaction_type": {
+ "name": "transaction_type",
+ "schema": "public",
+ "values": [
+ "BUY",
+ "SELL"
+ ]
+ }
+ },
+ "schemas": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/website/drizzle/meta/0006_snapshot.json b/website/drizzle/meta/0006_snapshot.json
new file mode 100644
index 0000000..4f7cd6d
--- /dev/null
+++ b/website/drizzle/meta/0006_snapshot.json
@@ -0,0 +1,913 @@
+{
+ "id": "1de8a094-65a4-41ad-ad06-038f801c9fc6",
+ "prevId": "a69a9696-6223-476a-ae09-79fb06baf0c0",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "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": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.coin": {
+ "name": "coin",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "symbol": {
+ "name": "symbol",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initial_supply": {
+ "name": "initial_supply",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "circulating_supply": {
+ "name": "circulating_supply",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "current_price": {
+ "name": "current_price",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "market_cap": {
+ "name": "market_cap",
+ "type": "numeric(30, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "volume_24h": {
+ "name": "volume_24h",
+ "type": "numeric(30, 2)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0.00'"
+ },
+ "change_24h": {
+ "name": "change_24h",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0.0000'"
+ },
+ "pool_coin_amount": {
+ "name": "pool_coin_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ },
+ "pool_base_currency_amount": {
+ "name": "pool_base_currency_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ },
+ "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()"
+ },
+ "is_listed": {
+ "name": "is_listed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "coin_creator_id_user_id_fk": {
+ "name": "coin_creator_id_user_id_fk",
+ "tableFrom": "coin",
+ "tableTo": "user",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "coin_symbol_unique": {
+ "name": "coin_symbol_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "symbol"
+ ]
+ }
+ }
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "likes_count": {
+ "name": "likes_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "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()"
+ },
+ "is_deleted": {
+ "name": "is_deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "comment_user_id_idx": {
+ "name": "comment_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_coin_id_idx": {
+ "name": "comment_coin_id_idx",
+ "columns": [
+ {
+ "expression": "coin_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_user_id_user_id_fk": {
+ "name": "comment_user_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_coin_id_coin_id_fk": {
+ "name": "comment_coin_id_coin_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.comment_like": {
+ "name": "comment_like",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment_id": {
+ "name": "comment_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "comment_like_user_id_user_id_fk": {
+ "name": "comment_like_user_id_user_id_fk",
+ "tableFrom": "comment_like",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_like_comment_id_comment_id_fk": {
+ "name": "comment_like_comment_id_comment_id_fk",
+ "tableFrom": "comment_like",
+ "tableTo": "comment",
+ "columnsFrom": [
+ "comment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "comment_like_user_id_comment_id_pk": {
+ "name": "comment_like_user_id_comment_id_pk",
+ "columns": [
+ "user_id",
+ "comment_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.price_history": {
+ "name": "price_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price": {
+ "name": "price",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "price_history_coin_id_coin_id_fk": {
+ "name": "price_history_coin_id_coin_id_fk",
+ "tableFrom": "price_history",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "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()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ }
+ },
+ "public.transaction": {
+ "name": "transaction",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "transaction_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price_per_coin": {
+ "name": "price_per_coin",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "total_base_currency_amount": {
+ "name": "total_base_currency_amount",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "transaction_user_id_user_id_fk": {
+ "name": "transaction_user_id_user_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transaction_coin_id_coin_id_fk": {
+ "name": "transaction_coin_id_coin_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "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()"
+ },
+ "is_admin": {
+ "name": "is_admin",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "is_banned": {
+ "name": "is_banned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "ban_reason": {
+ "name": "ban_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "base_currency_balance": {
+ "name": "base_currency_balance",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'10000.00000000'"
+ },
+ "bio": {
+ "name": "bio",
+ "type": "varchar(160)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'Hello am 48 year old man from somalia. Sorry for my bed england. I selled my wife for internet connection for play “conter stirk”'"
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(30)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_reward_claim": {
+ "name": "last_reward_claim",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_rewards_claimed": {
+ "name": "total_rewards_claimed",
+ "type": "numeric(20, 8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00000000'"
+ },
+ "login_streak": {
+ "name": "login_streak",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ },
+ "user_username_unique": {
+ "name": "user_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ }
+ },
+ "public.user_portfolio": {
+ "name": "user_portfolio",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coin_id": {
+ "name": "coin_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(30, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_portfolio_user_id_user_id_fk": {
+ "name": "user_portfolio_user_id_user_id_fk",
+ "tableFrom": "user_portfolio",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_portfolio_coin_id_coin_id_fk": {
+ "name": "user_portfolio_coin_id_coin_id_fk",
+ "tableFrom": "user_portfolio",
+ "tableTo": "coin",
+ "columnsFrom": [
+ "coin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_portfolio_user_id_coin_id_pk": {
+ "name": "user_portfolio_user_id_coin_id_pk",
+ "columns": [
+ "user_id",
+ "coin_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "public.transaction_type": {
+ "name": "transaction_type",
+ "schema": "public",
+ "values": [
+ "BUY",
+ "SELL"
+ ]
+ }
+ },
+ "schemas": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/website/drizzle/meta/_journal.json b/website/drizzle/meta/_journal.json
index 8898937..865ec9b 100644
--- a/website/drizzle/meta/_journal.json
+++ b/website/drizzle/meta/_journal.json
@@ -36,6 +36,20 @@
"when": 1748103716114,
"tag": "0004_cloudy_viper",
"breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "7",
+ "when": 1748189217541,
+ "tag": "0005_acoustic_santa_claus",
+ "breakpoints": true
+ },
+ {
+ "idx": 6,
+ "version": "7",
+ "when": 1748189449547,
+ "tag": "0006_happy_katie_power",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/website/src/lib/components/self/AppSidebar.svelte b/website/src/lib/components/self/AppSidebar.svelte
index a028a2a..c4edc75 100644
--- a/website/src/lib/components/self/AppSidebar.svelte
+++ b/website/src/lib/components/self/AppSidebar.svelte
@@ -25,8 +25,8 @@
import { USER_DATA } from '$lib/stores/user-data';
import { PORTFOLIO_DATA, fetchPortfolioData } from '$lib/stores/portfolio-data';
import { useSidebar } from '$lib/components/ui/sidebar/index.js';
-
import SignInConfirmDialog from './SignInConfirmDialog.svelte';
+ import DailyRewards from './DailyRewards.svelte';
import { signOut } from '$lib/auth-client';
import { getPublicUrl } from '$lib/utils';
import { goto } from '$app/navigation';
@@ -142,8 +142,18 @@
-
-
+
+
+
+ {#if $USER_DATA}
+
+
diff --git a/website/src/routes/api/coin/[coinSymbol]/+server.ts b/website/src/routes/api/coin/[coinSymbol]/+server.ts
index 7810510..2eb28c2 100644
--- a/website/src/routes/api/coin/[coinSymbol]/+server.ts
+++ b/website/src/routes/api/coin/[coinSymbol]/+server.ts
@@ -132,7 +132,8 @@ export async function GET({ params, url }) {
creatorId: coin.creatorId,
creatorName: user.name,
creatorUsername: user.username,
- creatorBio: user.bio
+ creatorBio: user.bio,
+ creatorImage: user.image
})
.from(coin)
.leftJoin(user, eq(coin.creatorId, user.id))
diff --git a/website/src/routes/api/rewards/claim/+server.ts b/website/src/routes/api/rewards/claim/+server.ts
new file mode 100644
index 0000000..bbdc11e
--- /dev/null
+++ b/website/src/routes/api/rewards/claim/+server.ts
@@ -0,0 +1,155 @@
+import { auth } from '$lib/auth';
+import { error, json } from '@sveltejs/kit';
+import { db } from '$lib/server/db';
+import { user } from '$lib/server/db/schema';
+import { eq } from 'drizzle-orm';
+import type { RequestHandler } from './$types';
+
+const TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
+const THIRTY_SIX_HOURS_MS = 36 * 60 * 60 * 1000;
+
+const REWARD_TIERS = [
+ 1200, // Day 1
+ 1500, // Day 2
+ 1800, // Day 3
+ 2100, // Day 4
+ 2500, // Day 5
+ 3000, // Day 6
+ 3500, // Day 7
+ 4000, // Day 8+
+];
+
+function calculateStreak(lastClaim: Date | null, currentStreak: number): number {
+ if (!lastClaim) return 1;
+
+ const timeSinceLastClaim = Date.now() - lastClaim.getTime();
+
+ // reset streak if more than 36 hours
+ if (timeSinceLastClaim > THIRTY_SIX_HOURS_MS) return 1;
+
+ // only increment if within valid window (12-36 hours)
+ if (timeSinceLastClaim >= TWELVE_HOURS_MS) return currentStreak + 1;
+
+ return currentStreak;
+}
+
+function calculateReward(streak: number): { total: number; base: number } {
+ const tierIndex = Math.min(streak - 1, REWARD_TIERS.length - 1);
+ const base = REWARD_TIERS[tierIndex];
+
+ return { total: base, base };
+}
+
+export const POST: RequestHandler = async ({ request }) => {
+ const session = await auth.api.getSession({ headers: request.headers });
+ if (!session?.user) throw error(401, 'Not authenticated');
+
+ const userId = Number(session.user.id);
+ const now = new Date();
+
+ return await db.transaction(async (tx) => {
+ const userData = await tx.select({
+ id: user.id,
+ baseCurrencyBalance: user.baseCurrencyBalance,
+ lastRewardClaim: user.lastRewardClaim,
+ totalRewardsClaimed: user.totalRewardsClaimed,
+ loginStreak: user.loginStreak
+ })
+ .from(user)
+ .where(eq(user.id, userId))
+ .limit(1);
+
+ if (!userData[0]) {
+ throw error(404, 'User not found');
+ }
+
+ const currentUser = userData[0];
+
+ if (currentUser.lastRewardClaim) {
+ const timeSinceLastClaim = now.getTime() - currentUser.lastRewardClaim.getTime();
+ if (timeSinceLastClaim < TWELVE_HOURS_MS) {
+ return json({
+ error: 'Daily reward not yet available',
+ canClaim: false,
+ timeRemaining: TWELVE_HOURS_MS - timeSinceLastClaim
+ }, { status: 429 });
+ }
+ }
+
+ const newStreak = calculateStreak(currentUser.lastRewardClaim, currentUser.loginStreak || 0);
+ const reward = calculateReward(newStreak);
+
+ const currentBalance = parseFloat(currentUser.baseCurrencyBalance || '0');
+ const currentTotalRewards = parseFloat(currentUser.totalRewardsClaimed || '0');
+ const newBalance = currentBalance + reward.total;
+ const newTotalRewards = currentTotalRewards + reward.total;
+
+ await tx.update(user)
+ .set({
+ baseCurrencyBalance: newBalance.toFixed(8),
+ lastRewardClaim: now,
+ totalRewardsClaimed: newTotalRewards.toFixed(8),
+ loginStreak: newStreak
+ })
+ .where(eq(user.id, currentUser.id));
+
+ return json({
+ success: true,
+ rewardAmount: reward.total,
+ baseReward: reward.base,
+ newBalance,
+ totalRewardsClaimed: newTotalRewards,
+ loginStreak: newStreak,
+ nextClaimTime: new Date(now.getTime() + TWELVE_HOURS_MS)
+ });
+ });
+};
+
+export const GET: RequestHandler = async ({ request }) => {
+ const session = await auth.api.getSession({ headers: request.headers });
+ if (!session?.user) throw error(401, 'Not authenticated');
+
+ const [currentUser] = await db.select({
+ id: user.id,
+ baseCurrencyBalance: user.baseCurrencyBalance,
+ lastRewardClaim: user.lastRewardClaim,
+ totalRewardsClaimed: user.totalRewardsClaimed,
+ loginStreak: user.loginStreak
+ })
+ .from(user)
+ .where(eq(user.id, Number(session.user.id)))
+ .limit(1);
+
+ if (!currentUser) {
+ throw error(404, 'User not found');
+ }
+
+ const now = new Date();
+
+ let canClaim = true;
+ let timeRemaining = 0;
+ let nextClaimTime = null;
+
+ if (currentUser.lastRewardClaim) {
+ const timeSinceLastClaim = now.getTime() - currentUser.lastRewardClaim.getTime();
+ if (timeSinceLastClaim < TWELVE_HOURS_MS) {
+ canClaim = false;
+ timeRemaining = TWELVE_HOURS_MS - timeSinceLastClaim;
+ nextClaimTime = new Date(currentUser.lastRewardClaim.getTime() + TWELVE_HOURS_MS);
+ }
+ }
+
+ const potentialStreak = calculateStreak(currentUser.lastRewardClaim, currentUser.loginStreak || 0);
+ const reward = calculateReward(potentialStreak);
+
+ return json({
+ canClaim,
+ rewardAmount: reward.total,
+ baseReward: reward.base,
+ timeRemaining,
+ nextClaimTime,
+ totalRewardsClaimed: Number(currentUser.totalRewardsClaimed || 0),
+ lastRewardClaim: currentUser.lastRewardClaim,
+ loginStreak: currentUser.loginStreak || 0
+ });
+};
\ No newline at end of file
diff --git a/website/src/routes/api/user/[userId]/+server.ts b/website/src/routes/api/user/[userId]/+server.ts
index 5260b81..d7c212e 100644
--- a/website/src/routes/api/user/[userId]/+server.ts
+++ b/website/src/routes/api/user/[userId]/+server.ts
@@ -24,6 +24,7 @@ export async function GET({ params }) {
createdAt: true,
baseCurrencyBalance: true,
isAdmin: true,
+ loginStreak: true,
}
});
diff --git a/website/src/routes/coin/[coinSymbol]/+page.svelte b/website/src/routes/coin/[coinSymbol]/+page.svelte
index 6b1feea..939aa40 100644
--- a/website/src/routes/coin/[coinSymbol]/+page.svelte
+++ b/website/src/routes/coin/[coinSymbol]/+page.svelte
@@ -7,18 +7,10 @@
import TradeModal from '$lib/components/self/TradeModal.svelte';
import CommentSection from '$lib/components/self/CommentSection.svelte';
import UserProfilePreview from '$lib/components/self/UserProfilePreview.svelte';
- import {
- TrendingUp,
- TrendingDown,
- DollarSign,
- Coins,
- ChartColumn,
- CalendarDays
- } from 'lucide-svelte';
+ import { TrendingUp, TrendingDown, DollarSign, Coins, ChartColumn } from 'lucide-svelte';
import {
createChart,
ColorType,
- type Time,
type IChartApi,
CandlestickSeries,
HistogramSeries
@@ -61,7 +53,6 @@
coin = result.coin;
chartData = result.candlestickData || [];
volumeData = result.volumeData || [];
-
} catch (e) {
console.error('Failed to fetch coin data:', e);
toast.error('Failed to load coin data');