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ệ
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user