Spaces:
Runtime error
Runtime error
Commit ·
1960fbf
1
Parent(s): 23b9370
LETS GOOOOOOOOOOOOOOOOOOOOOOOOO
Browse files
backend/app/routers/billing.py
CHANGED
|
@@ -21,7 +21,7 @@ def my_plan(ctx: Ctx = Depends(get_ctx)):
|
|
| 21 |
@router.post("/checkout")
|
| 22 |
def checkout(ctx: Ctx = Depends(get_ctx)):
|
| 23 |
if not settings.billing_enabled:
|
| 24 |
-
raise HTTPException(409, {"code": "billing_disabled", "message": "
|
| 25 |
raise HTTPException(501, "Checkout provider not wired yet")
|
| 26 |
|
| 27 |
|
|
|
|
| 21 |
@router.post("/checkout")
|
| 22 |
def checkout(ctx: Ctx = Depends(get_ctx)):
|
| 23 |
if not settings.billing_enabled:
|
| 24 |
+
raise HTTPException(409, {"code": "billing_disabled", "message": "Checkout is not available yet."})
|
| 25 |
raise HTTPException(501, "Checkout provider not wired yet")
|
| 26 |
|
| 27 |
|
docs/DESIGN.md
CHANGED
|
@@ -34,7 +34,7 @@ Every factual statement Notestack generates must trace back to the writer's own
|
|
| 34 |
| Email | Resend, behind a provider interface (mirrors blog2video `EmailService`) |
|
| 35 |
| Auth | Google sign-in + email 6-digit code signup + password login, JWT access/refresh with `token_version` revocation (mirrors blog2video) |
|
| 36 |
| LLM | Provider agnostic through DSPy + LiteLLM. Default Z.ai GLM-5.3 (fast tier GLM-5.3-Flash) via its OpenAI compatible endpoint. Retrieval is agentic search over a file system corpus, not embeddings (see 3.2). Any LiteLLM model string works by env var. |
|
| 37 |
-
| Billing | Three tiers defined in code (`app/services/plans.py`). `BILLING_ENABLED=false` for now: everyone gets the top tier limits free, checkout returns 409,
|
| 38 |
| Blog | Typed `blogPosts.ts` content array, `/blogs` and `/blogs/:slug`, same shape as blog2video |
|
| 39 |
|
| 40 |
## 3. Architecture
|
|
|
|
| 34 |
| Email | Resend, behind a provider interface (mirrors blog2video `EmailService`) |
|
| 35 |
| Auth | Google sign-in + email 6-digit code signup + password login, JWT access/refresh with `token_version` revocation (mirrors blog2video) |
|
| 36 |
| LLM | Provider agnostic through DSPy + LiteLLM. Default Z.ai GLM-5.3 (fast tier GLM-5.3-Flash) via its OpenAI compatible endpoint. Retrieval is agentic search over a file system corpus, not embeddings (see 3.2). Any LiteLLM model string works by env var. |
|
| 37 |
+
| Billing | Three tiers defined in code (`app/services/plans.py`). `BILLING_ENABLED=false` for now: everyone gets the top tier limits free, checkout returns 409, checkout is not wired up yet. |
|
| 38 |
| Blog | Typed `blogPosts.ts` content array, `/blogs` and `/blogs/:slug`, same shape as blog2video |
|
| 39 |
|
| 40 |
## 3. Architecture
|
frontend/src/components/PricingTiers.tsx
CHANGED
|
@@ -105,12 +105,6 @@ export default function PricingTiers() {
|
|
| 105 |
return (
|
| 106 |
<div className="pricing">
|
| 107 |
<div className="pricing-top">
|
| 108 |
-
{!billingEnabled && (
|
| 109 |
-
<p className="pricing-note">
|
| 110 |
-
<span className="badge">Early access</span>
|
| 111 |
-
<span className="muted">Every plan is free while we are in early access. No card needed.</span>
|
| 112 |
-
</p>
|
| 113 |
-
)}
|
| 114 |
<div className="cycle-toggle" role="radiogroup" aria-label="Billing cycle">
|
| 115 |
<button type="button" role="radio" aria-checked={cycle === "monthly"} className={cycle === "monthly" ? "on" : ""} onClick={() => setCycle("monthly")}>
|
| 116 |
Monthly
|
|
|
|
| 105 |
return (
|
| 106 |
<div className="pricing">
|
| 107 |
<div className="pricing-top">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
<div className="cycle-toggle" role="radiogroup" aria-label="Billing cycle">
|
| 109 |
<button type="button" role="radio" aria-checked={cycle === "monthly"} className={cycle === "monthly" ? "on" : ""} onClick={() => setCycle("monthly")}>
|
| 110 |
Monthly
|
frontend/src/pages/MissionControl.tsx
CHANGED
|
@@ -115,7 +115,7 @@ export default function MissionControl() {
|
|
| 115 |
<p className="eyebrow">Mission Control</p>
|
| 116 |
<h1>Good to see you, {(user?.name ?? user?.email ?? "").split(/[ @]/)[0]}</h1>
|
| 117 |
</div>
|
| 118 |
-
{plan && <span className="badge">{plan.name} plan
|
| 119 |
</header>
|
| 120 |
{error && <p className="error-text">{error}</p>}
|
| 121 |
|
|
|
|
| 115 |
<p className="eyebrow">Mission Control</p>
|
| 116 |
<h1>Good to see you, {(user?.name ?? user?.email ?? "").split(/[ @]/)[0]}</h1>
|
| 117 |
</div>
|
| 118 |
+
{plan && <span className="badge">{plan.name} plan</span>}
|
| 119 |
</header>
|
| 120 |
{error && <p className="error-text">{error}</p>}
|
| 121 |
|
frontend/src/pages/Settings.tsx
CHANGED
|
@@ -172,8 +172,7 @@ export default function Settings() {
|
|
| 172 |
<div className="stack">
|
| 173 |
<Section title="Plan and usage">
|
| 174 |
<p>
|
| 175 |
-
<strong>{s.plan.name}</strong>
|
| 176 |
-
<span className="badge">{s.billing_enabled ? "active" : "free during early access"}</span>
|
| 177 |
</p>
|
| 178 |
<Meter label="Audio overviews" used={u.used.audio_minutes} limit={u.limits.audio_minutes} unit="min" />
|
| 179 |
<Meter label="Video renders" used={u.used.video_minutes} limit={u.limits.video_minutes} unit="min" />
|
|
|
|
| 172 |
<div className="stack">
|
| 173 |
<Section title="Plan and usage">
|
| 174 |
<p>
|
| 175 |
+
<strong>{s.plan.name}</strong> plan
|
|
|
|
| 176 |
</p>
|
| 177 |
<Meter label="Audio overviews" used={u.used.audio_minutes} limit={u.limits.audio_minutes} unit="min" />
|
| 178 |
<Meter label="Video renders" used={u.used.video_minutes} limit={u.limits.video_minutes} unit="min" />
|
frontend/src/styles/showcase.css
CHANGED
|
@@ -525,7 +525,7 @@
|
|
| 525 |
display: flex;
|
| 526 |
flex-wrap: wrap;
|
| 527 |
align-items: center;
|
| 528 |
-
justify-content:
|
| 529 |
gap: 12px;
|
| 530 |
margin-bottom: 20px;
|
| 531 |
}
|
|
|
|
| 525 |
display: flex;
|
| 526 |
flex-wrap: wrap;
|
| 527 |
align-items: center;
|
| 528 |
+
justify-content: center;
|
| 529 |
gap: 12px;
|
| 530 |
margin-bottom: 20px;
|
| 531 |
}
|
renderer/src/compositions/LandingDemo.tsx
CHANGED
|
@@ -569,7 +569,7 @@ function Outro() {
|
|
| 569 |
<h1 style={{ margin: 0, fontFamily: theme.display, fontSize: 120, color: theme.white, opacity: s, textShadow: `0 0 40px ${BLUE}` }}>Notestack</h1>
|
| 570 |
<p style={{ margin: 0, fontFamily: theme.body, fontSize: 40, color: "rgba(255,255,255,0.8)", opacity: cta }}>Your knowledge, in orbit.</p>
|
| 571 |
<p style={{ margin: 0, padding: "18px 40px", borderRadius: 999, background: BLUE, color: theme.white, fontFamily: theme.body, fontWeight: 600, fontSize: 32, opacity: cta, boxShadow: `0 0 40px ${BLUE}` }}>
|
| 572 |
-
|
| 573 |
</p>
|
| 574 |
</AbsoluteFill>
|
| 575 |
<Flare at={20} duration={34} y={42} />
|
|
|
|
| 569 |
<h1 style={{ margin: 0, fontFamily: theme.display, fontSize: 120, color: theme.white, opacity: s, textShadow: `0 0 40px ${BLUE}` }}>Notestack</h1>
|
| 570 |
<p style={{ margin: 0, fontFamily: theme.body, fontSize: 40, color: "rgba(255,255,255,0.8)", opacity: cta }}>Your knowledge, in orbit.</p>
|
| 571 |
<p style={{ margin: 0, padding: "18px 40px", borderRadius: 999, background: BLUE, color: theme.white, fontFamily: theme.body, fontWeight: 600, fontSize: 32, opacity: cta, boxShadow: `0 0 40px ${BLUE}` }}>
|
| 572 |
+
Start free at notestack.ai
|
| 573 |
</p>
|
| 574 |
</AbsoluteFill>
|
| 575 |
<Flare at={20} duration={34} y={42} />
|