148 lines
5.0 KiB
TypeScript
148 lines
5.0 KiB
TypeScript
/**
|
||
* YÖK Atlas'tan üniversite logolarını indirir.
|
||
*
|
||
* Kaynak: yokatlas.yok.gov.tr/api — SPA'nın kendi kullandığı uç noktalar.
|
||
* Logo endpoint'i Referer olmadan 403 verdiği için hotlink yerine build
|
||
* öncesi tek seferlik indirme yapılır; dosyalar public/uni-logo/<slug>.png.
|
||
*
|
||
* Çıktı olarak src/lib/uni-logolar.ts manifestini de üretir; bileşenler
|
||
* logo var/yok kararını dosya sistemine bakmadan bu set üzerinden verir.
|
||
*
|
||
* Kullanım: npx tsx scripts/logo-indir.ts [--force]
|
||
*/
|
||
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||
import path from "node:path";
|
||
import Database from "better-sqlite3";
|
||
import { turkishSlugify } from "../src/lib/slug";
|
||
|
||
const API = "https://yokatlas.yok.gov.tr/api";
|
||
const HEADERS = {
|
||
"User-Agent":
|
||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
|
||
Referer: "https://yokatlas.yok.gov.tr/",
|
||
};
|
||
const BEKLEME_MS = 250;
|
||
const force = process.argv.includes("--force");
|
||
|
||
// katalog.ts/uniMapGetir ile aynı anahtar: "(İL)" eki atılmış adın slug'ı
|
||
function uniSlug(ad: string): string {
|
||
return turkishSlugify(ad.replace(/\s*\([^)]*\)\s*$/, "").trim());
|
||
}
|
||
|
||
const bekle = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||
|
||
async function main() {
|
||
const logoDir = path.join(process.cwd(), "public", "uni-logo");
|
||
mkdirSync(logoDir, { recursive: true });
|
||
|
||
// 1) Sitedeki üniversiteler (DB'deki ham adlardan slug kümesi)
|
||
const db = new Database(path.join(process.cwd(), "data", "yokatlas.db"), {
|
||
readonly: true,
|
||
});
|
||
const rows = db
|
||
.prepare("SELECT DISTINCT universite FROM programs")
|
||
.all() as { universite: string }[];
|
||
db.close();
|
||
const siteSluglari = new Set(
|
||
rows.map((r) => uniSlug(r.universite)).filter(Boolean),
|
||
);
|
||
|
||
// 2) YÖK Atlas üniversite listesi
|
||
const res = await fetch(`${API}/tercih-kilavuz/universiteler`, {
|
||
headers: HEADERS,
|
||
});
|
||
if (!res.ok) throw new Error(`üniversite listesi: HTTP ${res.status}`);
|
||
const apiListe = (await res.json()) as {
|
||
universiteAdi: string;
|
||
universiteId: number;
|
||
}[];
|
||
console.log(
|
||
`Site: ${siteSluglari.size} üniversite, API: ${apiListe.length} üniversite`,
|
||
);
|
||
|
||
// 3) Slug eşlemesi + indirme
|
||
const indirilen: string[] = [];
|
||
const atlanan: string[] = [];
|
||
const hatali: { slug: string; durum: string }[] = [];
|
||
const apiSluglari = new Set<string>();
|
||
|
||
for (const u of apiListe) {
|
||
const slug = uniSlug(u.universiteAdi);
|
||
if (!slug) continue;
|
||
apiSluglari.add(slug);
|
||
if (!siteSluglari.has(slug)) {
|
||
atlanan.push(`${u.universiteAdi} (sitede yok)`);
|
||
continue;
|
||
}
|
||
const dosya = path.join(logoDir, `${slug}.png`);
|
||
if (!force && existsSync(dosya)) {
|
||
indirilen.push(slug);
|
||
continue;
|
||
}
|
||
await bekle(BEKLEME_MS);
|
||
try {
|
||
const logoRes = await fetch(
|
||
`${API}/universite-logo?universiteId=${u.universiteId}`,
|
||
{ headers: HEADERS },
|
||
);
|
||
if (!logoRes.ok) {
|
||
hatali.push({ slug, durum: `HTTP ${logoRes.status}` });
|
||
continue;
|
||
}
|
||
const buf = Buffer.from(await logoRes.arrayBuffer());
|
||
// PNG imzası kontrolü — WAF hata sayfası vb. çöpü diske yazma
|
||
if (buf.length < 8 || buf.readUInt32BE(0) !== 0x89504e47) {
|
||
hatali.push({ slug, durum: "PNG değil" });
|
||
continue;
|
||
}
|
||
writeFileSync(dosya, buf);
|
||
indirilen.push(slug);
|
||
console.log(` ✓ ${slug} (${(buf.length / 1024).toFixed(0)} KB)`);
|
||
} catch (e) {
|
||
hatali.push({ slug, durum: String(e) });
|
||
}
|
||
}
|
||
|
||
// 4) Manifest: elle eklenen logolar da (API dışı) sete girsin
|
||
const { readdirSync } = await import("node:fs");
|
||
const diskteki = readdirSync(logoDir)
|
||
.filter((f) => f.endsWith(".png"))
|
||
.map((f) => f.slice(0, -4))
|
||
.sort();
|
||
|
||
const manifest = `// Bu dosya scripts/logo-indir.ts tarafından üretilir — elle düzenleme.
|
||
// Logo dosyaları: public/uni-logo/<slug>.png (kaynak: YÖK Atlas)
|
||
|
||
export const UNI_LOGO_SLUGLARI: ReadonlySet<string> = new Set([
|
||
${diskteki.map((s) => ` "${s}",`).join("\n")}
|
||
]);
|
||
|
||
export function uniLogoYolu(slug: string): string | null {
|
||
return UNI_LOGO_SLUGLARI.has(slug) ? \`/uni-logo/\${slug}.png\` : null;
|
||
}
|
||
`;
|
||
writeFileSync(path.join(process.cwd(), "src", "lib", "uni-logolar.ts"), manifest);
|
||
|
||
// 5) Rapor
|
||
const eksik = [...siteSluglari].filter((s) => !diskteki.includes(s)).sort();
|
||
console.log(`\nİndirilen/mevcut: ${indirilen.length}`);
|
||
if (hatali.length) {
|
||
console.log(`Hatalı (${hatali.length}):`);
|
||
for (const h of hatali) console.log(` ✗ ${h.slug}: ${h.durum}`);
|
||
}
|
||
if (atlanan.length) {
|
||
console.log(`API'de olup sitede olmayan (${atlanan.length}):`);
|
||
for (const a of atlanan) console.log(` - ${a}`);
|
||
}
|
||
if (eksik.length) {
|
||
console.log(`Logosu eksik kalan site üniversiteleri (${eksik.length}):`);
|
||
for (const e of eksik) console.log(` ! ${e}`);
|
||
}
|
||
console.log(`Manifest: ${diskteki.length} logo → src/lib/uni-logolar.ts`);
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
});
|