Skip to content

Commit

Permalink
Caddy and Pocketbase, together at last
Browse files Browse the repository at this point in the history
  • Loading branch information
Dokotela committed Nov 19, 2024
1 parent 38d94c5 commit 962f947
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 110 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.step.sm/cli-utils v0.9.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -940,8 +940,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqhe
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
Expand Down
97 changes: 28 additions & 69 deletions pocketfhir/caddy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,92 +10,51 @@ import (
_ "github.com/caddyserver/caddy/v2/modules/standard"
)

func CreateConfig(port, certFile, keyFile, upstreamURL, logFile, adminListen string) *caddy.Config {
// CreateConfig generates a basic Caddy configuration to run a reverse proxy.
func CreateConfig(port, upstreamURL, storagePath string) *caddy.Config {
return &caddy.Config{
Admin: &caddy.AdminConfig{
Listen: adminListen,
Listen: fmt.Sprintf(":%s", port),
},
AppsRaw: caddy.ModuleMap{
StorageRaw: json.RawMessage(fmt.Sprintf(`{
"module": "file_system",
"root": "%s"
}`, storagePath)),
AppsRaw: map[string]json.RawMessage{
"http": json.RawMessage(fmt.Sprintf(`{
"servers": {
"reverse_proxy_server": {
"listen": [":%s"],
"tls_connection_policies": [
{
"certificates": {
"load_files": [
{
"certificate_file": "%s",
"key_file": "%s"
}
]
}
}
],
"routes": [
{
"match": [{"host": ["*"]}],
"handle": [{"handler": "reverse_proxy", "upstreams": [{"dial": "%s"}]}]
},
{
"match": [{"path": ["/health"]}],
"handle": [{"handler": "static_response", "body": "PocketFHIR Proxy Active", "status_code": 200}]
}
],
"logs": {
"default_logger_name": "access_log"
}
}
}
}`, port, certFile, keyFile, upstreamURL)),
},
Logging: &caddy.Logging{
Logs: map[string]*caddy.CustomLog{
"access_log": {
BaseLog: caddy.BaseLog{
WriterRaw: json.RawMessage(fmt.Sprintf(`{
"output": {
"module": "file", // Add this line
"format": "file",
"filename": "%s"
}
}`, logFile)),
Level: "INFO",
},
},
},
"servers": {
"simple_reverse_proxy": {
"listen": [":%s"],
"routes": [{
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{
"dial": "%s"
}]
}]
}]
}
}
}`, port, upstreamURL)), // Ensure upstreamURL is used correctly
},
}
}

func StartCaddy(port, certFile, keyFile, upstreamURL, logFile, adminListen, caddyConfig string) {
// Use the provided dataPath to set the config directory
if err := os.MkdirAll(caddyConfig, 0755); err != nil {
log.Fatalf("Failed to create configuration directory at %s: %v", caddyConfig, err)
func StartCaddy(port, upstreamURL, storagePath string) {
if err := os.Chdir(storagePath); err != nil {
log.Fatalf("Failed to change working directory to %s: %v", storagePath, err)
}

log.Printf("Starting Caddy with configuration:\n"+
"Port: %s\nCertFile: %s\nKeyFile: %s\nUpstreamURL: %s\nLogFile: %s\nAdminListen: %s\nConfigDir: %s\n",
port, certFile, keyFile, upstreamURL, logFile, adminListen, caddyConfig)

// Set the Caddy config directory
errCaddyConfig := os.Setenv("CADDY_CONFIG_DIR", caddyConfig)
if errCaddyConfig != nil {
log.Fatalf("Failed to set CADDY_CONFIG_DIR environment variable: %v", errCaddyConfig)
}
errCaddyHome := os.Setenv("XDG_CONFIG_HOME", caddyConfig)
if errCaddyHome != nil {
log.Fatalf("Failed to set XDG_CONFIG_HOME environment variable: %v", errCaddyHome)
}
log.Printf("Starting Caddy with configuration:\nPort: %s\nUpstreamURL: %s\nStoragePath: %s\n", port, upstreamURL, storagePath)

cfg := CreateConfig(port, certFile, keyFile, upstreamURL, logFile, adminListen)
cfg := CreateConfig(port, upstreamURL, storagePath)
configJSON, err := json.Marshal(cfg)
if err != nil {
log.Fatalf("Failed to serialize Caddy config: %v", err)
}
log.Printf("Generated Caddy config: %s", string(configJSON))

// Start the Caddy server
// Start the Caddy server (no blocking)
log.Println("Initializing Caddy...")
if err := caddy.Run(cfg); err != nil {
log.Fatalf("Error running Caddy: %v", err)
Expand Down
75 changes: 37 additions & 38 deletions pocketfhir/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log"
"net/http"
"os"
"os/signal"
"syscall"

"github.com/labstack/echo/v5"
Expand All @@ -24,77 +23,64 @@ func RegisterNativeBridgeCallback(c NativeBridge) {

// RunServer starts the PocketFHIR server
func RunServer(dataDir string, hostname string, port string, getApiLogs bool) {
// Set environment variables for PocketBase configuration
if err := os.Setenv("POCKETBASE_DATA_DIR", dataDir); err != nil {
log.Fatalf("Failed to set data directory: %v", err)
}

// Set CLI-like arguments for PocketBase to specify server address and port
// This will work similar to calling the PocketBase server from CLI
log.Printf("[DEBUG] Setting CLI arguments for server address and port: %s:%s\n", hostname, port)
os.Args = append(os.Args[:1], "serve", "--http", fmt.Sprintf("%s:%s", hostname, port))

// Create a configuration object with custom settings
log.Println("[DEBUG] Creating PocketBase configuration object...")
config := pocketbase.Config{
DefaultDataDir: dataDir,
DefaultDev: getApiLogs, // Enabling dev mode for detailed logging
HideStartBanner: false,
}

// Initialize PocketBase with the default configuration
log.Println("Initializing PocketBase app...")
log.Println("[DEBUG] Initializing PocketBase app...")
app := pocketbase.NewWithConfig(config)
log.Println("[DEBUG] PocketBase app initialized.")

// Standard setup for the app
log.Println("[DEBUG] Running standard setup for PocketBase...")
standard(app)
log.Println("Standard setup for PocketBase app completed.")
log.Println("[DEBUG] Standard setup for PocketBase app completed.")

// Register hooks from hooks.go
log.Println("[DEBUG] Registering hooks...")
registerHooks(app)
log.Println("Hooks registered.")
log.Println("[DEBUG] Hooks registered.")

// Register FHIR routes
log.Println("[DEBUG] Registering FHIR routes...")
registerFHIRRoutes(app)
log.Println("FHIR routes registered.")
log.Println("[DEBUG] FHIR routes registered.")

// Register server management routes
log.Println("[DEBUG] Registering server management routes...")
registerManagementRoutes(app)
log.Println("Server management routes registered.")
log.Println("[DEBUG] Server management routes registered.")

// Setup additional native callbacks and routes
log.Println("[DEBUG] Setting up PocketBase callbacks and routes...")
setupPocketbaseCallbacks(app, getApiLogs)
log.Println("[DEBUG] PocketBase callbacks and routes set up.")

// Initialize collections if necessary
log.Println("Initializing collections...")
log.Println("[DEBUG] Initializing collections...")
if err := initializeCollections(app); err != nil {
log.Fatalf("Failed to initialize collections: %v", err)
} else {
log.Println("Collections initialized successfully.")
log.Println("[DEBUG] Collections initialized successfully.")
}

// Handle graceful shutdown
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

// Start the server in a separate goroutine
go func() {
log.Println("Starting PocketFHIR server...")
serverUrl := fmt.Sprintf("http://%s:%s", hostname, port)
sendCommand("onServerStarting", fmt.Sprintf("Server starting at: %s\n➜ REST API: %s/api/\n➜ Admin UI: %s/_/", serverUrl, serverUrl, serverUrl))

// Use the Start() method to start the PocketBase server
if err := app.Start(); err != nil {
sendCommand("error", fmt.Sprintf("Error: Failed to start PocketBase server: %v", err))
log.Fatalf("Failed to start the app: %v", err)
} else {
log.Println("PocketFHIR server started successfully.")
}
}()

// Wait for interrupt signal to gracefully shut down the server
<-stop
log.Println("Shutting down PocketFHIR server...")
app.ResetBootstrapState() // Optional: Clean up any resources before shutting down
log.Println("PocketFHIR server shut down gracefully.")
// Start the server (no need to wait on anything here)
log.Println("[DEBUG] Calling app.Start() to start the server...")
if err := app.Start(); err != nil {
sendCommand("error", fmt.Sprintf("Error: Failed to start PocketBase server: %v", err))
log.Fatalf("Failed to start the app: %v", err)
} else {
log.Println("[DEBUG] PocketFHIR server started successfully.")
}
}

// StopServer gracefully stops the running PocketFHIR server
Expand All @@ -113,25 +99,32 @@ type NativeBridge interface {
// sendCommand sends command to native and returns the response
func sendCommand(command string, data string) string {
if nativeBridge != nil {
log.Printf("[DEBUG] Sending command '%s' with data: %s", command, data)
return nativeBridge.HandleCallback(command, data)
}
log.Printf("[DEBUG] No NativeBridge defined. Command '%s' not sent.", command)
return ""
}

// setupPocketbaseCallbacks sets up additional callbacks and native routes for PocketBase
func setupPocketbaseCallbacks(app *pocketbase.PocketBase, getApiLogs bool) {
// Setup callbacks
log.Println("[DEBUG] Setting up OnBeforeServe callback...")
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
log.Println("[DEBUG] OnBeforeServe triggered.")
sendCommand("OnBeforeServe", "")
if getApiLogs {
log.Println("[DEBUG] Adding API logs middleware...")
e.Router.Use(ApiLogsMiddleWare(app))
}

// Setup a native GET request handler
log.Println("[DEBUG] Adding native GET request handler...")
e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/api/nativeGet",
Handler: func(context echo.Context) error {
log.Println("[DEBUG] Handling native GET request...")
var data = sendCommand("nativeGetRequest", context.QueryParams().Encode())
return context.JSON(http.StatusOK, map[string]string{
"success": data,
Expand All @@ -140,10 +133,12 @@ func setupPocketbaseCallbacks(app *pocketbase.PocketBase, getApiLogs bool) {
})

// Setup a native POST request handler
log.Println("[DEBUG] Adding native POST request handler...")
e.Router.AddRoute(echo.Route{
Method: http.MethodPost,
Path: "/api/nativePost",
Handler: func(context echo.Context) error {
log.Println("[DEBUG] Handling native POST request...")
form, err := context.FormValues()
if err != nil {
return context.JSON(http.StatusBadRequest, map[string]string{
Expand All @@ -161,14 +156,17 @@ func setupPocketbaseCallbacks(app *pocketbase.PocketBase, getApiLogs bool) {
})

app.OnBeforeBootstrap().Add(func(e *core.BootstrapEvent) error {
log.Println("[DEBUG] OnBeforeBootstrap triggered.")
sendCommand("OnBeforeBootstrap", "")
return nil
})
app.OnAfterBootstrap().Add(func(e *core.BootstrapEvent) error {
log.Println("[DEBUG] OnAfterBootstrap triggered.")
sendCommand("OnAfterBootstrap", "")
return nil
})
app.OnTerminate().Add(func(e *core.TerminateEvent) error {
log.Println("[DEBUG] OnTerminate triggered.")
sendCommand("OnTerminate", "")
return nil
})
Expand All @@ -180,6 +178,7 @@ func ApiLogsMiddleWare(app core.App) echo.MiddlewareFunc {
return func(c echo.Context) error {
request := c.Request()
fullPath := request.URL.Host + request.URL.Path + "?" + request.URL.RawQuery
log.Printf("[DEBUG] API request made to: %s", fullPath)
sendCommand("apiLogs", fullPath)
return next(c)
}
Expand Down
41 changes: 41 additions & 0 deletions pocketfhir/start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package pocketfhir

import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
)

func StartPocketFHIR(dataDir string, hostname string, port string, getApiLogs bool, caddyPort string, caddyStoragePath string) {
// Set environment variables for PocketBase configuration
log.Println("[DEBUG] Setting environment variables...")
if err := os.Setenv("POCKETBASE_DATA_DIR", dataDir); err != nil {
log.Fatalf("Failed to set data directory: %v", err)
}

// Handle graceful shutdown signal
log.Println("[DEBUG] Setting up channel for graceful shutdown...")
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

// Start the PocketFHIR server in a separate goroutine
go func() {
log.Println("[DEBUG] Starting PocketFHIR server...")
RunServer(dataDir, hostname, port, getApiLogs)
}()

// Start the Caddy server in a separate goroutine
go func() {
log.Println("[DEBUG] Starting Caddy server...")
StartCaddy(caddyPort, fmt.Sprintf("http://%s:%s", hostname, port), caddyStoragePath)
}()

// Wait for interrupt signal to gracefully shut down the server
log.Println("[DEBUG] Waiting for interrupt signal to shut down the servers...")
<-stop
log.Println("Shutting down PocketFHIR and Caddy servers...")
StopServer() // PocketFHIR shutdown
log.Println("PocketFHIR and Caddy servers shut down gracefully.")
}

0 comments on commit 962f947

Please sign in to comment.