feat: add CoopCredits Solana media rewards ecosystem

Add complete  token system with Plex/Tautulli/Overseer integration:
- Anchor program for SPL token mint/burn/transfer
- Express backend with OAuth, webhooks, Solana integration
- Next.js frontend with dashboard, admin panel, wallet management
- Docker deployment for 172.20.1.0/24 infrastructure
- Production configs with SSL, Nginx, health monitoring

Tautulli webhooks auto-mint  on watch events.
Overseer integration burns  for content requests.
This commit is contained in:
2026-04-14 11:09:50 -04:00
parent f106328f6a
commit e8d9b1fd42
69 changed files with 11382 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
'use client';
import { useEffect, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { authApi } from '@/lib/api';
import { useStore } from '@/lib/store';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Loader2, Tv } from 'lucide-react';
import { toast } from 'sonner';
export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const { setUser, setToken, isAuthenticated } = useStore();
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
// Check for Plex OAuth callback
const code = searchParams.get('code');
if (code) {
handlePlexCallback(code);
}
}, [searchParams]);
useEffect(() => {
if (isAuthenticated) {
router.push('/dashboard');
}
}, [isAuthenticated, router]);
const handlePlexCallback = async (code: string) => {
setIsLoading(true);
try {
const response = await authApi.plexCallback(code);
const { user, token } = response.data;
localStorage.setItem('token', token);
setUser(user);
setToken(token);
toast.success(`Welcome, ${user.plexUsername}!`);
router.push('/dashboard');
} catch (error) {
toast.error('Authentication failed');
console.error(error);
} finally {
setIsLoading(false);
}
};
const handlePlexLogin = async () => {
setIsLoading(true);
try {
const response = await authApi.getPlexUrl();
window.location.href = response.data.authUrl;
} catch (error) {
toast.error('Failed to initiate Plex login');
console.error(error);
setIsLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-4">
<Tv className="w-8 h-8 text-primary" />
</div>
<CardTitle className="text-2xl">CoopCredits</CardTitle>
<CardDescription>
Earn $COOP tokens for watching content on Plex
</CardDescription>
</CardHeader>
<CardContent>
<Button
onClick={handlePlexLogin}
disabled={isLoading}
className="w-full h-12 text-lg"
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-5 w-5 animate-spin" />
Connecting...
</>
) : (
<>
<Tv className="mr-2 h-5 w-5" />
Sign in with Plex
</>
)}
</Button>
<p className="mt-4 text-center text-sm text-muted-foreground">
Sign in with your Plex account to start earning rewards
</p>
</CardContent>
</Card>
</div>
);
}