feat: complete public CV sharing
This commit is contained in:
@@ -280,4 +280,21 @@ public sealed class CvBuilderTests
|
|||||||
var v = await svc.CreateAsync("user-1", "CV", null, null, default);
|
var v = await svc.CreateAsync("user-1", "CV", null, null, default);
|
||||||
Assert.Null(await svc.GetPublicOwnerAsync(v.PublicSlug, default));
|
Assert.Null(await svc.GetPublicOwnerAsync(v.PublicSlug, default));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Re_enabling_public_access_rotates_the_revoked_link()
|
||||||
|
{
|
||||||
|
var (db, svc) = NewService("user-1");
|
||||||
|
await using var _ = db;
|
||||||
|
var v = await svc.CreateAsync("user-1", "CV", null, null, default);
|
||||||
|
|
||||||
|
await svc.SetPublicAsync("user-1", v.Id, true, default);
|
||||||
|
var revokedSlug = v.PublicSlug;
|
||||||
|
await svc.SetPublicAsync("user-1", v.Id, false, default);
|
||||||
|
await svc.SetPublicAsync("user-1", v.Id, true, default);
|
||||||
|
|
||||||
|
Assert.NotEqual(revokedSlug, v.PublicSlug);
|
||||||
|
Assert.Null(await svc.GetPublicOwnerAsync(revokedSlug, default));
|
||||||
|
Assert.Equal("user-1", await svc.GetPublicOwnerAsync(v.PublicSlug, default));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public sealed class CvVariantService : ICvVariantService
|
|||||||
{
|
{
|
||||||
var variant = await GetAsync(ownerUserId, id, ct);
|
var variant = await GetAsync(ownerUserId, id, ct);
|
||||||
if (variant is null) return null;
|
if (variant is null) return null;
|
||||||
|
if (isPublic && !variant.IsPublic) variant.PublicSlug = NewSlug();
|
||||||
variant.IsPublic = isPublic;
|
variant.IsPublic = isPublic;
|
||||||
variant.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
variant.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
||||||
await _db.SaveChangesAsync(ct);
|
await _db.SaveChangesAsync(ct);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ Goal: commercialise. Last, per the guide's "do not over-engineer before needed.
|
|||||||
| 7.3 | **DONE (2026-07-30)** — existing AI interaction metering now enforces monthly generation limits: 25 for free accounts and 250 for Premium/Admin. Usage responses expose the active plan and limit. | **P3** | **M** | 5.2, 7.2 | Cost-bearing AI now has a clear monthly ceiling before registration opens. |
|
| 7.3 | **DONE (2026-07-30)** — existing AI interaction metering now enforces monthly generation limits: 25 for free accounts and 250 for Premium/Admin. Usage responses expose the active plan and limit. | **P3** | **M** | 5.2, 7.2 | Cost-bearing AI now has a clear monthly ceiling before registration opens. |
|
||||||
| 7.4 | **DONE (2026-07-30)** — attachment uploads enforce total per-user storage entitlements (250 MB free, 5 GB Premium/Admin) in addition to the existing 10 MB per-file cap. | **P3** | **S** | 7.2 | Storage limits match the exposed capability model. |
|
| 7.4 | **DONE (2026-07-30)** — attachment uploads enforce total per-user storage entitlements (250 MB free, 5 GB Premium/Admin) in addition to the existing 10 MB per-file cap. | **P3** | **S** | 7.2 | Storage limits match the exposed capability model. |
|
||||||
| 7.5 | **Stripe billing** | **P3** | **L** | 7.2 | Still blocked on **Stripe keys** — the only remaining hard blocker. Tiers are now decided. |
|
| 7.5 | **Stripe billing** | **P3** | **L** | 7.2 | Still blocked on **Stripe keys** — the only remaining hard blocker. Tiers are now decided. |
|
||||||
| 7.6 | **Public CV** (`/cv/{guid}`) | **P3** | **M** | 3.4, 4.2 | Documented in `docs/00-ai-context.md`; **zero code** — no route, no `IsPublic`, no slug. Privacy-first random GUID, no usernames. |
|
| 7.6 | ✅ **DONE (2026-07-30)** — public CV (`/cv/{guid}`), privacy-first random links, revoke/rotate sharing | **P3** | **M** | 3.4, 4.2 | Anonymous rendering is isolated behind an explicit public flag, served with `noindex`, and revoked links cannot be restored accidentally. |
|
||||||
| 7.7 | **Premium themes** | **P3** | **S** | 4.3, 7.2 | Trivial once themes are data. Impossible while they are C# methods. A decided premium lever. |
|
| 7.7 | **Premium themes** | **P3** | **S** | 4.3, 7.2 | Trivial once themes are data. Impossible while they are C# methods. A decided premium lever. |
|
||||||
| 7.8 | **DONE (2026-07-30)** — CI runs NuGet transitive vulnerability reporting and a production-only npm audit. The npm audit reports the existing no-fix advisory baseline without blocking unrelated deploys. | **P2** | **S** | none | Vulnerable dependencies are now visible before deployment. |
|
| 7.8 | **DONE (2026-07-30)** — CI runs NuGet transitive vulnerability reporting and a production-only npm audit. The npm audit reports the existing no-fix advisory baseline without blocking unrelated deploys. | **P2** | **S** | none | Vulnerable dependencies are now visible before deployment. |
|
||||||
| 7.9 | **Per-user AI provider cost controls** | **P3** | **S** | 5.2, 7.2 | With `AI_PROVIDER=gemini` the "advanced AI" tier spends real money per call. Metering (5.2) measures; this enforces. |
|
| 7.9 | **Per-user AI provider cost controls** | **P3** | **S** | 5.2, 7.2 | With `AI_PROVIDER=gemini` the "advanced AI" tier spends real money per call. Metering (5.2) measures; this enforces. |
|
||||||
|
|||||||
Reference in New Issue
Block a user