diff --git a/package.json b/package.json
index ffe6e6b..7d75976 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "@supabase/supabase-js": "2.110.9",
"react": "19.1.1",
"react-dom": "19.1.1"
},
diff --git a/src/App.jsx b/src/App.jsx
index 3992cd6..4595eb8 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,92 +1,438 @@
-import { useEffect, useRef } from 'react'
+import { useEffect, useRef, useState } from 'react'
+import { supabase } from './supabase.js'
+
+const ADMIN_EMAIL = 'dev@cod3uchiha.com'
+const API_URL = 'https://api.cod3uchiha.com'
+
+function useHashRoute() {
+ const read = () => window.location.hash.replace(/^#\/?/, '') || 'home'
+ const [route, setRoute] = useState(read)
+
+ useEffect(() => {
+ const onHashChange = () => setRoute(read())
+ window.addEventListener('hashchange', onHashChange)
+ return () => window.removeEventListener('hashchange', onHashChange)
+ }, [])
+
+ return route
+}
+
+function AuthModal({ open, onClose }) {
+ const [mode, setMode] = useState('signin')
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const [loading, setLoading] = useState(false)
+ const [message, setMessage] = useState('')
+ const [error, setError] = useState('')
+
+ useEffect(() => {
+ if (!open) {
+ setPassword('')
+ setMessage('')
+ setError('')
+ }
+ }, [open])
+
+ if (!open) return null
+
+ async function submit(event) {
+ event.preventDefault()
+ setLoading(true)
+ setMessage('')
+ setError('')
+
+ try {
+ if (mode === 'signup') {
+ const { data, error: signUpError } = await supabase.auth.signUp({ email, password })
+ if (signUpError) throw signUpError
+ if (data.session) {
+ onClose()
+ window.location.hash = 'dashboard'
+ } else {
+ setMessage('Account created. Check your email to confirm it, then sign in.')
+ }
+ } else {
+ const { error: signInError } = await supabase.auth.signInWithPassword({ email, password })
+ if (signInError) throw signInError
+ onClose()
+ window.location.hash = 'dashboard'
+ }
+ } catch (authError) {
+ setError(authError.message || 'Authentication failed.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
+
+
+ Cod3Uchiha account
+
{mode === 'signin' ? 'Sign in' : 'Create account'}
+
+
×
+
+
+
+
+ setMode(mode === 'signin' ? 'signup' : 'signin')}>
+ {mode === 'signin' ? 'Need an account? Sign up' : 'Already have an account? Sign in'}
+
+
+
+ )
+}
function Story() {
return (
The Uchiha Clan and Coding
-
- The Uchiha Clan is renowned for their exceptional abilities and Sharingan eye technique. In the world of coding, we strive to develop our own unique skills and techniques, just like the Uchiha Clan.
-
-
- Programming and developing, like the Sharingan, allow us to perceive and understand the intricacies of technology. With each line of code we write, we unlock new possibilities and shape the digital world around us.
-
-
- As the Uchiha Clan sought to protect and advance their abilities, we, as developers, aim to use our coding skills to create innovative solutions, build amazing projects, and contribute to the ever-evolving field of technology.
-
+ The Uchiha Clan is renowned for their exceptional abilities and Sharingan eye technique. In the world of coding, we strive to develop our own unique skills and techniques, just like the Uchiha Clan.
+ Programming and developing, like the Sharingan, allow us to perceive and understand the intricacies of technology. With each line of code we write, we unlock new possibilities and shape the digital world around us.
+ As the Uchiha Clan sought to protect and advance their abilities, we, as developers, aim to use our coding skills to create innovative solutions, build amazing projects, and contribute to the ever-evolving field of technology.
)
}
function AudioControls() {
const audioRef = useRef(null)
+ return (
+
+
+
audioRef.current?.play()}>Play
+
{
+ if (!audioRef.current) return
+ audioRef.current.pause()
+ audioRef.current.currentTime = 0
+ }}>Stop
+
+ )
+}
+
+function Home({ session, openAuth }) {
+ return (
+
+
+
+
Developer · APIs · Open source
+
Code Uchiha
+
Build with the Cod3Uchiha API. Accounts include 15 successful API requests every day across the entire endpoint catalog.
+
+
Browsing the site and endpoint catalog stays public. Login is only needed for account features and API usage.
+
+
+
+
+
+
+
+ 15 successful requests / day
+ All endpoints share one quota
+ 0 failed requests counted
+
- const play = () => {
- audioRef.current?.play()
+
+
+
+
+
+ )
+}
+
+function Dashboard({ session, openAuth }) {
+ const [data, setData] = useState(null)
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState('')
+ const [keyName, setKeyName] = useState('Default')
+ const [newKey, setNewKey] = useState('')
+ const [working, setWorking] = useState(false)
+
+ async function loadDashboard() {
+ if (!session) return
+ setLoading(true)
+ setError('')
+ try {
+ const today = new Date().toISOString().slice(0, 10)
+ const [profileResult, planResult, usageResult, keysResult, endpointResult] = await Promise.all([
+ supabase.from('profiles').select('display_name, plan_id, is_banned, total_requests, created_at').single(),
+ supabase.from('plans').select('id, name, requests_per_day, requests_per_minute, max_api_keys').eq('id', 'free').single(),
+ supabase.from('usage_daily').select('request_count, last_request_at').eq('usage_date', today).maybeSingle(),
+ supabase.from('api_keys').select('id, name, key_prefix, created_at, last_used_at, revoked_at, total_requests').order('created_at', { ascending: false }),
+ supabase.from('usage_endpoint_daily').select('endpoint, request_count').eq('usage_date', today).order('request_count', { ascending: false }).limit(12)
+ ])
+
+ const firstError = [profileResult, planResult, usageResult, keysResult, endpointResult].find((result) => result.error)?.error
+ if (firstError) throw firstError
+
+ setData({
+ profile: profileResult.data,
+ plan: planResult.data,
+ usage: usageResult.data,
+ keys: keysResult.data || [],
+ endpoints: endpointResult.data || []
+ })
+ } catch (loadError) {
+ setError(loadError.message || 'Unable to load dashboard.')
+ } finally {
+ setLoading(false)
+ }
}
- const stop = () => {
- const audio = audioRef.current
- if (!audio) return
+ useEffect(() => {
+ void loadDashboard()
+ }, [session?.user?.id])
+
+ async function createKey() {
+ setWorking(true)
+ setNewKey('')
+ setError('')
+ try {
+ const { data: created, error: createError } = await supabase.rpc('create_api_key', { p_name: keyName || 'Default' })
+ if (createError) throw createError
+ setNewKey(created?.api_key || '')
+ await loadDashboard()
+ } catch (createError) {
+ setError(createError.message || 'Unable to create API key.')
+ } finally {
+ setWorking(false)
+ }
+ }
- audio.pause()
- audio.currentTime = 0
+ async function revokeKey(id) {
+ setWorking(true)
+ try {
+ const { error: revokeError } = await supabase.rpc('revoke_api_key', { p_key_id: id })
+ if (revokeError) throw revokeError
+ await loadDashboard()
+ } catch (revokeError) {
+ setError(revokeError.message || 'Unable to revoke API key.')
+ } finally {
+ setWorking(false)
+ }
}
+ if (!session) {
+ return (
+
+ Dashboard
+ Sign in to view your API quota and manage API keys.
+ Sign in
+
+ )
+ }
+
+ if (loading) return
+ if (!data) return {error || 'Dashboard unavailable.'}
+
+ const used = data.usage?.request_count || 0
+ const limit = data.plan?.requests_per_day || 15
+ const remaining = Math.max(0, limit - used)
+ const activeKeys = data.keys.filter((key) => !key.revoked_at)
+
return (
-
-
-
Play
-
Stop
+
+
+
+ {error &&
{error}
}
+
+
+ Today {used} / {limit} successful requests
+ Remaining {remaining} resets daily
+ All time {data.profile?.total_requests || 0} successful requests
+ API keys {activeKeys.length} / {data.plan?.max_api_keys || 3} active keys
+
+
+
+
+
Credentials
API keys
+
+ setKeyName(event.target.value)} maxLength={40} aria-label="API key name" />
+ Create key
+
+
+
+ {newKey && (
+
+ Copy this key now. It is only shown once.
+ {newKey}
+ navigator.clipboard?.writeText(newKey)}>Copy
+
+ )}
+
+
+ {data.keys.length === 0 &&
No API keys yet.
}
+ {data.keys.map((key) => (
+
+
{key.name} {key.key_prefix}••••••••
+
{key.revoked_at ? 'Revoked' : `${key.total_requests || 0} requests`}
+ {!key.revoked_at &&
revokeKey(key.id)}>Revoke }
+
+ ))}
+
+
+
+
+
+
+ {data.endpoints.length === 0 &&
No successful requests yet today.
}
+ {data.endpoints.map((item) => (
+
{item.endpoint}{item.request_count}
+ ))}
+
+
)
}
-function ContactDetails() {
+function AdminDashboard({ session }) {
+ const [data, setData] = useState(null)
+ const [error, setError] = useState('')
+ const isAdmin = session?.user?.email?.toLowerCase() === ADMIN_EMAIL
+
+ useEffect(() => {
+ if (!isAdmin) return
+ supabase.rpc('get_admin_dashboard').then(({ data: adminData, error: adminError }) => {
+ if (adminError) setError(adminError.message)
+ else setData(adminData)
+ })
+ }, [isAdmin])
+
+ if (!session) return
Admin Sign in with the administrator account.
+ if (!isAdmin) return
403 This dashboard is restricted to {ADMIN_EMAIL}.
+ if (error) return
+ if (!data) return
+
return (
-
+
+
Restricted Admin dashboard {ADMIN_EMAIL}
+
+ Users {data.summary?.users || 0}
+ Requests today {data.summary?.requests_today || 0}
+ Total requests {data.summary?.requests_total || 0}
+ Active keys {data.summary?.active_keys || 0}
+
+
+
+
+
+
+ Email Plan Today Total Status
+
+ {(data.users || []).map((user) => (
+
+ {user.email} {user.plan} {user.requests_today} {user.requests_total} {user.banned ? 'Banned' : 'Active'}
+
+ ))}
+
+
+
+
+
+
+ Traffic
Top endpoints today
+
+ {(data.endpoints || []).map((item) =>
{item.endpoint}{item.requests}
)}
+
+
+
)
}
function App() {
+ const route = useHashRoute()
+ const [session, setSession] = useState(null)
+ const [authReady, setAuthReady] = useState(false)
+ const [authOpen, setAuthOpen] = useState(false)
+
useEffect(() => {
- const preventContextMenu = (event) => event.preventDefault()
- const preventCopyShortcuts = (event) => {
- if (event.ctrlKey && ['c', 'v', 'u'].includes(event.key.toLowerCase())) {
- event.preventDefault()
- }
- }
+ supabase.auth.getSession().then(({ data }) => {
+ setSession(data.session || null)
+ setAuthReady(true)
+ })
+ const { data: listener } = supabase.auth.onAuthStateChange((_event, nextSession) => {
+ setSession(nextSession)
+ setAuthReady(true)
+ })
+ return () => listener.subscription.unsubscribe()
+ }, [])
+ useEffect(() => {
+ const preventContextMenu = (event) => event.preventDefault()
document.addEventListener('contextmenu', preventContextMenu)
- document.addEventListener('keydown', preventCopyShortcuts)
-
- return () => {
- document.removeEventListener('contextmenu', preventContextMenu)
- document.removeEventListener('keydown', preventCopyShortcuts)
- }
+ return () => document.removeEventListener('contextmenu', preventContextMenu)
}, [])
+ const isAdmin = session?.user?.email?.toLowerCase() === ADMIN_EMAIL
+
+ async function signOut() {
+ await supabase.auth.signOut()
+ window.location.hash = 'home'
+ }
+
return (
- Code Uchiha
+
-
-
-
-
-
-
-
+ {route === 'dashboard' ? setAuthOpen(true)} />
+ : route === 'admin' ?
+ : setAuthOpen(true)} />}
+
+ setAuthOpen(false)} />
)
}
diff --git a/src/styles.css b/src/styles.css
index dd29b8f..6b90bdb 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1,77 +1,160 @@
-* {
- box-sizing: border-box;
-}
-
-html,
-body,
-#root {
- min-height: 100%;
-}
+* { box-sizing: border-box; }
-body {
- margin: 0;
- background-color: #000;
- color: #ff0000;
- text-align: center;
+:root {
font-family: "Courier New", monospace;
+ color: #f5f5f5;
+ background: #050505;
+ font-synthesis: none;
}
-.site-shell {
- padding: 8px;
-}
+html, body, #root { min-height: 100%; }
+body { margin: 0; background: #050505; color: #f5f5f5; }
+a { color: inherit; }
+button, input { font: inherit; }
+button { cursor: pointer; }
-h1 {
- color: #f00;
- font-size: 36px;
-}
+.site-shell { min-height: 100vh; }
+.page { width: min(1120px, calc(100% - 32px)); margin: 0 auto; }
-.image-container {
- margin-top: 20px;
+.topbar {
+ position: sticky;
+ top: 0;
+ z-index: 20;
+ min-height: 68px;
+ display: grid;
+ grid-template-columns: 1fr auto 1fr;
+ align-items: center;
+ gap: 20px;
+ padding: 0 28px;
+ border-bottom: 1px solid #252525;
+ background: rgba(5, 5, 5, 0.94);
+ backdrop-filter: blur(18px);
}
-.image-container img {
- width: min(300px, 80vw);
- border-radius: 50%;
-}
+.brand { color: #f00; text-decoration: none; font-weight: 800; letter-spacing: .12em; justify-self: start; }
+.topbar nav { display: flex; align-items: center; gap: 22px; }
+.topbar nav a { text-decoration: none; color: #c8c8c8; font-size: 14px; }
+.topbar nav a:hover { color: #fff; }
+.auth-actions { justify-self: end; }
-.story {
- margin: 20px;
- padding: 20px;
- background-color: rgba(0, 0, 0, 0.8);
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+.primary-button, .secondary-button, .danger-button, .hacker-button {
+ border: 1px solid #f00;
+ border-radius: 8px;
+ padding: 10px 16px;
+ transition: .15s ease;
}
+.primary-button { background: #f00; color: #fff; }
+.primary-button:hover { background: #d80000; }
+.secondary-button, .hacker-button { background: transparent; color: #fff; }
+.secondary-button:hover, .hacker-button:hover { background: #171717; }
+.danger-button { background: transparent; color: #ff7a7a; border-color: #742929; }
+.danger-button:hover { background: #291111; }
+.compact { padding: 8px 12px; font-size: 13px; }
+.button-link { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
+.text-button, .icon-button { border: 0; background: transparent; color: #ff6b6b; }
+.icon-button { font-size: 28px; line-height: 1; }
-.audio-controls {
- margin-top: 20px;
+.hero {
+ min-height: 620px;
+ display: grid;
+ grid-template-columns: 1.25fr .75fr;
+ align-items: center;
+ gap: 70px;
+ text-align: left;
+ padding: 78px 0 52px;
}
+.hero h1, .page-heading h1, .empty-state h1 { margin: 8px 0 14px; color: #f00; font-size: clamp(40px, 7vw, 78px); line-height: .98; }
+.hero-copy { max-width: 720px; color: #c6c6c6; font-size: clamp(17px, 2vw, 21px); line-height: 1.65; }
+.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
+.public-note { margin-top: 18px; color: #777; font-size: 13px; line-height: 1.55; }
+.eyebrow { color: #ff5c5c; text-transform: uppercase; letter-spacing: .15em; font-size: 12px; }
+.image-container { text-align: center; }
+.image-container img { width: min(320px, 76vw); border-radius: 50%; border: 1px solid #3a1111; box-shadow: 0 0 80px rgba(255, 0, 0, .12); }
-.hacker-button {
- background-color: #000;
- color: #f00;
- border: 2px solid #f00;
- padding: 10px 20px;
- font: inherit;
- font-size: 16px;
- cursor: pointer;
- margin: 0 5px;
-}
+.quota-strip { display: grid; grid-template-columns: repeat(3, 1fr); border: 1px solid #232323; border-radius: 14px; overflow: hidden; margin-bottom: 42px; }
+.quota-strip > div { padding: 24px; display: flex; flex-direction: column; gap: 6px; border-right: 1px solid #232323; }
+.quota-strip > div:last-child { border-right: 0; }
+.quota-strip strong { color: #f00; font-size: 28px; }
+.quota-strip span { color: #969696; font-size: 13px; }
-.hacker-button:hover,
-.hacker-button:focus-visible {
- background-color: #f00;
- color: #fff;
-}
+.story, .dashboard-card { margin: 24px 0; padding: 28px; background: #0b0b0b; border: 1px solid #222; border-radius: 14px; }
+.story { text-align: left; line-height: 1.75; color: #bbb; }
+.story h2 { color: #fff; }
+.audio-controls { text-align: center; margin: 28px 0; }
+.contact-details { text-align: center; color: #bbb; padding: 18px 0; }
+.contact-details a { color: #ff4d4d; }
+.copyright { text-align: center; color: #666; padding: 34px 18px 46px; font-size: 12px; }
-.contact-details {
- margin-top: 20px;
- color: #fff;
-}
+.dashboard-page { padding-top: 62px; padding-bottom: 24px; }
+.page-heading { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin-bottom: 28px; text-align: left; }
+.page-heading h1 { font-size: clamp(34px, 5vw, 58px); }
+.page-heading p { margin: 0; color: #888; }
+
+.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
+.stat-card { min-height: 132px; display: flex; flex-direction: column; justify-content: center; padding: 22px; border: 1px solid #242424; background: #0b0b0b; border-radius: 12px; text-align: left; }
+.stat-card span, .stat-card small { color: #838383; }
+.stat-card strong { margin: 8px 0; color: #fff; font-size: 27px; }
+
+.card-heading { display: flex; align-items: end; justify-content: space-between; gap: 18px; margin-bottom: 22px; text-align: left; }
+.card-heading h2 { margin: 5px 0 0; color: #fff; }
+.key-create-row { display: flex; gap: 8px; }
+.key-create-row input, .auth-form input { min-height: 42px; padding: 0 12px; border: 1px solid #303030; border-radius: 8px; background: #050505; color: #fff; outline: none; }
+.key-create-row input:focus, .auth-form input:focus { border-color: #f00; }
+
+.secret-box { display: grid; gap: 10px; padding: 16px; margin-bottom: 18px; border: 1px solid #5a2727; border-radius: 10px; background: #160808; text-align: left; }
+.secret-box code { overflow-wrap: anywhere; color: #ff8a8a; }
+.key-list, .usage-list { display: grid; gap: 8px; }
+.key-row, .usage-row { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 14px; min-height: 58px; padding: 11px 14px; border: 1px solid #1f1f1f; border-radius: 9px; text-align: left; }
+.key-row > div { display: grid; gap: 5px; }
+.key-row code, .usage-row code { color: #ff6c6c; overflow-wrap: anywhere; }
+.key-row span { color: #888; font-size: 12px; }
+.usage-row { grid-template-columns: 1fr auto; }
+.usage-row strong { color: #fff; }
+.muted { color: #777; }
+
+.table-scroll { overflow-x: auto; }
+table { width: 100%; border-collapse: collapse; text-align: left; }
+th, td { padding: 13px 12px; border-bottom: 1px solid #222; white-space: nowrap; }
+th { color: #777; font-size: 12px; text-transform: uppercase; letter-spacing: .08em; }
+td { color: #c5c5c5; }
+
+.empty-state { width: min(680px, calc(100% - 32px)); margin: 0 auto; padding: 110px 0; text-align: center; }
+.empty-state h1 { font-size: 52px; }
+.empty-state p { color: #999; line-height: 1.6; }
+
+.modal-shell { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center; padding: 18px; }
+.modal-backdrop { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: rgba(0, 0, 0, .78); backdrop-filter: blur(7px); }
+.auth-card { position: relative; z-index: 1; width: min(440px, 100%); padding: 26px; border: 1px solid #2d2d2d; border-radius: 14px; background: #0b0b0b; box-shadow: 0 30px 100px rgba(0,0,0,.6); text-align: left; }
+.auth-card-head { display: flex; justify-content: space-between; gap: 20px; }
+.auth-card h2 { color: #fff; margin: 5px 0 18px; font-size: 30px; }
+.auth-form { display: grid; gap: 14px; }
+.auth-form label { display: grid; gap: 7px; color: #aaa; font-size: 13px; }
+.form-error, .form-success, .panel-message { padding: 11px 13px; border-radius: 8px; font-size: 13px; }
+.form-error { color: #ffb0b0; background: #260b0b; border: 1px solid #5d2020; }
+.form-success { color: #b7ffbf; background: #0b2110; border: 1px solid #224a29; }
+.panel-message { margin-bottom: 16px; }
+.auth-card .text-button { display: block; margin: 18px auto 0; }
-.contact-details a {
- color: #f00;
+@media (max-width: 860px) {
+ .topbar { grid-template-columns: 1fr auto; padding: 0 16px; }
+ .topbar nav { order: 3; grid-column: 1 / -1; justify-content: center; padding-bottom: 12px; gap: 14px; flex-wrap: wrap; }
+ .hero { grid-template-columns: 1fr; gap: 30px; padding-top: 54px; text-align: center; }
+ .hero-copy { margin-left: auto; margin-right: auto; }
+ .hero-actions { justify-content: center; }
+ .quota-strip, .stat-grid { grid-template-columns: 1fr 1fr; }
+ .quota-strip > div { border-bottom: 1px solid #232323; }
+ .page-heading, .card-heading { align-items: stretch; flex-direction: column; }
+ .key-create-row { width: 100%; }
+ .key-create-row input { flex: 1; min-width: 0; }
}
-.copyright {
- margin-top: 20px;
- color: #fff;
+@media (max-width: 560px) {
+ .page { width: min(100% - 20px, 1120px); }
+ .quota-strip, .stat-grid { grid-template-columns: 1fr; }
+ .quota-strip > div { border-right: 0; }
+ .key-row { grid-template-columns: 1fr auto; }
+ .key-row > span { display: none; }
+ .key-create-row { flex-direction: column; }
+ .story, .dashboard-card { padding: 20px; }
+ .hero h1 { font-size: 48px; }
}
diff --git a/src/supabase.js b/src/supabase.js
new file mode 100644
index 0000000..337b18f
--- /dev/null
+++ b/src/supabase.js
@@ -0,0 +1,6 @@
+import { createClient } from '@supabase/supabase-js'
+
+const SUPABASE_URL = 'https://xevezwgeljeyorkkumhu.supabase.co'
+const SUPABASE_PUBLISHABLE_KEY = 'sb_publishable_kvMIifHrNIQEI9FJshT1fA_TYFzV__p'
+
+export const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY)