Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be715e3cea | |||
| cb740c0515 | |||
| c9f1437860 | |||
| eee7ef6055 |
@@ -62,10 +62,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# The runner image lacks pip, and a semgrep job-container lacks the node that
|
# The runner image lacks pip, and a semgrep job-container lacks the node that
|
||||||
# actions/checkout needs — so install pip via apt on the standard image.
|
# actions/checkout needs — so install pip via apt on the standard image.
|
||||||
|
# Gitea's Act runner has no sudo binary but already runs as root, so call
|
||||||
|
# apt-get directly; pip install --user needs no pipx.
|
||||||
- name: Install semgrep
|
- name: Install semgrep
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq && sudo apt-get install -y -qq python3-pip pipx
|
apt-get update -qq && apt-get install -y -qq python3-pip
|
||||||
pipx install semgrep
|
python3 -m pip install --user --break-system-packages semgrep || python3 -m pip install --user semgrep
|
||||||
- name: Semgrep scan
|
- name: Semgrep scan
|
||||||
run: |
|
run: |
|
||||||
export PATH="$HOME/.local/bin:$PATH"
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
restart: unless-stopped
|
||||||
# pgvector-enabled Postgres 16 (semantic search). Drop-in for postgres:16 data;
|
# pgvector-enabled Postgres 16 (semantic search). Drop-in for postgres:16 data;
|
||||||
# the 'vector' extension is created by the AddEmbeddingColumn migration.
|
# the 'vector' extension is created by the AddEmbeddingColumn migration.
|
||||||
image: pgvector/pgvector:pg16
|
image: pgvector/pgvector:pg16
|
||||||
@@ -27,6 +28,7 @@ services:
|
|||||||
# encrypted disk and be included in your off-machine backup regime (see SECURITY.md).
|
# encrypted disk and be included in your off-machine backup regime (see SECURITY.md).
|
||||||
# Restore: docker compose exec -T postgres psql -U inboxintel -d inboxintel < backups/<file>.sql
|
# Restore: docker compose exec -T postgres psql -U inboxintel -d inboxintel < backups/<file>.sql
|
||||||
backup:
|
backup:
|
||||||
|
restart: unless-stopped
|
||||||
image: pgvector/pgvector:pg16
|
image: pgvector/pgvector:pg16
|
||||||
entrypoint: /bin/sh
|
entrypoint: /bin/sh
|
||||||
command:
|
command:
|
||||||
@@ -64,6 +66,7 @@ services:
|
|||||||
- keys:/keys
|
- keys:/keys
|
||||||
|
|
||||||
api:
|
api:
|
||||||
|
restart: unless-stopped
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: src/InboxIntel.Api/Dockerfile
|
dockerfile: src/InboxIntel.Api/Dockerfile
|
||||||
@@ -98,6 +101,7 @@ services:
|
|||||||
- "127.0.0.1:8080:8080"
|
- "127.0.0.1:8080:8080"
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
|
restart: unless-stopped
|
||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
+10
-2
@@ -1,3 +1,11 @@
|
|||||||
|
# Honor a reverse proxy's X-Forwarded-Proto (e.g. traefik terminating TLS) so the API sees
|
||||||
|
# https and builds https OAuth redirects; fall back to the connection scheme for direct access.
|
||||||
|
map $http_x_forwarded_proto $fwd_proto {
|
||||||
|
default $scheme;
|
||||||
|
https https;
|
||||||
|
http http;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
@@ -27,7 +35,7 @@ server {
|
|||||||
proxy_pass http://api:8080;
|
proxy_pass http://api:8080;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $fwd_proto;
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
proxy_set_header Cookie $http_cookie;
|
proxy_set_header Cookie $http_cookie;
|
||||||
}
|
}
|
||||||
@@ -38,7 +46,7 @@ server {
|
|||||||
proxy_pass http://api:8080;
|
proxy_pass http://api:8080;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $fwd_proto;
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
proxy_set_header Cookie $http_cookie;
|
proxy_set_header Cookie $http_cookie;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import GridLayout from 'react-grid-layout';
|
import GridLayoutBase, { WidthProvider } from 'react-grid-layout';
|
||||||
|
const GridLayout = WidthProvider(GridLayoutBase);
|
||||||
import 'react-grid-layout/css/styles.css';
|
import 'react-grid-layout/css/styles.css';
|
||||||
import 'react-resizable/css/styles.css';
|
import 'react-resizable/css/styles.css';
|
||||||
import { AnalyticsApi, LayoutApi, ExportApi, SyncApi } from '../api/client.js';
|
import { AnalyticsApi, LayoutApi, ExportApi, SyncApi } from '../api/client.js';
|
||||||
@@ -137,7 +138,6 @@ export default function Dashboard() {
|
|||||||
layout={visibleLayout}
|
layout={visibleLayout}
|
||||||
cols={12}
|
cols={12}
|
||||||
rowHeight={60}
|
rowHeight={60}
|
||||||
width={1200}
|
|
||||||
isResizable
|
isResizable
|
||||||
isDraggable
|
isDraggable
|
||||||
resizeHandles={['se']}
|
resizeHandles={['se']}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# InboxIntel — TODO / follow-ups
|
||||||
|
|
||||||
|
Outstanding items after the 2026-07-04 work session (UI refactor, category fixes, login fix,
|
||||||
|
Pi deployment, mockups). Nothing here is blocking; these are the known loose ends.
|
||||||
|
|
||||||
|
## Sync — Sent / Drafts folders
|
||||||
|
The PHASE 2 category fix (PR #38) is correct — after a re-sync, **Spam and Trash now populate**
|
||||||
|
(Trash 1363, Spam 112 on staging). But:
|
||||||
|
|
||||||
|
- [ ] **Uncapped re-sync to confirm Sent.** Sent is still empty on staging only because of the
|
||||||
|
2000-message dev cap (`GmailSync:MaxMessages=2000`) + Gmail's newest-first ordering — the
|
||||||
|
newest 2000 are dominated by trash/promotions, so Sent/Draft/most Inbox mail falls outside
|
||||||
|
the window. An uncapped sync (`MAX_MESSAGES=0`, real deployment) should include Sent.
|
||||||
|
To re-run: `DELETE FROM sync_states` for the user, then reload the dashboard (auto-fires a
|
||||||
|
full sync). Note: the capped re-sync also left staging's Inbox sparse (~5) — staging-only.
|
||||||
|
|
||||||
|
- [ ] **Drafts fix.** User added a drafts scope in Google Cloud console. Code side: `gmail.readonly`
|
||||||
|
already grants draft *read*, but `messages.list` doesn't return drafts — add a `users.drafts.list`
|
||||||
|
fetch in `GmailService`/`SyncService` and link the DRAFT label. (Add the new scope to
|
||||||
|
`GoogleOAuth:Scopes` in appsettings if compose/modify is needed for drafts too.)
|
||||||
|
|
||||||
|
- [ ] Optional: with `IncludeSpamTrash=true`, a *capped* sync gets flooded by trash. If caps are
|
||||||
|
kept, consider prioritising non-trash or making the cap per-folder.
|
||||||
|
|
||||||
|
## Pi deployment — real login
|
||||||
|
InboxIntel is live on the homelab at http://192.168.50.13:8090 (auto-deploys from `develop`).
|
||||||
|
|
||||||
|
- [x] Live message cap removed — `MAX_MESSAGES=0` in the Pi `deploy/.env` (takes effect on next sync).
|
||||||
|
- [ ] **Google login on the Pi.** Subdomain: **inboxintel.cesnimda.uk** (user wiring traefik+TLS manually).
|
||||||
|
Redirect URI (in the new live Google OAuth client): `https://inboxintel.cesnimda.uk/signin-google`.
|
||||||
|
Once traefik is up: set `FRONTEND_ORIGIN=https://inboxintel.cesnimda.uk` in the Pi `deploy/.env`,
|
||||||
|
set `AllowedHosts`, and put the **new live** `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` in the Pi
|
||||||
|
`deploy/.env` (see below), then redeploy.
|
||||||
|
- [ ] **Where the live OAuth ID/secret go: the Pi `/opt/inboxintel/deploy/.env`** (git-ignored) — NOT
|
||||||
|
Gitea secrets. The Pi auto-deploys via a cron `git pull` + `docker compose`, which reads
|
||||||
|
`deploy/.env` directly; there's no Gitea Actions runner on the Pi, so Gitea secrets wouldn't be
|
||||||
|
injected. (Gitea secrets only matter for the staging CD runner.)
|
||||||
|
|
||||||
|
## Release / housekeeping
|
||||||
|
- [ ] Decide on promoting `develop` → `main` and cutting a release tag (e.g. v0.1.0). `main` is
|
||||||
|
stale; all current work lives on `develop`. Stale PR #6 (develop→main) can be closed/redone.
|
||||||
|
- [ ] Deferred by user: "Clean up my inbox" natural-language chat feature (do last).
|
||||||
|
- [ ] Accessibility pass before launch (green accent must not be colour-only) — noted as nice-to-have.
|
||||||
|
|
||||||
|
## Done this session (for reference)
|
||||||
|
- Login 500 fixed (root-owned DataProtection keys volume) — PR #37
|
||||||
|
- All 7 smart-category bugs fixed at source — PR #38
|
||||||
|
- Email dashboard UX: split-view, custom checkbox, row polish, bulk/keyboard, dashboard
|
||||||
|
heatmap→category-bar, skeletons/empty states — PR #44
|
||||||
|
- Dead backend heatmap removed — PR #45
|
||||||
|
- .NET 10 migration, Renovate, DB backups, OpenTelemetry, hybrid semantic search, keyset
|
||||||
|
pagination, feature-flags foundation, Semgrep SAST (earlier PRs)
|
||||||
|
- Resume mockups → F:\Pictures\website\inboxintel
|
||||||
|
- Live Docker deployment + cron auto-deploy on pi@192.168.50.13
|
||||||
Reference in New Issue
Block a user