BlogHub is a small full-stack app for discovering and sharing web publications: a FastAPI backend with PostgreSQL, a React frontend, Google sign-in, and optional enrichment when scraping links (including Google Gemini if you configure an API key).
This README assumes you are on macOS or Linux. On Windows, use venv\Scripts\activate and venv\Scripts\uvicorn.exe where paths below use venv/bin/.
- Python 3.11 or newer (3.12 is fine).
- Node.js 18 or newer and npm.
- PostgreSQL running locally (or any host you can put in
DATABASE_URL). - A Google OAuth client ID if you want sign-in and the landing page button to work (both backend and frontend use it).
Clone the repository after you create it on GitHub (replace the URL with yours):
git clone https://github.com/YOUR_USERNAME/YOUR_REPO.git
cd YOUR_REPO-
Create and activate a virtual environment (recommended so dependencies stay isolated):
cd backend python3 -m venv venv source venv/bin/activate pip install -U pip pip install -r requirements.txt
-
Configure environment variables. Copy the example file and edit it:
cp .env.example .env
At minimum set
DATABASE_URLto your PostgreSQL connection string,JWT_SECRETto a long random string,GOOGLE_CLIENT_IDto match your OAuth client, andFRONTEND_URLto your dev frontend (defaulthttp://localhost:5173).GEMINI_API_KEYandGEMINI_MODELare optional and only affect optional description enrichment on scrape. -
Apply database migrations:
source venv/bin/activate # if not already active alembic upgrade head
-
Start the API.
From the
backenddirectory with the virtual environment activated:uvicorn app.main:app --reload --port 8000
Why people often use
python3 -m uvicorn ...: if the virtualenv is not activated, the shell picks up whicheverpython3is on your PATH and you run Uvicorn as a module so imports resolve. Once the venv is active,uvicornalone works because pip installs theuvicornexecutable intovenv/bin.Alternative without typing
activate: from the repository root you can run:make backend
That runs
backend/venv/bin/uvicorndirectly. First-time setup can usemake install-backendto create the venv and install requirements (still usespython3 -m venvonce, which is the standard way to create the environment).
First-time migrations from the repo root:
make db-upgrade-
Install dependencies:
cd frontend npm install -
Environment file for Vite:
cp .env.example .env
Set
VITE_API_URLto your API origin (usuallyhttp://localhost:8000) andVITE_GOOGLE_CLIENT_IDto the same client ID as in the backend. -
Dev server:
npm run dev
From the repo root you can instead run:
make frontendProduction build:
cd frontend
npm run buildThe build runs TypeScript checking then Vite. Output goes to frontend/dist/ (ignored by git).
backend/– FastAPI app (app.main:app), SQLAlchemy, Alembic, scraper and auth routes.frontend/– Vite + React + TypeScript client.docker-compose.yml– production-style stack: PostgreSQL, API, and Nginx (static app plus reverse proxy to the API).deploy.env.example– template for the variables Docker Compose reads from a root.envfile.
The compose file runs three services: Postgres, the FastAPI app, and an Nginx container that serves the built frontend and proxies /api/*, /auth/*, and /health to the backend. Images use restart: unless-stopped, so after a reboot they come back when the Docker daemon starts (enable Docker on boot: sudo systemctl enable docker on Ubuntu).
-
Install Docker Engine and the Compose plugin on the droplet (official Docker docs for your distro).
-
Clone the repo and
cdinto it. -
Create the deployment env file at the repository root (Compose loads
.envautomatically; this is separate frombackend/.envused for local dev):cp deploy.env.example .env
Edit
.env. SetPOSTGRES_PASSWORD,JWT_SECRET,GOOGLE_CLIENT_ID, andFRONTEND_URLto the exact public URL visitors use (for examplehttp://203.0.113.10orhttps://blog.example.com, no trailing slash). In Google Cloud Console, add that same origin under the OAuth client’s authorized JavaScript origins. -
Build and start:
docker compose up -d --build
-
Open port 80 in the droplet firewall and (if applicable) the cloud firewall. Default HTTP port is 80; override with
HTTP_PORTin.envif needed.
The API container runs alembic upgrade head on each start, then Uvicorn. Database files live in the postgres_data volume.
The browser talks to the same origin only: the frontend is built with an empty VITE_API_URL so requests go to /api/... and /auth/... on the same host Nginx serves. The backend’s FRONTEND_URL must still match that public URL for CORS.
To watch logs: docker compose logs -f. To stop: docker compose down (add -v to remove the Postgres volume and data).
If Git is not initialized yet:
cd /path/to/BlogHub
git init
git add .
git commit -m "Initial commit"Create an empty repository on GitHub (no README if you already have one locally), then:
git branch -M main
git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO.git
git push -u origin mainUse SSH instead of HTTPS if you prefer:
git remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.git
git push -u origin mainDo not commit real secrets. This repo ignores .env files; commit only .env.example with placeholders.
uvicorn: command not found: Activatebackend/venv/bin/activate, or runmake backend, or call./venv/bin/uvicornfrom insidebackend.- Database errors about missing columns: Run
alembic upgrade headagainst the database in yourDATABASE_URL. - CORS errors in the browser: Ensure
FRONTEND_URLin the backend.envmatches exactly how you open the app (scheme, host, and port). - Docker 502 from Nginx: The
webservice can start before the API is listening; wait a few seconds or rundocker compose logs backend. EnsureFRONTEND_URLin the root.envmatches the URL in the browser.