initial commit
This commit is contained in:
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
venv
|
||||
.venv
|
||||
.env
|
||||
.DS_Store
|
||||
dist
|
||||
build
|
||||
\.pytest_cache
|
||||
3
.env
Normal file
3
.env
Normal file
@@ -0,0 +1,3 @@
|
||||
email="contact.dheerajgajula@gmail.com"
|
||||
apppassword="astkbadcyjwbwkrq"
|
||||
receipt_email="dheerajgajula.cse@gmail.com"
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set a working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Environment variables (provided as requested)
|
||||
ENV email=contact.dheerajgajula@gmail.com
|
||||
ENV apppassword=astkbadcyjwbwkrq
|
||||
ENV receipt_email=dheerajgajula.cse@gmail.com
|
||||
|
||||
# Expose port 9999 and run the FastAPI app with uvicorn
|
||||
EXPOSE 9999
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9999"]
|
||||
71
README.md
Normal file
71
README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Email Sender Service
|
||||
|
||||
This repository contains a simple `EmailSender` class and a small FastAPI service that exposes an endpoint to send emails from the configured sender to the configured recipient.
|
||||
|
||||
Required environment variables (e.g. in a `.env` file):
|
||||
|
||||
- `email` - sender email address (example: your Gmail address)
|
||||
- `apppassword` - SMTP app password (for Gmail, generate an app password)
|
||||
- `receipt_email` - recipient email address (where messages will be sent)
|
||||
|
||||
Optional environment variables for test script:
|
||||
|
||||
- `TEST_SUBJECT` - subject used by `test.py` if provided
|
||||
- `TEST_CONTENT` - content used by `test.py` if provided
|
||||
|
||||
Install dependencies
|
||||
|
||||
It's recommended to use a virtual environment. Then:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Run the FastAPI service locally with uvicorn:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
Request example (curl):
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/send" -H "Content-Type: application/json" -d '{"subject":"Hello","content":"This is a test"}'
|
||||
```
|
||||
|
||||
Notes
|
||||
|
||||
- The service uses Gmail's SMTP by default (the existing `EmailSender` implementation). If you want to use a different SMTP provider, update `_send_email` in `email_sender.py`.
|
||||
- For local development without sending real email, consider running a debug SMTP server or modifying `_send_email` to print the message when a `DRY_RUN` env var is set.
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
A simple Dockerfile is provided to containerize the service. It embeds the three environment values you requested directly into the image (`email`, `apppassword`, `receipt_email`). Embedding secrets in a Dockerfile is not generally recommended for production — see the security note below.
|
||||
|
||||
Build the image:
|
||||
|
||||
```bash
|
||||
docker build -t email-service:latest .
|
||||
```
|
||||
|
||||
Run the container (maps container port 9999 to host port 9999):
|
||||
|
||||
```bash
|
||||
docker run -p 9999:9999 --rm email-service:latest
|
||||
```
|
||||
|
||||
Then POST to the `/send` endpoint:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:9999/send" -H "Content-Type: application/json" -d '{"subject":"Hello","content":"This is a test"}'
|
||||
```
|
||||
|
||||
Security note
|
||||
-------------
|
||||
|
||||
Storing secrets (email and app passwords) in a Dockerfile is insecure because the resulting image and layers can be inspected and shared. Safer alternatives:
|
||||
|
||||
- Use an external environment file (`--env-file .env`) or pass `-e` flags to `docker run` to inject secrets at runtime (do not commit `.env` to source control).
|
||||
- Use Docker secrets or a secret manager when deploying to orchestration platforms.
|
||||
|
||||
75
_mon_tech_stack.txt
Normal file
75
_mon_tech_stack.txt
Normal file
@@ -0,0 +1,75 @@
|
||||
endpoints:
|
||||
- name: resume
|
||||
group: core
|
||||
url: "https://resume.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
- name: git
|
||||
group: core
|
||||
url: "https://git.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
|
||||
- name: docs
|
||||
group: core
|
||||
url: "https://docs.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
- name: photos backup
|
||||
group: core
|
||||
url: "https://pic.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
|
||||
- name: boulder-server-ping
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.10"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: intel-nuc
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.20"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: proxmox
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.30"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: check-domain-expiration
|
||||
url: "https://dheerajg.me"
|
||||
interval: 1h
|
||||
conditions:
|
||||
- "[DOMAIN_EXPIRATION] > 720h"
|
||||
|
||||
|
||||
- name: example-dns-query
|
||||
url: "8.8.8.8" # Address of the DNS server to use
|
||||
interval: 10s
|
||||
dns:
|
||||
query-name: "example.com"
|
||||
query-type: "A"
|
||||
conditions:
|
||||
- "[BODY] == pat(*.*.*.*)" # Matches any IPv4 address
|
||||
- "[DNS_RCODE] == NOERROR"
|
||||
66
email_sender.py
Normal file
66
email_sender.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
load_dotenv()
|
||||
|
||||
class EmailSender:
|
||||
def __init__(self):
|
||||
self.email_id = os.getenv("email")
|
||||
self.apppassword = os.getenv("apppassword")
|
||||
self.receipt_email_id = os.getenv("receipt_email")
|
||||
|
||||
def message_prep(self, subject:str, email: str, body:str):
|
||||
|
||||
msg = MIMEText(body)
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = self.email_id
|
||||
msg['To'] = email
|
||||
|
||||
return msg
|
||||
pass
|
||||
|
||||
def _send_email(self, msg:MIMEText, recipient:str):
|
||||
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp_server:
|
||||
# use the configured sender id and app password
|
||||
smtp_server.login(self.email_id, self.apppassword)
|
||||
smtp_server.sendmail(self.email_id, recipient, msg.as_string())
|
||||
pass
|
||||
|
||||
def send_email(self, reason:str, email:str, body:str):
|
||||
|
||||
if reason == "Job Opportunity":
|
||||
subject = "Your time has comeee !!!! get up !!!!"
|
||||
elif reason == "Collaboration":
|
||||
subject = "Time to build somthing !! getup !"
|
||||
elif reason == "General":
|
||||
subject = "Someone is saying hii !!"
|
||||
else:
|
||||
subject = "hmmm !! what could it be :)"
|
||||
# send email to self
|
||||
reciept_email = self.message_prep(subject=subject, email=self.receipt_email_id, body=body + f"\n \n {email} has contacted you ")
|
||||
self._send_email(msg=reciept_email, recipient=self.receipt_email_id)
|
||||
# send email to the contact person
|
||||
contact_person_email = self.message_prep(subject=os.getenv("contact_subject"), email=email, body=os.getenv("contact_body"))
|
||||
self._send_email(msg=contact_person_email, recipient=email)
|
||||
pass
|
||||
|
||||
def send_general_email(self, subject:str, email:str, body:str):
|
||||
# send email to self
|
||||
reciept_email = self.message_prep(subject=subject, email=self.receipt_email_id, body=body + f"\n \n {email} has contacted you ")
|
||||
self._send_email(msg=reciept_email, recipient=self.receipt_email_id)
|
||||
# send email to the contact person
|
||||
contact_person_email = self.message_prep(subject=os.getenv("contact_subject"), email=email, body=os.getenv("contact_body"))
|
||||
self._send_email(msg=contact_person_email, recipient=email)
|
||||
|
||||
|
||||
pass
|
||||
|
||||
def send_to_recipient(self, subject: str, content: str):
|
||||
"""Send a simple email with given subject and content from self.email_id to self.receipt_email_id."""
|
||||
if not self.email_id or not self.apppassword or not self.receipt_email_id:
|
||||
raise ValueError("Missing email configuration (email, apppassword, or receipt_email).")
|
||||
|
||||
msg = self.message_prep(subject=subject, email=self.receipt_email_id, body=content)
|
||||
self._send_email(msg=msg, recipient=self.receipt_email_id)
|
||||
return True
|
||||
42
main.py
Normal file
42
main.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
|
||||
from email_sender import EmailSender
|
||||
|
||||
app = FastAPI(title="Email Sender Service")
|
||||
|
||||
class SendRequest(BaseModel):
|
||||
subject: str
|
||||
content: str
|
||||
|
||||
# create a single EmailSender instance for the app
|
||||
es = EmailSender()
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
@app.post("/send")
|
||||
def send_email(req: SendRequest):
|
||||
"""Send an email from configured sender to configured recipient.
|
||||
|
||||
Expects JSON: {"subject": "...", "content": "..."}
|
||||
"""
|
||||
# basic config validation
|
||||
missing = []
|
||||
if not es.email_id:
|
||||
missing.append("email")
|
||||
if not es.apppassword:
|
||||
missing.append("apppassword")
|
||||
if not es.receipt_email_id:
|
||||
missing.append("receipt_email")
|
||||
if missing:
|
||||
raise HTTPException(status_code=500, detail={"error": "missing_config", "missing": missing})
|
||||
|
||||
try:
|
||||
es.send_to_recipient(subject=req.subject, content=req.content)
|
||||
return {"status": "sent", "to": es.receipt_email_id}
|
||||
except Exception as e:
|
||||
# return the error message but avoid leaking sensitive info
|
||||
raise HTTPException(status_code=500, detail={"error": "send_failed", "message": str(e)})
|
||||
76
monitoring_stack.txt
Normal file
76
monitoring_stack.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
endpoints:
|
||||
- name: resume
|
||||
group: core
|
||||
url: "https://resume.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
- name: git
|
||||
group: core
|
||||
url: "https://git.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
|
||||
- name: docs
|
||||
group: core
|
||||
url: "https://docs.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
- name: photos backup
|
||||
group: core
|
||||
url: "https://pic.dheerajg.me"
|
||||
interval: 10s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].status == UP"
|
||||
- "[RESPONSE_TIME] < 150"
|
||||
|
||||
|
||||
- name: boulder-server-ping
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.10"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: intel-nuc
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.20"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: proxmox
|
||||
group: server-stats
|
||||
url: "icmp://10.0.0.30"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[CONNECTED] == true"
|
||||
|
||||
- name: check-domain-expiration
|
||||
url: "https://dheerajg.me"
|
||||
interval: 1h
|
||||
conditions:
|
||||
- "[DOMAIN_EXPIRATION] > 720h"
|
||||
|
||||
|
||||
- name: example-dns-query
|
||||
url: "8.8.8.8" # Address of the DNS server to use
|
||||
interval: 10s
|
||||
dns:
|
||||
query-name: "example.com"
|
||||
query-type: "A"
|
||||
conditions:
|
||||
- "[BODY] == pat(*.*.*.*)" # Matches any IPv4 address
|
||||
- "[DNS_RCODE] == NOERROR"
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
python-dotenv==1.1.1
|
||||
fastapi==0.100.0
|
||||
uvicorn[standard]==0.22.0
|
||||
30
test.py
Normal file
30
test.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from email_sender import EmailSender
|
||||
import os
|
||||
|
||||
|
||||
def main():
|
||||
"""Simple test runner that sends an email using EmailSender.send_to_recipient.
|
||||
|
||||
Make sure the following environment variables are set (for example in a .env file):
|
||||
- email (sender email)
|
||||
- apppassword (smtp/app password)
|
||||
- receipt_email (recipient email)
|
||||
|
||||
Run this file and let me know whether you receive the email.
|
||||
"""
|
||||
|
||||
subject = os.getenv('TEST_SUBJECT', 'Test email from EmailSender')
|
||||
content = os.getenv('TEST_CONTENT', 'This is a test email sent by EmailSender.send_to_recipient')
|
||||
|
||||
es = EmailSender()
|
||||
|
||||
try:
|
||||
print(f"Sending -> from: {es.email_id} to: {es.receipt_email_id} subject: {subject}")
|
||||
es.send_to_recipient(subject=subject, content=content)
|
||||
print("Send attempted — if SMTP credentials are correct, the recipient should receive the message.")
|
||||
except Exception as e:
|
||||
print("Error while sending email:", repr(e))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user