From 9d669fefc7b093f55736fc4eb3df555d52c51538 Mon Sep 17 00:00:00 2001 From: DriveByAbuser Date: Tue, 11 Aug 2026 00:07:26 +0100 Subject: [PATCH 1/2] Restructure docs and simplify main README Moves detailed project documentation into a new `docs/` section with dedicated pages for overview, architecture, setup, API, integrations, database, testing, and deployment. Updates the root README to be a concise project summary with a table of contents linking to the new docs, making onboarding and navigation clearer. --- README.md | 87 +++++++------------------------------------- docs/README.md | 25 +++++++++++++ docs/api.md | 66 +++++++++++++++++++++++++++++++++ docs/architecture.md | 66 +++++++++++++++++++++++++++++++++ docs/database.md | 32 ++++++++++++++++ docs/deployment.md | 27 ++++++++++++++ docs/integrations.md | 35 ++++++++++++++++++ docs/overview.md | 44 ++++++++++++++++++++++ docs/setup.md | 56 ++++++++++++++++++++++++++++ docs/testing.md | 31 ++++++++++++++++ 10 files changed, 395 insertions(+), 74 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/api.md create mode 100644 docs/architecture.md create mode 100644 docs/database.md create mode 100644 docs/deployment.md create mode 100644 docs/integrations.md create mode 100644 docs/overview.md create mode 100644 docs/setup.md create mode 100644 docs/testing.md diff --git a/README.md b/README.md index e2786e2..abf989d 100644 --- a/README.md +++ b/README.md @@ -2,82 +2,21 @@ ![Python°](app/static/python.png) -> Production ready, open-source FastAPI application with PostgreSQL and blazing-fast full-text search +This project is a FastAPI-based backend for collecting, organizing, and serving business data. It brings together PostgreSQL storage, API endpoints, and a few practical automation features such as AI prompt handling, email sending, and data integration with services like GitHub, Flickr, and YouTube. -#### Overview +The app is designed to be a reliable backend layer for internal tools, admin workflows, or front-end applications that need structured data and simple API access. -This project provides a scalable API backend using FastAPI and PostgreSQL, featuring: +## Table of contents -- Automatic full-text search on all text fields (via tsvector) -- Endpoints for health checks, product management, prompt handling (via `/prompt`), notify email, and prospect management -- Efficient ingestion and processing of large CSV files +- [Project overview](docs/overview.md) +- [Architecture](docs/architecture.md) +- [Setup and development](docs/setup.md) +- [API reference](docs/api.md) +- [Integrations](docs/integrations.md) +- [Database](docs/database.md) +- [Testing](docs/testing.md) +- [Deployment](docs/deployment.md) -#### Features +## Quick note -- **Python 3.11+** -- **FastAPI** — Modern, high-performance REST API -- **PostgreSQL** — Robust relational database -- **tsvector + GIN** — Superfast full-text search -- **Uvicorn** — Lightning-fast ASGI server -- **Pytest** — Comprehensive testing - -#### Install & Use - -#### 1. Clone & Setup Environment - -```bash -git clone https://github.com/goldlabelapps/python.git -cd python -cp .env.sample .env # Add your Postgres credentials and settings -python -m venv venv -source venv/bin/activate -pip install -r requirements.txt -``` - -#### 2. Run the App - -```bash -uvicorn app.main:app --reload -``` - -Visit [localhost:8000](http://localhost:8000) or [onrender](https://nx-ai.onrender.com) - -#### API Documentation - -FastAPI auto-generates interactive docs: - -- [Swagger UI](https://nx-ai.onrender.com/docs) -- [ReDoc](https://nx-ai.onrender.com/redoc) - -#### Notable Endpoints - -- `GET /health` — Health check -- `GET /prompt` or `GET /prompts` — Prompt table metadata (`record_count`, `columns`) -- `POST /prompt` — LLM prompt completion (formerly `/llm`) -- `GET/POST /notify/email` — Send email via Resend API (see implementation in `app/api/notify/email.py`) -- `GET /prospects` — Paginated prospects -- `POST /prospects/process` — Bulk CSV ingestion - -#### Full-Text Search (tsvector) - -The `prospects` table includes a `search_vector` column (type: tsvector) computed from all text fields on insert/update. A GIN index enables fast, scalable full-text search: - -```sql -SELECT * FROM prospects WHERE search_vector @@ plainto_tsquery('english', 'search terms'); -``` - -**How it works:** -- On every insert/update, `search_vector` is computed using PostgreSQL's `to_tsvector('english', ...)`. -- The GIN index (`idx_prospects_search_vector`) enables efficient search across large datasets. - -#### Processing Large CSV Files - -The `/prospects/process` endpoint supports robust ingestion of large CSVs (e.g., 1300+ rows, 300KB+), following the same normalization and insertion pattern as `/prospects/seed` but optimized for scale. - -#### Contributing - -Contributions welcome. Please open issues or submit pull requests. - -#### License - -This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. +If you want to get started, the best place to begin is the [setup guide](docs/setup.md). If you want to understand the system as a whole, start with the [overview](docs/overview.md). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..5ba1eeb --- /dev/null +++ b/docs/README.md @@ -0,0 +1,25 @@ +# Project Documentation + +This directory contains the main documentation for the Python backend service in this repository. + +## Documentation map + +- [Overview](overview.md) — What the application does and why it exists +- [Architecture](architecture.md) — Application structure, runtime flow, and major components +- [Setup](setup.md) — Installation, environment variables, and local development +- [API Reference](api.md) — Routes, request patterns, and response shape +- [Integrations](integrations.md) — Gemini, email, and third-party data connectors +- [Database](database.md) — PostgreSQL usage, schemas, and search capabilities +- [Testing](testing.md) — How the project is tested and how to run tests +- [Deployment](deployment.md) — Render-style deployment considerations and runtime configuration + +## Quick start + +1. Install dependencies with `pip install -r requirements.txt` +2. Create a local environment file with the required variables +3. Start the app with `uvicorn app.main:app --reload` +4. Open the interactive documentation at `/docs` + +## Project summary + +This repository is a FastAPI-based backend that exposes APIs for data storage, retrieval, and automation. It is designed to support business workflows involving prospects, prompts, orders, queue operations, and integrations with external services. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..2532f9a --- /dev/null +++ b/docs/api.md @@ -0,0 +1,66 @@ +# API Reference + +## Core endpoints + +### Root + +- `GET /` — returns basic service metadata such as title, version, and base URL + +### Health + +- `GET /health` — health check endpoint used to confirm the service is available + +### Prompt endpoints + +- `GET /prompt` or `GET /prompts` — returns metadata for the prompt table, including row count and columns +- `POST /prompt` — accepts a prompt payload and returns either cached output or a generated response from Gemini + +### Prospects + +- `GET /prospects` — returns paginated prospects, with optional filtering and search +- `GET /prospects/{id}` — returns one prospect and any related prompt records +- `PATCH /prospects/{id}` — updates flag and hide state +- `PATCH /prospects/factoryreset` — resets prospect flags and hidden state + +### Orders + +- `GET /orders` — returns paginated and filterable order data + +### Queue routes + +The queue module exposes routes for creating, reading, deleting, emptying, and altering queue-related data. + +### Notifications + +- `GET /notify/email` — returns usage information for the email endpoint +- `POST /notify/email` — sends an email through Resend + +### External data endpoints + +- `GET /github` — returns GitHub-related table data +- `GET /flickr` — returns Flickr-related table data +- `GET /youtube` — returns YouTube-related table data + +## Response style + +Most endpoints return a response object shaped like: + +```json +{ + "meta": { + "status": "success", + "message": "..." + }, + "data": {} +} +``` + +## Authentication + +Some routes depend on an API key header: + +```http +X-API-Key: your_key +``` + +The key is validated through the shared authentication utility. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..f8c209c --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,66 @@ +# Architecture + +## Runtime stack + +The application is built around the following core components: + +- FastAPI for HTTP routing and request handling +- PostgreSQL for persistent storage +- Pydantic for request/response validation +- Uvicorn as the ASGI server +- Python dotenv for environment configuration + +## Application entry point + +The main application is initialized in [app/main.py](../app/main.py). It creates the FastAPI app, configures CORS, mounts static files, and includes the API router. + +## Router structure + +The main router is assembled in [app/api/routes.py](../app/api/routes.py). It includes multiple route modules for: + +- root metadata +- health checks +- prompt endpoints +- prospects +- orders +- queue routes +- notifications +- GitHub, Flickr, and YouTube integrations + +## Request flow + +A typical request follows this pattern: + +1. The FastAPI app receives an HTTP request +2. A route handler validates or parses input +3. The handler connects to PostgreSQL through the database utilities +4. Queries or updates are executed +5. A standardized response payload is returned using the shared metadata helper + +## Core modules + +### app/main.py + +Defines the application object and global middleware. + +### app/api + +Contains the route modules and feature-specific endpoints. + +### app/utils + +Contains shared support code for: + +- database connections +- API-key authentication +- response metadata +- health checks + +## Design characteristics + +The architecture favors a simple, service-oriented approach: + +- route modules are feature focused +- database access is centralized +- shared metadata responses keep output consistent +- integrations are isolated into dedicated modules diff --git a/docs/database.md b/docs/database.md new file mode 100644 index 0000000..9d207b4 --- /dev/null +++ b/docs/database.md @@ -0,0 +1,32 @@ +# Database + +## Storage approach + +The application relies on PostgreSQL for persistent storage. Database connection helpers are defined in [app/utils/db.py](../app/utils/db.py). + +## Main data areas + +The app uses several logical data areas: + +- prospects +- prompt history +- orders +- queue-related records +- platform-specific tables for GitHub, Flickr, and YouTube + +## Search capabilities + +The README describes PostgreSQL full-text search support for prospects using `tsvector` and a GIN index. This allows efficient search across text fields. + +## Why the database is central + +The database is the system of record for most application features. It provides: + +- reliable persistence +- filtering and pagination support +- search ability +- historical storage for AI prompt outputs and business records + +## Operational note + +The app expects database connection settings to be present in the environment. If the database is unavailable, many endpoints will not function properly. diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..4ba0944 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,27 @@ +# Deployment + +## Deployment target + +The project is compatible with deployment platforms such as Render. The repository includes a [render.yaml](../render.yaml) configuration file. + +## Runtime considerations + +For deployment, ensure the following are configured: + +- database environment variables +- `PYTHON_KEY` if protected routes are used +- `GEMINI_API_KEY` for prompt generation +- `RESEND_API_KEY` for email sending +- `BASE_URL` for environment-aware metadata + +## Recommended deployment checklist + +1. Set all required environment variables +2. Ensure PostgreSQL is available and reachable +3. Install Python dependencies +4. Run the application with Uvicorn or the deployment platform's startup command +5. Verify core endpoints such as `/health` and `/docs` + +## Notes + +Because the app depends on external services and a database, deployment should be treated as a full-stack environment rather than a simple static app. diff --git a/docs/integrations.md b/docs/integrations.md new file mode 100644 index 0000000..d03dfcf --- /dev/null +++ b/docs/integrations.md @@ -0,0 +1,35 @@ +# Integrations + +## Gemini / Google AI + +The prompt endpoint uses the Google GenAI client to generate completions when no suitable cached response is found. + +Key points: + +- the application reads `GEMINI_API_KEY` from the environment +- prompt requests can be cached in the database +- generated responses are stored with metadata such as model and prompt hash + +## Resend email + +The notify module sends email messages through Resend. + +Key points: + +- the application reads `RESEND_API_KEY` from the environment +- the endpoint accepts recipient, subject, and HTML content +- a template wrapper is used for consistent outbound email formatting + +## GitHub, Flickr, and YouTube + +Separate route modules expose endpoints that read from database tables associated with those platforms. + +These integrations are designed to provide a simple API layer over data that has already been imported or synced into the system. + +## General design + +The integration modules are intentionally isolated so that: + +- external services can be replaced or extended easily +- database access remains centralized +- route code stays simple and focused on HTTP behavior diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..6db307b --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,44 @@ +# Overview + +## Purpose + +This project is a backend application built with FastAPI. Its main purpose is to serve as a data and automation layer for a broader product or business workflow. + +Rather than acting as a standalone website, it focuses on exposing reliable API endpoints that can: + +- store and retrieve business data +- support search and filtering +- connect to external services +- power internal tools or front-end applications + +## What the app does + +The application currently supports several functional areas: + +- health checks and basic service metadata +- prompt handling with optional AI completion +- prospect management and search +- order retrieval and filtering +- queue-related operations for CSV and data processing +- email sending +- integrations with GitHub, Flickr, and YouTube data endpoints + +## Why it exists + +The codebase suggests a goal of combining several operational needs into one backend service: + +1. Centralize data access for multiple sources +2. Provide a consistent API for front-end or admin tools +3. Add automation features such as AI-generated content and notifications +4. Use PostgreSQL for structured storage and search + +## High-level concept + +Think of this app as a service-oriented backend that acts like a hub between: + +- a database +- external APIs +- automation tasks +- business data workflows + +It is especially useful when data needs to be collected, normalized, searched, and surfaced through a simple API interface. diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 0000000..8760b29 --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,56 @@ +# Setup and Local Development + +## Requirements + +- Python 3.11 or newer +- PostgreSQL access +- Optional: environment variables for AI and email services + +## Installation + +From the repository root: + +```bash +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +## Environment variables + +Create a local environment file and configure the required values: + +```bash +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=your_database +DB_USER=your_user +DB_PASSWORD=your_password +BASE_URL=http://localhost:8000 +PYTHON_KEY=your_api_key +GEMINI_API_KEY=your_gemini_key +RESEND_API_KEY=your_resend_key +``` + +## Running the app + +Start the development server: + +```bash +uvicorn app.main:app --reload +``` + +The service will then be available at: + +- http://localhost:8000 +- http://localhost:8000/docs for Swagger UI + +## Static assets + +Static files are mounted under `/static` from the application’s static folder. + +## Notes + +- Some endpoints require the API key header `X-API-Key` +- If the database is not configured correctly, many endpoints will fail at runtime +- The app expects the database schema to exist before it can serve data reliably diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..5f10882 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,31 @@ +# Testing + +## Test framework + +The project uses `pytest` for automated tests. + +## Running tests + +From the repository root: + +```bash +pytest +``` + +## Existing test areas + +The repository includes tests for: + +- GitHub integration behavior +- health endpoints +- metadata helpers +- orders +- prompts +- prospects +- queue routes +- resend email behavior +- route registration + +## Testing approach + +The tests appear to validate behavior at the route and utility level, focusing on expected API responses and core functionality rather than UI interaction. From 270520f0a5b59e7a8fe9481b5e9d80fbe24a70f4 Mon Sep 17 00:00:00 2001 From: DriveByAbuser Date: Tue, 11 Aug 2026 00:19:43 +0100 Subject: [PATCH 2/2] Configure CORS from allowed origins Replace the hardcoded CORS origin list with an `ALLOWED_ORIGINS` environment variable and tighten the default CORS settings. The docs and README now call out the deployment requirement to explicitly add frontend origins so browser requests are not blocked in production. --- README.md | 2 ++ app/__init__.py | 2 +- app/main.py | 39 +++++++++++++++++++------------------- app/static/SVGIcon.sketch | Bin 7243 -> 0 bytes docs/deployment.md | 3 +++ docs/setup.md | 3 +++ 6 files changed, 29 insertions(+), 20 deletions(-) delete mode 100644 app/static/SVGIcon.sketch diff --git a/README.md b/README.md index abf989d..1d68921 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,5 @@ The app is designed to be a reliable backend layer for internal tools, admin wor ## Quick note If you want to get started, the best place to begin is the [setup guide](docs/setup.md). If you want to understand the system as a whole, start with the [overview](docs/overview.md). + +Before deployment, make sure the frontend origin is included in `ALLOWED_ORIGINS`; otherwise browser requests from that domain will be rejected by CORS. diff --git a/app/__init__.py b/app/__init__.py index 1a00f3c..805b7c3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,5 +1,5 @@ """Python° - FastAPI, Postgres, tsvector""" # Current Version -__version__ = "3.1.3" +__version__ = "3.1.4" diff --git a/app/main.py b/app/main.py index 5dcad57..f0c013f 100644 --- a/app/main.py +++ b/app/main.py @@ -15,27 +15,28 @@ version=__version__, ) -# CORS middleware for development +def get_allowed_origins() -> list[str]: + configured_origins = os.getenv("ALLOWED_ORIGINS", "") + if configured_origins: + return [origin.strip() for origin in configured_origins.split(",") if origin.strip()] + + return [ + "http://localhost:3000", + "http://localhost:8000", + "http://127.0.0.1:3000", + "http://127.0.0.1:8000", + "https://goldlabel.pro" + ] + + +# CORS middleware with an explicit, environment-driven allow-list. app.add_middleware( CORSMiddleware, - allow_origins=[ - "http://localhost:1999", - "http://localhost:1998", - "http://localhost:1975", - "http://localhost:1980", - "http://localhost:2027", - "http://localhost:2020", - "http://localhost:2000", - "https://goldlabel.pro", - "https://nx-admin.goldlabel.pro", - "https://free.goldlabel.pro", - "https://listingslab.com", - "https://ed-tech.co", - "https://notheretofuckspiders.art", - ], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"] + allow_origins=get_allowed_origins(), + allow_origin_regex=os.getenv("CORS_ALLOW_ORIGIN_REGEX"), + allow_credentials=False, + allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], + allow_headers=["Accept", "Accept-Language", "Content-Language", "Content-Type", "Authorization", "X-API-Key"], ) diff --git a/app/static/SVGIcon.sketch b/app/static/SVGIcon.sketch deleted file mode 100644 index 9a3e3a1bd1d0efad26581cc3cddc031efe23298d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7243 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 diff --git a/docs/deployment.md b/docs/deployment.md index 4ba0944..bf22bbf 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -13,6 +13,9 @@ For deployment, ensure the following are configured: - `GEMINI_API_KEY` for prompt generation - `RESEND_API_KEY` for email sending - `BASE_URL` for environment-aware metadata +- `ALLOWED_ORIGINS` with the exact frontend origin(s) that will call the API from the browser + +> Deployment gotcha: CORS will block browser requests unless the frontend URL is explicitly allowed. Before deploying, add the production frontend URL to `ALLOWED_ORIGINS` (for example, `https://your-app.example.com`). If you use a different subdomain or preview URL, include that exact origin as well. ## Recommended deployment checklist diff --git a/docs/setup.md b/docs/setup.md index 8760b29..200e862 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -30,8 +30,11 @@ BASE_URL=http://localhost:8000 PYTHON_KEY=your_api_key GEMINI_API_KEY=your_gemini_key RESEND_API_KEY=your_resend_key +ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8000 ``` +> Important: if the API is called from a browser frontend, the frontend origin must be explicitly listed in `ALLOWED_ORIGINS`. This is a common deployment gotcha. If your app is hosted at a production URL such as `https://your-app.example.com`, add that exact origin to the environment variable before deploying. + ## Running the app Start the development server: