Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# replace ghp_example with your GitHub PAT token
TOKEN=ghp_example
WHITELIST=shinokada
14 changes: 14 additions & 0 deletions .vercelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
tests/
.git/
.github/
.devcontainer/
node_modules/
*.md
!README.md
.editorconfig
.prettierrc.js
.prettierignore
.deepsource.toml
phpunit.xml
Dockerfile
Procfile
62 changes: 62 additions & 0 deletions api/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

// Load functions with absolute paths for Vercel
require_once __DIR__ . "/../vendor/autoload.php";
require_once __DIR__ . "/../src/stats.php";
require_once __DIR__ . "/../src/card.php";

// Load .env
$dotenv = \Dotenv\Dotenv::createImmutable(__DIR__ . "/..");
$dotenv->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);
}
48 changes: 48 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/whitelist.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
23 changes: 23 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}