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

147
src/lib/appdb/schema.ts Normal file
View File

@@ -0,0 +1,147 @@
import {
sqliteTable,
text,
integer,
uniqueIndex,
index,
} from "drizzle-orm/sqlite-core";
// ---- better-auth çekirdek tabloları (+ kredi alanları) ----
export const user = sqliteTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: integer("email_verified", { mode: "boolean" })
.notNull()
.default(false),
image: text("image"),
creditBalance: integer("credit_balance").notNull().default(0),
hasPaket: integer("has_paket", { mode: "boolean" }).notNull().default(false),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const session = sqliteTable("session", {
id: text("id").primaryKey(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
token: text("token").notNull().unique(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const account = sqliteTable("account", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: integer("access_token_expires_at", {
mode: "timestamp",
}),
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
mode: "timestamp",
}),
scope: text("scope"),
password: text("password"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const verification = sqliteTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
createdAt: integer("created_at", { mode: "timestamp" }),
updatedAt: integer("updated_at", { mode: "timestamp" }),
});
// ---- Ürün tabloları ----
export const orders = sqliteTable(
"orders",
{
// cuid — iyzico conversationId olarak da kullanılır
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => user.id),
product: text("product", { enum: ["paket", "topup"] }).notNull(),
amountKurus: integer("amount_kurus").notNull(),
credits: integer("credits").notNull(),
status: text("status", { enum: ["pending", "paid", "failed"] })
.notNull()
.default("pending"),
iyzicoToken: text("iyzico_token"),
iyzicoPaymentId: text("iyzico_payment_id"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
paidAt: integer("paid_at", { mode: "timestamp" }),
},
(t) => [index("orders_user").on(t.userId)],
);
export const creditLedger = sqliteTable(
"credit_ledger",
{
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => user.id),
delta: integer("delta").notNull(),
reason: text("reason", {
enum: [
"trial_grant",
"purchase",
"topup",
"chat_message",
"refund",
"report_generate",
"report_revision",
],
}).notNull(),
refId: text("ref_id"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
},
(t) => [
uniqueIndex("ledger_reason_ref").on(t.reason, t.refId),
index("ledger_user").on(t.userId),
],
);
export const chatMessages = sqliteTable(
"chat_messages",
{
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => user.id),
role: text("role", { enum: ["user", "assistant"] }).notNull(),
content: text("content").notNull(),
clientMessageId: text("client_message_id").unique(),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
},
(t) => [index("chat_user_created").on(t.userId, t.createdAt)],
);
export const reports = sqliteTable("reports", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.unique()
.references(() => user.id),
params: text("params", { mode: "json" }).notNull(),
result: text("result", { mode: "json" }),
revisionCount: integer("revision_count").notNull().default(0),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});