This repository was archived by the owner on Jul 6, 2026. It is now read-only.
Conversation
…+ lock down /metrics
FR-4.7 says project data is invited-only, enforced backend, but three
project-scoped GET handlers only required authentication and filtered by
ProjectId — letting any signed-in user read another project's private
standups, standup CSV export, and commit history by guessing a project id:
- GetStandupFeedQueryHandler
- ExportStandupsCsvQueryHandler (previously only checked the project exists)
- GetProjectCommitsQueryHandler
Apply the same owner-or-accepted-member guard the sibling handlers
(GetProjectDashboard/GetProjectSummaries) already use, throwing
ForbiddenException (-> 403) for non-members. Document 403/404 on the
endpoints. Adds ProjectVisibilityGuardTests covering outsider/owner/member
for all three (6 tests).
Also closes public exposure of the Prometheus /metrics endpoint. The prod
nginx ingress has snippet directives disabled cluster-wide, so block by Host
in-app: MapMetrics("/metrics").RequireHost(Metrics:AllowedHosts). In-cluster
Prometheus scrapes the pod directly as 'loopless-backend:8080' (allow-listed)
while the public api host is not, returning 404 to the internet without
breaking scraping. Wired via Helm values config.backend.Metrics__AllowedHosts__0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d-metrics-auth fix(security): enforce project-member visibility + lock down /metrics
…g into develop line
…nfig Documents every NEXT_PUBLIC_* variable the frontend reads, the window.__ENV runtime-injection mechanism (APP_* vars written to public/__env.js by the container entrypoint), and which deployed values live in devops/helm/loopless/values.yaml. Un-ignores .env.example so it can be tracked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Message sends only broadcast over the MessagingHub, so the recipient never got a bell notification unless they refreshed. Now SendMessageCommandHandler also records an in-app notification (deduped to one unread entry per conversation), and the RealtimeProvider suppresses it as read when the recipient is already on the messages page. Issue 2 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nosable and resilient Support tickets and content flags already notify every admin, but delivery depended entirely on a live SignalR connection. The bell now refetches on open and on window focus as a fallback, and both handlers log how many admins were notified (warning on zero, which means the admin exists only in Keycloak, not the app DB). Issue 3 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Dismiss/Warn/Suspend buttons swallowed every mutation failure, so a failed review looked like a dead button. Failures now render an alert with the API message, success shows a status line, and only the clicked row is disabled while the request is in flight. Issue 4 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The upload allowlist rejected .txt and .md (browsers send .md as text/plain, often with no content type at all). Validation now treats the extension allowlist as the mandatory check and tolerates empty or octet-stream content types, while still excluding executable/markup types (.html, .svg, .js, .exe). Adds xlsx/pptx for docx parity, sets the file picker accept list, and fixes the frontend advertising 50 MB against the backend 20 MB cap. Issue 8 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Suspension was a permanent boolean that nothing enforced — suspended users could still log in. Adds User.SuspendedUntil (+migration), a duration prompt (24h/7d/30d/indefinite) when suspending from the admin dashboard, suspension checks on login and /me with lazy expiry, an hourly Hangfire SuspensionExpiryJob sweep, and the until-date on the admin user row. Issue 6 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The admin reset-password action sets the Keycloak UPDATE_PASSWORD required action, but the login page had no way to satisfy it — the direct grant failed with a generic invalid-credentials error. The Keycloak client now distinguishes account-not-fully-set-up (credentials correct, action pending) from a wrong password; login surfaces it as 409 with code password_change_required; and a new rate-limited anonymous POST /api/v1/auth/change-password verifies the old password, sets the new one, clears the required action, and returns tokens. The login page switches to an email + old/new/confirm password form and logs the user straight in. Issue 5 of docs/Issues_2026-06-11.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Profile URLs exposed the user GUID. Users now get a unique URL-safe
username (slugified display name, GitHub handle for SSO signups, -2/-3 on
collisions, reserved route words excluded), generated at signup and
backfilled by migration for existing rows. The lookup endpoint accepts a
GUID or username — old links still resolve and the page canonicalizes the
address bar to /profile/{username}. Match cards link by username; own
profile links use /profile/me. The previously dead Share buttons on both
profile cards now copy the profile URL to the clipboard (with a
non-secure-context fallback) and confirm with Copied!.
Issue 7 of docs/Issues_2026-06-11.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Projects were GUID-routed end to end, exposing raw GUIDs in the address
bar (e.g. /projects/{guid}). Mirror the existing username system: generate
a stable, unique slug from the title at creation, route /projects/{slug},
and canonicalize legacy GUID links to the slug after load.
- Project.Slug column + unique index + backfill migration (AddProjectSlug)
- ProjectSlugGenerator: slugify title, reserve route words, suffix
collisions with -2/-3 (slug is stable, never regenerated on rename)
- GET /projects/{idOrSlug} resolves a slug or a GUID; slug added to
ProjectDto and DiscoverProjectDto
- Notification action URLs emit the slug instead of the GUID
- Frontend resolves the canonical project.id for all sub-resource calls,
rewrites the address bar to the slug, and links by slug everywhere
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The backend service never set Seed:Admin*, so docker-compose devs had no admin account to exercise the admin panel (suspensions, forced password resets, etc.). Add overridable Seed__Admin* env so AdminSeeder bootstraps admin@loopless.local (Role=Admin) on startup. Defaults live in .env (gitignored); log in at http://localhost:3002. Also drops the logstash startup gate from the backend depends_on so the API no longer blocks on logstash being up in local dev. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enes1998
added a commit
that referenced
this pull request
Jun 13, 2026
develop changed GetProjectDashboardQuery to take string ProjectIdOrSlug (handler resolves guid-or-slug), but the GET /{id} and /{id}/dashboard endpoints still passed Guid -> CS1503 broke main build on merge #101. Relax the two routes to {projectIdOrSlug}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.