commit 663e3cdbd751f7507e0f41ee6aa3b79c3f2209aa Author: vickytechkey Date: Sun Aug 23 09:28:40 2026 +0530 Initial commit diff --git a/.forgejo/workflows/beta.yml b/.forgejo/workflows/beta.yml new file mode 100644 index 0000000..f3f83f6 --- /dev/null +++ b/.forgejo/workflows/beta.yml @@ -0,0 +1,101 @@ +name: Deploy Beta (NATIVE) + +on: + push: + branches: + - beta + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: https://github.com/actions/checkout@v4 + + - name: Generate CloudWatch Config Locally + run: | + mkdir -p tmp + cat << 'EOF' > tmp/amazon-cloudwatch-agent.json + { + "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, + "metrics": { + "metrics_collected": { + "disk": { "measurement": ["used_percent"], "metrics_collection_interval": 60, "resources": ["/"] }, + "mem": { "measurement": ["mem_used_percent"], "metrics_collection_interval": 60 } + } + }, + "logs": { + "logs_collected": { + "files": { + "collect_list": [ + { "file_path": "/home/ubuntu/emailservice/logs/app.log", "log_group_name": "/emailservice/app", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/error.log", "log_group_name": "/emailservice/error", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/requests.log", "log_group_name": "/emailservice/requests", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/gunicorn-access.log", "log_group_name": "/emailservice/gunicorn-access", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/gunicorn-error.log", "log_group_name": "/emailservice/gunicorn-error", "log_stream_name": "{instance_id}", "retention_in_days": 30 } + ] + } + } + } + } + EOF + + - name: Package Application + run: | + tar -czf app.tar.gz --exclude=.git --exclude=tmp . + + - name: Deploy to Beta EC2 + run: | + apk add --no-cache openssh-client aws-cli + mkdir -p ~/.ssh + echo "${{ secrets.BETA_EC2_SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + + # Copy app archive and configuration files + scp -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa app.tar.gz tmp/amazon-cloudwatch-agent.json emailservice.service ${{ secrets.BETA_EC2_USERNAME }}@${{ secrets.BETA_EC2_HOST }}:/tmp/ + + # Execute deployment commands on remote server + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ${{ secrets.BETA_EC2_USERNAME }}@${{ secrets.BETA_EC2_HOST }} << 'EOF' + set -e + + # ── 2. Install Host Dependencies ────────────────────────────── + sudo apt-get update + sudo apt-get install -y python3-pip python3-venv python3-dev default-libmysqlclient-dev pkg-config libpq-dev build-essential + + # ── 3. Extract codebase to target directory ─────────────────── + sudo mkdir -p /home/ubuntu/emailservice + sudo chown -R ubuntu:ubuntu /home/ubuntu/emailservice + tar -xzf /tmp/app.tar.gz -C /home/ubuntu/emailservice + + # ── 4. Set up python virtual environment & dependencies ──────── + cd /home/ubuntu/emailservice + mkdir -p logs + python3 -m venv venv + ./venv/bin/pip install --upgrade pip + ./venv/bin/pip install -r requirements.txt + + # ── 5. Configure environment variables ───────────────────────── + echo "DEBUG=0" > .env + echo "DB_USER=vignesh" >> .env + echo "DB_PASSWORD=vtechnosoft@123A" >> .env + echo "DB_HOST=127.0.0.1" >> .env + echo "DB_PORT=5432" >> .env + + # ── 6. Run Database Migrations ───────────────────────────────── + ./venv/bin/python manage.py migrate --noinput + + # ── 7. Configure and restart Systemd service ─────────────────── + sudo mv /tmp/emailservice.service /etc/systemd/system/emailservice.service + sudo systemctl daemon-reload + sudo systemctl enable emailservice + sudo systemctl restart emailservice + + # ── 8. Configure CloudWatch Agent ────────────────────────────── + sudo mv /tmp/amazon-cloudwatch-agent.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json + sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ + -a fetch-config -m ec2 -s \ + -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json + + # ── 9. Cleanup tmp files ─────────────────────────────────────── + rm -f /tmp/app.tar.gz + EOF diff --git a/.forgejo/workflows/prod.yml b/.forgejo/workflows/prod.yml new file mode 100644 index 0000000..5c47db6 --- /dev/null +++ b/.forgejo/workflows/prod.yml @@ -0,0 +1,101 @@ +name: Deploy Prod (NATIVE) + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: https://github.com/actions/checkout@v4 + + - name: Generate CloudWatch Config Locally + run: | + mkdir -p tmp + cat << 'EOF' > tmp/amazon-cloudwatch-agent.json + { + "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, + "metrics": { + "metrics_collected": { + "disk": { "measurement": ["used_percent"], "metrics_collection_interval": 60, "resources": ["/"] }, + "mem": { "measurement": ["mem_used_percent"], "metrics_collection_interval": 60 } + } + }, + "logs": { + "logs_collected": { + "files": { + "collect_list": [ + { "file_path": "/home/ubuntu/emailservice/logs/app.log", "log_group_name": "/emailservice/app", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/error.log", "log_group_name": "/emailservice/error", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/requests.log", "log_group_name": "/emailservice/requests", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/gunicorn-access.log", "log_group_name": "/emailservice/gunicorn-access", "log_stream_name": "{instance_id}", "retention_in_days": 30 }, + { "file_path": "/home/ubuntu/emailservice/logs/gunicorn-error.log", "log_group_name": "/emailservice/gunicorn-error", "log_stream_name": "{instance_id}", "retention_in_days": 30 } + ] + } + } + } + } + EOF + + - name: Package Application + run: | + tar -czf app.tar.gz --exclude=.git --exclude=tmp . + + - name: Deploy to Prod EC2 + run: | + apk add --no-cache openssh-client aws-cli + mkdir -p ~/.ssh + echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + + # Copy app archive and configuration files + scp -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa app.tar.gz tmp/amazon-cloudwatch-agent.json emailservice.service ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/tmp/ + + # Execute deployment commands on remote server + ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} << 'EOF' + set -e + + # ── 2. Install Host Dependencies ────────────────────────────── + sudo apt-get update + sudo apt-get install -y python3-pip python3-venv python3-dev default-libmysqlclient-dev pkg-config libpq-dev build-essential + + # ── 3. Extract codebase to target directory ─────────────────── + sudo mkdir -p /home/ubuntu/emailservice + sudo chown -R ubuntu:ubuntu /home/ubuntu/emailservice + tar -xzf /tmp/app.tar.gz -C /home/ubuntu/emailservice + + # ── 4. Set up python virtual environment & dependencies ──────── + cd /home/ubuntu/emailservice + mkdir -p logs + python3 -m venv venv + ./venv/bin/pip install --upgrade pip + ./venv/bin/pip install -r requirements.txt + + # ── 5. Configure environment variables ───────────────────────── + echo "DEBUG=0" > .env + echo "DB_USER=vignesh" >> .env + echo "DB_PASSWORD=vtechnosoft@123A" >> .env + echo "DB_HOST=127.0.0.1" >> .env + echo "DB_PORT=5432" >> .env + + # ── 6. Run Database Migrations ───────────────────────────────── + ./venv/bin/python manage.py migrate --noinput + + # ── 7. Configure and restart Systemd service ─────────────────── + sudo mv /tmp/emailservice.service /etc/systemd/system/emailservice.service + sudo systemctl daemon-reload + sudo systemctl enable emailservice + sudo systemctl restart emailservice + + # ── 8. Configure CloudWatch Agent ────────────────────────────── + sudo mv /tmp/amazon-cloudwatch-agent.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json + sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ + -a fetch-config -m ec2 -s \ + -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json + + # ── 9. Cleanup tmp files ─────────────────────────────────────── + rm -f /tmp/app.tar.gz + EOF diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..120a63a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +venv/ +__pycache__/ +*.pyc +db.sqlite3 +.env +.DS_Store diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f6a8edd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,35 @@ +--- +description: "Core project memory, architecture, and guidelines for the emailservice." +trigger: always_on +--- + +# Email Service: Project Context & Memory + +This file serves as the core memory for the `emailservice` project. It contains our understanding of the project's architecture, technologies, and guidelines. The agent will read this file automatically to regain context. + +## Project Overview +This project is an Email Service designed for an e-commerce website using a microservices architecture. It acts as a centralized, reusable service that other internal services can call (using an API key for authentication). +Key capabilities include: +1. **Sending emails** on behalf of other services (e.g., User Verification, Password Resets, Promotional Campaigns). +2. **Reading/fetching inbox emails** based on specific filters. + +**Architecture Flow:** +```text +[Calling Service (e.g., Website)] --(API call with key)--> [EmailService] --(Send/Read)--> [Internet/Email Provider] +``` + +## Tech Stack +- **Framework**: Django (Python) - Full-stack (Backend for APIs, Frontend for Admin/Dashboard). +- **API**: Django REST Framework (DRF) for API key authentication and email endpoints. +- **Database**: SQLite (default for development) or PostgreSQL (for production). +- **Email Protocol**: SMTP for sending, IMAP for reading (or 3rd-party integrations as needed). + +## Architecture & Design Decisions +- *(Add any major architectural decisions here as the project evolves)* + +## Agent Guidelines +- Focus on clean, maintainable code. +- *(Add specific rules for the agent here, e.g., "Always write unit tests for new routes", "Use standard logging")* + +--- +> **Note to user:** You can manually edit this file anytime to update my understanding of the project, or use the `/learn` command in chat to have me update my rules automatically! diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/asgi.py b/core/asgi.py new file mode 100644 index 0000000..a0b44fd --- /dev/null +++ b/core/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for core 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/6.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_asgi_application() diff --git a/core/aws_secrets.py b/core/aws_secrets.py new file mode 100644 index 0000000..50594eb --- /dev/null +++ b/core/aws_secrets.py @@ -0,0 +1,28 @@ +import boto3 +import json +import logging +from botocore.exceptions import ClientError + +logger = logging.getLogger(__name__) + +def get_secret(secret_name, region_name="ap-south-2"): + session = boto3.session.Session() + client = session.client( + service_name='secretsmanager', + region_name=region_name + ) + + try: + get_secret_value_response = client.get_secret_value(SecretId=secret_name) + except ClientError as e: + logger.error(f"Error fetching secret {secret_name}: {e}") + return {} + + if 'SecretString' in get_secret_value_response: + secret = get_secret_value_response['SecretString'] + try: + return json.loads(secret) + except json.JSONDecodeError: + logger.error(f"Secret {secret_name} is not valid JSON") + return {} + return {} diff --git a/core/settings.py b/core/settings.py new file mode 100644 index 0000000..2a8b70e --- /dev/null +++ b/core/settings.py @@ -0,0 +1,149 @@ +""" +Django settings for core project. + +Generated by 'django-admin startproject' using Django 6.1. + +For more information on this file, see +https://docs.djangoproject.com/en/6.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/6.1/ref/settings/ +""" + +from pathlib import Path +import os +from core.aws_secrets import get_secret + +# 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/6.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-h3*qqwi%s4(ve^!!27d1sn6gew&4ol0^eh^n*3$aq=p+ht#)6k' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework_api_key', + 'mailer', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + '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 = 'core.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'core.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/6.1/ref/settings/#databases + +if DEBUG: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +else: + db_secret = get_secret("arn:aws:secretsmanager:ap-south-2:764709663363:secret:emailservicebetadb-ZXQg5w") + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': db_secret.get('database', 'emailservice'), + 'USER': db_secret.get('username', 'vignesh'), + 'PASSWORD': db_secret.get('password', ''), + 'HOST': db_secret.get('host', '127.0.0.1'), + 'PORT': db_secret.get('port', '5432'), + } + } + + +# Password validation +# https://docs.djangoproject.com/en/6.1/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/6.1/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/6.1/howto/static-files/ + +STATIC_URL = 'static/' +MEDIA_URL = 'media/' +MEDIA_ROOT = BASE_DIR / 'media' + +# Email +# https://docs.djangoproject.com/en/6.1/topics/email/#topic-email-configuration + +email_secret = get_secret("arn:aws:secretsmanager:ap-south-2:764709663363:secret:emailcredentials-DOIQtc") + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = email_secret.get('smtp', 'smtp.hostinger.com') +EMAIL_PORT = int(email_secret.get('smtp_port', 465)) +EMAIL_USE_SSL = True # Assuming SSL for port 465 +EMAIL_HOST_USER = email_secret.get('username', 'info@tradhox.com') +EMAIL_HOST_PASSWORD = email_secret.get('password', '') diff --git a/core/urls.py b/core/urls.py new file mode 100644 index 0000000..3d63cc9 --- /dev/null +++ b/core/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for core project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/6.1/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('mailer.urls')), +] diff --git a/core/wsgi.py b/core/wsgi.py new file mode 100644 index 0000000..6ea9106 --- /dev/null +++ b/core/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for core 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/6.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_wsgi_application() diff --git a/emailservice.service b/emailservice.service new file mode 100644 index 0000000..2de4f6b --- /dev/null +++ b/emailservice.service @@ -0,0 +1,21 @@ +[Unit] +Description=Email Service Backend +After=network.target + +[Service] +User=ubuntu +WorkingDirectory=/home/ubuntu/emailservice +EnvironmentFile=/home/ubuntu/emailservice/.env +ExecStart=/home/ubuntu/emailservice/venv/bin/gunicorn core.wsgi:application \ + --bind 0.0.0.0:8081 \ + --workers 3 \ + --access-logfile /home/ubuntu/emailservice/logs/gunicorn-access.log \ + --error-logfile /home/ubuntu/emailservice/logs/gunicorn-error.log \ + --log-level info \ + --capture-output + +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/mailer/__init__.py b/mailer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mailer/admin.py b/mailer/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/mailer/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/mailer/apps.py b/mailer/apps.py new file mode 100644 index 0000000..2af0c1d --- /dev/null +++ b/mailer/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MailerConfig(AppConfig): + name = 'mailer' diff --git a/mailer/migrations/0001_initial.py b/mailer/migrations/0001_initial.py new file mode 100644 index 0000000..8a84c3f --- /dev/null +++ b/mailer/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 6.1 on 2026-08-22 06:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='EmailLog', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recipient', models.EmailField(max_length=254)), + ('subject', models.CharField(max_length=255)), + ('template_name', models.CharField(max_length=100)), + ('status', models.CharField(max_length=50)), + ('sent_at', models.DateTimeField(auto_now_add=True)), + ('error_message', models.TextField(blank=True)), + ], + ), + ] diff --git a/mailer/migrations/__init__.py b/mailer/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mailer/models.py b/mailer/models.py new file mode 100644 index 0000000..2e1eb3e --- /dev/null +++ b/mailer/models.py @@ -0,0 +1,12 @@ +from django.db import models + +class EmailLog(models.Model): + recipient = models.EmailField() + subject = models.CharField(max_length=255) + template_name = models.CharField(max_length=100) # 'verification', 'password_reset', 'promotional' + status = models.CharField(max_length=50) # 'success', 'failed' + sent_at = models.DateTimeField(auto_now_add=True) + error_message = models.TextField(blank=True) + + def __str__(self): + return f"{self.recipient} - {self.subject} ({self.status})" diff --git a/mailer/templates/emails/sellercentral/common/password_reset.html b/mailer/templates/emails/sellercentral/common/password_reset.html new file mode 100644 index 0000000..43b138a --- /dev/null +++ b/mailer/templates/emails/sellercentral/common/password_reset.html @@ -0,0 +1,262 @@ + + + + + +Reset Your Password - Tradhox + + + + + + + +
+ + + + + + + + + + + +
+ \ No newline at end of file diff --git a/mailer/templates/emails/sellercentral/common/verification.html b/mailer/templates/emails/sellercentral/common/verification.html new file mode 100644 index 0000000..02efb3d --- /dev/null +++ b/mailer/templates/emails/sellercentral/common/verification.html @@ -0,0 +1,82 @@ + + + + + +Verify Your Email Address - Tradhox + + + + + + + + + + +
+ +
+

Tradhox

+
+ +
+
+

Verify Your Email Address

+

+ Welcome to Tradhox Artisanal Marketplace. To ensure the security of your account and protect the integrity of our community, please verify your email address. +

+
+ +
+

Your Verification Code

+
+ 7 4 9 2 0 1 +
+
+ +OR + +
+ + Verify Email Address → + +
+ +
+

Why is this required?

+

+ Verifying your email helps us maintain a trusted environment for our artisans and buyers. It ensures you receive important updates about your orders and protects your account from unauthorized access. +

+
+
+

+ If you did not create an account with Tradhox, you can safely ignore this email. +

+
+
+ +
+

Tradhox

+ +

+ © {% now "Y" %} Tradhox Artisanal Marketplace. All rights reserved. +

+
+
+ \ No newline at end of file diff --git a/mailer/templates/emails/sellercentral/common/welcome_email.html b/mailer/templates/emails/sellercentral/common/welcome_email.html new file mode 100644 index 0000000..224e2e8 --- /dev/null +++ b/mailer/templates/emails/sellercentral/common/welcome_email.html @@ -0,0 +1,116 @@ + + + + + +Welcome to Tradhox + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+

Tradhox

+
+Artisanal craftsmanship +
+

Welcome to the Family of Makers

+

+ We are thrilled to welcome you to Tradhox, where traditional artistry meets a modern global marketplace. Here, we celebrate human craftsmanship and the stories woven into every handmade piece. +

+ + + + + +
+ + Start Your Journey → + +
+
+ + + + + + +
+ +

Sustainable Crafts

+
+ +

GI Tagged Heritage

+
+
+

+ Tradhox +

+ + + + + + + +
+Instagram + +Facebook + +Twitter +
+

+ © {% now "Y" %} Tradhox Artisanal Marketplace. All rights reserved.
+Privacy PolicyTerms of ServiceUnsubscribe +

+
+
+ \ No newline at end of file diff --git a/mailer/templates/mailer/base.html b/mailer/templates/mailer/base.html new file mode 100644 index 0000000..5e44bb0 --- /dev/null +++ b/mailer/templates/mailer/base.html @@ -0,0 +1,33 @@ + + + + + + Email Service Portal + + + + + + +
+
+ {% block content %}{% endblock %} +
+
+ + diff --git a/mailer/templates/mailer/dashboard.html b/mailer/templates/mailer/dashboard.html new file mode 100644 index 0000000..d72d9c1 --- /dev/null +++ b/mailer/templates/mailer/dashboard.html @@ -0,0 +1,38 @@ +{% extends "mailer/base.html" %} + +{% block content %} +
+
+
+

Dashboard

+

Welcome to the Email Service Administration Portal

+
+ +
+ This portal allows you to manage email templates, view email logs, and manage API keys for the e-commerce microservices. +
+ +
+
+
+
+

Test Emails

+

Send a test email using templates

+ Send Test Email +
+
+
+
+
+
+

Logs

+

View sent/received emails

+ View Logs +
+
+
+
+
+
+
+{% endblock %} diff --git a/mailer/templates/mailer/login.html b/mailer/templates/mailer/login.html new file mode 100644 index 0000000..b18621d --- /dev/null +++ b/mailer/templates/mailer/login.html @@ -0,0 +1,42 @@ +{% extends "mailer/base.html" %} + +{% block content %} +
+
+
+

Login

+ + {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} + +
+ {% csrf_token %} +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+{% endblock %} diff --git a/mailer/templates/mailer/send_test.html b/mailer/templates/mailer/send_test.html new file mode 100644 index 0000000..e378a51 --- /dev/null +++ b/mailer/templates/mailer/send_test.html @@ -0,0 +1,50 @@ +{% extends "mailer/base.html" %} + +{% block content %} +
+
+
+

Send Test Email

+

Use this form to test sending emails using your configured templates.

+ + {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} + +
+ {% csrf_token %} +
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ +
+
+ + Cancel +
+
+
+
+
+
+{% endblock %} diff --git a/mailer/tests.py b/mailer/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/mailer/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/mailer/urls.py b/mailer/urls.py new file mode 100644 index 0000000..092bd1a --- /dev/null +++ b/mailer/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.dashboard, name='dashboard'), + path('test-email/', views.send_test_email, name='send_test_email'), +] diff --git a/mailer/views.py b/mailer/views.py new file mode 100644 index 0000000..e9e6142 --- /dev/null +++ b/mailer/views.py @@ -0,0 +1,73 @@ +from django.shortcuts import render, redirect +from django.contrib import messages +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +from django.utils.html import strip_tags +from django.conf import settings +from .models import EmailLog + +def dashboard(request): + return render(request, 'mailer/dashboard.html') + +def send_test_email(request): + if request.method == 'POST': + recipient = request.POST.get('recipient') + template_name = request.POST.get('template_name') + + if not recipient or not template_name: + messages.error(request, "Recipient and template are required.") + return redirect('send_test_email') + + subject = f"Test Email: {template_name}" + context = {} + + # Mock data for templates + if template_name == 'verification': + context = {'verification_link': 'https://tradhox.com/verify/mock-token'} + subject = "Verify Your Email" + elif template_name == 'password_reset': + context = {'reset_link': 'https://tradhox.com/reset/mock-token'} + subject = "Password Reset Request" + elif template_name == 'promotional': + context = { + 'image_url': 'https://via.placeholder.com/600x200', + 'message_text': 'This is a test promotional message just for you!', + 'shop_link': 'https://tradhox.com/shop' + } + subject = "Welcome to Tradhox Family" + + try: + html_content = render_to_string(f'emails/sellercentral/common/{template_name}.html', context) + text_content = strip_tags(html_content) + + msg = EmailMultiAlternatives( + subject, + text_content, + settings.EMAIL_HOST_USER, + [recipient] + ) + msg.attach_alternative(html_content, "text/html") + msg.send(fail_silently=False) + + # Log success + EmailLog.objects.create( + recipient=recipient, + subject=subject, + template_name=template_name, + status='success' + ) + messages.success(request, f"Test email sent successfully to {recipient}!") + except Exception as e: + # Log failure + EmailLog.objects.create( + recipient=recipient, + subject=subject, + template_name=template_name, + status='failed', + error_message=str(e) + ) + messages.error(request, f"Failed to send email: {e}") + + return redirect('dashboard') + + return render(request, 'mailer/send_test.html') diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..f2a662c --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/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', 'core.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 new file mode 100644 index 0000000..f045d75 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +asgiref==3.12.1 +boto3==1.43.78 +botocore==1.43.78 +Django==6.1 +djangorestframework==3.18.0 +djangorestframework-api-key==3.1.0 +jmespath==1.1.0 +packaging==26.3 +psycopg2-binary==2.9.12 +python-dateutil==2.9.0.post0 +s3transfer==0.19.2 +six==1.17.0 +sqlparse==0.6.0 +urllib3==2.7.0 diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..5997e97 --- /dev/null +++ b/start.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Activate the virtual environment +source venv/bin/activate + +# Apply any pending migrations +echo "Applying database migrations..." +python manage.py migrate + +# Start the Django development server +echo "Starting the Django server on http://127.0.0.1:8000..." +python manage.py runserver