No updates detected yet. GitHub releases will appear here.
Secret Santa web app
翻訳待ち
Loading the full README.
Read the full README ↗Invite your buddies and do a secret santa draw. Trust me, it will be really random. Like, quantum physics level of random
You'll need the following in your dev machine to get started
cd into itgit clone [email protected]:prithajnath/my-secret-santa.git && cd my-secret-santa
$ uv sync
This starts the API (on https://localhost:9100), Postgres with PostGIS, Redis and the Celery worker. Migrations run when the web container starts.
$ docker compose up --build -d
The tests use pytest-postgresql to create a throwaway PostGIS database per test on the compose Postgres server, so run them in a container on the compose network:
$ docker compose run --rm --no-deps --entrypoint bash secret-santa-web \
-c "uv sync --frozen --group dev -q && pytest tests"
Set TEST_DATABASE_URL to point them at a different PostGIS server.
The hook checks that uv.lock matches pyproject.toml and that requirements.txt matches uv.lock
git config core.hooksPath .githooks
If requirements.txt is out of sync, update it and try committing again
$ uv export --format requirements.txt --no-dev -o requirements.txt
This service only serves the REST API (/api) and the admin panel (/admin); the web app is the separate Next.js frontend, and / redirects to it. Admins log in at /admin/login/ with a username and password. To create an admin, or set a password for an existing one (e.g. an account made with Google sign-in):
$ flask createsuperuser
$ flask set-password <username>
The application includes a REST API layer for modern frontend frameworks (React, Next.js, etc.) with JWT authentication.
Interactive Swagger documentation is available at:
https://localhost:9100/api/docs/
The API uses JWT Bearer tokens. Include the token in the Authorization header:
Authorization: Bearer <your_access_token>
| Category | Endpoints |
|---|---|
| Auth | /api/auth/register, /api/auth/login, /api/auth/refresh, /api/auth/logout |
| OAuth | /api/auth/oauth/google/url, /api/auth/oauth/google/callback |
| Users | /api/users/me, /api/users/<username> |
| Groups | /api/groups, /api/groups/<id>, /api/groups/<id>/members, /api/groups/<id>/pairs |
| Messages | /api/groups/<id>/messages, /api/pairs/<channel_id>/messages |
| Santa | /api/santa, /api/santa/reveal |
// Login
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'user', password: 'password' })
});
const { data } = await res.json();
const token = data.access_token;
// Use authenticated endpoints
const groups = await fetch('/api/groups', {
headers: { 'Authorization': `Bearer ${token}` }
});
The API supports Google OAuth for sign-in/sign-up using the token exchange pattern.
https://yourapp.com/oauth/callback)export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
const redirectUri = `${window.location.origin}/oauth/callback`;
const res = await fetch(
`/api/auth/oauth/google/url?redirect_uri=${encodeURIComponent(redirectUri)}`
);
const { data } = await res.json();
// Redirect user to Google
window.location.href = data.oauth_url;
After the user authorizes, Google redirects to your callback URL with a code parameter:
// On your /oauth/callback page
const handleOAuthCallback = async () => {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const res = await fetch('/api/auth/oauth/google/callback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
redirect_uri: `${window.location.origin}/oauth/callback`
})
});
const { data } = await res.json();
// Store tokens
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
// Check if new user for onboarding
if (data.user.is_new_user) {
router.push('/welcome');
} else {
router.push('/dashboard');
}
};
{
"success": true,
"data": {
"user": {
"id": 123,
"username": "john",
"email": "[email protected]",
"first_name": "John",
"last_name": "Doe",
"avatar_url": "https://...",
"is_new_user": false
},
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}
}
is_new_user flag helps identify first-time users for onboarding flowsredirect_uri must match exactly in both the URL request and callback exchangeInitially I exposed gunicorn workers to direct Internet traffic from ELB. This setup suffered multiple worker shutdowns. Most suggestions online said I needed to use a gevent or eventlet worker pool but I decided to expose an NGINX container to ELB traffic and send that traffic upstream (gunicorn workers). Almost immediately I saw drastically better response times
This is the beginning of the README. Read the rest at the original. Read the full README ↗