Skip to content

Commit

Permalink
(feat) added config | logger | repository | secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
F. committed Dec 15, 2024
1 parent bef7eda commit 1a10bb6
Show file tree
Hide file tree
Showing 39 changed files with 5,189 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BASE_DB_USERNAME=<username>
BASE_DB_PASSWORD=<password>
9 changes: 9 additions & 0 deletions .trunk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*out
*logs
*actions
*notifications
*tools
plugins
user_trunk.yaml
user.yaml
tmp
4 changes: 4 additions & 0 deletions .trunk/configs/.hadolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Following source doesn't work in most setups
ignored:
- SC1090
- SC1091
2 changes: 2 additions & 0 deletions .trunk/configs/.markdownlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Prettier friendly markdownlint config (all formatting rules disabled)
extends: markdownlint/style/prettier
7 changes: 7 additions & 0 deletions .trunk/configs/.shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enable=all
source-path=SCRIPTDIR
disable=SC2154

# If you're having issues with shellcheck following source, disable the errors via:
# disable=SC1090
# disable=SC1091
14 changes: 14 additions & 0 deletions .trunk/configs/svgo.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
plugins: [
{
name: "preset-default",
params: {
overrides: {
removeViewBox: false, // https://github.com/svg/svgo/issues/1128
sortAttrs: true,
removeOffCanvasPaths: true,
},
},
},
],
};
39 changes: 39 additions & 0 deletions .trunk/trunk.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This file controls the behavior of Trunk: https://docs.trunk.io/cli
# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml
version: 0.1
cli:
version: 1.22.7
# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins)
plugins:
sources:
- id: trunk
ref: v1.6.6
uri: https://github.com/trunk-io/plugins
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
runtimes:
enabled:
- go@1.21.0
- node@18.20.5
- python@3.10.8
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
lint:
enabled:
- checkov@3.2.334
- git-diff-check
- gofmt@1.20.4
- golangci-lint@1.62.2
- hadolint@2.12.1-beta
- markdownlint@0.43.0
- prettier@3.4.2
- shellcheck@0.10.0
- shfmt@3.6.0
- svgo@3.3.2
- trufflehog@3.86.1
- yamllint@1.35.1
actions:
disabled:
- trunk-announce
- trunk-check-pre-push
- trunk-fmt-pre-commit
enabled:
- trunk-upgrade-available
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"errname",
"errorlint",
"esbenp",
"ewrap",
"fatcontext",
"fieldalignment",
"FLUSHALL",
Expand Down Expand Up @@ -124,6 +125,7 @@
"varnamelen",
"wastedassign",
"wrapcheck",
"Wrapf",
"zerologlint"
]
}
46 changes: 46 additions & 0 deletions cmd/config/encrypt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"fmt"
"log/slog"
"os"

"github.com/hyp3rd/base/internal/constants"
"github.com/hyp3rd/base/internal/secrets"
"github.com/hyp3rd/base/internal/secrets/providers/dotenv"
)

const (
sourceEnvFile = ".env"
encryptedEnvFile = ".env.encrypted"
)

func main() {
encryptionPassword, ok := os.LookupEnv("SECRETS_ENCRYPTION_PASSWORD")
if !ok {
fmt.Fprintf(os.Stderr, "SECRETS_ENCRYPTION_PASSWORD environment variable not set\n")
os.Exit(1)
}

// Initialize the encrypted provider
secretsProviderCfg := secrets.Config{
Source: secrets.EnvFile,
Prefix: constants.EnvPrefix.String(),
EnvPath: encryptedEnvFile,
}

provider, err := dotenv.NewEncrypted(secretsProviderCfg, encryptionPassword)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initiate the configuration encryption provider: %v\n", err)
os.Exit(1)
}

// Encrypt the existing .env file
err = provider.EncryptFile(sourceEnvFile, encryptedEnvFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to encrypt the .env provided: %v\n", err)
os.Exit(1)
}

slog.Info("Encryption complete")
}
199 changes: 199 additions & 0 deletions cmd/pg/monitor/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package main

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/hyp3rd/base/internal/config"
"github.com/hyp3rd/base/internal/constants"
"github.com/hyp3rd/base/internal/logger"
"github.com/hyp3rd/base/internal/logger/adapter"
"github.com/hyp3rd/base/internal/logger/output"
"github.com/hyp3rd/base/internal/repository/pg"
"github.com/hyp3rd/base/internal/secrets"
"github.com/hyp3rd/base/internal/secrets/providers/dotenv"
)

const (
maxLogSize = 10 * 1024 * 1024 // 10 MB
logsDir = "logs/pg/monitor"
logsFile = "pg-monitor.log"

configFileName = "config"

monitorInterval = 10 * time.Second
)

func main() {
ctx := context.Background()

cfg := initConfig(ctx)
log, multiWriter := initLogger(ctx, cfg.Environment)
// Ensure proper cleanup with detailed error handling
defer func() {
if err := multiWriter.Sync(); err != nil {
fmt.Fprintf(os.Stderr, "Logger sync failed: %+v\n", err)
}

if err := multiWriter.Close(); err != nil {
fmt.Fprintf(os.Stderr, "Writer cleanup failed: %+v\n", err)
}
}()

log.Info("Database monitor starting")

dbManager := initDBmanager(ctx, cfg, log)

// Create monitor with 1 second slow query threshold
monitor := dbManager.NewMonitor(time.Second)

// Start monitoring
monitor.Start(ctx)
defer monitor.Stop()

// Create a ticker for periodic checks
ticker := time.NewTicker(monitorInterval)
defer ticker.Stop()

// Setup signal handling
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

// Main process loop
for {
select {
case <-ticker.C:
status := monitor.GetHealthStatus()
if !status.Connected {
log.Error("Database connection lost!")
} else {
log.Info("Database connection is healthy")
}

if status.PoolStats != nil {
if status.PoolStats.SlowQueries > 0 {
log.Warn("Detected slow queries")
}
}

case sig := <-sigChan:
log.Infof("Received signal: %v, shutting down...", sig)

return
case <-ctx.Done():
log.Info("Context cancelled, shutting down...")

return
}
}
}

func initConfig(ctx context.Context) *config.Config {
// Initialize the encrypted provider
secretsProviderCfg := secrets.Config{
Source: secrets.EnvFile,
Prefix: constants.EnvPrefix.String(),
EnvPath: ".env.encrypted",
}

encryptionPassword, ok := os.LookupEnv("SECRETS_ENCRYPTION_PASSWORD")
if !ok {
fmt.Fprintf(os.Stderr, "SECRETS_ENCRYPTION_PASSWORD environment variable not set\n")
os.Exit(1)
}

secretsProvider, err := dotenv.NewEncrypted(secretsProviderCfg, encryptionPassword)
if err != nil {
fmt.Fprintf(os.Stderr, "Secrets provider: %+v\n", err)
os.Exit(1)
}

// Configure options for config initialization
opts := config.Options{
ConfigName: configFileName,
SecretsProvider: secretsProvider,
Timeout: constants.DefaultTimeout,
}

cfg, err := config.NewConfig(ctx, opts)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to initialize config: %v\n", err)
os.Exit(1)
}

return cfg
}

func initLogger(_ context.Context, environment string) (logger.Logger, *output.MultiWriter) {
//nolint:mnd
if err := os.MkdirAll(logsDir, 0o755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create log directory: %v\n", err)
os.Exit(1)
}

// Create file writer with proper error handling
fileWriter, err := output.NewFileWriter(output.FileConfig{
Path: logsDir + "/" + logsFile,
MaxSize: maxLogSize,
Compress: true,
})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create file writer: %v\n", err)
os.Exit(1)
}

// Create console writer
consoleWriter := output.NewConsoleWriter(os.Stdout, output.ColorModeAuto)

// Create multi-writer with error handling
multiWriter, err := output.NewMultiWriter(consoleWriter, fileWriter)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create multi-writer: %v\n", err)
fileWriter.Close() // Clean up the file writer
os.Exit(1)
}

// Initialize the logger
loggerCfg := logger.DefaultConfig()
loggerCfg.Output = multiWriter
loggerCfg.EnableJSON = true
loggerCfg.TimeFormat = time.RFC3339
loggerCfg.EnableCaller = true
loggerCfg.Level = logger.DebugLevel
loggerCfg.AdditionalFields = []logger.Field{
{Key: "service", Value: "database-monitor"},
{Key: "environment", Value: environment},
}

// Create the logger
log, err := adapter.NewAdapter(loggerCfg)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create logger: %+v\n", err)
os.Exit(1)
}

return log, multiWriter
}

func initDBmanager(ctx context.Context, cfg *config.Config, log logger.Logger) *pg.Manager {
// Initialize the database manager
dbManager := pg.New(&cfg.DB, log)

err := dbManager.Connect(ctx)
if err != nil {
log.Error("Failed to connect to database")
panic(err)
}

if dbManager.IsConnected(ctx) {
log.Info("Database connection successfully established")
} else {
log.Error("Database connection wasn't established")
}

return dbManager
}
47 changes: 47 additions & 0 deletions configs/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
# development | production | local
environment: "development"
servers:
query_api:
port: 8000
read_timeout: 15s
write_timeout: 15s
shutdown_timeout: 5s
grpc:
port: 50051
max_connection_idle: 15m
max_connection_age: 30m
max_connection_age_grace: 5m
keepalive_time: 5m
keepalive_timeout: 20s

rate_limiter:
requests_per_second: 100
burst_size: 50

db:
host: <db_host>
port: "5432"
database: postgres
# session | transaction
pool_mode: "transaction"
max_open_conns: 25
max_idle_conns: 25
conn_max_lifetime: 5m
conn_attempts: 5
conn_timeout: 2s

pubsub:
project_id: "local-project"
topic_id: "fingerprints"
subscription_id: "base-sub"
emulator_host: "localhost:8085" # For local development
ack_deadline: 30s
subscription:
receive_max_outstanding_messages: 10
receive_num_goroutines: 4
receive_max_extension: 30s
retry_policy:
max_attempts: 5
minimum_backoff: 10s
maximum_backoff: 600s
Loading

0 comments on commit 1a10bb6

Please sign in to comment.