feat(auth): Google ile Giriş + landing/login + çalışan çıkış + admin rolü

- server/auth.mjs: ID token'ı oauth2.googleapis.com/tokeninfo ile doğrula (ek bağımlılık yok), HMAC imzalı app token, ADMIN_EMAILS ile admin
- POST /api/auth/google, GET /api/auth/google/config, GET /api/auth/me
- LoginPage: GIS butonlu landing/giriş; giriş yapılmadan uygulama açılmaz
- Header çıkış butonu artık çalışıyor; kullanıcı adı/avatar Google'dan
- AI Model Ayarları sekmesi sadece admin'e görünür
- Env: GOOGLE_CLIENT_ID + ADMIN_EMAILS (Coolify'dan)
This commit is contained in:
2026-08-28 00:43:08 +03:00
parent 1b28ebb603
commit 13f3e9d366
8 changed files with 409 additions and 18 deletions
+21 -8
View File
@@ -14,7 +14,10 @@ export const Header = () => {
setActiveTab,
clearAllFilters,
isRefreshingLive,
refreshLiveEKAPData
refreshLiveEKAPData,
authUser,
isAdmin,
handleLogout
} = useApp();
const [showNotifications, setShowNotifications] = useState(false);
@@ -160,18 +163,25 @@ export const Header = () => {
onClick={() => setShowUserMenu(!showUserMenu)}
className="bg-slate-700 hover:bg-slate-600 text-white px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-2 transition-colors border border-slate-600"
>
<div className="w-6 h-6 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold text-xs">
{userProfile.username.charAt(0).toUpperCase()}
</div>
<span>{userProfile.username}</span>
{authUser?.picture ? (
<img src={authUser.picture} alt="" className="w-6 h-6 rounded-full object-cover" referrerPolicy="no-referrer" />
) : (
<div className="w-6 h-6 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold text-xs">
{(authUser?.name || userProfile.username).charAt(0).toUpperCase()}
</div>
)}
<span>{(authUser?.name || userProfile.username).split(' ')[0]}</span>
<ChevronDown className="w-3.5 h-3.5 text-slate-400" />
</button>
{showUserMenu && (
<div className="absolute right-0 mt-2 w-60 bg-white text-slate-800 rounded-xl shadow-xl border border-slate-200 py-2 z-50 animate-fade-in">
<div className="px-4 py-2 border-b border-slate-100">
<p className="font-bold text-sm text-slate-900">{userProfile.name}</p>
<p className="text-xs text-slate-500 truncate">{userProfile.company}</p>
<p className="font-bold text-sm text-slate-900 flex items-center gap-1.5">
{authUser?.name || userProfile.name}
{isAdmin && <span className="text-[9px] font-black px-1.5 py-0.5 rounded bg-indigo-600 text-white">ADMIN</span>}
</p>
<p className="text-xs text-slate-500 truncate">{authUser?.email || userProfile.company}</p>
</div>
<div className="py-1 text-xs">
<a href="#profile" className="px-4 py-2 hover:bg-slate-50 flex items-center gap-2 text-slate-700">
@@ -185,7 +195,10 @@ export const Header = () => {
</a>
</div>
<div className="border-t border-slate-100 pt-1 text-xs">
<button className="w-full text-left px-4 py-2 text-red-600 hover:bg-red-50 flex items-center gap-2">
<button
onClick={handleLogout}
className="w-full text-left px-4 py-2 text-red-600 hover:bg-red-50 flex items-center gap-2"
>
<LogOut className="w-4 h-4" /> Çıkış Yap
</button>
</div>