Fixed subdomain routers not working if the service is activated after the file servers, which always happened. Also, do not load services not specified. Also, switch to a yaml config.
Signed-off-by: arzumify <jliwin98@danwin1210.de>
This commit is contained in:
parent
7bc3ca8a37
commit
d25e0a4877
|
@ -5,5 +5,5 @@
|
|||
/resources
|
||||
/services
|
||||
/services-src/eternity-web
|
||||
/config.conf
|
||||
/config.yaml
|
||||
fulgens.log
|
|
@ -1,120 +0,0 @@
|
|||
// NOTE: This is NOT a valid JSON file.
|
||||
// Comments are added here, which are stripped out by fulgens. This is not standard behavior for JSON and will only work with fulgens.
|
||||
|
||||
{
|
||||
// Global configuration
|
||||
"global": {
|
||||
// IP defines the IP address to bind to.
|
||||
"ip": "0.0.0.0",
|
||||
// httpPort defines the port to bind to for HTTP.
|
||||
"httpPort": "8080",
|
||||
// httpsPort defines the port to bind to for HTTPS (TLS).
|
||||
"httpsPort": "8443",
|
||||
// serviceDirectory defines the directory to look for services in.
|
||||
"serviceDirectory": "./services",
|
||||
// resourceDirectory defines the directory to look for resources in.
|
||||
"resourceDirectory": "./resources",
|
||||
// compression defines the compression settings on a global level - per-route settings override these.
|
||||
"compression": {
|
||||
// algorithm defines the compression algorithm to use, possible values are "gzip", "brotli" and "zstd".
|
||||
"algorithm": "gzip",
|
||||
// level defines the compression level to use, possible values are 1-9 for gzip, 0-11 for brotli and 1-22 for zstd.
|
||||
"level": 5
|
||||
},
|
||||
// logging defines the logging settings.
|
||||
"logging": {
|
||||
// enabled defines whether logging is enabled.
|
||||
"enabled": true,
|
||||
// file defines the file to log to, relative to the working directory.
|
||||
"file": "fulgens.log"
|
||||
},
|
||||
// database defines the database settings.
|
||||
"database": {
|
||||
// type defines the type of database to use, possible values are "sqlite" and "postgres".
|
||||
"type": "sqlite",
|
||||
// path defines the path to the directory to store database files in (sqlite only).
|
||||
"path": "./databases",
|
||||
// connectionString defines the connection string to use for the database (postgres only).
|
||||
"connectionString": "postgres://user:password@localhost:5432/database"
|
||||
}
|
||||
},
|
||||
// Routes define per-subdomain routing settings.
|
||||
"routes": [
|
||||
{
|
||||
// none is a special subdomain that matches all requests without a subdomain (Host header).
|
||||
"subdomain": "none",
|
||||
// services defines the services to use for this route. Services must be defined on a per-subdomain basis.
|
||||
// Each service may not be used more than once globally. The server will fail to start if this is violated.
|
||||
"services": ["authentication"]
|
||||
},
|
||||
{
|
||||
// any subdomain value that isn't "none" will match that specific subdomain.
|
||||
"subdomain": "www.localhost",
|
||||
// https defines the HTTPS settings for this route.
|
||||
"https": {
|
||||
// certificate defines the path to the certificate file.
|
||||
"certificate": "./certs/localhost.crt",
|
||||
// key defines the path to the key file.
|
||||
"key": "./certs/localhost.key"
|
||||
},
|
||||
// paths defines per-path settings (NOT for services, which MUST be defined on a per-subdomain basis).
|
||||
"paths": [
|
||||
{
|
||||
// path defines the path to match. They can contain wildcards.
|
||||
"path": "/static/*",
|
||||
// static defines the static file serving settings for this path. This conflicts with proxy.
|
||||
// If both proxy and static are defined, static will take precedence.
|
||||
"static": {
|
||||
// root defines the root directory to serve static files from.
|
||||
"root": "./static",
|
||||
// directoryListing defines whether to show a directory listing when a directory is requested.
|
||||
// if it is false or unset, a 403 Forbidden will be returned instead.
|
||||
"directoryListing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
// path defines the path to match. They can contain wildcards.
|
||||
"path": "/proxy/*",
|
||||
// proxy defines the proxy settings for this path. This conflicts with static.
|
||||
// If both proxy and static are defined, static will take precedence.
|
||||
"proxy": {
|
||||
// url defines the URL to proxy requests to.
|
||||
"url": "http://localhost:8000",
|
||||
// stripPrefix defines whether to strip the prefix from the path before proxying.
|
||||
"stripPrefix": true
|
||||
// TODO: In a future update, passing X-Forwarded-For and X-Forwarded-Proto headers will be supported.
|
||||
// TODO: In a future update, forbidding certain headers from being passed will be supported.
|
||||
// TODO: In a future update, passing X-Powered-By and Server headers will be supported.
|
||||
// TODO: In a future update, passing Host header will be supported.
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
// Services define the settings for services.
|
||||
"services": {
|
||||
// authentication defines the settings for the authentication service, which is built-in.
|
||||
"authentication": {
|
||||
// privacyPolicy defines the URL to the privacy policy.
|
||||
"privacyPolicy": "https://git.ailur.dev/Paperwork/nucleus/src/commit/5d191eea87cffae8bdca42017ac26dc19e6cb3de/Privacy.md",
|
||||
// url defines the publicly-facing URL of the service, in case of it being behind a reverse proxy.
|
||||
"url": "http://localhost:8000",
|
||||
// identifier defines the identifier for the service, in the form of [Identifier] Accounts.
|
||||
"identifier": "Authenticator",
|
||||
// adminKey defines the key to use for administrative operations, such as listing all users.
|
||||
"adminKey": "supersecretkey",
|
||||
// testAppIsInternalApp defines whether the test app is an internal app, which allows it to bypass the user consent screen.
|
||||
"testAppIsInternalApp": true,
|
||||
// testAppEnabled defines whether the test app is enabled, which is recommended for testing purposes.
|
||||
"testAppEnabled": true
|
||||
},
|
||||
// storage defines the settings for the storage service, which is built-in.
|
||||
"storage": {
|
||||
// path defines the path to store blobs in.
|
||||
"path": "./blob",
|
||||
// defaultQuota defines the default quota for users in bytes.
|
||||
"defaultQuota": 50000000
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
# This is just YAML, but I decided to use JSON-like formatting because I like it better.
|
||||
|
||||
# Global configuration
|
||||
global: {
|
||||
# IP defines the IP address to bind to.
|
||||
ip: "0.0.0.0",
|
||||
# httpPort defines the port to bind to for HTTP.
|
||||
httpPort: "8080",
|
||||
# httpsPort defines the port to bind to for HTTPS (TLS).
|
||||
httpsPort: "8443",
|
||||
# serviceDirectory defines the directory to look for services in.
|
||||
serviceDirectory: "./services",
|
||||
# resourceDirectory defines the directory to look for resources in.
|
||||
resourceDirectory: "./resources",
|
||||
# compression defines the compression settings on a global level - per-route settings override these.
|
||||
compression: {
|
||||
# algorithm defines the compression algorithm to use, possible values are "gzip", "brotli" and "zstd".
|
||||
algorithm: "gzip",
|
||||
# level defines the compression level to use, possible values are 1-9 for gzip, 0-11 for brotli and 1-22 for zstd.
|
||||
level: 5
|
||||
},
|
||||
# logging defines the logging settings.
|
||||
logging: {
|
||||
# enabled defines whether logging is enabled.
|
||||
enabled: true,
|
||||
# file defines the file to log to, relative to the working directory.
|
||||
file: "fulgens.log"
|
||||
},
|
||||
# database defines the database settings.
|
||||
database: {
|
||||
# type defines the type of database to use, possible values are "sqlite" and "postgres".
|
||||
type: "sqlite",
|
||||
# path defines the path to the directory to store database files in (sqlite only).
|
||||
path: "./databases",
|
||||
# connectionString defines the connection string to use for the database (postgres only).
|
||||
connectionString: "postgres://user:password@localhost:5432/database"
|
||||
}
|
||||
}
|
||||
|
||||
# Routes define per-subdomain routing settings.
|
||||
routes: [
|
||||
{
|
||||
# none is a special subdomain that matches all requests without a subdomain (Host header).
|
||||
subdomain: "none",
|
||||
# services defines the services to use for this route. Services must be defined on a per-subdomain basis.
|
||||
# Each service may not be used more than once globally. The server will fail to start if this is violated.
|
||||
services: ["authentication"]
|
||||
},
|
||||
{
|
||||
# any subdomain value that isn't "none" will match that specific subdomain.
|
||||
subdomain: "www.localhost",
|
||||
# https defines the HTTPS settings for this route.
|
||||
https: {
|
||||
# certificate defines the path to the certificate file.
|
||||
certificate: "./certs/localhost.crt",
|
||||
# key defines the path to the key file.
|
||||
key: "./certs/localhost.key"
|
||||
},
|
||||
# paths defines per-path settings (NOT for services, which MUST be defined on a per-subdomain basis).
|
||||
paths: [
|
||||
{
|
||||
# path defines the path to match. They can contain wildcards.
|
||||
path: "/static/*",
|
||||
# static defines the static file serving settings for this path. This conflicts with proxy.
|
||||
# If both proxy and static are defined, static will take precedence.
|
||||
static: {
|
||||
# root defines the root directory to serve static files from.
|
||||
root: "./static",
|
||||
# directoryListing defines whether to show a directory listing when a directory is requested.
|
||||
# if it is false or unset, a 403 Forbidden will be returned instead.
|
||||
directoryListing: true
|
||||
}
|
||||
},
|
||||
{
|
||||
# path defines the path to match. They can contain wildcards.
|
||||
path: "/proxy/*",
|
||||
# proxy defines the proxy settings for this path. This conflicts with static.
|
||||
# If both proxy and static are defined, static will take precedence.
|
||||
proxy: {
|
||||
# url defines the URL to proxy requests to.
|
||||
url: "http://localhost:8000",
|
||||
# stripPrefix defines whether to strip the prefix from the path before proxying.
|
||||
stripPrefix: true
|
||||
# TODO: In a future update, passing X-Forwarded-For and X-Forwarded-Proto headers will be supported.
|
||||
# TODO: In a future update, forbidding certain headers from being passed will be supported.
|
||||
# TODO: In a future update, passing X-Powered-By and Server headers will be supported.
|
||||
# TODO: In a future update, passing Host header will be supported.
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
# Services define the settings for services.
|
||||
services: {
|
||||
# authentication defines the settings for the authentication service, which is built-in.
|
||||
authentication: {
|
||||
# privacyPolicy defines the URL to the privacy policy.
|
||||
privacyPolicy: "https://git.ailur.dev/Paperwork/nucleus/src/commit/5d191eea87cffae8bdca42017ac26dc19e6cb3de/Privacy.md",
|
||||
# url defines the publicly-facing URL of the service, in case of it being behind a reverse proxy.
|
||||
url: "http://localhost:8000",
|
||||
# identifier defines the identifier for the service, in the form of [Identifier] Accounts.
|
||||
identifier: "Authenticator",
|
||||
# adminKey defines the key to use for administrative operations, such as listing all users.
|
||||
adminKey: "supersecretkey",
|
||||
# testAppIsInternalApp defines whether the test app is an internal app, which allows it to bypass the user consent screen.
|
||||
testAppIsInternalApp: true,
|
||||
# testAppEnabled defines whether the test app is enabled, which is recommended for testing purposes.
|
||||
testAppEnabled: true
|
||||
},
|
||||
# storage defines the settings for the storage service, which is built-in.
|
||||
storage: {
|
||||
# path defines the path to store blobs in.
|
||||
path: "./blob",
|
||||
# defaultQuota defines the default quota for users in bytes.
|
||||
defaultQuota: 50000000
|
||||
}
|
||||
}
|
4
go.mod
4
go.mod
|
@ -6,7 +6,6 @@ require (
|
|||
git.ailur.dev/ailur/fg-library/v2 v2.1.1
|
||||
git.ailur.dev/ailur/fg-nucleus-library v1.0.3
|
||||
git.ailur.dev/ailur/pow v1.0.2
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/andybalholm/brotli v1.1.1
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
|
@ -17,15 +16,18 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
golang.org/x/crypto v0.28.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
|
10
go.sum
10
go.sum
|
@ -4,8 +4,6 @@ git.ailur.dev/ailur/fg-nucleus-library v1.0.3 h1:C0xgfZg7bkULhh9Ci7ZoAcx4QIqxLh+
|
|||
git.ailur.dev/ailur/fg-nucleus-library v1.0.3/go.mod h1:RbBVFRwtQgYvCWoru1mC3vUJ1dMftkNbvd7hVFtREFw=
|
||||
git.ailur.dev/ailur/pow v1.0.2 h1:8tb6mXZdyQYjrKRW+AUmWMi5wJoHh9Ch3oRqiJr/ivs=
|
||||
git.ailur.dev/ailur/pow v1.0.2/go.mod h1:fjFb1z5KtF6V14HRhGWiDmmJKggO8KyAP20Lr5OJI/g=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
|
@ -30,6 +28,11 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
|
@ -50,5 +53,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
|||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
342
main.go
342
main.go
|
@ -9,8 +9,6 @@ import (
|
|||
"mime"
|
||||
"os"
|
||||
"plugin"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -19,7 +17,6 @@ import (
|
|||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
@ -31,6 +28,7 @@ import (
|
|||
"github.com/go-playground/validator/v10"
|
||||
"github.com/google/uuid"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
@ -38,54 +36,55 @@ import (
|
|||
|
||||
type Config struct {
|
||||
Global struct {
|
||||
IP string `json:"ip" validate:"required,ip_addr"`
|
||||
HTTPPort string `json:"httpPort" validate:"required"`
|
||||
HTTPSPort string `json:"httpsPort" validate:"required"`
|
||||
ServiceDirectory string `json:"serviceDirectory" validate:"required"`
|
||||
ResourceDirectory string `json:"resourceDirectory" validate:"required"`
|
||||
IP string `yaml:"ip" validate:"required,ip_addr"`
|
||||
HTTPPort string `yaml:"httpPort" validate:"required"`
|
||||
HTTPSPort string `yaml:"httpsPort" validate:"required"`
|
||||
ServiceDirectory string `yaml:"serviceDirectory" validate:"required"`
|
||||
ResourceDirectory string `yaml:"resourceDirectory" validate:"required"`
|
||||
Compression struct {
|
||||
Algorithm string `json:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||
Level float64 `json:"level" validate:"omitempty,min=1,max=22"`
|
||||
} `json:"compression"`
|
||||
Algorithm string `yaml:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||
Level float64 `yaml:"level" validate:"omitempty,min=1,max=22"`
|
||||
} `yaml:"compression"`
|
||||
Logging struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
File string `json:"file" validate:"required_if=Enabled true"`
|
||||
} `json:"logging"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
File string `yaml:"file" validate:"required_if=Enabled true"`
|
||||
} `yaml:"logging"`
|
||||
Database struct {
|
||||
Type string `json:"type" validate:"required,oneof=sqlite postgres"`
|
||||
ConnectionString string `json:"connectionString" validate:"required_if=Type postgres"`
|
||||
Path string `json:"path" validate:"required_if=Type sqlite"`
|
||||
} `json:"database" validate:"required"`
|
||||
} `json:"global" validate:"required"`
|
||||
Type string `yaml:"type" validate:"required,oneof=sqlite postgres"`
|
||||
ConnectionString string `yaml:"connectionString" validate:"required_if=Type postgres"`
|
||||
Path string `yaml:"path" validate:"required_if=Type sqlite"`
|
||||
} `yaml:"database" validate:"required"`
|
||||
} `yaml:"global" validate:"required"`
|
||||
Routes []struct {
|
||||
Subdomain string `json:"subdomain" validate:"required"`
|
||||
Services []string `json:"services"`
|
||||
Subdomain string `yaml:"subdomain" validate:"required"`
|
||||
Services []string `yaml:"services"`
|
||||
Paths []struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
Path string `yaml:"path" validate:"required"`
|
||||
Proxy struct {
|
||||
URL string `json:"url" validate:"required"`
|
||||
StripPrefix bool `json:"stripPrefix"`
|
||||
} `json:"proxy" validate:"required_without=Static"`
|
||||
URL string `yaml:"url" validate:"required"`
|
||||
StripPrefix bool `yaml:"stripPrefix"`
|
||||
} `yaml:"proxy" validate:"required_without=Static"`
|
||||
Static struct {
|
||||
Root string `json:"root" validate:"required,isDirectory"`
|
||||
DirectoryListing bool `json:"directoryListing"`
|
||||
} `json:"static" validate:"required_without=Proxy"`
|
||||
} `json:"paths"`
|
||||
Root string `yaml:"root" validate:"required,isDirectory"`
|
||||
DirectoryListing bool `yaml:"directoryListing"`
|
||||
} `yaml:"static" validate:"required_without=Proxy"`
|
||||
} `yaml:"paths"`
|
||||
HTTPS struct {
|
||||
CertificatePath string `json:"certificatePath" validate:"required"`
|
||||
KeyPath string `json:"keyPath" validate:"required"`
|
||||
} `json:"https"`
|
||||
CertificatePath string `yaml:"certificatePath" validate:"required"`
|
||||
KeyPath string `yaml:"keyPath" validate:"required"`
|
||||
} `yaml:"https"`
|
||||
Compression struct {
|
||||
Algorithm string `json:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||
Level float64 `json:"level" validate:"omitempty,min=1,max=22"`
|
||||
} `json:"compression"`
|
||||
} `json:"routes"`
|
||||
Services map[string]interface{} `json:"services"`
|
||||
Algorithm string `yaml:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||
Level float64 `yaml:"level" validate:"omitempty,min=1,max=22"`
|
||||
} `yaml:"compression"`
|
||||
} `yaml:"routes"`
|
||||
Services map[string]interface{} `yaml:"services"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
ServiceID uuid.UUID
|
||||
ServiceMetadata library.Service
|
||||
ServiceMainFunc func(library.ServiceInitializationInformation)
|
||||
Inbox chan library.InterServiceMessage
|
||||
}
|
||||
|
||||
|
@ -475,14 +474,14 @@ func hostRouter(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
var (
|
||||
validate *validator.Validate
|
||||
services = make(map[uuid.UUID]Service)
|
||||
lock sync.RWMutex
|
||||
config Config
|
||||
certificates = make(map[string]*tls.Certificate)
|
||||
compression = make(map[string]CompressionSettings)
|
||||
subdomains = make(map[string]*chi.Mux)
|
||||
serviceSubdomains = make(map[string]string)
|
||||
validate *validator.Validate
|
||||
lock sync.RWMutex
|
||||
config Config
|
||||
registeredServices = make(map[string]Service)
|
||||
activeServices = make(map[uuid.UUID]Service)
|
||||
certificates = make(map[string]*tls.Certificate)
|
||||
compression = make(map[string]CompressionSettings)
|
||||
subdomains = make(map[string]*chi.Mux)
|
||||
)
|
||||
|
||||
func loadTLSCertificate(certificatePath, keyPath string) (*tls.Certificate, error) {
|
||||
|
@ -506,14 +505,14 @@ func getTLSCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|||
func svInit(message library.InterServiceMessage) {
|
||||
// Service database initialization message
|
||||
// Check if the service has the necessary permissions
|
||||
if services[message.ServiceID].ServiceMetadata.Permissions.Database {
|
||||
if activeServices[message.ServiceID].ServiceMetadata.Permissions.Database {
|
||||
// Check if we are using sqlite or postgres
|
||||
if config.Global.Database.Type == "sqlite" {
|
||||
// Open the database and return the connection
|
||||
pluginConn, err := sql.Open("sqlite3", filepath.Join(config.Global.Database.Path, message.ServiceID.String()+".db"))
|
||||
if err != nil {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -522,7 +521,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Report a successful activation
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 2,
|
||||
|
@ -538,7 +537,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
conn, err := sql.Open("postgres", config.Global.Database.ConnectionString)
|
||||
if err != nil {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -550,7 +549,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
_, err = conn.Exec("CREATE SCHEMA IF NOT EXISTS \"" + message.ServiceID.String() + "\"")
|
||||
if err != nil {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -568,7 +567,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
pluginConn, err := sql.Open("postgres", connectionString)
|
||||
if err != nil {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -580,7 +579,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
err = pluginConn.Ping()
|
||||
if err != nil {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -589,7 +588,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Report a successful activation
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 2,
|
||||
|
@ -606,7 +605,7 @@ func svInit(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Report an error
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 1,
|
||||
|
@ -618,15 +617,15 @@ func svInit(message library.InterServiceMessage) {
|
|||
|
||||
func tryAuthAccess(message library.InterServiceMessage) {
|
||||
// We need to check if the service is allowed to access the Authentication service
|
||||
serviceMetadata, ok := services[message.ServiceID]
|
||||
serviceMetadata, ok := activeServices[message.ServiceID]
|
||||
if ok && serviceMetadata.ServiceMetadata.Permissions.Authenticate {
|
||||
// Send message to Authentication service
|
||||
service, ok := services[uuid.MustParse("00000000-0000-0000-0000-000000000004")]
|
||||
service, ok := activeServices[uuid.MustParse("00000000-0000-0000-0000-000000000004")]
|
||||
if ok {
|
||||
service.Inbox <- message
|
||||
} else if !ok {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -642,7 +641,7 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -659,7 +658,7 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -678,15 +677,15 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
|||
|
||||
func tryStorageAccess(message library.InterServiceMessage) {
|
||||
// We need to check if the service is allowed to access the Blob Storage service
|
||||
serviceMetadata, ok := services[message.ServiceID]
|
||||
serviceMetadata, ok := activeServices[message.ServiceID]
|
||||
if ok && serviceMetadata.ServiceMetadata.Permissions.BlobStorage {
|
||||
// Send message to Blob Storage service
|
||||
service, ok := services[uuid.MustParse("00000000-0000-0000-0000-000000000003")]
|
||||
service, ok := activeServices[uuid.MustParse("00000000-0000-0000-0000-000000000003")]
|
||||
if ok {
|
||||
service.Inbox <- message
|
||||
} else if !ok {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -702,7 +701,7 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -719,7 +718,7 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
|||
}
|
||||
} else {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -738,21 +737,21 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
|||
|
||||
func tryLogger(message library.InterServiceMessage) {
|
||||
// Logger service
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
switch message.MessageType {
|
||||
case 0:
|
||||
// Log message
|
||||
slog.Info(service.ServiceMetadata.Name + " says: " + message.Message.(string))
|
||||
slog.Info(strings.ToLower(service.ServiceMetadata.Name) + " says: " + message.Message.(string))
|
||||
case 1:
|
||||
// Warn message
|
||||
slog.Warn(service.ServiceMetadata.Name + " warns: " + message.Message.(string))
|
||||
slog.Warn(strings.ToLower(service.ServiceMetadata.Name) + " warns: " + message.Message.(string))
|
||||
case 2:
|
||||
// Error message
|
||||
slog.Error(service.ServiceMetadata.Name + " complains: " + message.Message.(string))
|
||||
slog.Error(strings.ToLower(service.ServiceMetadata.Name) + " complains: " + message.Message.(string))
|
||||
case 3:
|
||||
// Fatal message
|
||||
slog.Error(service.ServiceMetadata.Name + "'s dying wish: " + message.Message.(string))
|
||||
slog.Error(strings.ToLower(service.ServiceMetadata.Name) + "'s dying wish: " + message.Message.(string))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -763,7 +762,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
|||
message := <-channel
|
||||
if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000000") {
|
||||
// Broadcast message
|
||||
for _, service := range services {
|
||||
for _, service := range activeServices {
|
||||
service.Inbox <- message
|
||||
}
|
||||
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000001") {
|
||||
|
@ -772,7 +771,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
|||
case 0:
|
||||
// This has been deprecated, ignore it
|
||||
// Send "true" back
|
||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
ForServiceID: message.ServiceID,
|
||||
MessageType: 0,
|
||||
|
@ -789,13 +788,13 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
|||
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000004") {
|
||||
tryAuthAccess(message)
|
||||
} else {
|
||||
serviceMetadata, ok := services[message.ServiceID]
|
||||
serviceMetadata, ok := activeServices[message.ServiceID]
|
||||
if ok && serviceMetadata.ServiceMetadata.Permissions.InterServiceCommunication {
|
||||
// Send message to specific service
|
||||
service, ok := services[message.ForServiceID]
|
||||
service, ok := activeServices[message.ForServiceID]
|
||||
if !ok {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -813,7 +812,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
|||
service.Inbox <- message
|
||||
} else {
|
||||
// Send error message
|
||||
service, ok := services[message.ServiceID]
|
||||
service, ok := activeServices[message.ServiceID]
|
||||
if ok {
|
||||
service.Inbox <- library.InterServiceMessage{
|
||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||
|
@ -854,16 +853,14 @@ func parseConfig(path string) Config {
|
|||
}
|
||||
|
||||
// Parse the configuration file
|
||||
configFile, err := os.ReadFile(path)
|
||||
configFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
slog.Error("Error reading configuration file: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse the configuration file
|
||||
var config Config
|
||||
decoder := json.NewDecoder(strings.NewReader(string(regexp.MustCompile(`(?m)^\s*//.*`).ReplaceAll(configFile, []byte("")))))
|
||||
decoder.UseNumber()
|
||||
decoder := yaml.NewDecoder(configFile)
|
||||
err = decoder.Decode(&config)
|
||||
if err != nil {
|
||||
slog.Error("Error parsing configuration file: " + err.Error())
|
||||
|
@ -895,7 +892,7 @@ func parseConfig(path string) Config {
|
|||
return config
|
||||
}
|
||||
|
||||
func iterateThroughSubdomains() {
|
||||
func iterateThroughSubdomains(globalOutbox chan library.InterServiceMessage) {
|
||||
for _, route := range config.Routes {
|
||||
var subdomainRouter *chi.Mux
|
||||
// Create the subdomain router
|
||||
|
@ -919,12 +916,19 @@ func iterateThroughSubdomains() {
|
|||
if route.Services != nil {
|
||||
// Iterate through the services
|
||||
for _, service := range route.Services {
|
||||
_, ok := serviceSubdomains[strings.ToLower(service)]
|
||||
if !ok {
|
||||
serviceSubdomains[strings.ToLower(service)] = route.Subdomain
|
||||
// Check if the service is registered
|
||||
registeredService, ok := registeredServices[service]
|
||||
if ok {
|
||||
// Check if the service is already active
|
||||
_, ok := activeServices[registeredService.ServiceMetadata.ServiceID]
|
||||
if ok {
|
||||
slog.Error("Service with ID " + service + " is already active, will not activate again")
|
||||
os.Exit(1)
|
||||
}
|
||||
// Initialize the service
|
||||
initializeService(registeredService, globalOutbox, subdomainRouter)
|
||||
} else {
|
||||
slog.Error("Service " + service + " has multiple subdomains")
|
||||
os.Exit(1)
|
||||
slog.Warn("Service with ID " + service + " is not registered")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -963,81 +967,39 @@ func iterateThroughSubdomains() {
|
|||
}
|
||||
}
|
||||
|
||||
func initializeService(keys []time.Time, plugins map[time.Time]string, globalOutbox chan library.InterServiceMessage) {
|
||||
for _, k := range keys {
|
||||
// Get the plugin path
|
||||
pluginPath := plugins[k]
|
||||
func initializeService(service Service, globalOutbox chan library.InterServiceMessage, subdomainRouter *chi.Mux) {
|
||||
// Get the plugin from the map
|
||||
slog.Info("Activating service " + strings.ToLower(service.ServiceMetadata.Name) + " with ID " + service.ServiceMetadata.ServiceID.String())
|
||||
|
||||
// Load the plugin
|
||||
servicePlugin, err := plugin.Open(pluginPath)
|
||||
if err != nil {
|
||||
slog.Error("Could not load service: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Load the service information
|
||||
serviceInformationSymbol, err := servicePlugin.Lookup("ServiceInformation")
|
||||
if err != nil {
|
||||
slog.Error("Service lacks necessary information: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
serviceInformation := *serviceInformationSymbol.(*library.Service)
|
||||
|
||||
// Load the main function
|
||||
main, err := servicePlugin.Lookup("Main")
|
||||
if err != nil {
|
||||
slog.Error("Service lacks necessary main function: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Initialize the service
|
||||
var inbox = make(chan library.InterServiceMessage)
|
||||
lock.Lock()
|
||||
services[serviceInformation.ServiceID] = Service{
|
||||
ServiceID: serviceInformation.ServiceID,
|
||||
Inbox: inbox,
|
||||
ServiceMetadata: serviceInformation,
|
||||
}
|
||||
lock.Unlock()
|
||||
|
||||
slog.Info("Activating service " + serviceInformation.Name + " with ID " + serviceInformation.ServiceID.String())
|
||||
|
||||
serviceInitializationInformation := library.ServiceInitializationInformation{
|
||||
Domain: serviceInformation.Name,
|
||||
Configuration: config.Services[strings.ToLower(serviceInformation.Name)].(map[string]interface{}),
|
||||
Outbox: globalOutbox,
|
||||
Inbox: inbox,
|
||||
}
|
||||
|
||||
// Make finalRouter a subdomain router if necessary
|
||||
serviceSubdomain, ok := serviceSubdomains[strings.ToLower(serviceInformation.Name)]
|
||||
if ok {
|
||||
serviceInitializationInformation.Router = subdomains[serviceSubdomain]
|
||||
} else {
|
||||
if serviceInformation.ServiceID != uuid.MustParse("00000000-0000-0000-0000-000000000003") {
|
||||
slog.Warn("Service " + serviceInformation.Name + " does not have a subdomain, it will not be served")
|
||||
// Give it a blank router so it doesn't try to nil pointer dereference
|
||||
serviceInitializationInformation.Router = chi.NewRouter()
|
||||
}
|
||||
}
|
||||
|
||||
// Check if they want a resource directory
|
||||
if serviceInformation.Permissions.Resources {
|
||||
serviceInitializationInformation.ResourceDir = os.DirFS(filepath.Join(config.Global.ResourceDirectory, serviceInformation.ServiceID.String()))
|
||||
}
|
||||
|
||||
main.(func(library.ServiceInitializationInformation))(serviceInitializationInformation)
|
||||
|
||||
// Log the service activation
|
||||
slog.Info("Service " + serviceInformation.Name + " activated with ID " + serviceInformation.ServiceID.String())
|
||||
serviceInitializationInformation := library.ServiceInitializationInformation{
|
||||
Domain: strings.ToLower(service.ServiceMetadata.Name),
|
||||
Configuration: config.Services[strings.ToLower(service.ServiceMetadata.Name)].(map[string]interface{}),
|
||||
Outbox: globalOutbox,
|
||||
Inbox: service.Inbox,
|
||||
Router: subdomainRouter,
|
||||
}
|
||||
|
||||
// Check if they want a resource directory
|
||||
if service.ServiceMetadata.Permissions.Resources {
|
||||
serviceInitializationInformation.ResourceDir = os.DirFS(filepath.Join(config.Global.ResourceDirectory, service.ServiceMetadata.ServiceID.String()))
|
||||
}
|
||||
|
||||
// Add the service to the active services
|
||||
lock.Lock()
|
||||
activeServices[service.ServiceMetadata.ServiceID] = service
|
||||
lock.Unlock()
|
||||
|
||||
// Call the main function
|
||||
service.ServiceMainFunc(serviceInitializationInformation)
|
||||
|
||||
// Log the service activation
|
||||
slog.Info("Service " + strings.ToLower(service.ServiceMetadata.Name) + " activated with ID " + service.ServiceMetadata.ServiceID.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Parse the configuration file
|
||||
if len(os.Args) < 2 {
|
||||
info, err := os.Stat("config.conf")
|
||||
info, err := os.Stat("config.yaml")
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
slog.Error("No configuration file provided")
|
||||
|
@ -1053,7 +1015,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
config = parseConfig("config.conf")
|
||||
config = parseConfig("config.yaml")
|
||||
} else {
|
||||
config = parseConfig(os.Args[1])
|
||||
}
|
||||
|
@ -1067,18 +1029,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// Iterate through the subdomains and create the routers as well as the compression levels and service maps
|
||||
iterateThroughSubdomains()
|
||||
|
||||
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)
|
||||
|
||||
// Initialize all the services
|
||||
plugins := make(map[time.Time]string)
|
||||
// Walk through the service directory and load the plugins
|
||||
err := filepath.Walk(config.Global.ServiceDirectory, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1088,36 +1039,53 @@ func main() {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Add the plugin to the list of plugins
|
||||
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
|
||||
// Open the service
|
||||
service, err := plugin.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plugins[info.ModTime()] = path
|
||||
// Load the service information
|
||||
serviceInformation, err := service.Lookup("ServiceInformation")
|
||||
if err != nil {
|
||||
return errors.New("service lacks necessary information")
|
||||
}
|
||||
|
||||
// Load the main function
|
||||
mainFunc, err := service.Lookup("Main")
|
||||
if err != nil {
|
||||
return errors.New("service lacks necessary main function")
|
||||
}
|
||||
|
||||
// Register the service
|
||||
var inbox = make(chan library.InterServiceMessage, 1)
|
||||
lock.Lock()
|
||||
registeredServices[strings.ToLower(serviceInformation.(*library.Service).Name)] = Service{
|
||||
ServiceID: serviceInformation.(*library.Service).ServiceID,
|
||||
Inbox: inbox,
|
||||
ServiceMetadata: *serviceInformation.(*library.Service),
|
||||
ServiceMainFunc: mainFunc.(func(library.ServiceInitializationInformation)),
|
||||
}
|
||||
lock.Unlock()
|
||||
|
||||
// Log the service registration
|
||||
slog.Info("Service " + strings.ToLower(serviceInformation.(*library.Service).Name) + " registered with ID " + serviceInformation.(*library.Service).ServiceID.String())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Error walking the services directory: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
var globalOutbox = make(chan library.InterServiceMessage, 1)
|
||||
|
||||
// Sort the plugins by modification time, newest last
|
||||
var keys []time.Time
|
||||
for k := range plugins {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
// 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)
|
||||
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].Before(keys[j])
|
||||
})
|
||||
// Start the storage service
|
||||
initializeService(registeredServices["storage"], globalOutbox, nil)
|
||||
|
||||
initializeService(keys, plugins, globalOutbox)
|
||||
// Iterate through the subdomains and create the routers as well as the compression levels and service maps
|
||||
iterateThroughSubdomains(globalOutbox)
|
||||
|
||||
// Start the server
|
||||
slog.Info("Starting server on " + config.Global.IP + " with ports " + config.Global.HTTPPort + " and " + config.Global.HTTPSPort)
|
||||
|
|
Loading…
Reference in New Issue