Skip to content

Commit 5408520

Browse files
authored
Merge pull request #1 from metholyn/claude/quirky-mclaren-9dabf1
Заменить OAuth на анализ публичных профилей по ссылке
2 parents 726572d + d61eebd commit 5408520

33 files changed

Lines changed: 1013 additions & 1676 deletions

.env.example

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,3 @@
1-
# ── Server ──────────────────────────────────────────────────────────────
2-
PORT=4000
1+
SERVER_PORT=4000
32
NODE_ENV=development
4-
SESSION_SECRET=change_me_to_random_secret
5-
6-
# ── Instagram (Graph API) ─────────────────────────────────────────────
7-
# Create app at https://developers.facebook.com/
8-
INSTAGRAM_CLIENT_ID=
9-
INSTAGRAM_CLIENT_SECRET=
10-
INSTAGRAM_REDIRECT_URI=http://localhost:4000/api/auth/instagram/callback
11-
12-
# ── TikTok (Login Kit) ────────────────────────────────────────────────
13-
# Create app at https://developers.tiktok.com/
14-
TIKTOK_CLIENT_KEY=
15-
TIKTOK_CLIENT_SECRET=
16-
TIKTOK_REDIRECT_URI=http://localhost:4000/api/auth/tiktok/callback
17-
18-
# ── Client ───────────────────────────────────────────────────────────
19-
VITE_API_URL=http://localhost:4000
3+
CLIENT_URL=http://localhost:3000

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ npm-debug.log*
2323
.DS_Store
2424
.vercel
2525
.turbo
26+
.claude/

client/src/App.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import { BrowserRouter, Routes, Route } from 'react-router-dom'
22
import { Layout } from './components/Layout'
33
import { Home } from './pages/Home'
4-
import { Dashboard } from './pages/Dashboard'
5-
import { Callback } from './pages/Callback'
64

75
export default function App() {
86
return (
97
<BrowserRouter>
108
<Layout>
119
<Routes>
1210
<Route path="/" element={<Home />} />
13-
<Route path="/dashboard" element={<Dashboard />} />
14-
<Route path="/callback" element={<Callback />} />
1511
</Routes>
1612
</Layout>
1713
</BrowserRouter>

client/src/components/ConnectCard.tsx

Lines changed: 0 additions & 115 deletions
This file was deleted.

client/src/components/Layout.tsx

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { ReactNode } from 'react'
2-
import { Link, useLocation } from 'react-router-dom'
2+
import { Link } from 'react-router-dom'
33

44
export function Layout({ children }: { children: ReactNode }) {
5-
const { pathname } = useLocation()
6-
75
return (
86
<div style={{ minHeight: '100vh', background: '#f9fafb', fontFamily: 'system-ui, sans-serif' }}>
9-
{/* Navbar */}
107
<header
118
style={{
129
background: '#fff',
@@ -15,7 +12,6 @@ export function Layout({ children }: { children: ReactNode }) {
1512
height: 56,
1613
display: 'flex',
1714
alignItems: 'center',
18-
justifyContent: 'space-between',
1915
}}
2016
>
2117
<Link
@@ -30,44 +26,11 @@ export function Layout({ children }: { children: ReactNode }) {
3026
>
3127
💔 Unrequited
3228
</Link>
33-
<nav style={{ display: 'flex', gap: '1.5rem' }}>
34-
<NavLink to="/" active={pathname === '/'}>
35-
Главная
36-
</NavLink>
37-
<NavLink to="/dashboard" active={pathname === '/dashboard'}>
38-
Дашборд
39-
</NavLink>
40-
</nav>
4129
</header>
4230

43-
{/* Page content */}
4431
<main style={{ maxWidth: 900, margin: '0 auto', padding: '2rem 1rem' }}>
4532
{children}
4633
</main>
4734
</div>
4835
)
4936
}
50-
51-
function NavLink({
52-
to,
53-
active,
54-
children,
55-
}: {
56-
to: string
57-
active: boolean
58-
children: ReactNode
59-
}) {
60-
return (
61-
<Link
62-
to={to}
63-
style={{
64-
textDecoration: 'none',
65-
color: active ? '#6366f1' : '#6b7280',
66-
fontWeight: active ? 600 : 400,
67-
fontSize: '0.9rem',
68-
}}
69-
>
70-
{children}
71-
</Link>
72-
)
73-
}

client/src/components/UrlInput.tsx

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { useState, useEffect, FormEvent } from 'react'
2+
import { Spinner } from './Spinner'
3+
import type { PlatformInfo } from '../types'
4+
import { api } from '../services/api'
5+
6+
const PLATFORM_ICONS: Record<string, string> = {
7+
instagram: '📸',
8+
tiktok: '🎵',
9+
}
10+
11+
interface Props {
12+
onSubmit: (url: string) => void
13+
loading: boolean
14+
error: string | null
15+
}
16+
17+
export function UrlInput({ onSubmit, loading, error }: Props) {
18+
const [url, setUrl] = useState('')
19+
const [platforms, setPlatforms] = useState<PlatformInfo[]>([])
20+
const [detected, setDetected] = useState<PlatformInfo | null>(null)
21+
22+
useEffect(() => {
23+
api.platforms().then(setPlatforms).catch(() => {})
24+
}, [])
25+
26+
useEffect(() => {
27+
if (!url.trim() || platforms.length === 0) {
28+
setDetected(null)
29+
return
30+
}
31+
const lower = url.toLowerCase()
32+
const match = platforms.find((p) => lower.includes(p.platform))
33+
setDetected(match ?? null)
34+
}, [url, platforms])
35+
36+
function handleSubmit(e: FormEvent) {
37+
e.preventDefault()
38+
if (!url.trim() || loading) return
39+
onSubmit(url.trim())
40+
}
41+
42+
return (
43+
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
44+
<div style={{ position: 'relative' }}>
45+
<input
46+
type="url"
47+
placeholder="https://instagram.com/username"
48+
value={url}
49+
onChange={(e) => setUrl(e.target.value)}
50+
disabled={loading}
51+
style={{
52+
width: '100%',
53+
padding: '0.875rem 1rem',
54+
paddingRight: detected ? '3.5rem' : '1rem',
55+
borderRadius: '0.75rem',
56+
border: `2px solid ${error ? '#fca5a5' : detected ? '#a5b4fc' : '#e5e7eb'}`,
57+
fontSize: '1rem',
58+
outline: 'none',
59+
transition: 'border-color 0.2s',
60+
boxSizing: 'border-box',
61+
}}
62+
/>
63+
{detected && (
64+
<span
65+
style={{
66+
position: 'absolute',
67+
right: 12,
68+
top: '50%',
69+
transform: 'translateY(-50%)',
70+
fontSize: '1.5rem',
71+
}}
72+
title={detected.label}
73+
>
74+
{PLATFORM_ICONS[detected.platform] ?? ''}
75+
</span>
76+
)}
77+
</div>
78+
79+
{/* URL hint */}
80+
<div style={{ fontSize: '0.8rem', color: '#9ca3af', display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
81+
{platforms.map((p) => (
82+
<span
83+
key={p.platform}
84+
style={{
85+
padding: '0.2rem 0.5rem',
86+
borderRadius: '0.4rem',
87+
background: detected?.platform === p.platform ? '#eef2ff' : '#f9fafb',
88+
color: detected?.platform === p.platform ? '#6366f1' : '#9ca3af',
89+
fontWeight: detected?.platform === p.platform ? 600 : 400,
90+
transition: 'all 0.15s',
91+
}}
92+
>
93+
{PLATFORM_ICONS[p.platform]} {p.urlHint}
94+
</span>
95+
))}
96+
</div>
97+
98+
{/* Error */}
99+
{error && (
100+
<div
101+
style={{
102+
background: '#fef2f2',
103+
border: '1px solid #fca5a5',
104+
borderRadius: '0.6rem',
105+
padding: '0.6rem 0.75rem',
106+
color: '#dc2626',
107+
fontSize: '0.85rem',
108+
}}
109+
>
110+
{error}
111+
</div>
112+
)}
113+
114+
<button
115+
type="submit"
116+
disabled={loading || !url.trim()}
117+
style={{
118+
display: 'flex',
119+
alignItems: 'center',
120+
justifyContent: 'center',
121+
gap: '0.5rem',
122+
padding: '0.75rem 2rem',
123+
borderRadius: '0.75rem',
124+
border: 'none',
125+
background: loading || !url.trim() ? '#c7d2fe' : '#6366f1',
126+
color: '#fff',
127+
fontWeight: 700,
128+
fontSize: '1rem',
129+
cursor: loading || !url.trim() ? 'not-allowed' : 'pointer',
130+
transition: 'background 0.15s',
131+
boxShadow: loading || !url.trim() ? 'none' : '0 4px 14px rgba(99,102,241,0.4)',
132+
}}
133+
>
134+
{loading ? (
135+
<>
136+
<Spinner size={18} />
137+
Анализируем...
138+
</>
139+
) : (
140+
'Проверить'
141+
)}
142+
</button>
143+
</form>
144+
)
145+
}

client/src/hooks/useAuth.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)