Skip to content

Commit 10eb241

Browse files
committed
init
1 parent 92d6504 commit 10eb241

File tree

8 files changed

+353
-2
lines changed

8 files changed

+353
-2
lines changed

.github/workflows/deploy.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Deploy to App Engine
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
deploy:
10+
name: Deploying to Google App Engine
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout Code
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 20.x
21+
22+
- name: Install Dependencies
23+
run: yarn install
24+
25+
- name: Authenticate with Google Cloud
26+
uses: google-github-actions/auth@v2
27+
with:
28+
token_format: 'access_token'
29+
credentials_json: ${{ secrets.GCP_KEY }}
30+
31+
- name: Setup Google Cloud SDK
32+
uses: google-github-actions/setup-gcloud@v2
33+
34+
- name: Configure gcloud CLI
35+
run: |-
36+
gcloud config set account ${{ secrets.SERVICE_ACCOUNT }}
37+
gcloud config set project ${{ secrets.PROJECT_ID }}
38+
39+
- name: Deploy to App Engine
40+
uses: google-github-actions/deploy-appengine@v2
41+
with:
42+
deliverables: app.yaml

README.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
2+
# Faucet Backend API
3+
4+
This API provides endpoints for interacting with two main tables: `faucet.solana_balances` and `faucet.rate_limits`. Below are the available endpoints for each table.
5+
6+
---
7+
## How to Run
8+
9+
1. Clone the repository
10+
```bash
11+
git clone <repository-url>
12+
```
13+
14+
2. Install dependencies
15+
```bash
16+
yarn install
17+
```
18+
19+
3. Set up your `.env` file with the following
20+
```env
21+
POSTGRES_STRING=postgresql://<user>:<password>@<host>:<port>/<database>
22+
```
23+
24+
4. Start the server
25+
```bash
26+
yarn start
27+
```
28+
29+
5. Access the API at `http://localhost:3000/api`.
30+
31+
---
32+
33+
## Solana Balances Endpoints
34+
35+
### **Create a New Solana Balance**
36+
37+
**POST** `/api/solana-balances`
38+
39+
- **Description**: Adds a new Solana account balance.
40+
- **Request Body**:
41+
```json
42+
{
43+
"account": "string",
44+
"balance": "number"
45+
}
46+
```
47+
- **Curl Command**:
48+
```bash
49+
curl -v -X POST http://localhost:3000/api/solana-balances \
50+
-H "Content-Type: application/json" \
51+
-d '{"account": "test_account_1", "balance": 100.50}'
52+
```
53+
- **Response**:
54+
```json
55+
{
56+
"id": 1,
57+
"account": "string",
58+
"balance": "number",
59+
"date": "timestamp"
60+
}
61+
```
62+
63+
### **Get All Balances for an Account**
64+
65+
**GET** `/api/solana-balances/account/:account`
66+
67+
- **Description**: Retrieves all balances for a specific Solana account.
68+
- **Curl Command**:
69+
```bash
70+
curl -v http://localhost:3000/api/solana-balances/account/test_account_1
71+
```
72+
- **Response**:
73+
```json
74+
[
75+
{
76+
"id": 1,
77+
"account": "string",
78+
"balance": "number",
79+
"date": "timestamp"
80+
},
81+
...
82+
]
83+
```
84+
85+
### **Get Recent Balances (Last Month)**
86+
87+
**GET** `/api/solana-balances/recent`
88+
89+
- **Description**: Retrieves all Solana account balances from the past month, ordered by date.
90+
- **Curl Command**:
91+
```bash
92+
curl -v http://localhost:3000/api/solana-balances/recent
93+
```
94+
- **Response**:
95+
```json
96+
[
97+
{
98+
"account": "string",
99+
"balance": "number",
100+
"date": "timestamp"
101+
},
102+
...
103+
]
104+
```
105+
106+
---
107+
108+
## Rate Limits Endpoints
109+
110+
### **Create a New Rate Limit**
111+
112+
**POST** `/api/rate-limits`
113+
114+
- **Description**: Adds a new rate limit entry.
115+
- **Request Body**:
116+
```json
117+
{
118+
"key": "string",
119+
"timestamps": ["number"]
120+
}
121+
```
122+
- **Curl Command**:
123+
```bash
124+
curl -v -X POST http://localhost:3000/api/rate-limits \
125+
-H "Content-Type: application/json" \
126+
-d '{"key": "test_key_1", "timestamps": [1635793421]}'
127+
```
128+
- **Response**:
129+
```json
130+
{
131+
"key": "string",
132+
"timestamps": ["number"]
133+
}
134+
```
135+
136+
### **Get a Rate Limit by Key**
137+
138+
**GET** `/api/rate-limits/:key`
139+
140+
- **Description**: Retrieves the rate limit entry for a specific key.
141+
- **Curl Command**:
142+
```bash
143+
curl -v http://localhost:3000/api/rate-limits/test_key_1
144+
```
145+
- **Response**:
146+
```json
147+
{
148+
"key": "string",
149+
"timestamps": ["number"]
150+
}
151+
```
152+
153+
### **Update Timestamps for a Rate Limit**
154+
155+
**PUT** `/api/rate-limits/:key`
156+
157+
- **Description**: Updates the timestamps for a specific rate limit key.
158+
- **Request Body**:
159+
```json
160+
{
161+
"timestamps": ["number"]
162+
}
163+
```
164+
- **Curl Command**:
165+
```bash
166+
curl -v -X PUT http://localhost:3000/api/rate-limits/test_key_1 \
167+
-H "Content-Type: application/json" \
168+
-d '{"timestamps": [1635793500]}'
169+
```
170+
- **Response**:
171+
```json
172+
{
173+
"key": "string",
174+
"timestamps": ["number"]
175+
}
176+
```
177+
178+
---
179+
180+
## Error Handling
181+
182+
All endpoints return appropriate HTTP status codes:
183+
- `201 Created` for successful creations.
184+
- `200 OK` for successful data retrieval or updates.
185+
- `404 Not Found` if the requested resource does not exist.
186+
- `500 Internal Server Error` for unhandled exceptions.

app.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
runtime: nodejs20
2+
env: standard
3+
4+
# Define the entry point for your application
5+
entrypoint: yarn start
6+
7+
# Environment variables (replace with your actual values or secrets)
8+
env_variables:
9+
POSTGRES_STRING: ${{ secrets.POSTGRES_STRING }}
10+
11+
# Automatic scaling configuration
12+
automatic_scaling:
13+
target_cpu_utilization: 0.65
14+
target_throughput_utilization: 0.75
15+
min_instances: 1
16+
max_instances: 5
17+
18+
# Network configuration
19+
network:
20+
instance_tag: solana-devnet-faucet-backend
21+
name: default
22+
23+
# Optional: Specify resources
24+
resources:
25+
cpu: 1
26+
memory_gb: 0.5
27+
disk_size_gb: 10
28+
29+
# Optional: Health check configuration
30+
health_check:
31+
enable_health_check: True
32+
check_interval_sec: 10
33+
timeout_sec: 4
34+
unhealthy_threshold: 2
35+
healthy_threshold: 2

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "index.js",
66
"type": "module",
77
"scripts": {
8+
"start": "node app.js",
89
"test": "echo \"Error: no test specified\" && exit 1"
910
},
1011
"keywords": [],

src/db/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import dotenv from 'dotenv';
2-
import { Pool } from 'pg'; // Use 'mysql2/promise' if using MySQL
2+
import pg from 'pg'; // Use 'mysql2/promise' if using MySQL
3+
const { Pool } = pg;
34

45
dotenv.config();
56

src/db/solanaBalances.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import db from './config.js'; // Import the database config
2+
3+
// CREATE a new Solana balance
4+
const createSolanaBalance = async (account, balance) => {
5+
const query = `
6+
INSERT INTO faucet.solana_balances (account, balance)
7+
VALUES ($1, $2);
8+
`;
9+
const values = [account, balance];
10+
const result = await db.query(query, values);
11+
return result.rows[0];
12+
};
13+
14+
// READ all Solana balances for an account
15+
const getSolanaBalancesByAccount = async (account) => {
16+
const query = `
17+
SELECT * FROM faucet.solana_balances
18+
WHERE account = $1
19+
ORDER BY date DESC;
20+
`;
21+
const values = [account];
22+
const result = await db.query(query, values);
23+
return result.rows;
24+
};
25+
26+
const getRecentBalances = async () => {
27+
const query = `
28+
SELECT account, balance, date
29+
FROM faucet.solana_balances
30+
WHERE date >= CURRENT_DATE - INTERVAL '1 month'
31+
ORDER BY date;
32+
`;
33+
const result = await db.query(query);
34+
return result.rows;
35+
};
36+
37+
export default {
38+
createSolanaBalance,
39+
getRecentBalances,
40+
getSolanaBalancesByAccount,
41+
};

src/routes/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import express from 'express';
2-
import rateLimitRoute from './rateLimitRoute.js'; // Import the rate limit routes
2+
import rateLimitRoute from './rateLimitRoute.js';
3+
import solanaBalancesRoute from "./solanaBalancesRoute.js"; // Import the rate limit routes
34

45
const router = express.Router();
56

67
// Use rate limit routes
78
router.use(rateLimitRoute);
89

10+
// Use solana balances routes
11+
router.use(solanaBalancesRoute);
12+
913
export default router;

src/routes/solanaBalancesRoute.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import express from 'express';
2+
import solanaBalances from '../db/solanaBalances.js'; // Import the CRUD methods
3+
4+
const router = express.Router();
5+
6+
// CREATE a new Solana balance
7+
router.post('/solana-balances', async (req, res, next) => {
8+
const { account, balance } = req.body;
9+
10+
try {
11+
const newBalance = await solanaBalances.createSolanaBalance(account, balance);
12+
res.status(201).json(newBalance);
13+
} catch (error) {
14+
next(error);
15+
}
16+
});
17+
18+
// READ all Solana balances for an account
19+
router.get('/solana-balances/account/:account', async (req, res, next) => {
20+
const { account } = req.params;
21+
22+
try {
23+
const balances = await solanaBalances.getSolanaBalancesByAccount(account);
24+
res.status(200).json(balances);
25+
} catch (error) {
26+
next(error);
27+
}
28+
});
29+
30+
// GET recent Solana balances (last month)
31+
router.get('/solana-balances/recent', async (req, res, next) => {
32+
try {
33+
const recentBalances = await solanaBalances.getRecentBalances();
34+
res.status(200).json(recentBalances);
35+
} catch (error) {
36+
next(error);
37+
}
38+
});
39+
40+
41+
export default router;

0 commit comments

Comments
 (0)