2024-09-28 19:41:34 +01:00
package main
import (
"errors"
2024-10-15 19:06:05 +01:00
"fmt"
library "git.ailur.dev/ailur/fg-library/v2"
2024-09-28 19:41:34 +01:00
"io"
"log"
"os"
"plugin"
"sort"
2024-10-04 18:30:17 +01:00
"strings"
2024-10-03 18:33:41 +01:00
"sync"
2024-09-28 19:41:34 +01:00
"time"
"database/sql"
"encoding/json"
"log/slog"
"net/http"
"path/filepath"
2024-09-29 16:06:28 +01:00
"github.com/go-chi/chi/v5"
2024-10-15 18:29:16 +01:00
"github.com/go-chi/hostrouter"
2024-09-28 19:41:34 +01:00
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
_ "github.com/lib/pq"
_ "modernc.org/sqlite"
)
type Config struct {
Global struct {
IP string ` json:"ip" validate:"required,ip_addr" `
Port string ` json:"port" validate:"required" `
ServiceDirectory string ` json:"serviceDirectory" validate:"required" `
ResourceDirectory string ` json:"resourceDirectory" validate:"required" `
} ` json:"global" validate:"required" `
Logging struct {
Enabled bool ` json:"enabled" `
File string ` json:"file" validate:"required_if=Enabled true" `
} ` json:"logging" `
Database struct {
DatabaseType string ` json:"databaseType" validate:"required,oneof=sqlite postgres" `
ConnectionString string ` json:"connectionString" validate:"required_if=DatabaseType postgres" `
2024-10-13 19:20:19 +01:00
DatabasePath string ` json:"databasePath" validate:"required_if=DatabaseType sqlite" `
2024-09-28 19:41:34 +01:00
} ` json:"database" validate:"required" `
Services map [ string ] interface { } ` json:"services" `
}
2024-10-04 19:37:05 +01:00
type Service struct {
2024-10-15 19:17:29 +01:00
ServiceID uuid . UUID
ServiceMetadata library . Service
Inbox chan library . InterServiceMessage
2024-09-28 19:41:34 +01:00
}
var (
logger = func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
next . ServeHTTP ( w , r )
slog . Info ( r . Method + " " + r . URL . Path )
} )
}
2024-10-15 19:06:05 +01:00
validate * validator . Validate
services = make ( map [ uuid . UUID ] Service )
lock sync . RWMutex
hostRouter = hostrouter . New ( )
2024-09-28 19:41:34 +01:00
)
func processInterServiceMessage ( channel chan library . InterServiceMessage , config Config ) {
for {
message := <- channel
if message . ForServiceID == uuid . MustParse ( "00000000-0000-0000-0000-000000000000" ) {
// Broadcast message
2024-10-04 19:37:05 +01:00
for _ , service := range services {
2024-10-15 19:17:29 +01:00
service . Inbox <- message
2024-09-28 19:41:34 +01:00
}
} else if message . ForServiceID == uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) {
// Service initialization service
switch message . MessageType {
case 0 :
2024-10-15 19:17:29 +01:00
// This has been deprecated, ignore it
// Send "true" back
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 0 ,
SentAt : time . Now ( ) ,
Message : true ,
}
case 1 :
// Service database initialization message
// Check if the service has the necessary permissions
2024-10-04 19:37:05 +01:00
if services [ message . ServiceID ] . ServiceMetadata . Permissions . Database {
2024-09-28 19:41:34 +01:00
// Check if we are using sqlite or postgres
if config . Database . DatabaseType == "sqlite" {
// Open the database and return the connection
pluginConn , err := sql . Open ( "sqlite" , filepath . Join ( config . Database . DatabasePath , message . ServiceID . String ( ) + ".db" ) )
if err != nil {
// Report an error
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : err ,
}
} else {
// Report a successful activation
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 2 ,
SentAt : time . Now ( ) ,
2024-10-13 19:20:19 +01:00
Message : library . Database {
DB : pluginConn ,
DBType : library . Sqlite ,
} ,
2024-09-28 19:41:34 +01:00
}
}
} else if config . Database . DatabaseType == "postgres" {
// Connect to the database
conn , err := sql . Open ( "postgres" , config . Database . ConnectionString )
if err != nil {
// Report an error
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : err ,
}
} else {
// Try to create the schema
2024-10-13 19:20:19 +01:00
_ , err = conn . Exec ( "CREATE SCHEMA IF NOT EXISTS \"" + message . ServiceID . String ( ) + "\"" )
2024-09-28 19:41:34 +01:00
if err != nil {
// Report an error
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : err ,
}
} else {
// Create a new connection to the database
2024-10-13 19:20:19 +01:00
var connectionString string
if strings . Contains ( config . Database . ConnectionString , "?" ) {
connectionString = config . Database . ConnectionString + "&search_path=\"" + message . ServiceID . String ( ) + "\""
} else {
connectionString = config . Database . ConnectionString + "?search_path=\"" + message . ServiceID . String ( ) + "\""
}
pluginConn , err := sql . Open ( "postgres" , connectionString )
2024-09-28 19:41:34 +01:00
if err != nil {
// Report an error
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : err ,
}
} else {
2024-10-14 12:23:33 +01:00
// Test the connection
err = pluginConn . Ping ( )
if err != nil {
// Report an error
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : err ,
}
} else {
// Report a successful activation
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 2 ,
SentAt : time . Now ( ) ,
Message : library . Database {
DB : pluginConn ,
DBType : library . Postgres ,
} ,
}
2024-09-28 19:41:34 +01:00
}
}
}
}
}
} else {
// Report an error
2024-10-04 19:37:05 +01:00
services [ message . ServiceID ] . Inbox <- library . InterServiceMessage {
2024-09-28 19:41:34 +01:00
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "database access not permitted" ) ,
}
}
}
} else if message . ForServiceID == uuid . MustParse ( "00000000-0000-0000-0000-000000000002" ) {
// Logger service
service , ok := services [ message . ServiceID ]
if ok {
2024-10-04 18:30:17 +01:00
switch message . MessageType {
case 0 :
2024-09-28 19:41:34 +01:00
// Log message
2024-10-04 19:37:05 +01:00
slog . Info ( service . ServiceMetadata . Name + " says: " + message . Message . ( string ) )
2024-10-04 18:30:17 +01:00
case 1 :
2024-09-28 19:41:34 +01:00
// Warn message
2024-10-04 19:37:05 +01:00
slog . Warn ( service . ServiceMetadata . Name + " warns: " + message . Message . ( string ) )
2024-10-04 18:30:17 +01:00
case 2 :
2024-09-28 19:41:34 +01:00
// Error message
2024-10-04 19:37:05 +01:00
slog . Error ( service . ServiceMetadata . Name + " complains: " + message . Message . ( string ) )
2024-10-04 18:30:17 +01:00
case 3 :
2024-09-28 19:41:34 +01:00
// Fatal message
2024-10-04 19:37:05 +01:00
slog . Error ( service . ServiceMetadata . Name + "'s dying wish: " + message . Message . ( string ) )
2024-09-28 19:41:34 +01:00
os . Exit ( 1 )
}
}
} else if message . ForServiceID == uuid . MustParse ( "00000000-0000-0000-0000-000000000003" ) {
// We need to check if the service is allowed to access the Blob Storage service
serviceMetadata , ok := services [ message . ServiceID ]
2024-10-04 19:37:05 +01:00
if ok && serviceMetadata . ServiceMetadata . Permissions . BlobStorage {
2024-09-28 19:41:34 +01:00
// Send message to Blob Storage service
2024-10-04 19:37:05 +01:00
service , ok := services [ uuid . MustParse ( "00000000-0000-0000-0000-000000000003" ) ]
2024-10-15 19:17:29 +01:00
if ok {
2024-09-28 19:41:34 +01:00
service . Inbox <- message
} else if ! ok {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "blob storage service not found" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
} else {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "blob storage is not yet available" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
} else {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "blob storage is not permitted" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
} else if message . ForServiceID == uuid . MustParse ( "00000000-0000-0000-0000-000000000004" ) {
// We need to check if the service is allowed to access the Authentication service
serviceMetadata , ok := services [ message . ServiceID ]
2024-10-04 19:37:05 +01:00
if ok && serviceMetadata . ServiceMetadata . Permissions . Authenticate {
2024-09-28 19:41:34 +01:00
// Send message to Authentication service
2024-10-04 19:37:05 +01:00
service , ok := services [ uuid . MustParse ( "00000000-0000-0000-0000-000000000004" ) ]
2024-10-15 19:17:29 +01:00
if ok {
2024-09-28 19:41:34 +01:00
service . Inbox <- message
} else if ! ok {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "authentication service not found" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
} else {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "authentication service not yet available" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
} else {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "authentication not permitted" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
} else {
serviceMetadata , ok := services [ message . ServiceID ]
2024-10-04 19:37:05 +01:00
if ok && serviceMetadata . ServiceMetadata . Permissions . InterServiceCommunication {
2024-09-28 19:41:34 +01:00
// Send message to specific service
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ForServiceID ]
if ! ok {
2024-09-28 19:41:34 +01:00
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "requested service not found" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
2024-10-04 19:37:05 +01:00
service . Inbox <- message
2024-09-28 19:41:34 +01:00
} else {
// Send error message
2024-10-04 19:37:05 +01:00
service , ok := services [ message . ServiceID ]
2024-09-28 19:41:34 +01:00
if ok {
service . Inbox <- library . InterServiceMessage {
ServiceID : uuid . MustParse ( "00000000-0000-0000-0000-000000000001" ) ,
ForServiceID : message . ServiceID ,
MessageType : 1 ,
SentAt : time . Now ( ) ,
Message : errors . New ( "inter-service communication not permitted" ) ,
}
} else {
// This should never happen
slog . Error ( "Bit flip error: Impossible service ID. Move away from radiation or use ECC memory." )
os . Exit ( 1 )
}
}
}
}
}
func parseConfig ( path string ) Config {
// Register the custom validators
validate = validator . New ( )
// Register the custom isDirectory validator
err := validate . RegisterValidation ( "isDirectory" , func ( fl validator . FieldLevel ) bool {
// Check if it exists
fileInfo , err := os . Stat ( fl . Field ( ) . String ( ) )
if err != nil {
return false
}
// Check if it is a directory
return fileInfo . IsDir ( )
} )
if err != nil {
slog . Error ( "Error registering custom validator: " , err )
os . Exit ( 1 )
}
// Parse the configuration file
configFile , err := os . ReadFile ( path )
if err != nil {
slog . Error ( "Error reading configuration file: " , err )
os . Exit ( 1 )
}
// Parse the configuration file
var config Config
err = json . Unmarshal ( configFile , & config )
if err != nil {
slog . Error ( "Error parsing configuration file: " , err )
os . Exit ( 1 )
}
// Validate the configuration
err = validate . Struct ( config )
if err != nil {
2024-10-04 19:37:05 +01:00
slog . Error ( "Invalid configuration: " , err )
2024-09-28 19:41:34 +01:00
os . Exit ( 1 )
}
// Check if we are logging to a file
if config . Logging != ( Config { } . Logging ) && config . Logging . Enabled {
// Check if the log file is set
logFilePath := config . Logging . File
// Set the log file
logFile , err := os . OpenFile ( logFilePath , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 )
if err != nil {
slog . Error ( "Error opening log file: " , err )
os . Exit ( 1 )
}
log . SetOutput ( io . MultiWriter ( os . Stdout , logFile ) )
}
return config
}
func main ( ) {
// Parse the configuration file
var config Config
if len ( os . Args ) < 2 {
info , err := os . Stat ( "config.json" )
if err != nil {
if errors . Is ( err , os . ErrNotExist ) {
slog . Error ( "No configuration file provided" )
os . Exit ( 1 )
} else {
slog . Error ( "Error reading configuration file: " , err )
os . Exit ( 1 )
}
}
if info . IsDir ( ) {
slog . Error ( "No configuration file provided" )
os . Exit ( 1 )
}
config = parseConfig ( "config.json" )
} else {
config = parseConfig ( os . Args [ 1 ] )
}
// Create the router
router := chi . NewRouter ( )
router . Use ( logger )
var globalOutbox = make ( chan library . InterServiceMessage )
// Initialize the service discovery, health-check, and logging services
// Since these are core services, always allocate them the service IDs 0, 1, and 2
// These are not dynamically loaded, as they are integral to the system functioning
go processInterServiceMessage ( globalOutbox , config )
2024-10-04 18:30:17 +01:00
// Initialize all the services
2024-09-28 19:41:34 +01:00
plugins := make ( map [ time . Time ] string )
2024-10-04 18:30:17 +01:00
err := filepath . Walk ( config . Global . ServiceDirectory , func ( path string , info os . FileInfo , err error ) error {
2024-09-28 19:41:34 +01:00
if err != nil {
return err
}
2024-10-04 18:30:17 +01:00
if info . IsDir ( ) || filepath . Ext ( path ) != ".fgs" {
2024-09-28 19:41:34 +01:00
return nil
}
// Add the plugin to the list of plugins
2024-10-04 18:30:17 +01:00
if info . Name ( ) == "storage.fgs" {
plugins [ time . Unix ( 0 , 0 ) ] = path
return nil
} else if info . Name ( ) == "auth.fgs" {
plugins [ time . Unix ( 0 , 1 ) ] = path
return nil
}
2024-09-28 19:41:34 +01:00
plugins [ info . ModTime ( ) ] = path
return nil
} )
if err != nil {
slog . Error ( "Error walking the services directory: " , err )
os . Exit ( 1 )
}
// Sort the plugins by modification time, newest last
var keys [ ] time . Time
for k := range plugins {
keys = append ( keys , k )
}
sort . Slice ( keys , func ( i , j int ) bool {
return keys [ i ] . Before ( keys [ j ] )
} )
for _ , k := range keys {
// Get the plugin path
pluginPath := plugins [ k ]
// Load the plugin
servicePlugin , err := plugin . Open ( pluginPath )
if err != nil {
slog . Error ( "Could not load service: " , err )
os . Exit ( 1 )
}
// Load the service information
2024-10-04 19:37:05 +01:00
serviceInformationSymbol , err := servicePlugin . Lookup ( "ServiceInformation" )
2024-09-28 19:41:34 +01:00
if err != nil {
slog . Error ( "Service lacks necessary information: " , err )
os . Exit ( 1 )
}
2024-10-04 19:37:05 +01:00
serviceInformation := * serviceInformationSymbol . ( * library . Service )
2024-09-28 19:41:34 +01:00
// Load the main function
main , err := servicePlugin . Lookup ( "Main" )
if err != nil {
slog . Error ( "Service lacks necessary main function: " , err )
os . Exit ( 1 )
}
// Initialize the service
var inbox = make ( chan library . InterServiceMessage )
2024-10-03 18:33:41 +01:00
lock . Lock ( )
2024-10-04 19:37:05 +01:00
services [ serviceInformation . ServiceID ] = Service {
2024-10-15 19:17:29 +01:00
ServiceID : serviceInformation . ServiceID ,
Inbox : inbox ,
ServiceMetadata : serviceInformation ,
2024-09-28 19:41:34 +01:00
}
2024-10-03 18:33:41 +01:00
lock . Unlock ( )
2024-09-28 19:41:34 +01:00
2024-10-04 19:37:05 +01:00
slog . Info ( "Activating service " + serviceInformation . Name + " with ID " + serviceInformation . ServiceID . String ( ) )
2024-10-04 18:30:17 +01:00
2024-09-28 19:41:34 +01:00
// Check if they want a resource directory
2024-10-04 19:37:05 +01:00
if serviceInformation . Permissions . Resources {
2024-10-15 19:17:29 +01:00
appRouter := main . ( func ( library . ServiceInitializationInformation ) * chi . Mux ) ( library . ServiceInitializationInformation {
2024-10-04 19:37:05 +01:00
Domain : serviceInformation . Name ,
Configuration : config . Services [ strings . ToLower ( serviceInformation . Name ) ] . ( map [ string ] interface { } ) ,
2024-09-28 19:41:34 +01:00
Outbox : globalOutbox ,
Inbox : inbox ,
2024-10-04 19:37:05 +01:00
ResourceDir : os . DirFS ( filepath . Join ( config . Global . ResourceDirectory , serviceInformation . ServiceID . String ( ) ) ) ,
2024-09-28 19:41:34 +01:00
} )
2024-10-15 19:17:29 +01:00
if appRouter != nil {
if config . Services [ strings . ToLower ( serviceInformation . Name ) ] . ( map [ string ] interface { } ) [ "subdomain" ] != nil {
hostRouter . Map ( config . Services [ strings . ToLower ( serviceInformation . Name ) ] . ( map [ string ] interface { } ) [ "subdomain" ] . ( string ) , appRouter )
fmt . Println ( "Mapped subdomain " + config . Services [ strings . ToLower ( serviceInformation . Name ) ] . ( map [ string ] interface { } ) [ "subdomain" ] . ( string ) + " to service " + serviceInformation . Name )
} else {
hostRouter . Map ( "*" , appRouter )
fmt . Println ( "Mapped service " + serviceInformation . Name + " to all subdomains" )
}
}
2024-09-28 19:41:34 +01:00
} else {
2024-10-15 19:17:29 +01:00
main . ( func ( library . ServiceInitializationInformation ) * chi . Mux ) ( library . ServiceInitializationInformation {
2024-10-04 19:37:05 +01:00
Domain : serviceInformation . Name ,
Configuration : config . Services [ strings . ToLower ( serviceInformation . Name ) ] . ( map [ string ] interface { } ) ,
2024-09-28 19:41:34 +01:00
Outbox : globalOutbox ,
Inbox : inbox ,
} )
}
2024-10-04 18:30:17 +01:00
// Log the service activation
2024-10-04 19:37:05 +01:00
slog . Info ( "Service " + serviceInformation . Name + " activated with ID " + serviceInformation . ServiceID . String ( ) )
2024-09-28 19:41:34 +01:00
}
// Start the server
2024-10-04 19:37:05 +01:00
slog . Info ( "Starting server on " + config . Global . IP + ":" + config . Global . Port )
2024-10-15 18:46:04 +01:00
router . Mount ( "/" , hostRouter )
2024-09-28 19:41:34 +01:00
err = http . ListenAndServe ( config . Global . IP + ":" + config . Global . Port , router )
if err != nil {
slog . Error ( "Error starting server: " , err )
os . Exit ( 1 )
}
}