Update package.json scripts, enhance layout with new components, and improve API error handling
Some checks failed
Deploy / deploy (push) Failing after 1h28m6s
Some checks failed
Deploy / deploy (push) Failing after 1h28m6s
- Added new scripts for database operations and temporary production in package.json. - Integrated KaydetBannerLazy component into the layout for improved user notifications. - Enhanced API error handling in the soru route to mark credit exhaustion. - Updated the IletisimPage for better button styling and user experience. - Refactored ListemPage to streamline user flow and improve session handling. - Removed unused ListePaneli component to clean up the codebase.
This commit is contained in:
87
src/lib/kredi-hatirlatma.ts
Normal file
87
src/lib/kredi-hatirlatma.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { and, eq, isNull, lt, lte } from "drizzle-orm";
|
||||
import { appDb, schema } from "./appdb";
|
||||
import { RAPOR_KREDI } from "./credits";
|
||||
import { krediHatirlatmaEpostasi } from "./eposta";
|
||||
import { epostaGonder } from "./eposta-gonder";
|
||||
|
||||
const { user } = schema;
|
||||
|
||||
// 48 saat sonra tek seferlik hatırlatma (PRD İP-A / A2). Env override'ları
|
||||
// lokal testte akışı dakikalara sıkıştırmak için var.
|
||||
const GECIKME_MS = Number(
|
||||
process.env.KREDI_HATIRLATMA_GECIKME_MS ?? 48 * 60 * 60 * 1000,
|
||||
);
|
||||
const ARALIK_MS = Number(
|
||||
process.env.KREDI_HATIRLATMA_ARALIK_MS ?? 60 * 60 * 1000,
|
||||
);
|
||||
|
||||
/**
|
||||
* Kredisi bitmiş ve 48 saattir dönmemiş kullanıcılara hatırlatma e-postası
|
||||
* gönderir. Kredi yüklemek `krediBittiAt`'ı sıfırladığı (grantCredits) ve
|
||||
* bakiye filtresi de burada olduğu için yalnızca hâlâ takılı olanlar seçilir.
|
||||
* Gönderildi işareti e-postadan ÖNCE koşullu UPDATE ile atılır: yarışta
|
||||
* yalnızca bir taraf `rowsAffected === 1` görür — çift gönderim imkânsız,
|
||||
* Resend hatasında o kullanıcının hatırlatması yanar (bilinçli at-most-once).
|
||||
*/
|
||||
export async function krediHatirlatmaTaramasi(): Promise<void> {
|
||||
const esik = new Date(Date.now() - GECIKME_MS);
|
||||
const adaylar = await appDb
|
||||
.select({ id: user.id, email: user.email })
|
||||
.from(user)
|
||||
.where(
|
||||
and(
|
||||
lte(user.krediBittiAt, esik),
|
||||
isNull(user.krediHatirlatmaGonderildiAt),
|
||||
lt(user.creditBalance, RAPOR_KREDI),
|
||||
),
|
||||
)
|
||||
.limit(100);
|
||||
|
||||
for (const aday of adaylar) {
|
||||
const res = await appDb
|
||||
.update(user)
|
||||
.set({ krediHatirlatmaGonderildiAt: new Date() })
|
||||
.where(
|
||||
and(eq(user.id, aday.id), isNull(user.krediHatirlatmaGonderildiAt)),
|
||||
);
|
||||
if (res.rowsAffected !== 1) continue; // başka instance kazandı
|
||||
|
||||
try {
|
||||
await epostaGonder(aday.email, krediHatirlatmaEpostasi());
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[hatirlatma] Kredi hatırlatması gönderilemedi (${aday.email}):`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saatlik tarama zamanlayıcısı — instrumentation.ts'ten, sunucu başına bir
|
||||
* kez çağrılır. globalThis guard'ı dev hot-reload'da çifte interval'i önler
|
||||
* (appdb/index.ts deseni).
|
||||
*/
|
||||
export function krediHatirlatmaBaslat(): void {
|
||||
const g = globalThis as unknown as { __krediHatirlatmaKuruldu?: boolean };
|
||||
if (g.__krediHatirlatmaKuruldu) return;
|
||||
g.__krediHatirlatmaKuruldu = true;
|
||||
|
||||
let calisiyor = false;
|
||||
const tik = async () => {
|
||||
if (calisiyor) return; // önceki tarama uzadıysa üst üste binme
|
||||
calisiyor = true;
|
||||
try {
|
||||
await krediHatirlatmaTaramasi();
|
||||
} catch (err) {
|
||||
console.error("[hatirlatma] Tarama hatası:", err);
|
||||
} finally {
|
||||
calisiyor = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Container yeniden başlarsa saatlik açıklık büyümesin diye kısa bir
|
||||
// gecikmeyle açılış taraması da yapılır.
|
||||
setTimeout(tik, 60_000);
|
||||
setInterval(tik, ARALIK_MS);
|
||||
}
|
||||
Reference in New Issue
Block a user