From 6f66f33a9be7e8f7a3de208b815eb515105fa22f Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Sat, 21 Feb 2026 03:01:33 +0100 Subject: [PATCH] feat: implement primary account identifier and streamline add account flow via direct Steam login --- frontend/dist-electron/main.js | 12 ++ frontend/dist-electron/preload.js | 2 + frontend/dist-electron/services/backend.js | 32 +++- frontend/src/pages/Dashboard.tsx | 195 ++------------------- 4 files changed, 56 insertions(+), 185 deletions(-) diff --git a/frontend/dist-electron/main.js b/frontend/dist-electron/main.js index 3b8a1e4..939ee20 100644 --- a/frontend/dist-electron/main.js +++ b/frontend/dist-electron/main.js @@ -502,6 +502,18 @@ electron_1.ipcMain.handle('share-account-with-user', async (event, steamId, targ } throw new Error('Backend not configured'); }); +electron_1.ipcMain.handle('revoke-account-access', async (event, steamId, targetSteamId) => { + initBackend(); + if (backend) + return await backend.revokeAccess(steamId, targetSteamId); + throw new Error('Backend not configured'); +}); +electron_1.ipcMain.handle('revoke-all-account-access', async (event, steamId) => { + initBackend(); + if (backend) + return await backend.revokeAllAccess(steamId); + throw new Error('Backend not configured'); +}); electron_1.ipcMain.handle('get-community-accounts', async () => { initBackend(); return backend ? await backend.getCommunityAccounts() : []; }); electron_1.ipcMain.handle('get-server-users', async () => { initBackend(); return backend ? await backend.getServerUsers() : []; }); electron_1.ipcMain.handle('switch-account', async (event, loginName) => await handleSwitchAccount(loginName)); diff --git a/frontend/dist-electron/preload.js b/frontend/dist-electron/preload.js index 7be0dfb..56a17c2 100644 --- a/frontend/dist-electron/preload.js +++ b/frontend/dist-electron/preload.js @@ -8,6 +8,8 @@ electron_1.contextBridge.exposeInMainWorld('electronAPI', { deleteAccount: (id) => electron_1.ipcRenderer.invoke('delete-account', id), switchAccount: (loginName) => electron_1.ipcRenderer.invoke('switch-account', loginName), shareAccountWithUser: (steamId, targetSteamId) => electron_1.ipcRenderer.invoke('share-account-with-user', steamId, targetSteamId), + revokeAccountAccess: (steamId, targetSteamId) => electron_1.ipcRenderer.invoke('revoke-account-access', steamId, targetSteamId), + revokeAllAccountAccess: (steamId) => electron_1.ipcRenderer.invoke('revoke-all-account-access', steamId), openExternal: (url) => electron_1.ipcRenderer.invoke('open-external', url), openSteamLogin: (steamId) => electron_1.ipcRenderer.invoke('open-steam-login', steamId), // Server Config & Auth diff --git a/frontend/dist-electron/services/backend.js b/frontend/dist-electron/services/backend.js index d0d7341..d88977c 100644 --- a/frontend/dist-electron/services/backend.js +++ b/frontend/dist-electron/services/backend.js @@ -67,7 +67,8 @@ class BackendService { gameBans: account.gameBans, loginName: account.loginName, steamLoginSecure: account.steamLoginSecure, - loginConfig: account.loginConfig + loginConfig: account.loginConfig, + sessionUpdatedAt: account.sessionUpdatedAt }, { headers: this.headers }); } catch (e) { @@ -100,5 +101,34 @@ class BackendService { throw new Error(e.response?.data?.message || 'Failed to share account'); } } + async revokeAccess(steamId, targetSteamId) { + if (!this.token) + return; + try { + const response = await axios_1.default.delete(`${this.url}/api/sync/${steamId}/share`, { + headers: this.headers, + data: { targetSteamId } + }); + return response.data; + } + catch (e) { + console.error(`[Backend] Failed to revoke access for ${steamId} from ${targetSteamId}`); + throw new Error(e.response?.data?.message || 'Failed to revoke access'); + } + } + async revokeAllAccess(steamId) { + if (!this.token) + return; + try { + const response = await axios_1.default.delete(`${this.url}/api/sync/${steamId}/share/all`, { + headers: this.headers + }); + return response.data; + } + catch (e) { + console.error(`[Backend] Failed to revoke all access for ${steamId}`); + throw new Error(e.response?.data?.message || 'Failed to revoke all access'); + } + } } exports.BackendService = BackendService; diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index c71ab04..ddb0b96 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -25,6 +25,7 @@ import ShieldIcon from '@mui/icons-material/Shield'; import GppBadIcon from '@mui/icons-material/GppBad'; import PeopleIcon from '@mui/icons-material/People'; import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'; +import WorkspacePremiumIcon from '@mui/icons-material/WorkspacePremium'; import { useAccounts, type Account } from '../hooks/useAccounts'; import { useAppTheme } from '../theme/ThemeContext'; import type { ThemeType } from '../theme/SteamTheme'; @@ -39,47 +40,13 @@ const Dashboard: React.FC = () => { } = useAccounts(); const [searchTerm, setSearchTerm] = useState(''); - const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); const [isSettingsOpen, setIsSettingsOpen] = useState(false); - const [identifier, setIdentifier] = useState(''); - - const [addTab, setAddTab] = useState(0); - const [communityAccounts, setCommunityAccounts] = useState([]); - const [isCommunityLoading, setIsCommunityLoading] = useState(false); const [serverUrl, setServerUrl] = useState(''); useEffect(() => { if (serverConfig?.url) setServerUrl(serverConfig.url); }, [serverConfig?.url]); - const loadCommunity = async () => { - setIsCommunityLoading(true); - try { - const data = await getCommunityAccounts(); - setCommunityAccounts(Array.isArray(data) ? data : []); - } catch (e) { } finally { setIsCommunityLoading(false); } - }; - - useEffect(() => { - if (isAddDialogOpen && addTab === 1) loadCommunity(); - }, [isAddDialogOpen, addTab]); - - const handleAddAccount = async () => { - if (!identifier) return; - try { - await addAccount({ identifier }); - setIsAddDialogOpen(false); - setIdentifier(''); - } catch (e) { console.error("[Dashboard] Add failed:", e); } - }; - - const handleAddFromCommunity = async (commAcc: any) => { - try { - await addAccount({ identifier: commAcc.steamId }); - setIsAddDialogOpen(false); - } catch (e) { } - }; - const saveSettings = async () => { await updateServerConfig({ url: serverUrl }); alert("Server URL updated!"); @@ -139,7 +106,7 @@ const Dashboard: React.FC = () => { variant="contained" color="primary" startIcon={} - onClick={() => setIsAddDialogOpen(true)} + onClick={() => openSteamLogin('')} sx={{ height: 32 }} > Add @@ -185,161 +152,13 @@ const Dashboard: React.FC = () => { {!isLoading && filteredAccounts.length === 0 && ( - No accounts tracked. Click "Add Account" to get started! + No accounts tracked. Click "Add" to get started! )} {/* Settings Dialog */} - setIsSettingsOpen(false)} maxWidth="sm" fullWidth> - Settings & Customization - - THEME SELECTION - - Active Theme - - - - - - BACKEND CONFIGURATION - setServerUrl(e.target.value)} - placeholder="https://ultimate-ban-tracker.narl.io" - margin="dense" - sx={{ mb: 2 }} - InputProps={{ - endAdornment: ( - - - - ), - }} - /> - - - - COMMUNITY AUTHENTICATION - - - - {serverConfig?.token ? "Connected to Server" : "Not Authenticated"} - - - {serverConfig?.token ? "Your accounts can now be shared with others." : "Login to share and sync with your community."} - - - - {serverConfig?.token && ( - - )} - - - - - updateServerConfig({ enabled: e.target.checked })} - disabled={!serverConfig?.token} - /> - } - label="Enable Community Sync" - sx={{ mt: 2 }} - /> - - - - - - - {/* Add Account Dialog */} - setIsAddDialogOpen(false)} maxWidth="sm" fullWidth> - - setAddTab(v)} variant="fullWidth" textColor="inherit" indicatorColor="primary"> - } iconPosition="start" /> - } iconPosition="start" disabled={!serverConfig?.token} /> - - - - {addTab === 0 ? ( - <> - - Enter a SteamID64 or Profile URL. You will need to authenticate to enable full tracking and instant login features. - - setIdentifier(e.target.value)} - sx={{ '& .MuiOutlinedInput-root': { backgroundColor: 'rgba(0, 0, 0, 0.1)' } }} - /> - - ) : ( - - {isCommunityLoading ? ( - - ) : ( - - {communityAccounts - .filter(ca => !safeAccounts.find(a => a.steamId === ca.steamId)) - .map((ca) => ( - - - - - - - - ))} - {communityAccounts.length === 0 && No shared accounts found on server.} - - )} - - )} - - - - {addTab === 0 && } - - - - ); -}; // --- Sub-Component: AccountRow --- @@ -415,6 +234,9 @@ const AccountRow: React.FC<{ const isBanned = account?.vacBanned || (account?.gameBans && account.gameBans > 0); + // Primary account check + const isPrimaryAccount = serverConfig?.serverSteamId === account.steamId; + // Refined Shared Logic: // 1. It was shared WITH you (starts with shared_) // 2. OR you are the owner but you have shared it with at least one person @@ -427,6 +249,11 @@ const AccountRow: React.FC<{ + {isPrimaryAccount && ( + + + + )} {showCommunityIcon && (