diff --git a/.env.example b/.env.example index 9cabbc6f..21cb8624 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ # replace ghp_example with your GitHub PAT token TOKEN=ghp_example +WHITELIST=shinokada \ No newline at end of file diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 00000000..b8497ff3 --- /dev/null +++ b/.vercelignore @@ -0,0 +1,14 @@ +tests/ +.git/ +.github/ +.devcontainer/ +node_modules/ +*.md +!README.md +.editorconfig +.prettierrc.js +.prettierignore +.deepsource.toml +phpunit.xml +Dockerfile +Procfile diff --git a/api/index.php b/api/index.php new file mode 100644 index 00000000..9551b32f --- /dev/null +++ b/api/index.php @@ -0,0 +1,62 @@ +safeLoad(); + +// If environment variables are not loaded, display error +if (!isset($_SERVER["TOKEN"]) && !isset($_ENV["TOKEN"])) { + $message = "Missing TOKEN environment variable. Please set it in Vercel project settings."; + renderOutput($message, 500); + exit(); +} + +// Use environment variable if not in $_SERVER +if (!isset($_SERVER["TOKEN"]) && isset($_ENV["TOKEN"])) { + $_SERVER["TOKEN"] = $_ENV["TOKEN"]; +} + +// Set cache to refresh once per three hours +$cacheMinutes = 3 * 60 * 60; +header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cacheMinutes) . " GMT"); +header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); +header("Cache-Control: public, max-age=$cacheMinutes"); + +// Redirect to demo site if user is not given +if (!isset($_REQUEST["user"])) { + header("Location: /demo/"); + exit(); +} + +try { + // Get streak stats for user given in query string + $user = preg_replace("/[^a-zA-Z0-9\-]/", "", $_REQUEST["user"]); + $startingYear = isset($_REQUEST["starting_year"]) ? intval($_REQUEST["starting_year"]) : null; + $contributionGraphs = getContributionGraphs($user, $startingYear); + $contributions = getContributionDates($contributionGraphs); + if (isset($_GET["mode"]) && $_GET["mode"] === "weekly") { + $stats = getWeeklyContributionStats($contributions); + } else { + // Split and normalize excluded days + $excludeDays = normalizeDays(explode(",", $_GET["exclude_days"] ?? "")); + $stats = getContributionStats($contributions, $excludeDays); + } + renderOutput($stats); +} catch (InvalidArgumentException | AssertionError $error) { + error_log("Error {$error->getCode()}: {$error->getMessage()}"); + if ($error->getCode() >= 500) { + renderOutput("Unable to fetch contribution data. Please try again later.", 500); + } else { + renderOutput($error->getMessage(), $error->getCode()); + } +} catch (Exception $error) { + error_log("Unexpected error: " . $error->getMessage()); + renderOutput("An unexpected error occurred. Please try again later.", 500); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..360dd30b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,48 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@prettier/plugin-php': + specifier: ^0.24.0 + version: 0.24.0(prettier@3.7.4) + prettier: + specifier: ^3.6.2 + version: 3.7.4 + +packages: + + '@prettier/plugin-php@0.24.0': + resolution: {integrity: sha512-x9l65fCE/pgoET6RQowgdgG8Xmzs44z6j6Hhg3coINCyCw9JBGJ5ZzMR2XHAM2jmAdbJAIgqB6cUn4/3W3XLTA==} + peerDependencies: + prettier: ^3.0.0 + + linguist-languages@8.2.0: + resolution: {integrity: sha512-KCUUH9x97QWYU0SXOCGxUrZR6cSfuQrMhABB7L/0I8N0LXOeaKe7+RZs7FAwvWCV2qKfZ4Wv1luLq4OfMezSJg==} + + php-parser@3.2.5: + resolution: {integrity: sha512-M1ZYlALFFnESbSdmRtTQrBFUHSriHgPhgqtTF/LCbZM4h7swR5PHtUceB2Kzby5CfqcsYwBn7OXTJ0+8Sajwkw==} + + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + +snapshots: + + '@prettier/plugin-php@0.24.0(prettier@3.7.4)': + dependencies: + linguist-languages: 8.2.0 + php-parser: 3.2.5 + prettier: 3.7.4 + + linguist-languages@8.2.0: {} + + php-parser@3.2.5: {} + + prettier@3.7.4: {} diff --git a/src/whitelist.php b/src/whitelist.php index 21114834..26731476 100644 --- a/src/whitelist.php +++ b/src/whitelist.php @@ -8,6 +8,7 @@ */ function isWhitelisted(string $user): bool { - $whitelist = array_map("trim", array_filter(explode(",", $_SERVER["WHITELIST"] ?? ""))); + $whitelistStr = $_SERVER["WHITELIST"] ?? $_ENV["WHITELIST"] ?? getenv("WHITELIST") ?? ""; + $whitelist = array_map("trim", array_filter(explode(",", $whitelistStr))); return empty($whitelist) || in_array($user, $whitelist, true); } diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..5b25e87c --- /dev/null +++ b/vercel.json @@ -0,0 +1,23 @@ +{ + "version": 2, + "functions": { + "api/*.php": { + "runtime": "vercel-php@0.7.1" + } + }, + "routes": [ + { + "src": "/demo/(.*)", + "dest": "/src/demo/$1" + }, + { + "src": "/(.*)", + "dest": "/api/index.php" + } + ], + "build": { + "env": { + "COMPOSER_ALLOW_SUPERUSER": "1" + } + } +}