Files
sachgiaokhoaorg e6b265db99 Sách Giáo Khoa Việt Nam - sachgiaokhoa.org
CodeIgniter 4 website:
- Catalog 999 SGK (lớp 1-12) từ API, lọc theo lớp/môn/NXB, tìm kiếm
- Trang chủ grouped browse (bậc học -> lớp -> môn, ưu tiên môn quan trọng)
- Chi tiết sách: metadata đầy đủ, cover nghệ thuật /thumb/{W}x{H}/
- Đọc online qua gate captcha + countdown, PDF viewer pdf.js
- Download: captcha + token một lần + auto cache-bust version
- Tự đồng bộ catalog/cover khi API thay đổi (refresh:catalog)
- Legal: DMCA, miễn trừ trách nhiệm, điều khoản, bảo mật, liên hệ
2026-08-23 07:05:49 +00:00

75 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Libraries;
/**
* Simple self-hosted image captcha (GD), answer stored in session.
*/
class Captcha
{
public static function issue(): string
{
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$code = '';
for ($i = 0; $i < 5; $i++) {
$code .= $chars[random_int(0, strlen($chars) - 1)];
}
session()->set('captcha_code', $code);
session()->set('captcha_expires', time() + 600);
return $code;
}
public static function verify(?string $input): bool
{
$code = session()->get('captcha_code');
$exp = session()->get('captcha_expires');
session()->remove('captcha_code');
session()->remove('captcha_expires');
if (! $code || ! $exp || time() > $exp || ! $input) {
return false;
}
return hash_equals(strtoupper(trim($code)), strtoupper(trim($input)));
}
public static function render(string $code): void
{
$w = 180;
$h = 60;
$img = imagecreatetruecolor($w, $h);
$bg = imagecolorallocate($img, 243, 246, 252);
imagefilledrectangle($img, 0, 0, $w, $h, $bg);
// noise
for ($i = 0; $i < 6; $i++) {
$c = imagecolorallocate($img, random_int(190, 230), random_int(190, 230), random_int(220, 245));
imageline($img, random_int(0, $w), random_int(0, $h), random_int(0, $w), random_int(0, $h), $c);
}
for ($i = 0; $i < 220; $i++) {
$c = imagecolorallocate($img, random_int(150, 220), random_int(150, 220), random_int(150, 220));
imagesetpixel($img, random_int(0, $w - 1), random_int(0, $h - 1), $c);
}
$fonts = glob(SYSTEMPATH . 'fonts/*.{ttf,TTF}', GLOB_BRACE) ?: [];
for ($i = 0; $i < strlen($code); $i++) {
$color = imagecolorallocate($img, random_int(10, 90), random_int(30, 100), random_int(120, 200));
$x = 15 + $i * 32 + random_int(-4, 4);
$y = random_int(36, 48);
if ($fonts) {
imagettftext($img, random_int(22, 26), random_int(-12, 12), $x, $y, $color, $fonts[array_rand($fonts)], $code[$i]);
} else {
imagestring($img, 5, $x, $y - 16, $code[$i], $color);
}
}
header('Content-Type: image/png');
header('Cache-Control: no-store, no-cache, must-revalidate');
imagepng($img);
imagedestroy($img);
exit;
}
}