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
|
/resources
|
||||||
/services
|
/services
|
||||||
/services-src/eternity-web
|
/services-src/eternity-web
|
||||||
/config.conf
|
/config.yaml
|
||||||
fulgens.log
|
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-library/v2 v2.1.1
|
||||||
git.ailur.dev/ailur/fg-nucleus-library v1.0.3
|
git.ailur.dev/ailur/fg-nucleus-library v1.0.3
|
||||||
git.ailur.dev/ailur/pow v1.0.2
|
git.ailur.dev/ailur/pow v1.0.2
|
||||||
github.com/BurntSushi/toml v1.4.0
|
|
||||||
github.com/andybalholm/brotli v1.1.1
|
github.com/andybalholm/brotli v1.1.1
|
||||||
github.com/cespare/xxhash/v2 v2.3.0
|
github.com/cespare/xxhash/v2 v2.3.0
|
||||||
github.com/go-chi/chi/v5 v5.1.0
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
|
@ -17,15 +16,18 @@ require (
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/mattn/go-sqlite3 v1.14.24
|
github.com/mattn/go-sqlite3 v1.14.24
|
||||||
golang.org/x/crypto v0.28.0
|
golang.org/x/crypto v0.28.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.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/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/text v0.19.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/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 h1:8tb6mXZdyQYjrKRW+AUmWMi5wJoHh9Ch3oRqiJr/ivs=
|
||||||
git.ailur.dev/ailur/pow v1.0.2/go.mod h1:fjFb1z5KtF6V14HRhGWiDmmJKggO8KyAP20Lr5OJI/g=
|
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 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
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/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 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
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 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
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=
|
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/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 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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"
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
"plugin"
|
"plugin"
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -19,7 +17,6 @@ import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
@ -31,6 +28,7 @@ import (
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
@ -38,54 +36,55 @@ import (
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Global struct {
|
Global struct {
|
||||||
IP string `json:"ip" validate:"required,ip_addr"`
|
IP string `yaml:"ip" validate:"required,ip_addr"`
|
||||||
HTTPPort string `json:"httpPort" validate:"required"`
|
HTTPPort string `yaml:"httpPort" validate:"required"`
|
||||||
HTTPSPort string `json:"httpsPort" validate:"required"`
|
HTTPSPort string `yaml:"httpsPort" validate:"required"`
|
||||||
ServiceDirectory string `json:"serviceDirectory" validate:"required"`
|
ServiceDirectory string `yaml:"serviceDirectory" validate:"required"`
|
||||||
ResourceDirectory string `json:"resourceDirectory" validate:"required"`
|
ResourceDirectory string `yaml:"resourceDirectory" validate:"required"`
|
||||||
Compression struct {
|
Compression struct {
|
||||||
Algorithm string `json:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
Algorithm string `yaml:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||||
Level float64 `json:"level" validate:"omitempty,min=1,max=22"`
|
Level float64 `yaml:"level" validate:"omitempty,min=1,max=22"`
|
||||||
} `json:"compression"`
|
} `yaml:"compression"`
|
||||||
Logging struct {
|
Logging struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
File string `json:"file" validate:"required_if=Enabled true"`
|
File string `yaml:"file" validate:"required_if=Enabled true"`
|
||||||
} `json:"logging"`
|
} `yaml:"logging"`
|
||||||
Database struct {
|
Database struct {
|
||||||
Type string `json:"type" validate:"required,oneof=sqlite postgres"`
|
Type string `yaml:"type" validate:"required,oneof=sqlite postgres"`
|
||||||
ConnectionString string `json:"connectionString" validate:"required_if=Type postgres"`
|
ConnectionString string `yaml:"connectionString" validate:"required_if=Type postgres"`
|
||||||
Path string `json:"path" validate:"required_if=Type sqlite"`
|
Path string `yaml:"path" validate:"required_if=Type sqlite"`
|
||||||
} `json:"database" validate:"required"`
|
} `yaml:"database" validate:"required"`
|
||||||
} `json:"global" validate:"required"`
|
} `yaml:"global" validate:"required"`
|
||||||
Routes []struct {
|
Routes []struct {
|
||||||
Subdomain string `json:"subdomain" validate:"required"`
|
Subdomain string `yaml:"subdomain" validate:"required"`
|
||||||
Services []string `json:"services"`
|
Services []string `yaml:"services"`
|
||||||
Paths []struct {
|
Paths []struct {
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `yaml:"path" validate:"required"`
|
||||||
Proxy struct {
|
Proxy struct {
|
||||||
URL string `json:"url" validate:"required"`
|
URL string `yaml:"url" validate:"required"`
|
||||||
StripPrefix bool `json:"stripPrefix"`
|
StripPrefix bool `yaml:"stripPrefix"`
|
||||||
} `json:"proxy" validate:"required_without=Static"`
|
} `yaml:"proxy" validate:"required_without=Static"`
|
||||||
Static struct {
|
Static struct {
|
||||||
Root string `json:"root" validate:"required,isDirectory"`
|
Root string `yaml:"root" validate:"required,isDirectory"`
|
||||||
DirectoryListing bool `json:"directoryListing"`
|
DirectoryListing bool `yaml:"directoryListing"`
|
||||||
} `json:"static" validate:"required_without=Proxy"`
|
} `yaml:"static" validate:"required_without=Proxy"`
|
||||||
} `json:"paths"`
|
} `yaml:"paths"`
|
||||||
HTTPS struct {
|
HTTPS struct {
|
||||||
CertificatePath string `json:"certificatePath" validate:"required"`
|
CertificatePath string `yaml:"certificatePath" validate:"required"`
|
||||||
KeyPath string `json:"keyPath" validate:"required"`
|
KeyPath string `yaml:"keyPath" validate:"required"`
|
||||||
} `json:"https"`
|
} `yaml:"https"`
|
||||||
Compression struct {
|
Compression struct {
|
||||||
Algorithm string `json:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
Algorithm string `yaml:"algorithm" validate:"omitempty,oneof=gzip brotli zstd"`
|
||||||
Level float64 `json:"level" validate:"omitempty,min=1,max=22"`
|
Level float64 `yaml:"level" validate:"omitempty,min=1,max=22"`
|
||||||
} `json:"compression"`
|
} `yaml:"compression"`
|
||||||
} `json:"routes"`
|
} `yaml:"routes"`
|
||||||
Services map[string]interface{} `json:"services"`
|
Services map[string]interface{} `yaml:"services"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
ServiceID uuid.UUID
|
ServiceID uuid.UUID
|
||||||
ServiceMetadata library.Service
|
ServiceMetadata library.Service
|
||||||
|
ServiceMainFunc func(library.ServiceInitializationInformation)
|
||||||
Inbox chan library.InterServiceMessage
|
Inbox chan library.InterServiceMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,14 +474,14 @@ func hostRouter(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
validate *validator.Validate
|
validate *validator.Validate
|
||||||
services = make(map[uuid.UUID]Service)
|
lock sync.RWMutex
|
||||||
lock sync.RWMutex
|
config Config
|
||||||
config Config
|
registeredServices = make(map[string]Service)
|
||||||
certificates = make(map[string]*tls.Certificate)
|
activeServices = make(map[uuid.UUID]Service)
|
||||||
compression = make(map[string]CompressionSettings)
|
certificates = make(map[string]*tls.Certificate)
|
||||||
subdomains = make(map[string]*chi.Mux)
|
compression = make(map[string]CompressionSettings)
|
||||||
serviceSubdomains = make(map[string]string)
|
subdomains = make(map[string]*chi.Mux)
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadTLSCertificate(certificatePath, keyPath string) (*tls.Certificate, error) {
|
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) {
|
func svInit(message library.InterServiceMessage) {
|
||||||
// Service database initialization message
|
// Service database initialization message
|
||||||
// Check if the service has the necessary permissions
|
// 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
|
// Check if we are using sqlite or postgres
|
||||||
if config.Global.Database.Type == "sqlite" {
|
if config.Global.Database.Type == "sqlite" {
|
||||||
// Open the database and return the connection
|
// Open the database and return the connection
|
||||||
pluginConn, err := sql.Open("sqlite3", filepath.Join(config.Global.Database.Path, message.ServiceID.String()+".db"))
|
pluginConn, err := sql.Open("sqlite3", filepath.Join(config.Global.Database.Path, message.ServiceID.String()+".db"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -522,7 +521,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Report a successful activation
|
// Report a successful activation
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 2,
|
MessageType: 2,
|
||||||
|
@ -538,7 +537,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
conn, err := sql.Open("postgres", config.Global.Database.ConnectionString)
|
conn, err := sql.Open("postgres", config.Global.Database.ConnectionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -550,7 +549,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
_, err = conn.Exec("CREATE SCHEMA IF NOT EXISTS \"" + message.ServiceID.String() + "\"")
|
_, err = conn.Exec("CREATE SCHEMA IF NOT EXISTS \"" + message.ServiceID.String() + "\"")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -568,7 +567,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
pluginConn, err := sql.Open("postgres", connectionString)
|
pluginConn, err := sql.Open("postgres", connectionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -580,7 +579,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
err = pluginConn.Ping()
|
err = pluginConn.Ping()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -589,7 +588,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Report a successful activation
|
// Report a successful activation
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 2,
|
MessageType: 2,
|
||||||
|
@ -606,7 +605,7 @@ func svInit(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Report an error
|
// Report an error
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 1,
|
MessageType: 1,
|
||||||
|
@ -618,15 +617,15 @@ func svInit(message library.InterServiceMessage) {
|
||||||
|
|
||||||
func tryAuthAccess(message library.InterServiceMessage) {
|
func tryAuthAccess(message library.InterServiceMessage) {
|
||||||
// We need to check if the service is allowed to access the Authentication service
|
// 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 {
|
if ok && serviceMetadata.ServiceMetadata.Permissions.Authenticate {
|
||||||
// Send message to Authentication service
|
// 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 {
|
if ok {
|
||||||
service.Inbox <- message
|
service.Inbox <- message
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -642,7 +641,7 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -659,7 +658,7 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -678,15 +677,15 @@ func tryAuthAccess(message library.InterServiceMessage) {
|
||||||
|
|
||||||
func tryStorageAccess(message library.InterServiceMessage) {
|
func tryStorageAccess(message library.InterServiceMessage) {
|
||||||
// We need to check if the service is allowed to access the Blob Storage service
|
// 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 {
|
if ok && serviceMetadata.ServiceMetadata.Permissions.BlobStorage {
|
||||||
// Send message to Blob Storage service
|
// 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 {
|
if ok {
|
||||||
service.Inbox <- message
|
service.Inbox <- message
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -702,7 +701,7 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -719,7 +718,7 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -738,21 +737,21 @@ func tryStorageAccess(message library.InterServiceMessage) {
|
||||||
|
|
||||||
func tryLogger(message library.InterServiceMessage) {
|
func tryLogger(message library.InterServiceMessage) {
|
||||||
// Logger service
|
// Logger service
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
switch message.MessageType {
|
switch message.MessageType {
|
||||||
case 0:
|
case 0:
|
||||||
// Log message
|
// Log message
|
||||||
slog.Info(service.ServiceMetadata.Name + " says: " + message.Message.(string))
|
slog.Info(strings.ToLower(service.ServiceMetadata.Name) + " says: " + message.Message.(string))
|
||||||
case 1:
|
case 1:
|
||||||
// Warn message
|
// Warn message
|
||||||
slog.Warn(service.ServiceMetadata.Name + " warns: " + message.Message.(string))
|
slog.Warn(strings.ToLower(service.ServiceMetadata.Name) + " warns: " + message.Message.(string))
|
||||||
case 2:
|
case 2:
|
||||||
// Error message
|
// Error message
|
||||||
slog.Error(service.ServiceMetadata.Name + " complains: " + message.Message.(string))
|
slog.Error(strings.ToLower(service.ServiceMetadata.Name) + " complains: " + message.Message.(string))
|
||||||
case 3:
|
case 3:
|
||||||
// Fatal message
|
// 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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,7 +762,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
||||||
message := <-channel
|
message := <-channel
|
||||||
if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000000") {
|
if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000000") {
|
||||||
// Broadcast message
|
// Broadcast message
|
||||||
for _, service := range services {
|
for _, service := range activeServices {
|
||||||
service.Inbox <- message
|
service.Inbox <- message
|
||||||
}
|
}
|
||||||
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000001") {
|
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000001") {
|
||||||
|
@ -772,7 +771,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
||||||
case 0:
|
case 0:
|
||||||
// This has been deprecated, ignore it
|
// This has been deprecated, ignore it
|
||||||
// Send "true" back
|
// Send "true" back
|
||||||
services[message.ServiceID].Inbox <- library.InterServiceMessage{
|
activeServices[message.ServiceID].Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
ForServiceID: message.ServiceID,
|
ForServiceID: message.ServiceID,
|
||||||
MessageType: 0,
|
MessageType: 0,
|
||||||
|
@ -789,13 +788,13 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
||||||
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000004") {
|
} else if message.ForServiceID == uuid.MustParse("00000000-0000-0000-0000-000000000004") {
|
||||||
tryAuthAccess(message)
|
tryAuthAccess(message)
|
||||||
} else {
|
} else {
|
||||||
serviceMetadata, ok := services[message.ServiceID]
|
serviceMetadata, ok := activeServices[message.ServiceID]
|
||||||
if ok && serviceMetadata.ServiceMetadata.Permissions.InterServiceCommunication {
|
if ok && serviceMetadata.ServiceMetadata.Permissions.InterServiceCommunication {
|
||||||
// Send message to specific service
|
// Send message to specific service
|
||||||
service, ok := services[message.ForServiceID]
|
service, ok := activeServices[message.ForServiceID]
|
||||||
if !ok {
|
if !ok {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -813,7 +812,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage) {
|
||||||
service.Inbox <- message
|
service.Inbox <- message
|
||||||
} else {
|
} else {
|
||||||
// Send error message
|
// Send error message
|
||||||
service, ok := services[message.ServiceID]
|
service, ok := activeServices[message.ServiceID]
|
||||||
if ok {
|
if ok {
|
||||||
service.Inbox <- library.InterServiceMessage{
|
service.Inbox <- library.InterServiceMessage{
|
||||||
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
|
||||||
|
@ -854,16 +853,14 @@ func parseConfig(path string) Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the configuration file
|
// Parse the configuration file
|
||||||
configFile, err := os.ReadFile(path)
|
configFile, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error reading configuration file: " + err.Error())
|
slog.Error("Error reading configuration file: " + err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the configuration file
|
// Parse the configuration file
|
||||||
var config Config
|
decoder := yaml.NewDecoder(configFile)
|
||||||
decoder := json.NewDecoder(strings.NewReader(string(regexp.MustCompile(`(?m)^\s*//.*`).ReplaceAll(configFile, []byte("")))))
|
|
||||||
decoder.UseNumber()
|
|
||||||
err = decoder.Decode(&config)
|
err = decoder.Decode(&config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error parsing configuration file: " + err.Error())
|
slog.Error("Error parsing configuration file: " + err.Error())
|
||||||
|
@ -895,7 +892,7 @@ func parseConfig(path string) Config {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateThroughSubdomains() {
|
func iterateThroughSubdomains(globalOutbox chan library.InterServiceMessage) {
|
||||||
for _, route := range config.Routes {
|
for _, route := range config.Routes {
|
||||||
var subdomainRouter *chi.Mux
|
var subdomainRouter *chi.Mux
|
||||||
// Create the subdomain router
|
// Create the subdomain router
|
||||||
|
@ -919,12 +916,19 @@ func iterateThroughSubdomains() {
|
||||||
if route.Services != nil {
|
if route.Services != nil {
|
||||||
// Iterate through the services
|
// Iterate through the services
|
||||||
for _, service := range route.Services {
|
for _, service := range route.Services {
|
||||||
_, ok := serviceSubdomains[strings.ToLower(service)]
|
// Check if the service is registered
|
||||||
if !ok {
|
registeredService, ok := registeredServices[service]
|
||||||
serviceSubdomains[strings.ToLower(service)] = route.Subdomain
|
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 {
|
} else {
|
||||||
slog.Error("Service " + service + " has multiple subdomains")
|
slog.Warn("Service with ID " + service + " is not registered")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -963,81 +967,39 @@ func iterateThroughSubdomains() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initializeService(keys []time.Time, plugins map[time.Time]string, globalOutbox chan library.InterServiceMessage) {
|
func initializeService(service Service, globalOutbox chan library.InterServiceMessage, subdomainRouter *chi.Mux) {
|
||||||
for _, k := range keys {
|
// Get the plugin from the map
|
||||||
// Get the plugin path
|
slog.Info("Activating service " + strings.ToLower(service.ServiceMetadata.Name) + " with ID " + service.ServiceMetadata.ServiceID.String())
|
||||||
pluginPath := plugins[k]
|
|
||||||
|
|
||||||
// Load the plugin
|
serviceInitializationInformation := library.ServiceInitializationInformation{
|
||||||
servicePlugin, err := plugin.Open(pluginPath)
|
Domain: strings.ToLower(service.ServiceMetadata.Name),
|
||||||
if err != nil {
|
Configuration: config.Services[strings.ToLower(service.ServiceMetadata.Name)].(map[string]interface{}),
|
||||||
slog.Error("Could not load service: " + err.Error())
|
Outbox: globalOutbox,
|
||||||
os.Exit(1)
|
Inbox: service.Inbox,
|
||||||
}
|
Router: subdomainRouter,
|
||||||
|
|
||||||
// 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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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() {
|
func main() {
|
||||||
// Parse the configuration file
|
// Parse the configuration file
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
info, err := os.Stat("config.conf")
|
info, err := os.Stat("config.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
slog.Error("No configuration file provided")
|
slog.Error("No configuration file provided")
|
||||||
|
@ -1053,7 +1015,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
config = parseConfig("config.conf")
|
config = parseConfig("config.yaml")
|
||||||
} else {
|
} else {
|
||||||
config = parseConfig(os.Args[1])
|
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
|
// Walk through the service directory and load the plugins
|
||||||
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)
|
|
||||||
err := filepath.Walk(config.Global.ServiceDirectory, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(config.Global.ServiceDirectory, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1088,36 +1039,53 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the plugin to the list of plugins
|
// Open the service
|
||||||
if info.Name() == "storage.fgs" {
|
service, err := plugin.Open(path)
|
||||||
plugins[time.Unix(0, 0)] = path
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
} else if info.Name() == "auth.fgs" {
|
|
||||||
plugins[time.Unix(0, 1)] = path
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
var globalOutbox = make(chan library.InterServiceMessage, 1)
|
||||||
slog.Error("Error walking the services directory: " + err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the plugins by modification time, newest last
|
// Initialize the service discovery, health-check, and logging services
|
||||||
var keys []time.Time
|
// Since these are core services, always allocate them the service IDs 0, 1, and 2
|
||||||
for k := range plugins {
|
// These are not dynamically loaded, as they are integral to the system functioning
|
||||||
keys = append(keys, k)
|
go processInterServiceMessage(globalOutbox)
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(keys, func(i, j int) bool {
|
// Start the storage service
|
||||||
return keys[i].Before(keys[j])
|
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
|
// Start the server
|
||||||
slog.Info("Starting server on " + config.Global.IP + " with ports " + config.Global.HTTPPort + " and " + config.Global.HTTPSPort)
|
slog.Info("Starting server on " + config.Global.IP + " with ports " + config.Global.HTTPPort + " and " + config.Global.HTTPSPort)
|
||||||
|
|
Loading…
Reference in New Issue