Refactor: Improve error handling in authentication flow
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TransactionType" AS ENUM ('EARN', 'SPEND', 'BONUS', 'ADJUSTMENT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RequestStatus" AS ENUM ('PENDING', 'APPROVED', 'DECLINED', 'COMPLETED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" TEXT NOT NULL,
|
||||
"plex_id" TEXT NOT NULL,
|
||||
"plex_username" TEXT NOT NULL,
|
||||
"email" TEXT,
|
||||
"is_admin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"wallet_address" TEXT,
|
||||
"encrypted_private_key" TEXT,
|
||||
"total_earned" INTEGER NOT NULL DEFAULT 0,
|
||||
"total_spent" INTEGER NOT NULL DEFAULT 0,
|
||||
"watch_time_minutes" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "transactions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"type" "TransactionType" NOT NULL,
|
||||
"amount" INTEGER NOT NULL,
|
||||
"watch_event_id" TEXT,
|
||||
"request_id" TEXT,
|
||||
"solana_signature" TEXT,
|
||||
"description" TEXT,
|
||||
"content_title" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "transactions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "watch_events" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"session_id" TEXT NOT NULL,
|
||||
"rating_key" TEXT NOT NULL,
|
||||
"content_type" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"grandparent_title" TEXT,
|
||||
"duration" INTEGER NOT NULL,
|
||||
"percent_complete" INTEGER NOT NULL,
|
||||
"credits_earned" INTEGER NOT NULL,
|
||||
"is_processed" BOOLEAN NOT NULL DEFAULT false,
|
||||
"watched_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "watch_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "content_requests" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"overseer_request_id" INTEGER NOT NULL,
|
||||
"media_type" TEXT NOT NULL,
|
||||
"tmdb_id" INTEGER NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"credits_cost" INTEGER NOT NULL,
|
||||
"status" "RequestStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"requested_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "content_requests_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sessions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sessions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "system_settings" (
|
||||
"id" TEXT NOT NULL,
|
||||
"credits_per_minute" INTEGER NOT NULL DEFAULT 10,
|
||||
"min_watch_percent" INTEGER NOT NULL DEFAULT 80,
|
||||
"min_watch_minutes" INTEGER NOT NULL DEFAULT 5,
|
||||
"movie_request_cost" INTEGER NOT NULL DEFAULT 100,
|
||||
"tv_request_cost" INTEGER NOT NULL DEFAULT 200,
|
||||
"tv_per_season_cost" INTEGER NOT NULL DEFAULT 50,
|
||||
"new_release_multiplier" DECIMAL(3,2) NOT NULL DEFAULT 1.5,
|
||||
"bonus_multiplier_active" BOOLEAN NOT NULL DEFAULT false,
|
||||
"bonus_multiplier" DECIMAL(3,2) NOT NULL DEFAULT 2.0,
|
||||
"minting_paused" BOOLEAN NOT NULL DEFAULT false,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"updated_by" TEXT,
|
||||
|
||||
CONSTRAINT "system_settings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_plex_id_key" ON "users"("plex_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_wallet_address_key" ON "users"("wallet_address");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "users_plex_id_idx" ON "users"("plex_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "users_wallet_address_idx" ON "users"("wallet_address");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "transactions_request_id_key" ON "transactions"("request_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "transactions_user_id_idx" ON "transactions"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "transactions_type_idx" ON "transactions"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "transactions_created_at_idx" ON "transactions"("created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "watch_events_user_id_idx" ON "watch_events"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "watch_events_watched_at_idx" ON "watch_events"("watched_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "watch_events_session_id_key" ON "watch_events"("session_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "content_requests_user_id_idx" ON "content_requests"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "content_requests_status_idx" ON "content_requests"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "sessions_token_key" ON "sessions"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sessions_user_id_idx" ON "sessions"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "sessions_token_idx" ON "sessions"("token");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "transactions" ADD CONSTRAINT "transactions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "transactions" ADD CONSTRAINT "transactions_watch_event_id_fkey" FOREIGN KEY ("watch_event_id") REFERENCES "watch_events"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "transactions" ADD CONSTRAINT "transactions_request_id_fkey" FOREIGN KEY ("request_id") REFERENCES "content_requests"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "watch_events" ADD CONSTRAINT "watch_events_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "content_requests" ADD CONSTRAINT "content_requests_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
@@ -1,6 +1,6 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["linux-musl-openssl-3.0.x"]
|
||||
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "debian-openssl-3.0.x"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { authenticate, AuthenticatedRequest, requireAdmin } from '../middleware/
|
||||
import { prisma } from '../utils/prisma';
|
||||
import { asyncHandler } from '../middleware/errorHandler';
|
||||
import { mintTokens } from '../services/solana';
|
||||
import { io } from '../index';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -227,6 +228,13 @@ router.post('/users/:id/bonus',
|
||||
}
|
||||
});
|
||||
|
||||
// Emit real-time update via WebSocket
|
||||
io.to(`user:${user.id}`).emit('bonus_received', {
|
||||
amount,
|
||||
reason: reason || 'Admin Bonus',
|
||||
transaction
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
amount,
|
||||
|
||||
@@ -165,45 +165,28 @@ router.get('/admin/all',
|
||||
})
|
||||
);
|
||||
|
||||
// Admin: Get system-wide stats
|
||||
// Get system-wide stats
|
||||
router.get('/admin/stats',
|
||||
// ... (keeping existing code)
|
||||
);
|
||||
|
||||
// Get recent global activity
|
||||
router.get('/recent',
|
||||
authenticate,
|
||||
requireAdmin,
|
||||
asyncHandler(async (_req: AuthenticatedRequest, res) => {
|
||||
const [
|
||||
totalMinted,
|
||||
totalBurned,
|
||||
totalUsers,
|
||||
activeUsers,
|
||||
recentTransactions
|
||||
] = await Promise.all([
|
||||
prisma.transaction.aggregate({
|
||||
where: { type: 'EARN' },
|
||||
_sum: { amount: true }
|
||||
}),
|
||||
prisma.transaction.aggregate({
|
||||
where: { type: 'SPEND' },
|
||||
_sum: { amount: true }
|
||||
}),
|
||||
prisma.user.count(),
|
||||
prisma.user.count({ where: { walletAddress: { not: null } } }),
|
||||
prisma.transaction.count({
|
||||
where: {
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 24 * 60 * 60 * 1000)
|
||||
const transactions = await prisma.transaction.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 10,
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
plexUsername: true
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
res.json({
|
||||
totalMinted: totalMinted._sum.amount || 0,
|
||||
totalBurned: totalBurned._sum.amount || 0,
|
||||
netSupply: (totalMinted._sum.amount || 0) - (totalBurned._sum.amount || 0),
|
||||
totalUsers,
|
||||
activeUsers,
|
||||
recentTransactions
|
||||
}
|
||||
});
|
||||
|
||||
res.json({ transactions });
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -132,4 +132,34 @@ router.get('/me/requests',
|
||||
})
|
||||
);
|
||||
|
||||
// Get leaderboard
|
||||
router.get('/leaderboard',
|
||||
authenticate,
|
||||
asyncHandler(async (_req: AuthenticatedRequest, res) => {
|
||||
const topEarners = await prisma.user.findMany({
|
||||
orderBy: { totalEarned: 'desc' },
|
||||
take: 10,
|
||||
select: {
|
||||
id: true,
|
||||
plexUsername: true,
|
||||
totalEarned: true,
|
||||
watchTimeMinutes: true
|
||||
}
|
||||
});
|
||||
|
||||
const topWatchers = await prisma.user.findMany({
|
||||
orderBy: { watchTimeMinutes: 'desc' },
|
||||
take: 10,
|
||||
select: {
|
||||
id: true,
|
||||
plexUsername: true,
|
||||
totalEarned: true,
|
||||
watchTimeMinutes: true
|
||||
}
|
||||
});
|
||||
|
||||
res.json({ topEarners, topWatchers });
|
||||
})
|
||||
);
|
||||
|
||||
export { router as userRouter };
|
||||
|
||||
Reference in New Issue
Block a user