From 996c50841bd48976ceeb76181fc2ec4f4bd76946 Mon Sep 17 00:00:00 2001 From: thecyberlearn Date: Sat, 30 Aug 2025 09:38:51 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20TRANSFORM:=20Django=20VPS=20Depl?= =?UTF-8?q?oyment=20Toolkit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔄 Complete transformation from mixed demo project to pure deployment toolkit REMOVED: ❌ Django demo project (core/, demo_project/, manage.py, requirements.txt) ❌ Demo-specific files and configurations ❌ Mixed-purpose confusion RESTRUCTURED: ✅ deploy-django-project.sh - Universal Django deployment ✅ setup-django-user.sh - VPS user setup ✅ setup-multi-webhook.sh - Auto-deploy webhooks ✅ webhook-router.py - Multi-project webhook handler ✅ templates/ - Configuration templates ✅ Clean root-level organization NEW PURPOSE: đŸŽ¯ Universal toolkit to deploy ANY Django project to VPS đŸˇī¸ Auto-extracts GitHub repo names 🔄 Multi-project support with path-based routing 📡 GitHub webhook auto-deploy integration 🔒 Secure non-root deployment BENEFITS: - Deploy any Django project with one command - Zero configuration required - Professional deployment toolkit - Reusable for unlimited projects - Industry-standard VPS setup Now it's a PURE deployment toolkit, not a demo project! 🎉 --- README.md | 186 ++++++++++--- core/__init__.py | 0 core/admin.py | 21 -- core/apps.py | 6 - core/forms.py | 27 -- core/migrations/0001_initial.py | 44 ---- core/migrations/__init__.py | 0 core/models.py | 31 --- core/static/core/css/style.css | 30 --- core/static/core/js/main.js | 23 -- core/templates/core/about.html | 63 ----- core/templates/core/base.html | 63 ----- core/templates/core/blog.html | 31 --- core/templates/core/contact.html | 68 ----- core/templates/core/home.html | 77 ------ core/tests.py | 3 - core/urls.py | 9 - core/views.py | 31 --- demo_project/__init__.py | 0 demo_project/asgi.py | 16 -- demo_project/settings.py | 162 ------------ demo_project/urls.py | 23 -- demo_project/wsgi.py | 16 -- ...loy-project.sh => deploy-django-project.sh | 6 +- deploy/gunicorn.conf.py | 35 --- deploy/nginx.conf | 66 ----- deploy/systemd.service | 22 -- deploy/webhook-receiver.py | 248 ------------------ deploy/webhook-service.sh | 162 ------------ manage.py | 22 -- requirements.txt | 13 - ...multi-webhook.sh => setup-multi-webhook.sh | 0 .../.env.production.template | 0 .../gunicorn.service.template | 0 .../gunicorn.socket.template | 0 .../nginx.conf.template | 0 .../production_settings.py | 0 deploy/webhook-router.py => webhook-router.py | 0 38 files changed, 146 insertions(+), 1358 deletions(-) delete mode 100644 core/__init__.py delete mode 100644 core/admin.py delete mode 100644 core/apps.py delete mode 100644 core/forms.py delete mode 100644 core/migrations/0001_initial.py delete mode 100644 core/migrations/__init__.py delete mode 100644 core/models.py delete mode 100644 core/static/core/css/style.css delete mode 100644 core/static/core/js/main.js delete mode 100644 core/templates/core/about.html delete mode 100644 core/templates/core/base.html delete mode 100644 core/templates/core/blog.html delete mode 100644 core/templates/core/contact.html delete mode 100644 core/templates/core/home.html delete mode 100644 core/tests.py delete mode 100644 core/urls.py delete mode 100644 core/views.py delete mode 100644 demo_project/__init__.py delete mode 100644 demo_project/asgi.py delete mode 100644 demo_project/settings.py delete mode 100644 demo_project/urls.py delete mode 100644 demo_project/wsgi.py rename deploy/deploy-project.sh => deploy-django-project.sh (98%) delete mode 100644 deploy/gunicorn.conf.py delete mode 100644 deploy/nginx.conf delete mode 100644 deploy/systemd.service delete mode 100644 deploy/webhook-receiver.py delete mode 100644 deploy/webhook-service.sh delete mode 100755 manage.py delete mode 100644 requirements.txt rename deploy/setup-multi-webhook.sh => setup-multi-webhook.sh (100%) rename {deploy/templates => templates}/.env.production.template (100%) rename {deploy/templates => templates}/gunicorn.service.template (100%) rename {deploy/templates => templates}/gunicorn.socket.template (100%) rename {deploy/templates => templates}/nginx.conf.template (100%) rename {deploy/templates => templates}/production_settings.py (100%) rename deploy/webhook-router.py => webhook-router.py (100%) diff --git a/README.md b/README.md index ccbad4a..56a48eb 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,164 @@ -# Django VPS Demo Project +# 🚀 Django VPS Deployment Toolkit -Simple Django project optimized for VPS deployment with multi-project support. +**Universal toolkit to deploy ANY Django project to VPS with zero configuration.** ## ✨ Features -- **Minimal Dependencies**: Clean, lightweight setup with only essential packages -- **Environment Configuration**: Production settings managed via environment variables -- **Database Flexible**: Works with SQLite for development and PostgreSQL for production -- **Static Files Handling**: Configured with WhiteNoise for efficient static file serving -- **Contact Form**: Functional contact form with email validation -- **Simple Blog System**: Basic blog functionality with admin interface -- **Bootstrap UI**: Responsive design using Bootstrap 5 -- **Production Security**: Security headers, HTTPS support, and production optimizations -- **VPS Deployment**: Complete deployment scripts and configuration files +- đŸŽ¯ **Universal** - Deploy any Django project from GitHub +- đŸˇī¸ **Auto-naming** - Extracts project name from repo URL +- 🔄 **Multi-project** - Unlimited Django projects per VPS +- 🔧 **Zero config** - Automatic nginx + gunicorn + systemd setup +- 🚀 **One command** - Complete deployment in minutes +- 🔒 **Secure** - Non-root deployment with proper permissions +- 📡 **Auto-deploy** - GitHub webhook integration for CI/CD -## 🛠 Tech Stack +## 🚀 Quick Deploy -- **Backend**: Django 4.2.7 -- **Database**: SQLite (development) / PostgreSQL (production) -- **Web Server**: Gunicorn + Nginx -- **Frontend**: Bootstrap 5, Vanilla JavaScript -- **Static Files**: WhiteNoise -- **Configuration**: python-decouple - -## 🚀 Deploy to VPS - -### After VPS Reset: +### Setup VPS (Once per VPS) ```bash -# 1. Fix SSH key -ssh-keygen -f '/home/amit/.ssh/known_hosts' -R '69.62.81.168' +# 1. Fix SSH after VPS reset +ssh-keygen -f '~/.ssh/known_hosts' -R 'YOUR_VPS_IP' -# 2. Setup django user -scp setup-django-user.sh akvps:/root/ -ssh akvps "sudo bash /root/setup-django-user.sh" +# 2. Upload and run user setup +scp setup-django-user.sh root@YOUR_VPS_IP:/root/ +ssh root@YOUR_VPS_IP "sudo bash /root/setup-django-user.sh" -# 3. Clone and deploy +# 3. Clone this toolkit +ssh root@YOUR_VPS_IP "cd /home/django && git clone https://github.com/thecyberlearn/hostinger-django-demo.git django-vps-toolkit" ``` -### Deploy Your Project +### Deploy Any Django Project ```bash -ssh akvps "cd /home/django && git clone https://github.com/thecyberlearn/hostinger-django-demo.git" -ssh akvps "cd /home/django/hostinger-django-demo && sudo bash deploy/deploy-project.sh https://github.com/thecyberlearn/hostinger-django-demo.git" +# Deploy any Django project with one command! +ssh root@YOUR_VPS_IP "cd /home/django/django-vps-toolkit && sudo bash deploy-django-project.sh https://github.com/USER/PROJECT.git" ``` -**Your site**: http://69.62.81.168/ +**Examples:** +```bash +# Deploy a blog +sudo bash deploy-django-project.sh https://github.com/johndoe/my-blog.git +# → Live at: http://YOUR_VPS_IP/my-blog/ -## 📁 Key Files +# Deploy an e-commerce site +sudo bash deploy-django-project.sh https://github.com/company/shop-backend.git +# → Live at: http://YOUR_VPS_IP/shop-backend/ -**Main Deployment Script:** -- `deploy/deploy-project.sh` - Deploys any Django project with auto repo naming +# Deploy a portfolio +sudo bash deploy-django-project.sh https://github.com/jane/portfolio-site.git +# → Live at: http://YOUR_VPS_IP/portfolio-site/ +``` -**Documentation:** -- `MULTI_PROJECT_SETUP.md` - Complete multi-project guide +## đŸŽ¯ What It Does -**Setup:** -- `setup-django-user.sh` - Create django user on fresh VPS -- `requirements.txt` - Python dependencies +1. **Extracts project name** from GitHub URL +2. **Clones project** to `/var/www/PROJECT_NAME/` +3. **Creates virtual environment** and installs dependencies +4. **Runs Django migrations** and collects static files +5. **Creates systemd service** `gunicorn-PROJECT_NAME.service` +6. **Configures nginx** for path-based routing +7. **Starts everything** and tests deployment -## đŸŽ¯ That's It! +## 📁 Toolkit Files -For detailed multi-project setup, see `MULTI_PROJECT_SETUP.md` +**🚀 Main Scripts:** +- `deploy-django-project.sh` - Deploy any Django project +- `setup-django-user.sh` - VPS user setup (run once) -Simple, clean, and no confusion! 🚀 \ No newline at end of file +**🔧 Advanced Features:** +- `setup-multi-webhook.sh` - GitHub auto-deploy webhooks +- `webhook-router.py` - Multi-project webhook handler +- `MULTI_PROJECT_SETUP.md` - Advanced webhook guide + +**📝 Templates:** +- `templates/nginx.conf.template` - Nginx configuration +- `templates/gunicorn.service.template` - Systemd service +- `templates/production_settings.py` - Django production settings + +## 🔄 Auto-Deploy Setup + +Want GitHub auto-deploy like Render/Vercel? + +```bash +# Setup webhook system +sudo bash setup-multi-webhook.sh + +# Add webhook to your GitHub repos: +# URL: http://YOUR_VPS_IP/webhook +# Secret: [from setup output] +# Events: Push events + +# Now push to GitHub → Auto-deploy! 🚀 +``` + +## 💡 Examples + +### Deploy Multiple Projects +```bash +# Each project gets its own URL path +sudo bash deploy-django-project.sh https://github.com/user/blog.git +sudo bash deploy-django-project.sh https://github.com/user/shop.git +sudo bash deploy-django-project.sh https://github.com/user/api.git + +# Results: +# http://YOUR_VPS_IP/blog/ +# http://YOUR_VPS_IP/shop/ +# http://YOUR_VPS_IP/api/ +``` + +### Project Management +```bash +# Check project status +systemctl status gunicorn-blog.service +systemctl status gunicorn-shop.service + +# View project logs +journalctl -u gunicorn-blog.service -f + +# Restart project +systemctl restart gunicorn-blog.service + +# Update project +cd /var/www/blog && sudo -u django git pull && systemctl restart gunicorn-blog.service +``` + +## đŸŽ¯ Requirements + +**Your Django Project Needs:** +- `requirements.txt` file +- Working `manage.py` +- Proper Django project structure + +**VPS Requirements:** +- Ubuntu 20.04+ or similar +- Root/sudo access +- 1GB+ RAM recommended + +## 🚨 Troubleshooting + +**Deployment fails?** +```bash +# Check logs +journalctl -u gunicorn-PROJECT_NAME.service -f + +# Test Django +cd /var/www/PROJECT_NAME +sudo -u django bash -c "source venv/bin/activate && python manage.py check" + +# Test nginx +nginx -t +``` + +**Can't access site?** +```bash +# Check services +systemctl status gunicorn-PROJECT_NAME.service nginx + +# Check firewall +ufw status +``` + +## 🎉 Success! + +You now have a **universal Django deployment toolkit** that can deploy any Django project to VPS with zero configuration! + +**Just provide a GitHub URL and get a working Django site!** 🚀 \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/admin.py b/core/admin.py deleted file mode 100644 index e9d3fc7..0000000 --- a/core/admin.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.contrib import admin -from .models import Contact, BlogPost - - -@admin.register(Contact) -class ContactAdmin(admin.ModelAdmin): - list_display = ['name', 'email', 'subject', 'created_at'] - list_filter = ['created_at'] - search_fields = ['name', 'email', 'subject'] - readonly_fields = ['created_at'] - ordering = ['-created_at'] - - -@admin.register(BlogPost) -class BlogPostAdmin(admin.ModelAdmin): - list_display = ['title', 'is_published', 'created_at', 'updated_at'] - list_filter = ['is_published', 'created_at'] - search_fields = ['title', 'content'] - prepopulated_fields = {'slug': ('title',)} - readonly_fields = ['created_at', 'updated_at'] - ordering = ['-created_at'] diff --git a/core/apps.py b/core/apps.py deleted file mode 100644 index 8115ae6..0000000 --- a/core/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class CoreConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'core' diff --git a/core/forms.py b/core/forms.py deleted file mode 100644 index 78deebf..0000000 --- a/core/forms.py +++ /dev/null @@ -1,27 +0,0 @@ -from django import forms -from .models import Contact - - -class ContactForm(forms.ModelForm): - class Meta: - model = Contact - fields = ['name', 'email', 'subject', 'message'] - widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Your Name' - }), - 'email': forms.EmailInput(attrs={ - 'class': 'form-control', - 'placeholder': 'your.email@example.com' - }), - 'subject': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Subject' - }), - 'message': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 5, - 'placeholder': 'Your message...' - }), - } \ No newline at end of file diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py deleted file mode 100644 index 587cf58..0000000 --- a/core/migrations/0001_initial.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 4.2.7 on 2025-08-29 16:12 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='BlogPost', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=200)), - ('slug', models.SlugField(unique=True)), - ('content', models.TextField()), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('is_published', models.BooleanField(default=True)), - ], - options={ - 'ordering': ['-created_at'], - }, - ), - migrations.CreateModel( - name='Contact', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('email', models.EmailField(max_length=254)), - ('subject', models.CharField(max_length=200)), - ('message', models.TextField()), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ], - options={ - 'ordering': ['-created_at'], - }, - ), - ] diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/models.py b/core/models.py deleted file mode 100644 index 8a68930..0000000 --- a/core/models.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.db import models -from django.utils import timezone - - -class Contact(models.Model): - name = models.CharField(max_length=100) - email = models.EmailField() - subject = models.CharField(max_length=200) - message = models.TextField() - created_at = models.DateTimeField(default=timezone.now) - - def __str__(self): - return f"{self.name} - {self.subject}" - - class Meta: - ordering = ['-created_at'] - - -class BlogPost(models.Model): - title = models.CharField(max_length=200) - slug = models.SlugField(unique=True) - content = models.TextField() - created_at = models.DateTimeField(default=timezone.now) - updated_at = models.DateTimeField(auto_now=True) - is_published = models.BooleanField(default=True) - - def __str__(self): - return self.title - - class Meta: - ordering = ['-created_at'] diff --git a/core/static/core/css/style.css b/core/static/core/css/style.css deleted file mode 100644 index c892000..0000000 --- a/core/static/core/css/style.css +++ /dev/null @@ -1,30 +0,0 @@ -.hero-section { - background: linear-gradient(135deg, #007bff 0%, #0056b3 100%); -} - -.card { - transition: transform 0.2s; -} - -.card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0,0,0,0.1); -} - -.navbar-brand { - font-weight: bold; -} - -footer { - margin-top: auto; -} - -body { - min-height: 100vh; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} \ No newline at end of file diff --git a/core/static/core/js/main.js b/core/static/core/js/main.js deleted file mode 100644 index eef10fc..0000000 --- a/core/static/core/js/main.js +++ /dev/null @@ -1,23 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - // Auto-hide alerts after 5 seconds - const alerts = document.querySelectorAll('.alert'); - alerts.forEach(function(alert) { - setTimeout(function() { - const bsAlert = new bootstrap.Alert(alert); - bsAlert.close(); - }, 5000); - }); - - // Smooth scrolling for internal links - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function (e) { - e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); - if (target) { - target.scrollIntoView({ - behavior: 'smooth' - }); - } - }); - }); -}); \ No newline at end of file diff --git a/core/templates/core/about.html b/core/templates/core/about.html deleted file mode 100644 index c5e371e..0000000 --- a/core/templates/core/about.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends 'core/base.html' %} - -{% block title %}About - Django Demo Project{% endblock %} - -{% block content %} -
-
-
-

About This Project

- -
-
-
Purpose
-

This Django demo project is designed as a production-ready template for testing VPS deployment workflows. It serves as a foundation for future Django projects with all the essential configurations already in place.

-
-
- -
-
-
Features
-
    -
  • ✓ Minimal dependencies for fast deployment
  • -
  • ✓ Environment-based configuration
  • -
  • ✓ SQLite development / PostgreSQL production
  • -
  • ✓ Static files handling with WhiteNoise
  • -
  • ✓ Contact form functionality
  • -
  • ✓ Simple blog system
  • -
  • ✓ Bootstrap-based responsive UI
  • -
  • ✓ Production security settings
  • -
-
-
- -
-
-
Tech Stack
-
-
-
    -
  • Backend: Django 4.2.7
  • -
  • Database: SQLite/PostgreSQL
  • -
  • Server: Gunicorn
  • -
-
-
-
    -
  • Frontend: Bootstrap 5
  • -
  • Static Files: WhiteNoise
  • -
  • Config: python-decouple
  • -
-
-
-
-
- - -
-
-
-{% endblock %} \ No newline at end of file diff --git a/core/templates/core/base.html b/core/templates/core/base.html deleted file mode 100644 index 4db926e..0000000 --- a/core/templates/core/base.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - {% block title %}Django Demo Project{% endblock %} - - {% load static %} - - - - - -
- {% if messages %} -
- {% for message in messages %} - - {% endfor %} -
- {% endif %} - - {% block content %} - {% endblock %} -
- - - - - {% load static %} - - - \ No newline at end of file diff --git a/core/templates/core/blog.html b/core/templates/core/blog.html deleted file mode 100644 index bdbf728..0000000 --- a/core/templates/core/blog.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends 'core/base.html' %} - -{% block title %}Blog - Django Demo Project{% endblock %} - -{% block content %} -
-

Blog

- - {% if posts %} -
- {% for post in posts %} -
-
-
-
{{ post.title }}
-

{{ post.content|truncatewords:30 }}

- Published on {{ post.created_at|date:"F d, Y" }} -
-
-
- {% endfor %} -
- {% else %} -
-

No blog posts yet

-

Blog posts will appear here once they're created in the admin panel.

- Back to Home -
- {% endif %} -
-{% endblock %} \ No newline at end of file diff --git a/core/templates/core/contact.html b/core/templates/core/contact.html deleted file mode 100644 index 3cfa6a8..0000000 --- a/core/templates/core/contact.html +++ /dev/null @@ -1,68 +0,0 @@ -{% extends 'core/base.html' %} - -{% block title %}Contact - Django Demo Project{% endblock %} - -{% block content %} -
-
-
-

Contact Us

-

Have questions about this Django demo project? Get in touch!

- -
-
-
- {% csrf_token %} -
- - {{ form.name }} - {% if form.name.errors %} -
{{ form.name.errors }}
- {% endif %} -
- -
- - {{ form.email }} - {% if form.email.errors %} -
{{ form.email.errors }}
- {% endif %} -
- -
- - {{ form.subject }} - {% if form.subject.errors %} -
{{ form.subject.errors }}
- {% endif %} -
- -
- - {{ form.message }} - {% if form.message.errors %} -
{{ form.message.errors }}
- {% endif %} -
- - -
-
- -
-
Project Info
-

Django Version: 4.2.7

-

Purpose: VPS Deployment Testing

-

Features:

-
    -
  • Contact Form
  • -
  • Blog System
  • -
  • Environment Configuration
  • -
  • Production Ready
  • -
-
-
-
-
-
-{% endblock %} \ No newline at end of file diff --git a/core/templates/core/home.html b/core/templates/core/home.html deleted file mode 100644 index b194ad2..0000000 --- a/core/templates/core/home.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends 'core/base.html' %} - -{% block title %}Home - Django Demo Project{% endblock %} - -{% block content %} -
-
-
-
-

Django Demo Project

-

A production-ready Django application template optimized for VPS hosting deployment.

- Get Started -
-
-
-
- -
-
-
-

Features

-
-
-
-
-
Production Ready
-

Configured with environment variables, security settings, and optimized for deployment.

-
-
-
-
-
-
-
Database Flexible
-

Works with SQLite for development and PostgreSQL for production.

-
-
-
-
-
-
-
VPS Optimized
-

Includes deployment scripts and configuration for easy VPS hosting.

-
-
-
-
-
-
-
Minimal Dependencies
-

Clean, lightweight setup with only essential packages.

-
-
-
-
-
- -
-

Recent Blog Posts

- {% if blog_posts %} - {% for post in blog_posts %} -
-
-
{{ post.title }}
-

{{ post.content|truncatewords:20 }}

- {{ post.created_at|date:"M d, Y" }} -
-
- {% endfor %} - View All Posts - {% else %} -

No blog posts yet. Check back later!

- {% endif %} -
-
-
-{% endblock %} \ No newline at end of file diff --git a/core/tests.py b/core/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/core/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/core/urls.py b/core/urls.py deleted file mode 100644 index b9ceec5..0000000 --- a/core/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.urls import path -from . import views - -urlpatterns = [ - path('', views.home, name='home'), - path('contact/', views.contact, name='contact'), - path('blog/', views.blog, name='blog'), - path('about/', views.about, name='about'), -] \ No newline at end of file diff --git a/core/views.py b/core/views.py deleted file mode 100644 index 832b055..0000000 --- a/core/views.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.shortcuts import render, redirect -from django.contrib import messages -from .models import BlogPost -from .forms import ContactForm - - -def home(request): - blog_posts = BlogPost.objects.filter(is_published=True)[:3] - return render(request, 'core/home.html', {'blog_posts': blog_posts}) - - -def contact(request): - if request.method == 'POST': - form = ContactForm(request.POST) - if form.is_valid(): - form.save() - messages.success(request, 'Thank you for your message! We will get back to you soon.') - return redirect('contact') - else: - form = ContactForm() - - return render(request, 'core/contact.html', {'form': form}) - - -def blog(request): - posts = BlogPost.objects.filter(is_published=True) - return render(request, 'core/blog.html', {'posts': posts}) - - -def about(request): - return render(request, 'core/about.html') diff --git a/demo_project/__init__.py b/demo_project/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/demo_project/asgi.py b/demo_project/asgi.py deleted file mode 100644 index ddea556..0000000 --- a/demo_project/asgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -ASGI config for demo_project project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ -""" - -import os - -from django.core.asgi import get_asgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings') - -application = get_asgi_application() diff --git a/demo_project/settings.py b/demo_project/settings.py deleted file mode 100644 index e240d36..0000000 --- a/demo_project/settings.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Django settings for demo_project project. - -Generated by 'django-admin startproject' using Django 4.2.7. - -For more information on this file, see -https://docs.djangoproject.com/en/4.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.2/ref/settings/ -""" - -import os -from pathlib import Path -from decouple import config - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = config('SECRET_KEY', default='django-insecure-%%73_#43bfj4u2^usxt&(hmx$*r!&@w6z7!r-psjm%gu_5(s35') - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = config('DEBUG', default=True, cast=bool) - -ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=lambda v: [s.strip() for s in v.split(',')]) - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'core', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'whitenoise.middleware.WhiteNoiseMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'demo_project.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'demo_project.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/4.2/ref/settings/#databases - -# Database configuration -if config('DATABASE_URL', default=None): - import dj_database_url - DATABASES = { - 'default': dj_database_url.parse(config('DATABASE_URL')) - } -elif config('USE_POSTGRES', default=False, cast=bool): - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': config('DB_NAME', default='demo_db'), - 'USER': config('DB_USER', default='demo_user'), - 'PASSWORD': config('DB_PASSWORD', default=''), - 'HOST': config('DB_HOST', default='localhost'), - 'PORT': config('DB_PORT', default='5432'), - } - } -else: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } - } - - -# Password validation -# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/4.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.2/howto/static-files/ - -STATIC_URL = '/static/' -STATIC_ROOT = BASE_DIR / 'staticfiles' -STATICFILES_DIRS = [] - -# Whitenoise settings -STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' - -# Default primary key field type -# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - -# Production security settings -if not DEBUG: - SECURE_BROWSER_XSS_FILTER = True - SECURE_CONTENT_TYPE_NOSNIFF = True - SECURE_HSTS_INCLUDE_SUBDOMAINS = True - SECURE_HSTS_SECONDS = 31536000 - SECURE_REDIRECT_EXEMPT = [] - SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool) - SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') - SESSION_COOKIE_SECURE = True - CSRF_COOKIE_SECURE = True diff --git a/demo_project/urls.py b/demo_project/urls.py deleted file mode 100644 index 27e033e..0000000 --- a/demo_project/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -URL configuration for demo_project project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/4.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -from django.contrib import admin -from django.urls import path, include - -urlpatterns = [ - path('admin/', admin.site.urls), - path('', include('core.urls')), -] diff --git a/demo_project/wsgi.py b/demo_project/wsgi.py deleted file mode 100644 index fe16259..0000000 --- a/demo_project/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for demo_project project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings') - -application = get_wsgi_application() diff --git a/deploy/deploy-project.sh b/deploy-django-project.sh similarity index 98% rename from deploy/deploy-project.sh rename to deploy-django-project.sh index 7cb58f6..9ea5803 100755 --- a/deploy/deploy-project.sh +++ b/deploy-django-project.sh @@ -163,8 +163,8 @@ DJANGO_PROJECT_DIR=$(sudo -u django find "$PROJECT_PATH" -name "settings.py" -ex DJANGO_PROJECT_NAME=$(basename "$DJANGO_PROJECT_DIR") # Create Gunicorn configuration -mkdir -p "$PROJECT_PATH/deploy" -cat > "$PROJECT_PATH/deploy/gunicorn.conf.py" << EOF +mkdir -p "$PROJECT_PATH/config" +cat > "$PROJECT_PATH/config/gunicorn.conf.py" << EOF # Gunicorn configuration for $PROJECT_NAME bind = "unix:/run/gunicorn-$PROJECT_NAME.sock" workers = 3 @@ -204,7 +204,7 @@ WorkingDirectory=$PROJECT_PATH Environment=PYTHONPATH=$PROJECT_PATH EnvironmentFile=$PROJECT_PATH/.env ExecStart=$PROJECT_PATH/venv/bin/gunicorn \\ - --config $PROJECT_PATH/deploy/gunicorn.conf.py \\ + --config $PROJECT_PATH/config/gunicorn.conf.py \\ $DJANGO_PROJECT_NAME.wsgi:application ExecReload=/bin/kill -s HUP \$MAINPID KillMode=mixed diff --git a/deploy/gunicorn.conf.py b/deploy/gunicorn.conf.py deleted file mode 100644 index 781abe3..0000000 --- a/deploy/gunicorn.conf.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Gunicorn configuration for production deployment""" - -import multiprocessing -import os - -# Server socket -bind = "127.0.0.1:8000" -backlog = 2048 - -# Worker processes -workers = multiprocessing.cpu_count() * 2 + 1 -worker_class = "sync" -worker_connections = 1000 -timeout = 30 -keepalive = 2 -max_requests = 1000 -max_requests_jitter = 100 - -# Restart workers after this many requests, with up to max_requests_jitter additional requests -preload_app = True - -# Logging -accesslog = "/var/log/django/access.log" -errorlog = "/var/log/django/error.log" -loglevel = "info" - -# Process naming -proc_name = "django_demo_project" - -# Server mechanics -daemon = False -pidfile = "/var/run/gunicorn/django_demo.pid" -user = "www-data" -group = "www-data" -tmp_upload_dir = None \ No newline at end of file diff --git a/deploy/nginx.conf b/deploy/nginx.conf deleted file mode 100644 index d7455f3..0000000 --- a/deploy/nginx.conf +++ /dev/null @@ -1,66 +0,0 @@ -server { - listen 80; - server_name yourdomain.com www.yourdomain.com; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_proxied any; - gzip_comp_level 6; - gzip_types - text/plain - text/css - text/xml - text/javascript - application/json - application/javascript - application/xml+rss - application/atom+xml - image/svg+xml; - - # Static files - location /static/ { - alias /home/ubuntu/django-demo/staticfiles/; - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Media files (if you add file uploads later) - location /media/ { - alias /home/ubuntu/django-demo/media/; - expires 1y; - add_header Cache-Control "public"; - } - - # Django application - location / { - proxy_pass http://127.0.0.1:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_redirect off; - - # Timeouts - proxy_connect_timeout 60s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - # Block access to sensitive files - location ~* /\.(?!well-known\/) { - deny all; - } - - location ~* /(requirements\.txt|\.env|deploy/) { - deny all; - } -} \ No newline at end of file diff --git a/deploy/systemd.service b/deploy/systemd.service deleted file mode 100644 index 990d23e..0000000 --- a/deploy/systemd.service +++ /dev/null @@ -1,22 +0,0 @@ -[Unit] -Description=Django Demo Project Gunicorn daemon -After=network.target - -[Service] -Type=notify -User=www-data -Group=www-data -RuntimeDirectory=gunicorn -WorkingDirectory=/home/ubuntu/django-demo -ExecStart=/home/ubuntu/django-demo/venv/bin/gunicorn --config /home/ubuntu/django-demo/deploy/gunicorn.conf.py demo_project.wsgi:application -ExecReload=/bin/kill -s HUP $MAINPID -KillMode=mixed -TimeoutStopSec=5 -PrivateTmp=true - -# Environment variables -Environment=DJANGO_SETTINGS_MODULE=demo_project.settings -EnvironmentFile=/home/ubuntu/django-demo/.env - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/deploy/webhook-receiver.py b/deploy/webhook-receiver.py deleted file mode 100644 index b4f113a..0000000 --- a/deploy/webhook-receiver.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python3 -""" -GitHub Webhook Receiver for Auto-Deployment -Listens for GitHub push events and triggers automatic deployment -""" - -import os -import sys -import json -import hmac -import hashlib -import subprocess -import logging -from datetime import datetime -from flask import Flask, request, jsonify -from threading import Thread -import time - -# Configuration -WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', 'your-webhook-secret-here') -REPO_PATH = '/var/www/django-app' -ALLOWED_BRANCHES = ['main', 'master'] -LOG_FILE = '/var/log/django/webhook.log' - -# Setup logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(levelname)s - %(message)s', - handlers=[ - logging.FileHandler(LOG_FILE), - logging.StreamHandler() - ] -) -logger = logging.getLogger(__name__) - -app = Flask(__name__) - -def verify_signature(payload_body, signature_header): - """Verify GitHub webhook signature""" - if not signature_header: - return False - - hash_object = hmac.new( - WEBHOOK_SECRET.encode('utf-8'), - payload_body, - hashlib.sha256 - ) - expected_signature = "sha256=" + hash_object.hexdigest() - - return hmac.compare_digest(expected_signature, signature_header) - -def run_deployment(): - """Execute deployment in background thread""" - try: - logger.info("🚀 Starting deployment...") - - # Change to app directory - os.chdir(REPO_PATH) - - # Run deployment script - result = subprocess.run([ - 'sudo', '-u', 'django', 'bash', '-c', - f''' - cd {REPO_PATH} - - # Store current commit for rollback - echo "$(git rev-parse HEAD)" > /tmp/last_working_commit.txt - - # Pull latest changes - git fetch origin - git reset --hard origin/main - - # Activate virtual environment and update - source venv/bin/activate - - # Install/update dependencies - pip install -r requirements.txt - - # Run Django management commands - python manage.py migrate - python manage.py collectstatic --noinput - - # Test if Django can start (quick check) - python manage.py check --deploy - ''' - ], capture_output=True, text=True, timeout=300) - - if result.returncode == 0: - # Restart services - subprocess.run(['systemctl', 'restart', 'gunicorn.service'], check=True) - - # Wait a moment and check if service is running - time.sleep(3) - service_check = subprocess.run(['systemctl', 'is-active', 'gunicorn.service'], - capture_output=True, text=True) - - if service_check.stdout.strip() == 'active': - logger.info("✅ Deployment successful!") - - # Send success notification (optional) - send_notification("✅ Deployment successful!", "success") - - else: - logger.error("❌ Service failed to start after deployment") - rollback() - else: - logger.error(f"❌ Deployment failed: {result.stderr}") - rollback() - - except subprocess.TimeoutExpired: - logger.error("❌ Deployment timed out") - rollback() - except Exception as e: - logger.error(f"❌ Deployment error: {str(e)}") - rollback() - -def rollback(): - """Rollback to previous working commit""" - try: - logger.info("🔄 Rolling back to previous commit...") - - if os.path.exists('/tmp/last_working_commit.txt'): - with open('/tmp/last_working_commit.txt', 'r') as f: - last_commit = f.read().strip() - - subprocess.run([ - 'sudo', '-u', 'django', 'bash', '-c', - f'cd {REPO_PATH} && git reset --hard {last_commit}' - ], check=True) - - subprocess.run(['systemctl', 'restart', 'gunicorn.service'], check=True) - logger.info("✅ Rollback completed") - send_notification("🔄 Rolled back due to deployment failure", "warning") - else: - logger.error("❌ No previous commit found for rollback") - - except Exception as e: - logger.error(f"❌ Rollback failed: {str(e)}") - -def send_notification(message, status="info"): - """Send deployment notification (extend this for Slack/Discord/Email)""" - logger.info(f"đŸ“ĸ Notification: {message}") - - # You can extend this to send notifications to: - # - Slack webhook - # - Discord webhook - # - Email - # - SMS - - # Example Slack notification (uncomment and configure): - # import requests - # slack_webhook = "YOUR_SLACK_WEBHOOK_URL" - # requests.post(slack_webhook, json={"text": f"🚀 Django App: {message}"}) - -@app.route('/webhook', methods=['POST']) -def handle_webhook(): - """Handle GitHub webhook""" - - # Verify signature - signature = request.headers.get('X-Hub-Signature-256') - if not verify_signature(request.data, signature): - logger.warning("❌ Invalid webhook signature") - return jsonify({"error": "Invalid signature"}), 403 - - # Parse payload - try: - payload = request.json - except: - logger.warning("❌ Invalid JSON payload") - return jsonify({"error": "Invalid JSON"}), 400 - - # Check if it's a push event - if request.headers.get('X-GitHub-Event') != 'push': - logger.info(f"â„šī¸ Ignoring non-push event: {request.headers.get('X-GitHub-Event')}") - return jsonify({"message": "Not a push event"}), 200 - - # Extract branch name - ref = payload.get('ref', '') - branch = ref.replace('refs/heads/', '') - - # Check if it's a branch we care about - if branch not in ALLOWED_BRANCHES: - logger.info(f"â„šī¸ Ignoring push to branch: {branch}") - return jsonify({"message": f"Ignoring branch {branch}"}), 200 - - # Log the deployment request - commit_hash = payload.get('after', 'unknown') - commit_message = "" - if payload.get('head_commit'): - commit_message = payload['head_commit'].get('message', '') - - logger.info(f"🔔 Deployment triggered by push to {branch}") - logger.info(f"📝 Commit: {commit_hash[:8]} - {commit_message[:100]}") - - # Start deployment in background thread - deployment_thread = Thread(target=run_deployment) - deployment_thread.start() - - return jsonify({ - "message": "Deployment started", - "branch": branch, - "commit": commit_hash[:8] - }), 200 - -@app.route('/health', methods=['GET']) -def health_check(): - """Health check endpoint""" - return jsonify({ - "status": "healthy", - "timestamp": datetime.now().isoformat(), - "repo_path": REPO_PATH - }) - -@app.route('/status', methods=['GET']) -def deployment_status(): - """Get current deployment status""" - try: - # Check if services are running - gunicorn_status = subprocess.run(['systemctl', 'is-active', 'gunicorn.service'], - capture_output=True, text=True) - nginx_status = subprocess.run(['systemctl', 'is-active', 'nginx'], - capture_output=True, text=True) - - # Get current commit - os.chdir(REPO_PATH) - commit_result = subprocess.run(['git', 'rev-parse', 'HEAD'], - capture_output=True, text=True) - current_commit = commit_result.stdout.strip()[:8] if commit_result.returncode == 0 else "unknown" - - return jsonify({ - "gunicorn": gunicorn_status.stdout.strip(), - "nginx": nginx_status.stdout.strip(), - "current_commit": current_commit, - "timestamp": datetime.now().isoformat() - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -if __name__ == '__main__': - # Create log directory if it doesn't exist - os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) - - logger.info("🚀 Starting GitHub webhook receiver...") - logger.info(f"📁 Monitoring repository: {REPO_PATH}") - logger.info(f"đŸŒŋ Allowed branches: {ALLOWED_BRANCHES}") - - # Run Flask app - app.run(host='127.0.0.1', port=8001, debug=False) \ No newline at end of file diff --git a/deploy/webhook-service.sh b/deploy/webhook-service.sh deleted file mode 100644 index a24ffdd..0000000 --- a/deploy/webhook-service.sh +++ /dev/null @@ -1,162 +0,0 @@ -#!/bin/bash -""" -Auto-Deployment Service Setup Script -Sets up the webhook receiver as a systemd service -""" - -set -e - -echo "🔄 Setting up Auto-Deployment Service..." - -# Configuration -WEBHOOK_SECRET=${1:-$(openssl rand -hex 32)} -SERVICE_USER="django" -APP_PATH="/var/www/django-app" -WEBHOOK_PATH="$APP_PATH/deploy/webhook-receiver.py" - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -# Install Flask if not present -echo -e "${YELLOW}đŸ“Ļ Installing Flask...${NC}" -sudo -u django bash -c "cd $APP_PATH && source venv/bin/activate && pip install flask" - -# Create webhook secret file -echo -e "${YELLOW}🔐 Setting up webhook secret...${NC}" -echo "WEBHOOK_SECRET=$WEBHOOK_SECRET" > /var/www/django-app/.env.webhook -chown django:www-data /var/www/django-app/.env.webhook -chmod 600 /var/www/django-app/.env.webhook - -echo -e "${GREEN}🔑 Webhook Secret: $WEBHOOK_SECRET${NC}" -echo -e "${YELLOW}📝 Save this secret - you'll need it for GitHub webhook configuration!${NC}" - -# Create systemd service file -echo -e "${YELLOW}âš™ī¸ Creating systemd service...${NC}" -cat > /etc/systemd/system/django-webhook.service << EOF -[Unit] -Description=Django Auto-Deployment Webhook Receiver -After=network.target - -[Service] -Type=simple -User=$SERVICE_USER -Group=www-data -WorkingDirectory=$APP_PATH -Environment=PYTHONPATH=$APP_PATH -EnvironmentFile=$APP_PATH/.env.webhook -ExecStart=$APP_PATH/venv/bin/python $WEBHOOK_PATH -Restart=always -RestartSec=3 - -[Install] -WantedBy=multi-user.target -EOF - -# Create nginx configuration for webhook -echo -e "${YELLOW}🌐 Configuring Nginx proxy...${NC}" -cat > /etc/nginx/sites-available/django-webhook << 'EOF' -server { - listen 80; - server_name webhook.YOUR_DOMAIN.com; # Replace with your subdomain - - location /webhook { - proxy_pass http://127.0.0.1:8001/webhook; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /health { - proxy_pass http://127.0.0.1:8001/health; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /status { - proxy_pass http://127.0.0.1:8001/status; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Optional: Add basic auth for status endpoint - # auth_basic "Deployment Status"; - # auth_basic_user_file /etc/nginx/.htpasswd; - } -} -EOF - -# Enable webhook nginx site (optional - you might want to use main domain with /webhook path) -echo -e "${YELLOW}â„šī¸ Webhook Nginx config created at /etc/nginx/sites-available/django-webhook${NC}" -echo -e "${YELLOW}â„šī¸ You can enable it with: ln -s /etc/nginx/sites-available/django-webhook /etc/nginx/sites-enabled/${NC}" - -# Or add webhook endpoint to existing site -echo -e "${YELLOW}🔧 Adding webhook endpoint to main site...${NC}" -MAIN_NGINX_CONFIG="/etc/nginx/sites-available/django-app" -if [ -f "$MAIN_NGINX_CONFIG" ]; then - # Add webhook location block before the last closing brace - sed -i '/^}/i\ - # GitHub Webhook endpoint\ - location /webhook {\ - proxy_pass http://127.0.0.1:8001/webhook;\ - proxy_set_header Host $host;\ - proxy_set_header X-Real-IP $remote_addr;\ - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\ - proxy_set_header X-Forwarded-Proto $scheme;\ - }\ -\ - # Deployment status endpoint\ - location /deploy-status {\ - proxy_pass http://127.0.0.1:8001/status;\ - proxy_set_header Host $host;\ - proxy_set_header X-Real-IP $remote_addr;\ - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\ - proxy_set_header X-Forwarded-Proto $scheme;\ - }' "$MAIN_NGINX_CONFIG" -fi - -# Make webhook receiver executable -chmod +x $WEBHOOK_PATH - -# Start and enable services -echo -e "${YELLOW}🚀 Starting services...${NC}" -systemctl daemon-reload -systemctl enable django-webhook.service -systemctl start django-webhook.service - -# Restart nginx -nginx -t && systemctl restart nginx - -# Check service status -if systemctl is-active --quiet django-webhook.service; then - echo -e "${GREEN}✅ Webhook service is running${NC}" -else - echo -e "${RED}❌ Webhook service failed to start${NC}" - echo "Check logs: journalctl -u django-webhook.service -f" - exit 1 -fi - -echo -e "${GREEN}🎉 Auto-Deployment Setup Complete!${NC}" -echo -echo -e "${YELLOW}📋 Next Steps:${NC}" -echo -e "1. 🔐 Webhook Secret: ${GREEN}$WEBHOOK_SECRET${NC}" -echo -e "2. 🌐 Webhook URL: ${GREEN}http://YOUR_VPS_IP/webhook${NC}" -echo -e "3. 📝 Go to GitHub → Settings → Webhooks → Add webhook" -echo -e "4. 🔧 Configure webhook:" -echo -e " - Payload URL: http://YOUR_VPS_IP/webhook" -echo -e " - Content type: application/json" -echo -e " - Secret: $WEBHOOK_SECRET" -echo -e " - Events: Just the push event" -echo -echo -e "${YELLOW}🔍 Monitoring:${NC}" -echo -e "- Service logs: ${GREEN}journalctl -u django-webhook.service -f${NC}" -echo -e "- Deployment status: ${GREEN}http://YOUR_VPS_IP/deploy-status${NC}" -echo -e "- Health check: ${GREEN}http://YOUR_VPS_IP/health${NC}" -echo -echo -e "${GREEN}🚀 Your VPS now works like Render - just push to GitHub!${NC}" \ No newline at end of file diff --git a/manage.py b/manage.py deleted file mode 100755 index 8da2b48..0000000 --- a/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 301e9de..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Core Django -Django==4.2.7 - -# Configuration & Environment -python-decouple==3.8 -dj-database-url==2.1.0 - -# Production Server & Static Files -gunicorn==21.2.0 -whitenoise==6.6.0 - -# Database (PostgreSQL) -psycopg2-binary==2.9.7 \ No newline at end of file diff --git a/deploy/setup-multi-webhook.sh b/setup-multi-webhook.sh similarity index 100% rename from deploy/setup-multi-webhook.sh rename to setup-multi-webhook.sh diff --git a/deploy/templates/.env.production.template b/templates/.env.production.template similarity index 100% rename from deploy/templates/.env.production.template rename to templates/.env.production.template diff --git a/deploy/templates/gunicorn.service.template b/templates/gunicorn.service.template similarity index 100% rename from deploy/templates/gunicorn.service.template rename to templates/gunicorn.service.template diff --git a/deploy/templates/gunicorn.socket.template b/templates/gunicorn.socket.template similarity index 100% rename from deploy/templates/gunicorn.socket.template rename to templates/gunicorn.socket.template diff --git a/deploy/templates/nginx.conf.template b/templates/nginx.conf.template similarity index 100% rename from deploy/templates/nginx.conf.template rename to templates/nginx.conf.template diff --git a/deploy/templates/production_settings.py b/templates/production_settings.py similarity index 100% rename from deploy/templates/production_settings.py rename to templates/production_settings.py diff --git a/deploy/webhook-router.py b/webhook-router.py similarity index 100% rename from deploy/webhook-router.py rename to webhook-router.py