Skip to content

Repository files navigation

AI Article Generator

Generate a structured, illustrated article from a single keyword. Pick a topic, pick the sections you want, and the app writes the introduction, each section, the conclusion, and a cover image using the OpenAI API.

Stack: Django 5.2 + Django REST Framework · React 19 + Vite 7 + Tailwind CSS 3

The application is stateless — it defines no models and stores no user content. Generated articles are returned in the HTTP response and never persisted.


Quick start

Requires Python 3.12+ and Node 22 (see frontend/.nvmrc). No database server is needed: the default configuration uses SQLite, which Django creates on first run.

Backend

cd backend
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt

cp .env.example .env             # then set OPENAI_API_KEY and DEBUG=True
python manage.py migrate
python manage.py runserver

The API is now on http://localhost:8000/api/.

Frontend

cd frontend
npm ci
cp .env.example .env             # defaults already point at localhost:8000
npm run dev

The UI is now on http://localhost:5173.

Docker Compose

cp backend/.env.example backend/.env
# set OPENAI_API_KEY and DJANGO_SECRET_KEY in that file
docker compose up --build

Frontend on http://localhost:5173, backend on http://localhost:8000. Both containers run production servers (gunicorn and nginx) as non-root users.


Configuration

Backend — backend/.env

Variable Default Description
DJANGO_SECRET_KEY (dev fallback) Django signing key. Required when DEBUG=False — startup fails if unset.
DEBUG False Enables the insecure dev fallback for the secret key. Never enable in production.
ALLOWED_HOSTS localhost,127.0.0.1,[::1] Comma-separated. * is rejected when DEBUG=False.
CSRF_TRUSTED_ORIGINS (empty) Comma-separated absolute origins.
CORS_ALLOWED_ORIGINS http://localhost:5173,http://127.0.0.1:5173 Origins allowed to call the API from a browser. Never a wildcard.
LOG_LEVEL INFO Root logger level.
SECURE_SSL_REDIRECT True Applied only when DEBUG=False.
SECURE_HSTS_SECONDS 31536000 Applied only when DEBUG=False. 0 disables HSTS.
OPENAI_API_KEY (empty) Required for generation. Without it the API returns 503.
OPENAI_CHAT_MODEL gpt-4o-mini Model used for all text generation.
OPENAI_IMAGE_MODEL dall-e-2 Model used for the cover image.
OPENAI_IMAGE_SIZE 512x512 Cover image dimensions.
OPENAI_TIMEOUT_SECONDS 60 Per-request timeout for the OpenAI client.
DB_ENGINE django.db.backends.sqlite3 Set to django.db.backends.mysql or ...postgresql to use a server.
DB_NAME <BASE_DIR>/db.sqlite3 Database name, or SQLite file path.
DB_USER / DB_PASSWORD / DB_HOST / DB_PORT (empty) Ignored by SQLite.

Non-SQLite engines need their driver installed separately — pip install mysqlclient or pip install "psycopg[binary]".

Frontend — frontend/.env

Variable Default Description
VITE_API_URL http://localhost:8000/api Base URL of the API, including the /api prefix. Inlined at build time, so it is public — never put a secret in a VITE_ variable.

API reference

All endpoints accept and return JSON. There is no authentication; deploy behind your own gateway if you need it.

Method Endpoint Description
GET /healthz/ Liveness probe. Returns {"status": "ok"}.
POST /api/search_topics/ Suggest article topics for a keyword.
POST /api/select_topics/ List the sections available for a chosen topic.
POST /api/generate_article/ Generate the full article.
/admin/ Django admin. No app models are registered.

POST /api/search_topics/

// request
{ "keyword": "kubernetes" }

// 200
{
  "keyword": "kubernetes",
  "topics": { "1": "Kubernetes For Beginners", "2": "Scaling Workloads", "3": "kubernetes" }
}

The keyword itself is always appended as the final topic so it can be used verbatim.

POST /api/select_topics/

Send either topic_id (with the topics map from the previous call) or custom_topic — never both.

// request
{ "topic_id": 1, "topics": { "1": "Kubernetes For Beginners" } }

// 200
{
  "selected_topics": { "1": "Kubernetes For Beginners" },
  "topic_sections": {
    "1": { "sections": { "1": "What Is A Pod", "2": "Services And Ingress" }, "type": "predefined" }
  }
}

For a custom topic the key is custom_1 and type is "custom".

POST /api/generate_article/

// request
{
  "selected_sections": [{ "section_name": "What Is A Pod" }],
  "selected_topics": { "1": "Kubernetes For Beginners" },
  "topic_sections": { "1": { "sections": ["What Is A Pod", "Services And Ingress"] } },
  "keyword": "kubernetes"
}

// 200
{
  "keyword": "kubernetes",
  "main_topic": "kubernetes",
  "selected_topic": { "1": "Kubernetes For Beginners" },
  "section_headers": ["What Is A Pod"],
  "introduction": "...",
  "conclusion": "...",
  "image_url": "https://...",
  "section_content": { "What Is A Pod": "..." }
}

Sections that the chosen topic did not offer are dropped; if none remain the request is rejected with 400.

Error responses

Status Meaning
400 Validation failed, or the selection was inconsistent. Body carries the field errors.
502 The OpenAI API failed or returned unusable content.
503 OPENAI_API_KEY is not configured on the server.

A failed generation is never returned as a 200 with empty content.


Limitations

  • No authentication and no rate limiting. Every request spends OpenAI credits, so do not expose this to the open internet without a gateway that enforces both.
  • Cost and latency scale with section count. One article is 3 + n chat completions plus one image; a 6-section article takes roughly 30–60 seconds.
  • Article state travels in the URL between pages. Very long topic lists can approach browser query-string limits.
  • image_url points at OpenAI's CDN and expires (typically within an hour). Download it if you need to keep it.
  • Generated content is unverified. The model can and does state things that are wrong. Treat every article as a first draft.
  • No streaming. The full article is returned in one response.
  • English only — prompts are hardcoded in English.

Development

Setup, the exact commands CI runs, and the project layout are documented in CONTRIBUTING.md. Security reporting and a pre-deployment checklist are in SECURITY.md.

Licence

Copyright (C) 2026 Article Generator contributors.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file for the full text.

About

Article generator designed to help bloggers, marketers, businesses, and content creators produce high-quality, SEO-friendly articles faster and more efficiently.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages