2020-04-21 12:24:16 +02:00
|
|
|
//
|
|
|
|
// lurkcoin configuration
|
|
|
|
// Copyright © 2020 by luk3yx
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-02-22 07:19:35 +01:00
|
|
|
"github.com/luk3yx/lurkcoin-core/lurkcoin"
|
|
|
|
"github.com/luk3yx/lurkcoin-core/lurkcoin/databases"
|
2020-04-21 12:24:16 +02:00
|
|
|
"gopkg.in/yaml.v2"
|
2020-04-22 10:45:19 +02:00
|
|
|
"io/ioutil"
|
2020-04-21 12:24:16 +02:00
|
|
|
"log"
|
2020-06-15 07:44:51 +02:00
|
|
|
"net"
|
2020-04-21 12:24:16 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2021-02-08 06:28:38 +01:00
|
|
|
"path/filepath"
|
2020-04-21 12:24:16 +02:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
// The name of this service (for example "lurkcoin"). This is also used as
|
|
|
|
// the default server name for the v2 API.
|
|
|
|
Name string `yaml:"name"`
|
|
|
|
|
2020-06-15 07:44:51 +02:00
|
|
|
// The network protocol to use when binding to the socket. Defaults to
|
|
|
|
// "tcp", can be set to "unix" for example.
|
|
|
|
NetworkProtocol string `yaml:"network_protocol"`
|
|
|
|
|
2020-04-21 12:24:16 +02:00
|
|
|
// The address to bind to (optional) and port.
|
|
|
|
Address string `yaml:"address"`
|
|
|
|
Port uint16 `yaml:"port"`
|
|
|
|
|
|
|
|
// An optional logfile
|
|
|
|
Logfile string `yaml:"logfile"`
|
|
|
|
|
|
|
|
Database struct {
|
|
|
|
Type string `yaml:"type"`
|
|
|
|
Location string `yaml:"location"`
|
|
|
|
Options map[string]string `yaml:"options"`
|
|
|
|
} `yaml:"database"`
|
|
|
|
|
|
|
|
// TLS
|
|
|
|
TLS struct {
|
|
|
|
Enable bool `yaml:"enable"`
|
|
|
|
CertFile string `yaml:"cert_file"`
|
|
|
|
KeyFile string `yaml:"key_file"`
|
|
|
|
} `yaml:"tls"`
|
|
|
|
|
|
|
|
// Admin pages
|
|
|
|
AdminPages struct {
|
|
|
|
Enable bool `yaml:"enable"`
|
|
|
|
Users AdminLoginDetails `yaml:"users"`
|
|
|
|
} `yaml:"admin_pages"`
|
|
|
|
|
|
|
|
// HTTP redirects
|
|
|
|
Redirects map[string]string `yaml:"redirects"`
|
|
|
|
|
|
|
|
// The minimum HTTPS API version to support.
|
|
|
|
MinAPIVersion uint8 `yaml:"min_api_version"`
|
2020-04-22 10:45:19 +02:00
|
|
|
|
|
|
|
// Suppresses any HTTP-related logs such as TLS handshake errors.
|
|
|
|
SuppressHTTPLogs bool `yaml:"suppress_http_logs"`
|
|
|
|
|
|
|
|
// Disables HTTP keep-alives.
|
|
|
|
DisableHTTPKeepAlives bool `yaml:"disable_http_keepalives"`
|
2020-04-21 12:24:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func LoadConfig(filename string) (*Config, error) {
|
|
|
|
f, err := os.OpenFile(filename, os.O_RDONLY, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
var config Config
|
|
|
|
decoder := yaml.NewDecoder(f)
|
|
|
|
decoder.SetStrict(true)
|
|
|
|
err = decoder.Decode(&config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.Name == "lurkcoin" {
|
|
|
|
log.Println("Warning: The selected server name already exists!")
|
|
|
|
}
|
|
|
|
return &config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func OpenDatabase(config *Config) (lurkcoin.Database, error) {
|
|
|
|
return databases.OpenDatabase(
|
|
|
|
config.Database.Type,
|
|
|
|
config.Database.Location,
|
|
|
|
config.Database.Options,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func StartServer(config *Config) {
|
|
|
|
lurkcoin.SeedPRNG()
|
|
|
|
lurkcoin.PrintASCIIArt()
|
|
|
|
log.Printf("Supported database types: %s",
|
|
|
|
strings.Join(databases.GetSupportedDatabaseTypes(), ", "))
|
|
|
|
db, err := OpenDatabase(config)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
router := MakeHTTPRouter(db, config)
|
|
|
|
|
2020-06-15 07:44:51 +02:00
|
|
|
var address, networkProtocol, urlAddress string
|
|
|
|
switch config.NetworkProtocol {
|
|
|
|
case "", "tcp":
|
|
|
|
if config.Port == 0 {
|
|
|
|
address = config.Address
|
|
|
|
} else {
|
|
|
|
address = fmt.Sprintf("%s:%d", address, config.Port)
|
|
|
|
}
|
|
|
|
networkProtocol = "tcp"
|
|
|
|
urlAddress = address
|
|
|
|
if address != "" && address[0] == ':' {
|
|
|
|
urlAddress = "[::]" + urlAddress
|
|
|
|
}
|
|
|
|
case "unix":
|
|
|
|
address = config.Address
|
|
|
|
networkProtocol = "unix"
|
|
|
|
urlAddress = "unix:" + address + ":"
|
|
|
|
if config.Port != 0 {
|
|
|
|
log.Fatal("The port option is invalid with UNIX sockets.")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
log.Fatalf("Unrecognised network protocol: %q", config.NetworkProtocol)
|
2020-04-21 12:24:16 +02:00
|
|
|
}
|
2020-06-15 07:44:51 +02:00
|
|
|
|
2020-04-21 12:24:16 +02:00
|
|
|
if config.TLS.Enable {
|
|
|
|
log.Printf("Starting server on https://%s/", urlAddress)
|
|
|
|
} else {
|
|
|
|
log.Printf("Starting server on http://%s/", urlAddress)
|
|
|
|
}
|
|
|
|
|
2020-06-15 07:55:43 +02:00
|
|
|
// Remove any socket file that already exists
|
2021-02-08 06:28:38 +01:00
|
|
|
var changeSocketPermissions bool
|
2020-06-15 07:55:43 +02:00
|
|
|
if networkProtocol == "unix" {
|
|
|
|
os.Remove(address)
|
2021-02-08 06:28:38 +01:00
|
|
|
|
|
|
|
// Only call chmod if no other users can write to the directory
|
|
|
|
if stat, err := os.Stat(filepath.Dir(address)); err == nil {
|
2021-02-22 07:19:35 +01:00
|
|
|
changeSocketPermissions = stat.Mode()&022 == 0
|
2021-02-08 06:28:38 +01:00
|
|
|
}
|
2020-06-15 07:55:43 +02:00
|
|
|
}
|
|
|
|
|
2020-06-15 07:44:51 +02:00
|
|
|
// Bind to the address
|
|
|
|
var ln net.Listener
|
|
|
|
ln, err = net.Listen(networkProtocol, address)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-06-15 07:55:43 +02:00
|
|
|
// Change permissions on the UNIX socket
|
2021-02-08 06:28:38 +01:00
|
|
|
if changeSocketPermissions {
|
2020-06-15 07:55:43 +02:00
|
|
|
if err := os.Chmod(address, 0777); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 07:44:51 +02:00
|
|
|
// Switch to the logfile
|
2020-04-21 12:24:16 +02:00
|
|
|
if config.Logfile != "" {
|
|
|
|
f, err := os.OpenFile(
|
|
|
|
config.Logfile,
|
|
|
|
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
|
|
|
0600,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
log.Printf("Using logfile %#v.", config.Logfile)
|
|
|
|
log.SetOutput(f)
|
|
|
|
}
|
|
|
|
|
2020-04-22 10:45:19 +02:00
|
|
|
// Suppress HTTP logs.
|
2020-04-21 12:24:16 +02:00
|
|
|
server := &http.Server{Addr: address, Handler: router}
|
2020-04-22 10:45:19 +02:00
|
|
|
if config.SuppressHTTPLogs {
|
|
|
|
server.ErrorLog = log.New(ioutil.Discard, "", 0)
|
|
|
|
}
|
2020-04-21 12:24:16 +02:00
|
|
|
|
|
|
|
// My laptop doesn't work nicely with Keep-Alive.
|
2020-04-22 10:45:19 +02:00
|
|
|
if config.DisableHTTPKeepAlives {
|
|
|
|
server.SetKeepAlivesEnabled(false)
|
|
|
|
}
|
2020-04-21 12:24:16 +02:00
|
|
|
|
2020-06-15 07:44:51 +02:00
|
|
|
// Serve the webpage
|
2020-04-21 12:24:16 +02:00
|
|
|
if config.TLS.Enable {
|
2020-06-15 07:44:51 +02:00
|
|
|
err = server.ServeTLS(ln, config.TLS.CertFile, config.TLS.KeyFile)
|
2020-04-21 12:24:16 +02:00
|
|
|
} else {
|
2020-06-15 07:44:51 +02:00
|
|
|
err = server.Serve(ln)
|
2020-04-21 12:24:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|