Skip to content

Commit 1d5d002

Browse files
committed
Production stabilization and SaaS cleanup
1 parent 06905aa commit 1d5d002

30 files changed

Lines changed: 310 additions & 2608 deletions

.env.example

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
1+
# Database
12
DATABASE_URL=
3+
4+
# Auth
25
NEXTAUTH_URL=
36
NEXTAUTH_SECRET=
47

5-
GOOGLE_CLIENT_ID=
6-
GOOGLE_CLIENT_SECRET=
7-
8+
# AI
89
ANTHROPIC_API_KEY=
910

11+
# Admin
1012
ADMIN_EMAILS=aams1969@gmail.com,ayman@teosegypt.com
1113

14+
# Payments
1215
TAP_WEBHOOK_SECRET=
1316
TAP_PRO_INVOICE_LINK=
1417
TAP_AGENCY_INVOICE_LINK=
1518

19+
# Email (Resend)
1620
RESEND_API_KEY=
1721
EMAIL_FROM=noreply@teosegypt.com
22+
23+
# Upstash Redis KV (lifetime seat counter)
24+
KV_REST_API_URL=
25+
KV_REST_API_TOKEN=
26+
INTERNAL_KEY=
27+
28+
# App
29+
NEXT_PUBLIC_APP_URL=https://teos-ai-engine.vercel.app
30+
APP_URL=https://teos-ai-engine.vercel.app

.eslintrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "next/core-web-vitals",
3+
"rules": {
4+
"@next/next/no-img-element": "off",
5+
"react-hooks/exhaustive-deps": "warn",
6+
"no-console": ["warn", { "allow": ["warn", "error"] }]
7+
}
8+
}

app/api/audit-example/route.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

app/api/stats/route.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { NextResponse } from "next/server";
2+
import { prisma } from "@/lib/prisma";
3+
4+
export const dynamic = "force-dynamic";
5+
6+
export async function GET() {
7+
try {
8+
const now = new Date();
9+
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
10+
11+
const [totalPostsThisWeek, totalPosts, totalUsers] = await Promise.all([
12+
prisma.post.count({ where: { createdAt: { gte: weekAgo } } }),
13+
prisma.post.count(),
14+
prisma.user.count(),
15+
]);
16+
17+
return NextResponse.json({ totalPostsThisWeek, totalPosts, totalUsers });
18+
} catch {
19+
return NextResponse.json(
20+
{ error: "Failed to fetch stats" },
21+
{ status: 500 }
22+
);
23+
}
24+
}

app/api/test-cases/route.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.

app/dashboard/page.tsx

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,44 @@ import { PLANS, getPlan, isWithinDailyLimit, isWithinTotalLimit } from "@/lib/pl
66
import Link from "next/link";
77

88
type Platform = "x" | "facebook" | "instagram" | "linkedin";
9-
type PostStatus = "draft" | "published";
9+
type Tone = "professional" | "casual" | "witty" | "inspirational" | "urgent";
1010

1111
interface Post {
1212
id: string;
1313
platform: Platform;
1414
prompt: string;
1515
content: string;
16-
status: PostStatus;
16+
status: "draft" | "published";
1717
createdAt: string;
1818
}
1919

20+
interface UserMeta {
21+
dailyPostsUsed: number;
22+
plan: string;
23+
name?: string;
24+
email?: string;
25+
}
26+
2027
const PLATFORM_ICONS: Record<Platform, string> = {
2128
x: "𝕏",
2229
facebook: "f",
2330
instagram: "◈",
2431
linkedin: "in",
2532
};
2633

34+
const TONES: { value: Tone; label: string }[] = [
35+
{ value: "professional", label: "Professional" },
36+
{ value: "casual", label: "Casual" },
37+
{ value: "witty", label: "Witty" },
38+
{ value: "inspirational", label: "Inspirational" },
39+
{ value: "urgent", label: "Urgent" },
40+
];
41+
2742
export default function DashboardPage() {
2843
const { data: session } = useSession();
2944
const [tab, setTab] = useState<"generate" | "saved">("generate");
3045
const [platform, setPlatform] = useState<Platform>("x");
46+
const [tone, setTone] = useState<Tone>("professional");
3147
const [prompt, setPrompt] = useState("");
3248
const [generated, setGenerated] = useState("");
3349
const [generating, setGenerating] = useState(false);
@@ -106,7 +122,8 @@ export default function DashboardPage() {
106122
);
107123
}
108124

109-
const dailyUsed = (session.user as any)?.dailyPostsUsed ?? 0;
125+
const userMeta = session.user as unknown as UserMeta;
126+
const dailyUsed = userMeta?.dailyPostsUsed ?? 0;
110127
const remaining = isWithinDailyLimit(plan, dailyUsed)
111128
? (plan.dailyPostLimit === -1 ? Infinity : plan.dailyPostLimit - dailyUsed)
112129
: 0;
@@ -141,7 +158,7 @@ export default function DashboardPage() {
141158
</span>
142159
</div>
143160

144-
<div className="flex gap-2">
161+
<div className="flex gap-2 flex-wrap">
145162
{(["x", "linkedin", "instagram", "facebook"] as Platform[]).map(p => (
146163
<button key={p} onClick={() => setPlatform(p)}
147164
className={`px-3 py-1.5 rounded-lg text-xs transition-all ${platform === p ? "bg-gold-500/15 text-gold-500 border border-gold-500/30" : "text-[#5a5870] border border-white/[0.06]"}`}>
@@ -150,6 +167,13 @@ export default function DashboardPage() {
150167
))}
151168
</div>
152169

170+
<select value={tone} onChange={e => setTone(e.target.value as Tone)}
171+
className="w-full rounded-xl border border-white/[0.1] bg-white/[0.03] px-4 py-3 text-sm text-[#e8e6f0] outline-none focus:border-gold-500/40">
172+
{TONES.map(t => (
173+
<option key={t.value} value={t.value} className="bg-bg">{t.label}</option>
174+
))}
175+
</select>
176+
153177
<textarea value={prompt} onChange={e => setPrompt(e.target.value)}
154178
placeholder="What do you want to post about?"
155179
rows={3}
@@ -176,6 +200,17 @@ export default function DashboardPage() {
176200
className="text-xs px-3 py-1.5 rounded-lg bg-teal-500/10 text-teal-400 border border-teal-500/20">
177201
{saving ? "Saving..." : saveSuccess ? "Saved!" : "Save"}
178202
</button>
203+
<button onClick={() => {
204+
const blob = new Blob([generated], { type: "text/plain" });
205+
const a = document.createElement("a");
206+
a.href = URL.createObjectURL(blob);
207+
a.download = platform + "-post.txt";
208+
a.click();
209+
URL.revokeObjectURL(a.href);
210+
}}
211+
className="text-xs px-3 py-1.5 rounded-lg bg-violet-500/10 text-violet-400 border border-violet-500/20">
212+
Export
213+
</button>
179214
</div>
180215
</div>
181216
)}

0 commit comments

Comments
 (0)