Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/workflows/pr-build-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: Update Build Triggers

on:
pull_request:
types: [opened, reopened]
branches: [ main ]

jobs:
update-build-triggers:
name: Update Build Triggers
runs-on: ubuntu-latest

# Only run if not triggered by the bot itself to avoid infinite loops
if: github.actor != 'github-actions[bot]'

permissions:
contents: write
pull-requests: write

steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}
fetch-depth: 0

- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

- name: Update build trigger files
run: |
# Get current timestamp in ISO format
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "Updating build triggers with timestamp: $TIMESTAMP"

# Update all .render-build-trigger files
UPDATED_FILES=()

for component in backend collect frontend proxy; do
if [ -f "$component/.render-build-trigger" ]; then
echo "Updating $component/.render-build-trigger"
perl -i -pe "s/LAST_UPDATE=.*/LAST_UPDATE=$TIMESTAMP/" "$component/.render-build-trigger"
UPDATED_FILES+=("$component/.render-build-trigger")
else
echo "Warning: $component/.render-build-trigger not found, skipping"
fi
done

# Check if any files were actually changed
if git diff --quiet; then
echo "No changes detected in build trigger files"
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "Build trigger files updated successfully"
echo "changed=true" >> $GITHUB_OUTPUT

# Show what changed
echo "Changed files:"
git diff --name-only

# Show the diff
echo "Changes:"
git diff
fi
id: update

- name: Commit and push changes
if: steps.update.outputs.changed == 'true'
run: |
# Add all modified .render-build-trigger files
git add */.render-build-trigger

# Commit the changes
git commit -m "chore: update build triggers for PR deployment

Auto-updated .render-build-trigger files to ensure all services
are deployed in PR preview environments.

🤖 Generated by GitHub Actions"

# Push changes back to the PR branch
git push origin ${{ github.head_ref }}

- name: Add comment to PR
if: steps.update.outputs.changed == 'true'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 **Build triggers updated!**\n\nAll `.render-build-trigger` files have been automatically updated to ensure fresh deployments of all services in the PR preview environment.\n\n_This comment was generated automatically by GitHub Actions._'
})

- name: Summary
run: |
if [ "${{ steps.update.outputs.changed }}" == "true" ]; then
echo "✅ Build triggers updated and committed to PR"
echo "All Docker services will be rebuilt in Render preview environment"
else
echo "ℹ️ No build trigger updates needed"
echo "Build trigger timestamps are already current"
fi
13 changes: 13 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ DATABASE_URL=postgresql://noc_user:noc_password@localhost:5432/noc?sslmode=disab
# Redis connection URL
REDIS_URL=redis://localhost:6379

# ===========================================
# SMTP EMAIL CONFIGURATION (Optional)
# ===========================================
# SMTP server configuration for welcome emails
# If not configured, welcome emails will be skipped (user creation still succeeds)
#SMTP_HOST=smtp.gmail.com
#SMTP_PORT=587
#SMTP_USERNAME=your-email@gmail.com
#SMTP_PASSWORD=your-app-password
#SMTP_FROM=noreply@yourdomain.com
#SMTP_FROM_NAME=Nethesis Operation Center
#SMTP_TLS=true

# ===========================================
# OPTIONAL CONFIGURATION
# ===========================================
Expand Down
10 changes: 10 additions & 0 deletions backend/.render-build-trigger
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Render Build Trigger File
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2025-07-30T00:00:00Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
# 2. Run: perl -i -pe "s/LAST_UPDATE=.*/LAST_UPDATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)/" .render-build-trigger
# 2. Commit and push changes to trigger Docker rebuilds
3 changes: 3 additions & 0 deletions backend/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ RUN apk add --no-cache git ca-certificates
# Set working directory
WORKDIR /app

# Copy build trigger file to force rebuilds when it changes
COPY .render-build-trigger /tmp/build-trigger

# Copy go mod files first for better caching
COPY go.mod go.sum ./

Expand Down
6 changes: 3 additions & 3 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ validate-docs:
echo "Install with: npm install -g @apidevtools/swagger-cli"; \
fi

# Database migration (placeholder)
# Database migrations are handled automatically on backend startup
.PHONY: db-migrate
db-migrate:
@echo "Running database migrations..."
@echo "Note: Migrations are handled automatically by the application on startup"
@echo "Database migrations are handled automatically when the backend starts"
@echo "Simply run 'make run' or 'go run main.go' to apply pending migrations"

# Pre-commit checks
.PHONY: pre-commit
Expand Down
150 changes: 138 additions & 12 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ DATABASE_URL=postgresql://backend:backend@localhost:5432/noc?sslmode=disable

# Redis connection URL
REDIS_URL=redis://localhost:6379

# ===========================================
# SMTP EMAIL CONFIGURATION (Optional)
# ===========================================
# SMTP server configuration for welcome emails
# If not configured, welcome emails will be skipped (user creation still succeeds)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM=noreply@yourdomain.com
SMTP_FROM_NAME=Nethesis Operation Center
SMTP_TLS=true
```

**Auto-derived URLs:**
Expand All @@ -57,6 +70,80 @@ REDIS_URL=redis://localhost:6379
- `LOGTO_MANAGEMENT_BASE_URL` = `https://{TENANT_ID}.logto.app/api`
- `JWT_ISSUER` = `{TENANT_DOMAIN}`

## Email Configuration

The backend automatically sends welcome emails to newly created users with their temporary password and login instructions. Email functionality is optional and degrades gracefully if not configured.

### SMTP Setup

Configure SMTP settings in your environment:

```bash
# SMTP server details
SMTP_HOST=smtp.gmail.com # Your SMTP server hostname
SMTP_PORT=587 # SMTP port (587 for TLS, 465 for SSL, 25 for plain)
SMTP_USERNAME=your-email@gmail.com # SMTP authentication username
SMTP_PASSWORD=your-app-password # SMTP authentication password
SMTP_FROM=noreply@yourdomain.com # From email address for outgoing emails
SMTP_FROM_NAME=Your Company Name # Display name for sender
SMTP_TLS=true # Enable TLS encryption (recommended)
```

### Supported Providers

**Gmail/Google Workspace:**
```bash
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password # Use App Password, not account password
SMTP_TLS=true
```

**AWS SES:**
```bash
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_USERNAME=your-ses-smtp-username
SMTP_PASSWORD=your-ses-smtp-password
SMTP_TLS=true
```

**SendGrid:**
```bash
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USERNAME=apikey
SMTP_PASSWORD=your-sendgrid-api-key
SMTP_TLS=true
```

### Email Features

- **Automatic Delivery**: Welcome emails sent when users are created via API
- **Modern Templates**: Responsive HTML and text templates with dark/light mode support
- **Secure Password Delivery**: Auto-generated temporary passwords sent securely
- **Smart Links**: Login URLs point directly to password change page
- **Graceful Degradation**: User creation succeeds even if email delivery fails
- **Security**: Passwords never logged, email content is sanitized

### Template Customization

Email templates are located in `services/email/templates/`:
- `welcome.html` - Modern HTML template with dark mode support
- `welcome.txt` - Plain text fallback template

Templates support Go template syntax with variables:
- `{{.UserName}}` - User's full name
- `{{.UserEmail}}` - User's email address
- `{{.OrganizationName}}` - Organization name
- `{{.OrganizationType}}` - Organization type (Owner, Distributor, etc.)
- `{{.UserRoles}}` - Array of user role names
- `{{.TempPassword}}` - Generated temporary password
- `{{.LoginURL}}` - Direct link to password change page
- `{{.SupportEmail}}` - Support contact email
- `{{.CompanyName}}` - Company name from SMTP configuration

## Architecture

### Two-Layer Authorization
Expand Down Expand Up @@ -164,6 +251,8 @@ make validate-docs
```

### Testing

#### Authentication Testing
```bash
# Test token exchange
curl -X POST http://localhost:8080/api/auth/exchange \
Expand All @@ -175,21 +264,58 @@ curl -X GET http://localhost:8080/api/me \
-H "Authorization: Bearer YOUR_CUSTOM_JWT"
```

#### Email Testing
```bash
# Test welcome email service configuration
curl -X POST http://localhost:8080/api/users \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"name": "Test User",
"userRoleIds": ["role_123"],
"organizationId": "org_456"
}'

# Check logs for email delivery status
docker logs backend-container 2>&1 | grep "Welcome email"
```

#### SMTP Connection Testing
The backend automatically validates SMTP configuration on startup. Check logs for:
```
{"level":"info","message":"Welcome email service configuration test successful"}
```

If SMTP is misconfigured, you'll see:
```
{"level":"warn","message":"SMTP not configured, skipping welcome email"}
```

## Project Structure
```
backend/
├── main.go # Server entry point
├── cache/ # Redis caching system
├── configuration/ # Environment config
├── helpers/ # Utilities for JWT context
├── jwt/ # Utilities for JWT claims
├── logger/ # Structured logging
├── methods/ # HTTP handlers
├── middleware/ # Auth and RBAC middleware
├── models/ # Data structures
├── response/ # HTTP response helpers
├── services/ # Business logic
└── .env.example # Environment variables template
├── main.go # Server entry point
├── cache/ # Redis caching system
├── configuration/ # Environment config
├── helpers/ # Utilities for JWT context
├── jwt/ # Utilities for JWT claims
├── logger/ # Structured logging
├── methods/ # HTTP handlers
├── middleware/ # Auth and RBAC middleware
├── models/ # Data structures
├── response/ # HTTP response helpers
├── services/ # Business logic
│ ├── email/ # Email service
│ │ ├── smtp.go # SMTP client implementation
│ │ ├── templates.go # Template rendering service
│ │ ├── welcome.go # Welcome email service
│ │ └── templates/ # Email templates
│ │ ├── welcome.html # HTML email template
│ │ └── welcome.txt # Text email template
│ ├── local/ # Local database services
│ └── logto/ # Logto API integration
└── .env.example # Environment variables template
```


Expand Down
Loading