Skip to content
Open
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
70 changes: 49 additions & 21 deletions .sample.env
Original file line number Diff line number Diff line change
@@ -1,27 +1,55 @@
# Server Configuration
CAYGNUS_SERVER_ENV=local
CAYGNUS_SERVER_ADDRESS=:8080

CAYGNUS_SERVER_ENV=dev

# Logging Configuration
# Options: debug, info, warn, error, fatal, panic
CAYGNUS_LOGGING_LEVEL=info

# postgress
CAYGNUS_POSTGRES_HOST=xxx
CAYGNUS_POSTGRES_PORT=xxx
CAYGNUS_POSTGRES_USER=xxx
CAYGNUS_POSTGRES_PASSWORD=xxx
CAYGNUS_POSTGRES_DBNAME=xxx
CAYGNUS_POSTGRES_SSLMODE=xxx
CAYGNUS_POSTGRES_MAX_OPEN_CONNS=xxx
CAYGNUS_POSTGRES_MAX_IDLE_CONNS=xxx
CAYGNUS_POSTGRES_CONN_MAX_LIFETIME_MINUTES=xxx
CAYGNUS_POSTGRES_AUTO_MIGRATE=xxx

# Supabase Configuration
CAYGNUS_SUPABASE_URL=xxx
CAYGNUS_SUPABASE_KEY=xxx
CAYGNUS_SUPABASE_JWT_SECRET=xxx
CAYGNUS_SUPABASE_SERVICE_KEY=xxx

CAYGNUS_SECRETS_ENCRYPTION_KEY=xxxxxx
# PostgreSQL Config
CAYGNUS_POSTGRES_HOST=<your_postgres_host>
CAYGNUS_POSTGRES_PORT=5432
CAYGNUS_POSTGRES_USER=<your_postgres_user>
CAYGNUS_POSTGRES_PASSWORD=<your_postgres_password>
CAYGNUS_POSTGRES_DBNAME=<your_database_name>
CAYGNUS_POSTGRES_SSLMODE=require
CAYGNUS_POSTGRES_MAX_OPEN_CONNS=10
CAYGNUS_POSTGRES_MAX_IDLE_CONNS=5
CAYGNUS_POSTGRES_CONN_MAX_LIFETIME_MINUTES=60
CAYGNUS_POSTGRES_AUTO_MIGRATE=false

# Supabase Config
CAYGNUS_SUPABASE_URL=<your_supabase_url>
CAYGNUS_SUPABASE_KEY=<your_supabase_anon_key>
CAYGNUS_SUPABASE_JWT_SECRET=<your_supabase_jwt_secret>
CAYGNUS_SUPABASE_SERVICE_KEY=<your_supabase_service_role_key>
CAYGNUS_SUPABASE_DEV_API_KEY=<your_development_api_key_for_testing>

# Encryption Configuration
CAYGNUS_SECRETS_ENCRYPTION_KEY=<your_32_byte_encryption_key>

# Cloudinary Configs
CAYGNUS_CLOUDINARY_CLOUD_NAME=<your_cloudinary_cloud_name>
CAYGNUS_CLOUDINARY_API_KEY=<your_cloudinary_api_key>
CAYGNUS_CLOUDINARY_API_SECRET=<your_cloudinary_api_secret>
CAYGNUS_CLOUDINARY_API_BASE_URL=https://api.cloudinary.com # Optional

# Cache Configuration
CAYGNUS_CACHE_ENABLED=true

# Event Publisher Configuration
CAYGNUS_EVENT_PUBLISH_DESTINATION=kafka

# Webhook Configuration
CAYGNUS_WEBHOOK_ENABLED=true
CAYGNUS_WEBHOOK_TOPIC=internal_events
CAYGNUS_WEBHOOK_PUBSUB=memory
CAYGNUS_WEBHOOK_MAX_RETRIES=3
CAYGNUS_WEBHOOK_INITIAL_INTERVAL=1s
CAYGNUS_WEBHOOK_MAX_INTERVAL=10s
CAYGNUS_WEBHOOK_MULTIPLIER=2.0
CAYGNUS_WEBHOOK_MAX_ELAPSED_TIME=2m

# Razorpay Config
CAYGNUS_RAZORPAY_API_KEY=<your_razorpay_key>
CAYGNUS_RAZORPAY_API_SECRET=<your_razorpay_secret>
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ generate-dev-keys:
@./scripts/generate-dev-keys.sh
@echo "✅ Development key pair generated"

.PHONY: generate-dev-token
generate-dev-token:
@echo "Generating development JWT token..."
@echo "Usage: make generate-dev-token JWT_SECRET=your_jwt_secret"
@if [ -z "$(JWT_SECRET)" ]; then \
echo "❌ Error: JWT_SECRET is required"; \
echo "Example: make generate-dev-token JWT_SECRET=your_jwt_secret_here"; \
exit 1; \
fi
@go run cmd/dev-token/main.go "$(JWT_SECRET)"
@echo "✅ Development JWT token generated"

.PHONY: install-hooks
install-hooks:
@echo "Installing Git hooks..."
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,8 @@ Whether you're a newbie coder or a wizard 🧙‍♀️, your perspective is gol

## Contributors

<a href="https://github.com/omkar273/codegeeky/graphs/contributors">
<img src="https://contrib.rocks/image?repo=omkar273/codegeeky" />
<a href="https://github.com/Caygnus/codegeeky-lms-backend/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Caygnus/codegeeky-lms-backend" />
</a>

## Repo Activity
Expand Down
73 changes: 73 additions & 0 deletions cmd/dev-token/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"crypto/rand"
"fmt"
"log"
"os"
"time"

"github.com/golang-jwt/jwt/v5"
)

func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run cmd/dev-token/main.go <jwt_secret>")
fmt.Println("This script generates a long-lived JWT token for development purposes.")
fmt.Println("The token will be valid for 1 year from now.")
os.Exit(1)
}

jwtSecret := os.Args[1]

// Generate a random user ID for development
userID := generateRandomID()

// Create claims for the token
claims := jwt.MapClaims{
"sub": userID,
"aud": "authenticated",
"role": "authenticated",
"email": "dev@example.com",
"phone": "",
"app_metadata": map[string]interface{}{
"role": "ADMIN",
},
"iat": time.Now().Unix(),
"exp": time.Now().AddDate(1, 0, 0).Unix(), // 1 year from now
}

// Create the token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign the token
tokenString, err := token.SignedString([]byte(jwtSecret))
if err != nil {
log.Fatalf("Failed to sign token: %v", err)
}

fmt.Println("=== Development JWT Token ===")
fmt.Println("This token is valid for 1 year from now.")
fmt.Println("Use it in your Authorization header as: Bearer <token>")
fmt.Println()
fmt.Println("Token:")
fmt.Println(tokenString)
fmt.Println()
fmt.Println("User ID:", userID)
fmt.Println("Email: dev@example.com")
fmt.Println("Role: ADMIN")
fmt.Println("Expires:", time.Now().AddDate(1, 0, 0).Format("2006-01-02 15:04:05"))
fmt.Println()
fmt.Println("⚠️ WARNING: This token is for development only!")
fmt.Println("⚠️ Never use this in production!")
fmt.Println("⚠️ Keep your JWT secret secure!")
}

func generateRandomID() string {
bytes := make([]byte, 16)
_, err := rand.Read(bytes)
if err != nil {
log.Fatalf("Failed to generate random ID: %v", err)
}
return fmt.Sprintf("%x-%x-%x-%x-%x", bytes[0:4], bytes[4:6], bytes[6:8], bytes[8:10], bytes[10:16])
}
20 changes: 14 additions & 6 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ func main() {
// internship batch repository
repository.NewInternshipBatchRepository,

// cart repository
repository.NewCartRepository,

// pubsub router
pubsubRouter.NewRouter,
),
Expand All @@ -118,6 +121,7 @@ func main() {
service.NewPricingService,
service.NewPaymentService,
service.NewInternshipEnrollmentService,
service.NewCartService,
))

// factory layer
Expand Down Expand Up @@ -160,16 +164,20 @@ func provideHandlers(
authService service.AuthService,
userService service.UserService,
internshipService service.InternshipService,
internshipBatchService service.InternshipBatchService,
categoryService service.CategoryService,
discountService service.DiscountService,
cartService service.CartService,
) *api.Handlers {
return &api.Handlers{
Health: v1.NewHealthHandler(logger),
Auth: v1.NewAuthHandler(authService),
User: v1.NewUserHandler(userService),
Internship: v1.NewInternshipHandler(internshipService, logger),
Category: v1.NewCategoryHandler(categoryService, logger),
Discount: v1.NewDiscountHandler(discountService, logger),
Health: v1.NewHealthHandler(logger),
Auth: v1.NewAuthHandler(authService),
User: v1.NewUserHandler(userService),
Internship: v1.NewInternshipHandler(internshipService, logger),
InternshipBatch: v1.NewInternshipBatchHandler(internshipBatchService, logger),
Category: v1.NewCategoryHandler(categoryService, logger),
Discount: v1.NewDiscountHandler(discountService, logger),
Cart: v1.NewCartHandler(cartService, logger),
}
}

Expand Down
Loading
Loading