restructure the API and update the admin script

This commit is contained in:
ngn
2025-01-04 00:00:10 +03:00
parent 03586da8df
commit 26e8909998
34 changed files with 1699 additions and 983 deletions

View File

@ -2,59 +2,112 @@ package config
import (
"fmt"
"net/url"
"os"
"strings"
"github.com/ngn13/website/api/util"
)
type Option struct {
Name string
Value string
Required bool
type Type struct {
Options []Option
Count int
}
func (o *Option) Env() string {
return strings.ToUpper(fmt.Sprintf("API_%s", o.Name))
}
var options []Option = []Option{
{Name: "password", Value: "", Required: true},
{Name: "frontend_url", Value: "http://localhost:5173/", Required: true},
}
func Load() bool {
var val string
for i := range options {
if val = os.Getenv(options[i].Env()); val == "" {
func (c *Type) Find(name string, typ uint8) (*Option, error) {
for i := 0; i < c.Count; i++ {
if c.Options[i].Name != name {
continue
}
options[i].Value = val
options[i].Required = false
}
for i := range options {
if options[i].Required && options[i].Value == "" {
util.Fail("please specify the required config option \"%s\" (\"%s\")", options[i].Name, options[i].Env())
return false
if c.Options[i].Type != typ {
return nil, fmt.Errorf("bad option type")
}
if options[i].Required && options[i].Value != "" {
util.Fail("using the default value \"%s\" for required config option \"%s\" (\"%s\")", options[i].Value, options[i].Name, options[i].Env())
}
return &c.Options[i], nil
}
return true
return nil, fmt.Errorf("option not found")
}
func Get(name string) string {
for i := range options {
if options[i].Name != name {
continue
}
return options[i].Value
func (c *Type) Load() (err error) {
var (
env_val string
env_name string
opt *Option
exists bool
)
// default options
c.Options = []Option{
{Name: "debug", Value: "false", Type: OPTION_TYPE_BOOL, Required: true}, // should display debug messgaes?
{Name: "index", Value: "true", Type: OPTION_TYPE_BOOL, Required: false}, // should display the index page (view/index.md)?
{Name: "api_url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // API URL for the website
{Name: "frontend_url", Value: "http://localhost:5173/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
{Name: "password", Value: "", Type: OPTION_TYPE_STR, Required: true}, // admin password
{Name: "host", Value: "0.0.0.0:7001", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on
{Name: "ip_header", Value: "X-Real-IP", Type: OPTION_TYPE_STR, Required: false}, // header that should be checked for obtaining the client IP
{Name: "interval", Value: "1h", Type: OPTION_TYPE_STR, Required: false}, // service status check interval
{Name: "timeout", Value: "15s", Type: OPTION_TYPE_STR, Required: false}, // timeout for the service status check
{Name: "limit", Value: "5s", Type: OPTION_TYPE_STR, Required: false}, // if the service responds slower than this limit, it will be marked as "slow"
}
return ""
c.Count = len(c.Options)
for i := 0; i < c.Count; i++ {
opt = &c.Options[i]
env_name = opt.Env()
if env_val, exists = os.LookupEnv(env_name); exists {
opt.Value = env_val
}
if opt.Value == "" && opt.Required {
return fmt.Errorf("please specify a value for the config option \"%s\" (\"%s\")", opt.Name, env_name)
}
if err = opt.Load(); err != nil {
return fmt.Errorf("failed to load option \"%s\" (\"%s\"): %s", opt.Name, env_name, err.Error())
}
}
return nil
}
func (c *Type) GetStr(name string) string {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_STR); err != nil {
return ""
}
return opt.TypeValue.Str
}
func (c *Type) GetBool(name string) bool {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_BOOL); err != nil {
return false
}
return opt.TypeValue.Bool
}
func (c *Type) GetURL(name string) *url.URL {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_URL); err != nil {
return nil
}
return opt.TypeValue.URL
}

49
api/config/option.go Normal file
View File

@ -0,0 +1,49 @@
package config
import (
"fmt"
"net/url"
"strings"
)
const (
OPTION_TYPE_STR = 0
OPTION_TYPE_BOOL = 1
OPTION_TYPE_URL = 2
)
type Option struct {
Name string
Value string
Required bool
Type uint8
TypeValue struct {
URL *url.URL
Str string
Bool bool
}
}
func (o *Option) Env() string {
return strings.ToUpper(fmt.Sprintf("API_%s", o.Name))
}
func (o *Option) Load() (err error) {
err = nil
switch o.Type {
case OPTION_TYPE_STR:
o.TypeValue.Str = o.Value
case OPTION_TYPE_BOOL:
o.TypeValue.Bool = "1" == o.Value || "true" == strings.ToLower(o.Value)
case OPTION_TYPE_URL:
o.TypeValue.URL, err = url.Parse(o.Value)
default:
return fmt.Errorf("invalid option type")
}
return err
}