diff --git a/Dockerfile b/Dockerfile index 3981367d..911749da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ RUN a2enmod rewrite headers && \ echo 'ServerTokens Prod\n\ ServerSignature Off\n\ PassEnv TOKEN\n\ +PassEnv WHITELIST\n\ \n\ ServerAdmin webmaster@localhost\n\ DocumentRoot /var/www/html/src\n\ @@ -60,4 +61,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ EXPOSE 80 # Start Apache -CMD ["apache2-foreground"] \ No newline at end of file +CMD ["apache2-foreground"] diff --git a/README.md b/README.md index fb2d4bf8..adb16991 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ The longest streak is the highest number of consecutive days on which you have m The current streak is the number of consecutive days ending with the current day on which you have made at least one contribution. If you have made a contribution today, it will be counted towards the current streak, however, if you have not made a contribution today, the streak will only count days before today so that your streak will not be zero. -> [!NOTE] +> [!NOTE] > You may need to wait up to 24 hours for new contributions to show up ([Learn how contributions are counted](https://docs.github.com/articles/why-are-my-contributions-not-showing-up-on-my-profile)) ## 📤 Deploying it on your own @@ -164,7 +164,7 @@ The Inkscape dependency is required for PNG rendering, as well as Segoe UI font Vercel is the recommended option for hosting the files since it is **free** and easy to set up. Watch the video below or expand the instructions to learn how to deploy to Vercel. -> [!NOTE] +> [!NOTE] > PNG mode is not supported since Inkscape will not be installed but the default SVG mode will work. ### 📺 [Click here for a video tutorial on how to self-host on Vercel](https://www.youtube.com/watch?v=maoXtlb8t44) @@ -210,14 +210,15 @@ Vercel is the recommended option for hosting the files since it is **free** and 9. Scroll to the bottom of the page and click on **"Generate token"** 10. Visit the Vercel dashboard at and select your project. Then, click on **"Settings"** and choose **"Environment Variables"**. 11. Add a new environment variable with the key `TOKEN` and the value as the token you generated in step 9, then save your changes -12. To apply the new environment variable, you need to redeploy the app. Run `vercel --prod` to deploy the app to production. +12. (Optional) You can also set the `WHITELIST` environment variable to restrict which GitHub usernames can be accessed through the service. Provide the usernames as a comma-separated list, for example: `user1,user2,user3`. If the variable is not set, information can be requested for any GitHub user. +13. To apply the new environment variable(s), you need to redeploy the app. Run `vercel --prod` to deploy the app to production. ![image](https://user-images.githubusercontent.com/20955511/209588756-8bf5b0cd-9aa6-41e8-909c-97bf41e525b3.png) -> ⚠️ **Note** +> ⚠️ **Note** > To set up automatic Vercel deployments from GitHub, make sure to turn **off** "Include source files outside of the Root Directory" in the General settings and use `vercel` as the production branch in the Git settings. -> ⚠️ **Note** +> ⚠️ **Note** > If you receive an error related to libssl or Node 20.x, you can fix this by opening your Vercel project settings and changing the Node.js version to 18.x. > > ![image](https://github.com/DenverCoder1/github-readme-streak-stats/assets/20955511/5fb18fb5-debe-4620-9c8b-193ab442a617) @@ -230,9 +231,9 @@ Heroku is another great option for hosting the files. All features are supported
Instructions for deploying to Heroku (Paid) - + ### Step-by-step instructions for deploying to Heroku - + 1. Sign in to **Heroku** or create a new account at 2. Visit [this link](https://github.com/settings/tokens/new?description=GitHub%20Readme%20Streak%20Stats) to create a new Personal Access Token (no scopes required) 3. Scroll to the bottom and click **"Generate token"** @@ -244,8 +245,9 @@ Heroku is another great option for hosting the files. All features are supported ![heroku config variables](https://user-images.githubusercontent.com/20955511/136292022-a8d9b3b5-d7d8-4a5e-a049-8d23b51ce9d7.png) -6. Click **"Deploy App"** at the end of the form -7. Once the app is deployed, you can use `.herokuapp.com` in place of `streak-stats.demolab.com` +6. (Optional) You can also set the `WHITELIST` Config Var to restrict which GitHub usernames can be accessed through the service. Provide the usernames as a comma-separated list, for example: `user1,user2,user3`. If the variable is not set, information can be requested for any GitHub user. +7. Click **"Deploy App"** at the end of the form +8. Once the app is deployed, you can use `.herokuapp.com` in place of `streak-stats.demolab.com`
@@ -285,7 +287,14 @@ Docker is a great option for self-hosting with full control over your environmen docker run -d -p 8080:80 -e TOKEN=your_github_token_here streak-stats ``` -6. Visit http://localhost:8080 to access your self-hosted instance +6. You can also optionally set the `WHITELIST` environment variable to restrict which GitHub usernames can be accessed through the service. If the `WHITELIST` variable is not set, information can be requested for any GitHub user. + Provide the usernames as a comma-separated list, for example: + + ```bash + docker run -d -p 8080:80 -e TOKEN=your_github_token_here -e WHITELIST=user1,user2,user3 streak-stats + ``` + +7. Visit http://localhost:8080 to access your self-hosted instance diff --git a/src/stats.php b/src/stats.php index 74e29e55..902eb453 100644 --- a/src/stats.php +++ b/src/stats.php @@ -2,6 +2,8 @@ declare(strict_types=1); +require_once "whitelist.php"; + /** * Build a GraphQL query for a contribution graph * @@ -121,6 +123,10 @@ function executeContributionGraphRequests(string $user, array $years): array */ function getContributionGraphs(string $user, ?int $startingYear = null): array { + if (!isWhitelisted($user)) { + throw new InvalidArgumentException("User not in whitelist.", 403); + } + // get the list of years the user has contributed and the current year's contribution graph $currentYear = intval(date("Y")); $responses = executeContributionGraphRequests($user, [$currentYear]); diff --git a/src/whitelist.php b/src/whitelist.php new file mode 100644 index 00000000..21114834 --- /dev/null +++ b/src/whitelist.php @@ -0,0 +1,13 @@ +assertIsArray($contributionGraphs); + $this->assertNotEmpty($contributionGraphs); + } finally { + unset($_SERVER["WHITELIST"]); + } + } + + /** + * Test that an not whitelisted username returns 'not whitelisted' error + */ + public function testNotWhitelistedUsername(): void + { + $_SERVER["WHITELIST"] = "DenverCoder1"; + try { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("User not in whitelist."); + getContributionGraphs("help"); + } finally { + unset($_SERVER["WHITELIST"]); + } + } + /** * Test that an organization name returns 'not a user' error */