feat: kimlik doğrulama, ödeme, kredi ve AI rapor sihirbazı ekle

- better-auth ile giriş/oturum yönetimi (src/lib/auth, giris sayfası)
- iyzico ödeme entegrasyonu ve paket satın alma akışı
- kredi sistemi ve uygulama veritabanı şeması (drizzle)
- AI destekli rapor üretimi ve tercih sihirbazı
- rapor yazdırma sayfası ve site header/kullanıcı menüsü

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
bilalgursen
2026-07-22 02:13:39 +03:00
parent 7c3f17b9b5
commit cee6200237
64 changed files with 7966 additions and 89 deletions

71
src/lib/auth.ts Normal file
View File

@@ -0,0 +1,71 @@
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { magicLink } from "better-auth/plugins";
import { appDb, schema } from "./appdb";
import { grantCredits, DENEME_KREDISI } from "./credits";
async function sendMagicLinkEmail(email: string, url: string) {
if (!process.env.RESEND_API_KEY) {
// Dev fallback: Resend anahtarı yoksa linki terminale yaz
console.log(`\n[giris] Magic link for ${email}:\n${url}\n`);
return;
}
const { Resend } = await import("resend");
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: process.env.EMAIL_FROM ?? "KolayTercih <onboarding@resend.dev>",
to: email,
subject: "KolayTercih giriş bağlantın",
html: `<p>Merhaba,</p><p>KolayTercih'e giriş yapmak için <a href="${url}">buraya tıkla</a>. Bağlantı 5 dakika geçerlidir.</p><p>Bu isteği sen yapmadıysan bu e-postayı yok sayabilirsin.</p>`,
});
}
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL ?? "http://localhost:3000",
secret: process.env.BETTER_AUTH_SECRET,
database: drizzleAdapter(appDb, {
provider: "sqlite",
schema: {
user: schema.user,
session: schema.session,
account: schema.account,
verification: schema.verification,
},
}),
user: {
additionalFields: {
creditBalance: { type: "number", defaultValue: 0, input: false },
hasPaket: { type: "boolean", defaultValue: false, input: false },
},
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID ?? "",
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
},
},
plugins: [
magicLink({
sendMagicLink: async ({ email, url }) => {
await sendMagicLinkEmail(email, url);
},
}),
],
databaseHooks: {
user: {
create: {
after: async (newUser) => {
// refId = userId → UNIQUE(reason, refId) ile tek seferlik
await grantCredits({
userId: newUser.id,
delta: DENEME_KREDISI,
reason: "trial_grant",
refId: newUser.id,
});
},
},
},
},
});
export type Session = typeof auth.$Infer.Session;