diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c36332d..7232f5a 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -39,7 +39,7 @@ jobs: - name: Replace app.yml secrets uses: 73h/gae-app-yaml-replace-env-variables@v0.1 env: - POSTGRES_STRING: ${{ secrets.POSTGRES_STRING }} # Format: postgres://user:password@/cloudsql/INSTANCE_CONNECTION_NAME/db_name + GH_TOKEN: ${{ secrets.GH_TOKEN }} PROJECT_ID: ${{ secrets.PROJECT_ID }} DB_USER: ${{ secrets.DB_USER}} DB_NAME: ${{ secrets.DB_NAME}} diff --git a/README.md b/README.md index cd49d55..c3d6bba 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ This API provides endpoints for interacting with two main tables: `faucet.solana Below are the available endpoints for each table. --- - -## How to Run +## Development 1. Clone the repository ```bash @@ -22,8 +21,17 @@ Below are the available endpoints for each table. POSTGRES_STRING=postgresql://:@:/ PROJECT_ID= ``` + **NOTE** if you want to send request directly to Analytics DB, use [Cloud SQL Auth Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy) to setup a connection + ``` + ./cloud-sql-proxy --address 0.0.0.0 --port 5434 + ``` + +4. **OPTIONAL** In order to test the Github API locally, you need to provide a [Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) in your `.env` file. The token only needs `read:user` and `public_repo` + ``` + GH_TOKEN= + ``` -4. Start the server +5. Start the server ```bash yarn start ``` @@ -211,6 +219,29 @@ Below are the available endpoints for each table. --- +## Github Validation Endpoints + +### **Validate Github User ID** + +**GET** `/api/github-validation/:userId` + +- **Description**: Validates a Github user by fetching their information from the Github API using their user ID. +- **Request Params**: + - `userId` (string): The Github User ID to validate. + +- **Curl Command**: + ```bash + curl -v http://localhost:3000/api/gh-validation/exampleUser + ``` +-**Response**: +```json +{ + "valid": "boolean" +} +``` + +--- + ## Error Handling All endpoints return appropriate HTTP status codes: diff --git a/app.yaml b/app.yaml index 5be95a6..1be6ef1 100644 --- a/app.yaml +++ b/app.yaml @@ -11,7 +11,7 @@ entrypoint: yarn start # Environment variables (replace with your actual values or secrets) env_variables: - POSTGRES_STRING: $POSTGRES_STRING + GH_TOKEN: GH_TOKEN PROJECT_ID: $PROJECT_ID DB_USER: $DB_USER DB_NAME: $DB_NAME diff --git a/src/routes/githubValidationRoute.js b/src/routes/githubValidationRoute.js new file mode 100644 index 0000000..503484b --- /dev/null +++ b/src/routes/githubValidationRoute.js @@ -0,0 +1,49 @@ +import express from "express"; + +const ACCOUNT_AGE_MINIMUM_DAYS = 30; +const router = express.Router(); + +const daysSince = (date) => { + const msPerDay = 1000 * 60 * 60 * 24; + return Math.floor((new Date() - new Date(date)) / msPerDay); +}; + +router.get('/gh-validation/:userId', async (req, res) => { + const { userId } = req.params; + + const GH_TOKEN = process.env.GH_TOKEN; + if (!GH_TOKEN) { + return res.status(500).json({ error: "GitHub token not configured." }); + } + + try { + const response = await fetch(`https://api.github.com/user/${userId}`, { + headers: { + Authorization: `token ${GH_TOKEN}`, + Accept: 'application/vnd.github.v3+json' + } + }); + + if (!response.ok) { + const error = await response.json(); + return res.status(response.status).json({ error: error.message || "GitHub API error." }); + } + const userData = await response.json(); + let valid; + + const accountAge = daysSince(userData.created_at); + valid = accountAge >= ACCOUNT_AGE_MINIMUM_DAYS; + + if(!valid){ + console.error(`Github User ID ${userId} is invalid. Username: ${userData.login}`) + } + res.status(200).json({ + valid, + }); + } catch (error) { + console.error("Error calling GitHub API:", error); + res.status(500).json({ error: "Internal server error." }); + } +}); + +export default router; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index ebdb622..070fe0c 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,13 +1,17 @@ import express from 'express'; import rateLimitRoute from './rateLimitRoute.js'; -import solanaBalancesRoute from "./solanaBalancesRoute.js"; // Import the rate limit routes +import solanaBalancesRoute from "./solanaBalancesRoute.js"; +import githubValidationRoute from "./githubValidationRoute.js"; const router = express.Router(); // Use rate limit routes router.use(rateLimitRoute); -// Use solana balances routes +// Use Solana balances routes router.use(solanaBalancesRoute); +// Use Github validation routes +router.use(githubValidationRoute); + export default router; \ No newline at end of file