Feature/pcd activity sync#873
Conversation
Greptile SummaryThis PR introduces a "PCD Activity Sync" admin feature into ActivityWatch's web UI, adding a dedicated
Confidence Score: 2/5Not safe to merge into the open-source ActivityWatch master branch in its current state. The two admin components call the same
Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant U as User
participant H as Home.vue (PCDAdminPanel)
participant P as PcdAdmin.vue (/pcd-admin)
participant API as /api/0/pcd
U->>H: Click "PCD" trigger (Home page)
H->>API: "POST /verify {username, password}"
API-->>H: "{token}"
H->>API: GET /email (X-PCD-Admin-Token)
API-->>H: "{email}"
H->>API: "PUT /email {new_email}"
API-->>H: "{ok}"
U->>P: Navigate to /pcd-admin
P->>API: "POST /verify {password}"
API-->>P: "{token}"
P->>API: GET /config
P->>API: GET /email
P->>API: GET /logs
API-->>P: config / email / logs
P->>API: "PUT /config {base_url}"
P->>API: "PUT /email {new_email}"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant U as User
participant H as Home.vue (PCDAdminPanel)
participant P as PcdAdmin.vue (/pcd-admin)
participant API as /api/0/pcd
U->>H: Click "PCD" trigger (Home page)
H->>API: "POST /verify {username, password}"
API-->>H: "{token}"
H->>API: GET /email (X-PCD-Admin-Token)
API-->>H: "{email}"
H->>API: "PUT /email {new_email}"
API-->>H: "{ok}"
U->>P: Navigate to /pcd-admin
P->>API: "POST /verify {password}"
API-->>P: "{token}"
P->>API: GET /config
P->>API: GET /email
P->>API: GET /logs
API-->>P: config / email / logs
P->>API: "PUT /config {base_url}"
P->>API: "PUT /email {new_email}"
Reviews (1): Last reviewed commit: "feat(pcd): add PCDAdminPanel quick-acces..." | Re-trigger Greptile |
| async verifyAdmin() { | ||
| if (!this.username || !this.password) return; | ||
| this.loading = true; | ||
| this.authError = ''; | ||
| try { | ||
| const res = await fetch(`${API_BASE}/verify`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ username: this.username, password: this.password }), | ||
| }); | ||
| if (res.ok) { | ||
| const data = await res.json(); | ||
| this.sessionToken = data.token; | ||
| this.authenticated = true; | ||
| await this.loadEmail(); | ||
| } else { | ||
| const data = await res.json().catch(() => ({})); | ||
| this.authError = data.error || 'Invalid credentials.'; | ||
| this.password = ''; | ||
| } | ||
| } catch { | ||
| this.authError = 'Could not reach the local server.'; | ||
| } finally { | ||
| this.loading = false; | ||
| } | ||
| }, |
There was a problem hiding this comment.
Inconsistent auth payload vs.
PcdAdmin.vue
PCDAdminPanel.vue calls /api/0/pcd/verify with { username, password } (lines 93–96), while PcdAdmin.vue calls the same endpoint with only { password } (line 140). These two components cannot both work correctly against the same backend endpoint. Whichever payload the server actually expects will cause the other component's login to silently fail or error.
| async saveEmail() { | ||
| if (!this.emailChanged || this.emailState === false) return; |
There was a problem hiding this comment.
Empty email can bypass the save guard
When the user clears the email field, emailState returns null (not false), so the this.emailState === false guard does not block the call. A PUT request with { new_email: '' } is then sent to the backend. The fix is to treat null as an invalid state in the guard, and also disable the Save button when emailState is null.
| async saveEmail() { | |
| if (!this.emailChanged || this.emailState === false) return; | |
| async saveEmail() { | |
| if (!this.emailChanged || this.emailState !== true) return; |
| variant="outline-primary" | ||
| size="sm" | ||
| @click="saveEmail" | ||
| :disabled="saving || !emailChanged || emailState === false" |
There was a problem hiding this comment.
Save button not disabled for an empty email
The button disables on emailState === false, but emailState returns null (not false) when the field is empty. A user who clears the registered email will see the Save button enabled and be able to submit an empty address.
| :disabled="saving || !emailChanged || emailState === false" | |
| :disabled="saving || !emailChanged || emailState !== true" |
| const ENV_URLS: Record<string, string> = { | ||
| local: 'http://127.0.0.1:8005', | ||
| dev: 'https://api.dev.prescribingcaredirect.co.uk', | ||
| qa: 'https://api.qa.prescribingcaredirect.co.uk', | ||
| prod: 'https://api.prescribingcaredirect.co.uk', | ||
| }; |
There was a problem hiding this comment.
Organization-specific internal URLs hard-coded in an open-source repo
The ENV_URLS map embeds prescribingcaredirect.co.uk production, QA, and dev API hostnames in a public ActivityWatch repository. This permanently exposes internal infrastructure topology in git history and is inappropriate for a community open-source project. These should be driven by environment variables, a build-time config, or removed entirely before landing in the upstream codebase.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| i | ||
| | You can change which page opens when you open ActivityWatch (instead of this page) in the #[router-link(to="/settings") settings]. | ||
|
|
||
| PCDAdminPanel |
There was a problem hiding this comment.
Organization-specific panel embedded in the community Home page
PCDAdminPanel is now injected into the Home view that every ActivityWatch user sees. This panel is an internal admin tool for a specific organisation (PCD) and has no business living on the welcome screen of an open-source project used by thousands of unrelated users. Even with the low-opacity "PCD" trigger, the component ships its endpoint URLs and auth logic globally. This should be restricted to the /pcd-admin dedicated route rather than embedded here.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
No description provided.