{"id":24333357,"url":"https://github.com/ericgitangu/blog","last_synced_at":"2026-04-09T20:05:40.653Z","repository":{"id":164669640,"uuid":"638736833","full_name":"ericgitangu/blog","owner":"ericgitangu","description":"Django-based blog biography","archived":false,"fork":false,"pushed_at":"2024-12-20T04:01:48.000Z","size":3724,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-18T03:15:09.482Z","etag":null,"topics":["azure","azureblobstorage","azureweb","ci","django","django-rest-framework","github-actions","middleware","postgresql","whitenoise"],"latest_commit_sha":null,"homepage":"https://deveric-blog.azurewebsites.net","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericgitangu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-10T02:05:42.000Z","updated_at":"2024-12-20T04:05:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0b2352c-5012-4a4f-8b2d-3bc5318ad372","html_url":"https://github.com/ericgitangu/blog","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fblog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fblog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fblog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fblog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericgitangu","download_url":"https://codeload.github.com/ericgitangu/blog/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243048002,"owners_count":20227592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","azureblobstorage","azureweb","ci","django","django-rest-framework","github-actions","middleware","postgresql","whitenoise"],"created_at":"2025-01-18T03:15:14.832Z","updated_at":"2026-04-09T20:05:40.642Z","avatar_url":"https://github.com/ericgitangu.png","language":"CSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Eric Gitangu's Tech Blog\n\n![CI/CD](https://github.com/ericgitangu/blog/actions/workflows/main_deveric-blog.yml/badge.svg)\n![Django](https://img.shields.io/badge/Django-5.1-green?style=flat-square\u0026logo=django)\n![Python](https://img.shields.io/badge/Python-3.11-blue?style=flat-square\u0026logo=python)\n![Fly.io](https://img.shields.io/badge/Deployed-Fly.io-purple?style=flat-square\u0026logo=fly-dot-io)\n![PostgreSQL](https://img.shields.io/badge/Database-PostgreSQL-blue?style=flat-square\u0026logo=postgresql)\n![WhiteNoise](https://img.shields.io/badge/Static-WhiteNoise-lightgrey?style=flat-square)\n![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)\n\n\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://developer.ericgitangu.com\"\u003e\n        \u003cimg src=\"https://developer.ericgitangu.com/_next/image?url=%2Ffavicon.png\u0026w=128\u0026q=75\" width=\"128\" height=\"128\" style=\"border-radius: 50%; overflow: hidden; display: inline-block;\" alt=\"Eric Gitangu\"/\u003e\n        \u003cbr/\u003e\n        \u003ch2\u003eEric Gitangu\u003c/h2\u003e\n    \u003c/a\u003e\n\nA Django-powered blog showcasing insights in technology, AI, systems architecture, cybersecurity, and software development.\n\n**Live:** [blog.ericgitangu.com](https://blog.ericgitangu.com)\n\n\u003c/div\u003e\n\n---\n\n## Features\n\n- **Dynamic Content Management**: Built-in Django admin interface for content creation\n- **Fly.io Deployment**: Production hosting with PostgreSQL and persistent volumes\n- **Production-Ready Security**: HTTPS, HSTS, secure cookies, and CSRF protection\n- **PostgreSQL Database**: Robust data storage with connection pooling\n- **WhiteNoise Static Files**: Compressed and cached static file serving\n- **Console Logging**: Streamlined logging for fly.io log aggregation\n\n## Tech Stack\n\n| Category | Technology |\n|----------|------------|\n| **Framework** | Django 5.1 |\n| **Language** | Python 3.11 |\n| **Database** | PostgreSQL (Fly Postgres) |\n| **Static Files** | WhiteNoise |\n| **Media Storage** | Fly Volumes |\n| **Server** | Gunicorn |\n| **Deployment** | Fly.io |\n\n## Architecture\n\n```mermaid\nflowchart TB\n    subgraph Client [\"Client Layer\"]\n        Browser[Web Browser]\n        Mobile[Mobile Browser]\n    end\n\n    subgraph Edge [\"Fly.io Edge\"]\n        LB[Load Balancer]\n        SSL[SSL/TLS Termination]\n    end\n\n    subgraph App [\"Application Layer\"]\n        subgraph Django [\"Django Application\"]\n            WSGI[Gunicorn WSGI]\n            MW[Middleware Stack]\n            Views[Views \u0026 Templates]\n            Admin[Admin Dashboard]\n            Models[ORM Models]\n        end\n\n        subgraph Static [\"Static Files\"]\n            WN[WhiteNoise]\n            CSS[CSS/JS Assets]\n        end\n    end\n\n    subgraph Data [\"Data Layer\"]\n        PG[(PostgreSQL)]\n        Vol[Fly Volume\u003cbr\u003e/data/media]\n    end\n\n    subgraph Models_Detail [\"Data Models\"]\n        Post[Post]\n        Author[Author]\n        Tag[Tag]\n        Comments[Comments]\n    end\n\n    Browser --\u003e LB\n    Mobile --\u003e LB\n    LB --\u003e SSL\n    SSL --\u003e WSGI\n    WSGI --\u003e MW\n    MW --\u003e Views\n    MW --\u003e Admin\n    Views --\u003e Models\n    Admin --\u003e Models\n    Models --\u003e PG\n    Views --\u003e WN\n    WN --\u003e CSS\n    Models -.-\u003e Vol\n\n    Post --\u003e Author\n    Post --\u003e Tag\n    Comments --\u003e Post\n\n    style Django fill:#092E20,color:#fff\n    style PG fill:#336791,color:#fff\n    style Edge fill:#7C3AED,color:#fff\n```\n\n### Component Interaction Flow\n\n```mermaid\nsequenceDiagram\n    participant U as User\n    participant F as Fly.io Edge\n    participant G as Gunicorn\n    participant D as Django\n    participant P as PostgreSQL\n    participant V as Volume\n\n    U-\u003e\u003eF: HTTPS Request\n    F-\u003e\u003eG: Forward Request\n    G-\u003e\u003eD: WSGI Handler\n\n    alt Static Asset\n        D-\u003e\u003eD: WhiteNoise serves\n        D--\u003e\u003eU: Static file\n    else Blog Post\n        D-\u003e\u003eP: Query posts\n        P--\u003e\u003eD: Post data\n        D-\u003e\u003eV: Fetch media\n        V--\u003e\u003eD: Image files\n        D--\u003e\u003eU: Rendered HTML\n    else Admin Action\n        D-\u003e\u003eD: Auth check\n        D-\u003e\u003eP: CRUD operation\n        P--\u003e\u003eD: Confirmation\n        D--\u003e\u003eU: Admin response\n    end\n```\n\n## Local Development\n\n### Prerequisites\n- Python 3.11+\n- PostgreSQL (or use SQLite for local dev)\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/ericgitangu/blog.git\ncd blog\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate  # On Windows: venv\\Scripts\\activate\n\n# Install dependencies\npip install -r requirements.txt\n\n# Set environment variables\nexport SECRET_KEY=\"your-secret-key\"\nexport DEBUG=\"True\"\nexport DBNAME=\"blog_dev\"\nexport DBUSER=\"postgres\"\nexport DBPASS=\"password\"\nexport DBHOST=\"localhost\"\nexport DBPORT=\"5432\"\n\n# Run migrations\npython manage.py migrate\n\n# Create superuser\npython manage.py createsuperuser\n\n# Run development server\npython manage.py runserver\n```\n\n## Deployment to Fly.io\n\n### Prerequisites\n- [Fly CLI](https://fly.io/docs/hands-on/install-flyctl/)\n- Fly.io account\n\n### Deploy\n\n```bash\n# Authenticate\nfly auth login\n\n# Create app and Postgres\nfly apps create deveric-blog\nfly postgres create --name deveric-blog-db --region iad\nfly postgres attach deveric-blog-db --app deveric-blog\n\n# Create volume for media files\nfly volumes create blog_data --region iad --size 1 --app deveric-blog\n\n# Set secrets\nfly secrets set SECRET_KEY=\"$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')\" --app deveric-blog\nfly secrets set DEBUG=\"False\" --app deveric-blog\n\n# Deploy\nfly deploy --app deveric-blog\n\n# Create superuser\nfly ssh console --app deveric-blog\npython manage.py create_superuser --username egitangu --email admin@ericgitangu.com --password yourpassword\n```\n\n### Custom Domain\n\n```bash\nfly certs add blog.ericgitangu.com --app deveric-blog\nfly ips list --app deveric-blog\n# Add A/AAAA records in your DNS provider\n```\n\n## Project Structure\n\n```\nblog/\n├── blog/                 # Django project settings\n│   ├── settings.py       # Configuration (Fly.io aware)\n│   ├── urls.py           # URL routing\n│   └── wsgi.py           # WSGI entry point\n├── portfolio/            # Main blog app\n│   ├── models.py         # Post, Author, Tag, Comments\n│   ├── views.py          # ListView, DetailView\n│   ├── admin.py          # Admin customization\n│   └── templates/        # HTML templates\n├── Dockerfile            # Multi-stage production build\n├── fly.toml              # Fly.io configuration\n└── requirements.txt      # Python dependencies\n```\n\n## Security\n\n- HTTPS enforced via Fly.io\n- HSTS with 1-year max-age\n- Secure session and CSRF cookies\n- Password validation policies\n- XFrame options protection\n\n## Connect\n\n- **Portfolio**: [developer.ericgitangu.com](https://developer.ericgitangu.com)\n- **LinkedIn**: [linkedin.com/in/ericgitangu](https://linkedin.com/in/ericgitangu)\n- **GitHub**: [github.com/ericgitangu](https://github.com/ericgitangu)\n- **Blog**: [blog.ericgitangu.com](https://blog.ericgitangu.com)\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericgitangu%2Fblog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericgitangu%2Fblog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericgitangu%2Fblog/lists"}