basic frontend #3
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Base Configuration
|
||||||
|
DATABASE_URL=postgres://postgres:password@localhost:5432/counter_db?sslmode=disable
|
||||||
|
PORT=8080
|
||||||
|
LOG_LEVEL=info
|
||||||
|
GIN_MODE=debug
|
||||||
7
.env.development
Normal file
7
.env.development
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Development Environment
|
||||||
|
ENVIRONMENT=development
|
||||||
|
DATABASE_URL=postgres://postgres:password@postgres:5432/counter_db?sslmode=disable
|
||||||
|
JWT_SECRET=dev-secret-key-change-in-production
|
||||||
|
PORT=8080
|
||||||
|
GIN_MODE=debug
|
||||||
|
LOG_LEVEL=debug
|
||||||
7
.env.production
Normal file
7
.env.production
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Production Environment
|
||||||
|
ENVIRONMENT=production
|
||||||
|
DATABASE_URL=postgres://postgres:password@postgres:5432/counter_db?sslmode=disable
|
||||||
|
JWT_SECRET=super-secure-production-secret-change-this
|
||||||
|
PORT=8080
|
||||||
|
GIN_MODE=release
|
||||||
|
LOG_LEVEL=warn
|
||||||
7
.env.staging
Normal file
7
.env.staging
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Staging Environment
|
||||||
|
ENVIRONMENT=staging
|
||||||
|
DATABASE_URL=postgres://postgres:password@postgres:5432/counter_db?sslmode=disable
|
||||||
|
JWT_SECRET=staging-secret-key-change-this
|
||||||
|
PORT=8080
|
||||||
|
GIN_MODE=release
|
||||||
|
LOG_LEVEL=info
|
||||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -17,12 +17,12 @@
|
|||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables (keeping all env files in git as requested)
|
||||||
.env
|
# .env
|
||||||
.env.local
|
# .env.local
|
||||||
.env.development.local
|
# .env.development.local
|
||||||
.env.test.local
|
# .env.test.local
|
||||||
.env.production.local
|
# .env.production.local
|
||||||
|
|
||||||
# Node.js
|
# Node.js
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ COPY go.mod go.sum ./
|
|||||||
# Download dependencies
|
# Download dependencies
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
# Copy source code
|
# Copy source code and environment files
|
||||||
COPY *.go ./
|
COPY *.go ./
|
||||||
|
COPY .env* ./
|
||||||
|
|
||||||
# Build the application
|
# Build the application
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
|
||||||
@@ -43,6 +44,9 @@ WORKDIR /root/
|
|||||||
# Copy the Go binary from go-builder stage
|
# Copy the Go binary from go-builder stage
|
||||||
COPY --from=go-builder /app/main .
|
COPY --from=go-builder /app/main .
|
||||||
|
|
||||||
|
# Copy environment files from go-builder stage
|
||||||
|
COPY --from=go-builder /app/.env* ./
|
||||||
|
|
||||||
# Copy the React build from frontend-builder stage
|
# Copy the React build from frontend-builder stage
|
||||||
COPY --from=frontend-builder /app/frontend/build ./frontend/build
|
COPY --from=frontend-builder /app/frontend/build ./frontend/build
|
||||||
|
|
||||||
|
|||||||
7
auth.go
7
auth.go
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
var jwtSecret []byte
|
var jwtSecret []byte
|
||||||
|
|
||||||
// InitJWT initializes JWT secret
|
// InitJWT initializes JWT secret (legacy function)
|
||||||
func InitJWT() {
|
func InitJWT() {
|
||||||
secret := os.Getenv("JWT_SECRET")
|
secret := os.Getenv("JWT_SECRET")
|
||||||
if secret == "" {
|
if secret == "" {
|
||||||
@@ -28,6 +28,11 @@ func InitJWT() {
|
|||||||
jwtSecret = []byte(secret)
|
jwtSecret = []byte(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitJWTWithConfig initializes JWT secret with configuration
|
||||||
|
func InitJWTWithConfig(config *Config) {
|
||||||
|
jwtSecret = []byte(config.JWTSecret)
|
||||||
|
}
|
||||||
|
|
||||||
// generateRandomSecret generates a random secret for JWT
|
// generateRandomSecret generates a random secret for JWT
|
||||||
func generateRandomSecret() string {
|
func generateRandomSecret() string {
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
|
|||||||
216
config.go
Normal file
216
config.go
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Environment string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Development Environment = "development"
|
||||||
|
Staging Environment = "staging"
|
||||||
|
Production Environment = "production"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Environment Environment
|
||||||
|
DatabaseURL string
|
||||||
|
JWTSecret string
|
||||||
|
Port string
|
||||||
|
GinMode string
|
||||||
|
LogLevel string
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads configuration with proper environment file precedence
|
||||||
|
func LoadConfig() *Config {
|
||||||
|
// Load environment files in priority order
|
||||||
|
loadEnvironmentFiles()
|
||||||
|
|
||||||
|
// Get environment
|
||||||
|
env := getEnvironment()
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
config := &Config{
|
||||||
|
Environment: env,
|
||||||
|
DatabaseURL: getEnv("DATABASE_URL", getDefaultDatabaseURL(env)),
|
||||||
|
JWTSecret: getRequiredEnv("JWT_SECRET"),
|
||||||
|
Port: getEnv("PORT", "8080"),
|
||||||
|
GinMode: getGinMode(env),
|
||||||
|
LogLevel: getLogLevel(env),
|
||||||
|
Debug: env == Development,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log configuration (without sensitive data)
|
||||||
|
logConfig(config)
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadEnvironmentFiles loads environment files in priority order
|
||||||
|
func loadEnvironmentFiles() {
|
||||||
|
// Get environment first (from system env or default)
|
||||||
|
env := getEnvironmentFromSystem()
|
||||||
|
|
||||||
|
log.Printf("🔍 Detected environment: %s", env)
|
||||||
|
|
||||||
|
// Define file loading order (later files override earlier ones)
|
||||||
|
files := []string{
|
||||||
|
".env", // Base configuration
|
||||||
|
fmt.Sprintf(".env.%s", env), // Environment-specific
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load files in order
|
||||||
|
for _, file := range files {
|
||||||
|
if _, err := os.Stat(file); err == nil {
|
||||||
|
if err := godotenv.Load(file); err != nil {
|
||||||
|
log.Printf("⚠️ Warning: Could not load %s: %v", file, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("✅ Loaded: %s", file)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("❌ Not found: %s", file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnvironmentFromSystem gets environment from system variables only
|
||||||
|
func getEnvironmentFromSystem() string {
|
||||||
|
// Check if ENVIRONMENT is already set
|
||||||
|
if env := os.Getenv("ENVIRONMENT"); env != "" {
|
||||||
|
return strings.ToLower(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback detection
|
||||||
|
if ginMode := os.Getenv("GIN_MODE"); ginMode == "release" {
|
||||||
|
return "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "development"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnvironment gets the current environment
|
||||||
|
func getEnvironment() Environment {
|
||||||
|
env := strings.ToLower(getEnv("ENVIRONMENT", "development"))
|
||||||
|
|
||||||
|
switch env {
|
||||||
|
case "development", "dev":
|
||||||
|
return Development
|
||||||
|
case "staging", "stage":
|
||||||
|
return Staging
|
||||||
|
case "production", "prod":
|
||||||
|
return Production
|
||||||
|
default:
|
||||||
|
log.Printf("⚠️ Unknown environment '%s', defaulting to development", env)
|
||||||
|
return Development
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGinMode returns the appropriate Gin mode for the environment
|
||||||
|
func getGinMode(env Environment) string {
|
||||||
|
switch env {
|
||||||
|
case Production, Staging:
|
||||||
|
return "release"
|
||||||
|
case Development:
|
||||||
|
return "debug"
|
||||||
|
default:
|
||||||
|
return "debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLogLevel returns the appropriate log level for the environment
|
||||||
|
func getLogLevel(env Environment) string {
|
||||||
|
switch env {
|
||||||
|
case Production:
|
||||||
|
return "warn"
|
||||||
|
case Staging:
|
||||||
|
return "info"
|
||||||
|
case Development:
|
||||||
|
return "debug"
|
||||||
|
default:
|
||||||
|
return "debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultDatabaseURL returns default database URL for environment
|
||||||
|
func getDefaultDatabaseURL(env Environment) string {
|
||||||
|
switch env {
|
||||||
|
case Development:
|
||||||
|
return "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
||||||
|
case Staging:
|
||||||
|
return "postgres://postgres:password@postgres:5432/counter_db?sslmode=disable"
|
||||||
|
case Production:
|
||||||
|
return "postgres://postgres:password@postgres:5432/counter_db?sslmode=require"
|
||||||
|
default:
|
||||||
|
return "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnv gets environment variable with default value
|
||||||
|
func getEnv(key, defaultValue string) string {
|
||||||
|
if value := os.Getenv(key); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRequiredEnv gets required environment variable
|
||||||
|
func getRequiredEnv(key string) string {
|
||||||
|
value := os.Getenv(key)
|
||||||
|
if value == "" {
|
||||||
|
log.Fatalf("❌ Required environment variable %s is not set", key)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// logConfig logs configuration (without sensitive data)
|
||||||
|
func logConfig(config *Config) {
|
||||||
|
// Environment banner
|
||||||
|
log.Printf("")
|
||||||
|
log.Printf("╔══════════════════════════════════════════════════════════════╗")
|
||||||
|
log.Printf("║ COUNTER APPLICATION ║")
|
||||||
|
log.Printf("║ ║")
|
||||||
|
log.Printf("║ 🌍 ENVIRONMENT: %-15s ║", strings.ToUpper(string(config.Environment)))
|
||||||
|
log.Printf("║ 🚀 MODE: %-20s ║", config.GinMode)
|
||||||
|
log.Printf("║ 🔧 DEBUG: %-20s ║", fmt.Sprintf("%t", config.Debug))
|
||||||
|
log.Printf("║ 📊 LOG LEVEL: %-15s ║", config.LogLevel)
|
||||||
|
log.Printf("║ 🌐 PORT: %-20s ║", config.Port)
|
||||||
|
log.Printf("║ ║")
|
||||||
|
log.Printf("║ 📁 Configuration Files Loaded: ║")
|
||||||
|
log.Printf("║ • .env (base configuration) ║")
|
||||||
|
log.Printf("║ • .env.%s (environment-specific) ║", config.Environment)
|
||||||
|
log.Printf("║ ║")
|
||||||
|
log.Printf("║ 🔐 Security: ║")
|
||||||
|
log.Printf("║ • Database: %s ║", maskDatabaseURL(config.DatabaseURL))
|
||||||
|
log.Printf("║ • JWT Secret: %s ║", maskSecret(config.JWTSecret))
|
||||||
|
log.Printf("║ ║")
|
||||||
|
log.Printf("╚══════════════════════════════════════════════════════════════╝")
|
||||||
|
log.Printf("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskDatabaseURL masks sensitive parts of database URL
|
||||||
|
func maskDatabaseURL(url string) string {
|
||||||
|
// Simple masking - replace password with ***
|
||||||
|
if strings.Contains(url, "://") {
|
||||||
|
parts := strings.Split(url, "://")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
// Replace password in connection string
|
||||||
|
masked := strings.Replace(parts[1], ":", ":***@", 1)
|
||||||
|
return parts[0] + "://" + masked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "***"
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskSecret masks JWT secret for logging
|
||||||
|
func maskSecret(secret string) string {
|
||||||
|
if len(secret) <= 8 {
|
||||||
|
return "***"
|
||||||
|
}
|
||||||
|
return secret[:4] + "***" + secret[len(secret)-4:]
|
||||||
|
}
|
||||||
16
database.go
16
database.go
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
|
|
||||||
// InitDB initializes the database connection
|
// InitDB initializes the database connection (legacy function)
|
||||||
func InitDB() error {
|
func InitDB() error {
|
||||||
// Get database URL from environment variable
|
// Get database URL from environment variable
|
||||||
dbURL := os.Getenv("DATABASE_URL")
|
dbURL := os.Getenv("DATABASE_URL")
|
||||||
@@ -19,6 +19,16 @@ func InitDB() error {
|
|||||||
// Default for local development
|
// Default for local development
|
||||||
dbURL = "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
dbURL = "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
||||||
}
|
}
|
||||||
|
return initDBWithURL(dbURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitDBWithConfig initializes the database connection with configuration
|
||||||
|
func InitDBWithConfig(config *Config) error {
|
||||||
|
return initDBWithURL(config.DatabaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initDBWithURL initializes the database connection with a specific URL
|
||||||
|
func initDBWithURL(dbURL string) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
db, err = sql.Open("postgres", dbURL)
|
db, err = sql.Open("postgres", dbURL)
|
||||||
@@ -31,7 +41,7 @@ func InitDB() error {
|
|||||||
return fmt.Errorf("failed to ping database: %w", err)
|
return fmt.Errorf("failed to ping database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Database connection established")
|
log.Println("✅ Database connection established successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +82,7 @@ func CreateTables() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Database tables created successfully")
|
log.Println("✅ Database tables created successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
docker-compose.prod.yml
Normal file
6
docker-compose.prod.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
environment:
|
||||||
|
- ENVIRONMENT=production
|
||||||
|
env_file:
|
||||||
|
- .env.production
|
||||||
6
docker-compose.staging.yml
Normal file
6
docker-compose.staging.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
environment:
|
||||||
|
- ENVIRONMENT=staging
|
||||||
|
env_file:
|
||||||
|
- .env.staging
|
||||||
@@ -20,9 +20,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://postgres:password@postgres:5432/counter_db?sslmode=disable
|
- ENVIRONMENT=development
|
||||||
JWT_SECRET: your-super-secret-jwt-key-change-in-production
|
env_file:
|
||||||
GIN_MODE: release
|
- .env.development
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
4
frontend/.env
Normal file
4
frontend/.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Base Frontend Configuration
|
||||||
|
REACT_APP_API_URL=http://localhost:8080/api/v1
|
||||||
|
REACT_APP_ENVIRONMENT=development
|
||||||
|
REACT_APP_DEBUG=true
|
||||||
5
frontend/.env.development
Normal file
5
frontend/.env.development
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Development Frontend Configuration
|
||||||
|
REACT_APP_API_URL=http://localhost:8080/api/v1
|
||||||
|
REACT_APP_ENVIRONMENT=development
|
||||||
|
REACT_APP_DEBUG=true
|
||||||
|
REACT_APP_LOG_LEVEL=debug
|
||||||
5
frontend/.env.production
Normal file
5
frontend/.env.production
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Production Frontend Configuration
|
||||||
|
REACT_APP_API_URL=/api/v1
|
||||||
|
REACT_APP_ENVIRONMENT=production
|
||||||
|
REACT_APP_DEBUG=false
|
||||||
|
REACT_APP_LOG_LEVEL=warn
|
||||||
5
frontend/.env.staging
Normal file
5
frontend/.env.staging
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Staging Frontend Configuration
|
||||||
|
REACT_APP_API_URL=https://staging-api.yourdomain.com/api/v1
|
||||||
|
REACT_APP_ENVIRONMENT=staging
|
||||||
|
REACT_APP_DEBUG=false
|
||||||
|
REACT_APP_LOG_LEVEL=info
|
||||||
102
frontend/src/config/environment.ts
Normal file
102
frontend/src/config/environment.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Environment configuration for React frontend
|
||||||
|
export type Environment = 'development' | 'staging' | 'production';
|
||||||
|
|
||||||
|
export interface AppConfig {
|
||||||
|
environment: Environment;
|
||||||
|
apiUrl: string;
|
||||||
|
debug: boolean;
|
||||||
|
logLevel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get environment from process.env
|
||||||
|
export const getEnvironment = (): Environment => {
|
||||||
|
const env = process.env.REACT_APP_ENVIRONMENT as Environment;
|
||||||
|
|
||||||
|
// Fallback to NODE_ENV if REACT_APP_ENVIRONMENT is not set
|
||||||
|
if (!env) {
|
||||||
|
const nodeEnv = process.env.NODE_ENV;
|
||||||
|
switch (nodeEnv) {
|
||||||
|
case 'production':
|
||||||
|
return 'production';
|
||||||
|
case 'development':
|
||||||
|
return 'development';
|
||||||
|
default:
|
||||||
|
return 'development';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get API URL based on environment
|
||||||
|
export const getApiUrl = (): string => {
|
||||||
|
const apiUrl = process.env.REACT_APP_API_URL;
|
||||||
|
|
||||||
|
if (apiUrl) {
|
||||||
|
return apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback based on environment
|
||||||
|
const env = getEnvironment();
|
||||||
|
switch (env) {
|
||||||
|
case 'production':
|
||||||
|
return '/api/v1'; // Relative URL for production
|
||||||
|
case 'staging':
|
||||||
|
return 'https://staging-api.yourdomain.com/api/v1';
|
||||||
|
case 'development':
|
||||||
|
default:
|
||||||
|
return 'http://localhost:8080/api/v1';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get debug flag
|
||||||
|
export const isDebugMode = (): boolean => {
|
||||||
|
const debug = process.env.REACT_APP_DEBUG;
|
||||||
|
if (debug !== undefined) {
|
||||||
|
return debug === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback based on environment
|
||||||
|
return getEnvironment() === 'development';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get log level
|
||||||
|
export const getLogLevel = (): string => {
|
||||||
|
return process.env.REACT_APP_LOG_LEVEL || 'info';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get complete app configuration
|
||||||
|
export const getAppConfig = (): AppConfig => {
|
||||||
|
return {
|
||||||
|
environment: getEnvironment(),
|
||||||
|
apiUrl: getApiUrl(),
|
||||||
|
debug: isDebugMode(),
|
||||||
|
logLevel: getLogLevel(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Environment checks
|
||||||
|
export const isDevelopment = (): boolean => {
|
||||||
|
return getEnvironment() === 'development';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isStaging = (): boolean => {
|
||||||
|
return getEnvironment() === 'staging';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isProduction = (): boolean => {
|
||||||
|
return getEnvironment() === 'production';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log configuration (only in development)
|
||||||
|
export const logConfig = (): void => {
|
||||||
|
if (isDevelopment()) {
|
||||||
|
const config = getAppConfig();
|
||||||
|
console.log('🚀 Frontend Configuration:', {
|
||||||
|
environment: config.environment,
|
||||||
|
apiUrl: config.apiUrl,
|
||||||
|
debug: config.debug,
|
||||||
|
logLevel: config.logLevel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -12,8 +12,11 @@ import {
|
|||||||
CounterStats,
|
CounterStats,
|
||||||
User,
|
User,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
import { getApiUrl, logConfig } from '../config/environment';
|
||||||
|
|
||||||
const API_BASE_URL = process.env.REACT_APP_API_URL || '/api/v1';
|
// Initialize configuration
|
||||||
|
logConfig();
|
||||||
|
const API_BASE_URL = getApiUrl();
|
||||||
|
|
||||||
// Create axios instance
|
// Create axios instance
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
|
|||||||
52
main.go
52
main.go
@@ -2,24 +2,23 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/joho/godotenv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Load environment variables
|
// Load configuration with environment file precedence
|
||||||
if err := godotenv.Load(); err != nil {
|
config := LoadConfig()
|
||||||
log.Println("No .env file found, using system environment variables")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize JWT
|
// Set Gin mode based on configuration
|
||||||
InitJWT()
|
gin.SetMode(config.GinMode)
|
||||||
|
|
||||||
// Initialize database
|
// Initialize JWT with configuration
|
||||||
if err := InitDB(); err != nil {
|
InitJWTWithConfig(config)
|
||||||
|
|
||||||
|
// Initialize database with configuration
|
||||||
|
if err := InitDBWithConfig(config); err != nil {
|
||||||
log.Fatal("Failed to initialize database:", err)
|
log.Fatal("Failed to initialize database:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,21 +27,16 @@ func main() {
|
|||||||
log.Fatal("Failed to create tables:", err)
|
log.Fatal("Failed to create tables:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Gin mode
|
|
||||||
if os.Getenv("GIN_MODE") == "release" {
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Gin router
|
// Create Gin router
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
// Configure CORS
|
// Configure CORS
|
||||||
config := cors.DefaultConfig()
|
corsConfig := cors.DefaultConfig()
|
||||||
config.AllowOrigins = []string{"http://localhost:3000", "http://localhost:5173"} // React dev servers
|
corsConfig.AllowOrigins = []string{"http://localhost:3000", "http://localhost:5173"} // React dev servers
|
||||||
config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
|
corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
|
||||||
config.AllowHeaders = []string{"Origin", "Content-Type", "Accept", "Authorization"}
|
corsConfig.AllowHeaders = []string{"Origin", "Content-Type", "Accept", "Authorization"}
|
||||||
config.AllowCredentials = true
|
corsConfig.AllowCredentials = true
|
||||||
r.Use(cors.New(config))
|
r.Use(cors.New(corsConfig))
|
||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
r.GET("/health", func(c *gin.Context) {
|
r.GET("/health", func(c *gin.Context) {
|
||||||
@@ -83,11 +77,17 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
port := os.Getenv("PORT")
|
port := config.Port
|
||||||
if port == "" {
|
|
||||||
port = "8080"
|
log.Printf("")
|
||||||
}
|
log.Printf("🚀 Starting Counter Application Server...")
|
||||||
|
log.Printf(" 🌐 Listening on: http://localhost:%s", port)
|
||||||
|
log.Printf(" 📊 Health check: http://localhost:%s/health", port)
|
||||||
|
log.Printf(" 🔗 API endpoint: http://localhost:%s/api/v1", port)
|
||||||
|
log.Printf(" 🎨 Frontend: http://localhost:%s/", port)
|
||||||
|
log.Printf("")
|
||||||
|
log.Printf("✅ Server is ready and accepting connections!")
|
||||||
|
log.Printf("")
|
||||||
|
|
||||||
log.Printf("Server starting on port %s", port)
|
|
||||||
log.Fatal(r.Run(":" + port))
|
log.Fatal(r.Run(":" + port))
|
||||||
}
|
}
|
||||||
|
|||||||
10
scripts/dev.sh
Executable file
10
scripts/dev.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Development environment script
|
||||||
|
|
||||||
|
echo "🚀 Starting Counter app in development mode..."
|
||||||
|
|
||||||
|
# Set environment
|
||||||
|
export ENVIRONMENT=development
|
||||||
|
|
||||||
|
# Start with development configuration
|
||||||
|
docker-compose -f docker-compose.yml up --build
|
||||||
10
scripts/prod.sh
Executable file
10
scripts/prod.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Production environment script
|
||||||
|
|
||||||
|
echo "🚀 Starting Counter app in production mode..."
|
||||||
|
|
||||||
|
# Set environment
|
||||||
|
export ENVIRONMENT=production
|
||||||
|
|
||||||
|
# Start with production configuration
|
||||||
|
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --build -d
|
||||||
10
scripts/staging.sh
Executable file
10
scripts/staging.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Staging environment script
|
||||||
|
|
||||||
|
echo "🚀 Starting Counter app in staging mode..."
|
||||||
|
|
||||||
|
# Set environment
|
||||||
|
export ENVIRONMENT=staging
|
||||||
|
|
||||||
|
# Start with staging configuration
|
||||||
|
docker-compose -f docker-compose.yml -f docker-compose.staging.yml up --build -d
|
||||||
Reference in New Issue
Block a user