Modern, Laravel-inspired web framework for Go. Built with security, performance, and developer experience in mind.
- SQL Injection Protection
- CSRF Protection
- Rate Limiting
- Graceful Shutdown
- Memory Leak Protection
-
JWT Authentication
- Access & refresh tokens
- Token rotation
- Secure token storage
-
User Management
- Registration with validation
- Login with bcrypt password hashing
- Profile management
- Password change
-
Password Reset
- Forgot password flow
- Secure reset tokens (1-hour expiry)
- Email notifications (ready for Phase 3)
-
Role-Based Authorization
- Admin, Editor, User roles
- Protected routes
- Policy-based access control
-
Redis Queue
- Push/Later (immediate/delayed dispatch)
- Pop (blocking job fetch)
- Failed job handling
- Retry mechanism with exponential backoff
-
Job System
- Job interface with Handle() and Failed()
- Serialization/deserialization
- Job metadata (ID, attempts, queue name)
- Job registry for type mapping
-
Worker
- Multiple queue support
- Graceful shutdown
- Concurrent processing
- Failed job tracking
-
Event Dispatcher
- Laravel-inspired event-driven architecture
- Multiple listeners per event
- Async/sync dispatch
- Thread-safe operations
-
Event Features
- Built-in events (user, email, payment, cache)
- Custom event creation
- Conditional listeners
- Async listeners for slow operations
- Event statistics and monitoring
-
SMTP Driver
- Send emails via any SMTP server
- Support for Gmail, SendGrid, AWS SES, Mailhog
- HTML & plain text emails
- File attachments
- Multiple recipients (To, Cc, Bcc)
-
Message Builder
- Fluent API for email construction
- Priority levels (High, Normal, Low)
- Custom headers support
- Reply-To support
-
Drivers
- SMTP (production)
- Log (development/testing)
-
Local Storage
- Local filesystem operations
- Path traversal protection
- Stream support for large files
- Directory management
- URL generation
-
Storage Features
- Upload/download files
- File existence checks
- File size and metadata
- Unique name generation
- Image detection helpers
// Create dispatcher
dispatcher := events.NewDispatcher(logger)
// Register listener
dispatcher.Listen("user.registered", events.ListenerFunc(func(e events.Event) error {
user := e.Payload().(*models.User)
log.Printf("New user: %s", user.Email)
return nil
}))
// Dispatch event
event := events.NewUserRegisteredEvent(user)
dispatcher.Dispatch(event)
// Async dispatch (non-blocking)
dispatcher.DispatchAsync(event)// Configure SMTP (Mailhog for development)
config := &mail.SMTPConfig{
Host: "localhost",
Port: 1025,
From: mail.Address{Email: "noreply@conduit.com", Name: "Conduit"},
}
mailer := mail.NewSMTPMailer(config, logger)
// Send email
message := mail.NewMessage().
To("user@example.com", "John Doe").
Subject("Welcome to Conduit!").
Body("Thank you for joining.").
Html("<h1>Welcome!</h1>")
err := mailer.Send(message)// Initialize local storage
storage, _ := storage.NewLocalStorage("/var/www/uploads", logger)
storage.SetBaseURL("https://cdn.myapp.com")
// Upload file
imageData := []byte{...}
storage.Put("avatars/user-1.jpg", imageData)
// Get URL
url := storage.Url("avatars/user-1.jpg")
// β "https://cdn.myapp.com/avatars/user-1.jpg"
// Stream large file
file, _ := os.Open("large-video.mp4")
storage.PutFile("videos/video.mp4", file)
// Download
data, _ := storage.Get("avatars/user-1.jpg")// Create a job
emailJob := jobs.NewSendEmailJob(
"user@example.com",
"Welcome to Conduit-Go",
"Hello! Welcome to our platform.",
)
// Push to queue (immediate)
queue.Push(emailJob, "emails")
// Push to queue (delayed)
queue.Later(5*time.Minute, emailJob, "emails")# Start worker for default queue
make worker
# Start worker for specific queues
make worker-emails
# Start worker for all queues
make worker-all
# Or directly
go run cmd/worker/main.go emails notificationspackage jobs
import (
"encoding/json"
"github.com/biyonik/conduit-go/pkg/queue"
)
type MyCustomJob struct {
queue.BaseJob
Data string `json:"data"`
}
func (j *MyCustomJob) Handle() error {
// Your job logic here
return nil
}
func (j *MyCustomJob) Failed(err error) error {
// Failed job handling
return nil
}
func (j *MyCustomJob) GetPayload() ([]byte, error) {
return json.Marshal(j)
}
func (j *MyCustomJob) SetPayload(data []byte) error {
return json.Unmarshal(data, j)
}
// Register the job type
func init() {
queue.RegisterJob("*jobs.MyCustomJob", func() queue.Job {
return &MyCustomJob{}
})
}- Go 1.25 or higher
- MySQL 8.0 or higher
- Docker (optional)
# Clone repository
git clone https://github.com/yourusername/conduit-go.git
cd conduit-go
# Environment setup
cp .env.example .env
nano .env # Edit with your credentials
# Start Docker services
docker-compose up -d
sleep 10 # Wait for MySQL
# Run application
make runServer runs on: http://localhost:8000
POST /api/auth/register
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "Secret123!",
"password_confirm": "Secret123!"
}Response:
{
"success": true,
"data": {
"user": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"token_type": "Bearer",
"expires_in": 3600
}
}POST /api/auth/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "Secret123!"
}GET /api/auth/profile
Authorization: Bearer {access_token}POST /api/auth/refresh
Content-Type: application/json
{
"refresh_token": "eyJhbGc..."
}POST /api/auth/forgot-password
Content-Type: application/json
{
"email": "john@example.com"
}POST /api/auth/reset-password
Content-Type: application/json
{
"token": "abc123...",
"email": "john@example.com",
"password": "NewSecret123!",
"password_confirm": "NewSecret123!"
}All /api/v1/* routes require authentication:
GET /api/v1/check
Authorization: Bearer {access_token}Admin-only routes require admin role:
GET /api/admin/users
Authorization: Bearer {admin_access_token}// Register
const registerResponse = await fetch('http://localhost:8000/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
password: 'Secret123!',
password_confirm: 'Secret123!',
}),
});
const { data } = await registerResponse.json();
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
// Protected API call
const profileResponse = await fetch('http://localhost:8000/api/auth/profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
},
});
// Handle token expiration
if (profileResponse.status === 401) {
// Refresh token
const refreshResponse = await fetch('http://localhost:8000/api/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refresh_token: localStorage.getItem('refresh_token'),
}),
});
const { data } = await refreshResponse.json();
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
// Retry original request
}# Run all tests
make test
# Run auth tests only
go test -v ./tests -run Auth
# Run with coverage
make test-coverage
# Security audit
make security- Minimum 8 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 number
- At least 1 special character
- Access tokens expire in 1 hour
- Refresh tokens expire in 7 days
- Tokens use HS256 algorithm
- Secret keys must be stored in environment variables
- Public auth endpoints: 10 requests/minute
- Protected API endpoints: 50 requests/minute
- Admin endpoints: 30 requests/minute
Import postman/Conduit-Go-API.postman_collection.json to test all endpoints.
Variables:
base_url: http://localhost:8000access_token: Auto-populated after loginrefresh_token: Auto-populated after login
# Format code
make fmt
# Run linter
make lint
# Build binary
make build
# Run with hot reload
make runConduit comes with a powerful Laravel Artisan-inspired CLI tool for rapid development.
# Build conduit CLI tool
go build -o conduit cmd/conduit/main.go
# Or add to PATH
sudo mv conduit /usr/local/bin/Generate controllers, models, middleware, jobs, events, and listeners with a single command:
# Create a controller
conduit make:controller UserController
# Create a resource controller with CRUD methods
conduit make:controller UserController --resource
# Create an API controller (no views)
conduit make:controller UserController --api
# Create a model
conduit make:model User
# Create a model with migration
conduit make:model Post --migration
# Create middleware
conduit make:middleware AuthMiddleware
# Create a job
conduit make:job ProcessVideoJob
# Create an event
conduit make:event UserRegistered
# Create a listener
conduit make:listener SendWelcomeEmail --event=UserRegisteredManage database schema changes with Laravel-style migrations:
# Run pending migrations
conduit migrate
# Rollback the last migration
conduit migrate:rollback
# Rollback multiple migrations
conduit migrate:rollback --step=3
# Drop all tables and re-run migrations
conduit migrate:fresh
# Show migration status
conduit migrate:status# Clear all cache
conduit cache:clear
# Forget a specific cache key
conduit cache:forget user:123# Start queue worker
conduit queue:work
# Start queue worker for specific queue
conduit queue:work --queue=emails
# Limit number of jobs
conduit queue:work --max-jobs=100
# Start queue listener (auto-restart on code changes)
conduit queue:listen
# Restart all queue workers
conduit queue:restart# Start development server (default: localhost:8080)
conduit serve
# Custom host and port
conduit serve --host=0.0.0.0 --port=3000The dev server includes:
- β Auto-logging of all requests
- β Beautiful welcome page
- β
Health check endpoint (
/health) - β Graceful shutdown
- β Hot reload ready
# Show all available commands
conduit help
# Show version
conduit versionConduit provides Laravel-inspired testing utilities:
package controllers_test
import (
"testing"
"github.com/biyonik/conduit-go/pkg/testing"
)
func TestUserCreation(t *testing.T) {
// HTTP Testing
resp := testing.NewTestRequest("POST", "/api/users").
WithJSON(map[string]interface{}{
"name": "John Doe",
"email": "john@example.com",
}).
Send(router)
// Assertions
resp.AssertStatus(t, 201).
AssertJSON(t).
AssertJSONPath(t, "message", "User created")
// Database Testing
testing.RefreshDatabase(t)
testing.DatabaseTransaction(t, func(tx *sql.Tx) {
// Test code runs in transaction, auto-rolled back
})
// Factory Pattern
user := testing.UserFactory().Make(map[string]interface{}{
"email": "custom@example.com",
})
// Assertions
testing.AssertEquals(t, "custom@example.com", user["email"])
testing.AssertNotNil(t, user["name"])
testing.AssertTrue(t, user["active"].(bool), "User should be active")
}β Comprehensive security audit completed - See SECURITY_AUDIT_REPORT.md
- β Event System: Goroutine leak prevention with graceful shutdown
- β Storage System: Path traversal attacks prevented (100+ test cases)
- β Database: SQL injection protection verified (50+ test cases)
- β
Race Conditions: No data races detected (
go test -race) - β Test Coverage: >80% on all critical security paths
Security Rating: π’ PRODUCTION READY
-
Phase 1: Security & Stability
-
Phase 2: Authentication & Authorization
-
Phase 3: Advanced Features
- β Queue system (Redis)
- β Event system
- β Cache facade
- β Mail system
- β File storage
- β Email verification
-
Phase 4: Developer Experience (β COMPLETED)
- β CLI tool (Artisan-inspired)
- β Code generators (make commands)
- β Migration system
- β Testing helpers
- β Development server
- β Comprehensive security audit
-
Phase 5: Production Enhancements (Next)
- SMTP connection pooling
- Distributed tracing
- Metrics & monitoring
- Load balancing
- Docker orchestration
- Rate limiting improvements
# Application
APP_NAME=Conduit-Go
APP_ENV=development
PORT=8000
# Database
DB_DSN=user:pass@tcp(localhost:3306)/conduit_go?parseTime=true
# JWT
JWT_SECRET=your-super-secret-key
JWT_EXPIRATION=3600
# Rate Limiting
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_SECONDS=60We welcome contributions! Please read our Contributing Guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes.
Thank you to all the people who have contributed to Conduit-Go! π
- @biyonik - Creator & Maintainer
This project is licensed under the MIT License - see the LICENSE file for details.
Ahmet Altun
- Email: ahmet.altun60@gmail.com
- GitHub: @biyonik
- LinkedIn: linkedin.com/in/biyonik
Built with β€οΈ and β in Turkey