Skip to content

Commit ec910b5

Browse files
committed
up
1 parent 330d6c2 commit ec910b5

3 files changed

Lines changed: 185 additions & 1 deletion

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# StormCom - Copilot Agent Onboarding
1+
# StormCom - Copilot Agent Onboarding
22

33
**Multi-tenant e-commerce SaaS platform: Next.js 16, TypeScript 5.9.3, Prisma ORM, React 19 Server Components**
44

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
openapi: 3.1.0
2+
info:
3+
title: StormCom Hardened APIs
4+
version: 0.1.0
5+
description: APIs for secure checkout, multi-tenant storefront, newsletter, and exports
6+
servers:
7+
- url: https://api.stormcom.local
8+
paths:
9+
/api/checkout:
10+
post:
11+
summary: Secure checkout
12+
description: Recalculate server-side pricing, validate payment intent, create order transactionally
13+
requestBody:
14+
required: true
15+
content:
16+
application/json:
17+
schema:
18+
type: object
19+
properties:
20+
paymentIntent:
21+
type: string
22+
items:
23+
type: array
24+
items:
25+
type: object
26+
properties:
27+
productId: { type: string }
28+
quantity: { type: integer, minimum: 1 }
29+
address:
30+
type: object
31+
required: [paymentIntent, items]
32+
responses:
33+
'201':
34+
description: Order created
35+
content:
36+
application/json:
37+
schema:
38+
type: object
39+
properties:
40+
data:
41+
$ref: '#/components/schemas/Order'
42+
'400': { $ref: '#/components/responses/ValidationError' }
43+
'401': { $ref: '#/components/responses/Unauthorized' }
44+
'409': { description: Conflict (e.g., insufficient inventory) }
45+
'429': { $ref: '#/components/responses/RateLimited' }
46+
'500': { $ref: '#/components/responses/ServerError' }
47+
48+
/api/newsletter/subscribe:
49+
post:
50+
summary: Subscribe to newsletter (single opt-in)
51+
requestBody:
52+
required: true
53+
content:
54+
application/json:
55+
schema:
56+
type: object
57+
properties:
58+
email: { type: string, format: email }
59+
required: [email]
60+
responses:
61+
'200':
62+
description: Subscribed (idempotent)
63+
content:
64+
application/json:
65+
schema:
66+
type: object
67+
properties:
68+
data:
69+
type: object
70+
properties:
71+
status: { type: string, enum: [active] }
72+
'400': { $ref: '#/components/responses/ValidationError' }
73+
'429': { $ref: '#/components/responses/RateLimited' }
74+
'500': { $ref: '#/components/responses/ServerError' }
75+
76+
/api/orders/export.csv:
77+
get:
78+
summary: Stream CSV export (≤10k rows) or enqueue async job (>10k)
79+
parameters:
80+
- in: query
81+
name: since
82+
schema: { type: string, format: date-time }
83+
responses:
84+
'200':
85+
description: CSV stream
86+
content:
87+
text/csv: {}
88+
'202':
89+
description: Async job accepted, link will be sent via email and in-app notification
90+
'401': { $ref: '#/components/responses/Unauthorized' }
91+
'500': { $ref: '#/components/responses/ServerError' }
92+
93+
components:
94+
responses:
95+
ValidationError:
96+
description: Input validation failed
97+
content:
98+
application/json:
99+
schema:
100+
$ref: '#/components/schemas/Error'
101+
Unauthorized:
102+
description: Not authenticated
103+
content:
104+
application/json:
105+
schema:
106+
$ref: '#/components/schemas/Error'
107+
RateLimited:
108+
description: Too many requests
109+
content:
110+
application/json:
111+
schema:
112+
$ref: '#/components/schemas/Error'
113+
ServerError:
114+
description: Internal server error
115+
content:
116+
application/json:
117+
schema:
118+
$ref: '#/components/schemas/Error'
119+
schemas:
120+
Error:
121+
type: object
122+
properties:
123+
error:
124+
type: object
125+
properties:
126+
code: { type: string }
127+
message: { type: string }
128+
details: {}
129+
Order:
130+
type: object
131+
properties:
132+
id: { type: string }
133+
storeId: { type: string }
134+
totals:
135+
type: object
136+
properties:
137+
subtotal: { type: number }
138+
discountTotal: { type: number }
139+
taxTotal: { type: number }
140+
shippingTotal: { type: number }
141+
grandTotal: { type: number }
142+
currency: { type: string }
143+
status: { type: string, enum: [pending, paid, failed, refunded] }
144+
createdAt: { type: string, format: date-time }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Quickstart — Harden Checkout, Tenancy, Newsletter
2+
3+
Date: 2025-11-13
4+
Branch: 002-harden-checkout-tenancy
5+
6+
## Prerequisites
7+
- Node.js ≥ 20.0.0, npm ≥ 10.0.0
8+
- Run `npm install`
9+
- Configure `.env` from `.env.example` (DATABASE_URL, NEXTAUTH_SECRET, NEXTAUTH_URL)
10+
11+
## Database
12+
- Dev (SQLite):
13+
- `npm run db:push` (or `npx prisma db push`)
14+
- Optional: `npm run db:seed`
15+
- Prod (PostgreSQL):
16+
- `npm run db:migrate`
17+
18+
## Development
19+
- Start dev server:
20+
- `npm run dev`
21+
- Type check:
22+
- `npm run type-check`
23+
- Lint:
24+
- `npx eslint .`
25+
26+
## Testing
27+
- Unit/Integration:
28+
- `npx vitest run`
29+
- E2E (Playwright):
30+
- `npx playwright install` (first time)
31+
- `npx playwright test`
32+
33+
## What to Verify (This Feature)
34+
- Checkout: authentication required, server-side price recalculation, payment validation, atomic transaction.
35+
- Multi-tenancy: domain/subdomain → storeId, subdomain → custom domain redirect, no hardcoded storeId.
36+
- Newsletter: single opt-in, consent record, rate limiting, deduplication (email+store).
37+
- API responses: standardized error shape, X-Request-Id header.
38+
- Caching: tag invalidation on product/category/page changes.
39+
- CSV export: ≤10k streamed; >10k async job with email + in-app notification.
40+
- Accessibility: WCAG 2.1 AA on forms and analytics (<figure>/<figcaption>, aria-live).

0 commit comments

Comments
 (0)