147 lines
2.8 KiB
Go
147 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
|
|
"go.opentelemetry.io/otel/metric"
|
|
"go.sour.is/xt/internal/otel"
|
|
)
|
|
|
|
const name = "go.sour.is/xt"
|
|
|
|
var m_up metric.Int64Gauge
|
|
|
|
func main() {
|
|
dotEnv() // load .env
|
|
|
|
ctx, console := newConsole(args{
|
|
dbtype: env("XT_DBTYPE", "sqlite3"),
|
|
dbfile: env("XT_DBFILE", "file:twt.db"),
|
|
baseFeed: env("XT_BASE_FEED", "feed"),
|
|
Nick: env("XT_NICK", "xuu"),
|
|
URI: env("XT_URI", "https://txt.sour.is/user/xuu/twtxt.txt"),
|
|
Listen: env("XT_LISTEN", ":8080"),
|
|
})
|
|
|
|
finish, err := otel.Init(ctx, name)
|
|
console.IfFatal(err)
|
|
console.AddCancel(finish)
|
|
|
|
m_up, err = otel.Meter().Int64Gauge("up")
|
|
console.IfFatal(err)
|
|
|
|
m_up.Record(ctx, 1)
|
|
defer m_up.Record(context.Background(), 0)
|
|
|
|
err = run(console)
|
|
if !errors.Is(err, context.Canceled) {
|
|
console.IfFatal(err)
|
|
}
|
|
}
|
|
|
|
type console struct {
|
|
io.Reader
|
|
io.Writer
|
|
err io.Writer
|
|
context.Context
|
|
abort func()
|
|
cancelfns []func(context.Context) error
|
|
}
|
|
|
|
func newConsole(args args) (context.Context, *console) {
|
|
ctx := context.Background()
|
|
ctx, abort := context.WithCancel(ctx)
|
|
ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
|
|
go func() { <-ctx.Done(); stop() }() // restore interrupt function
|
|
|
|
console := &console{Reader: os.Stdin, Writer: os.Stdout, err: os.Stderr, Context: ctx, abort: abort}
|
|
console.Set("console", console)
|
|
console.Set("args", args)
|
|
return ctx, console
|
|
}
|
|
|
|
func (c *console) Args() args {
|
|
v, ok := c.Get("args").(args)
|
|
if !ok {
|
|
return args{}
|
|
}
|
|
return v
|
|
}
|
|
func (c *console) Shutdown() error {
|
|
fmt.Fprintln(c.err, "shutting down ", len(c.cancelfns), " cancel functions...")
|
|
defer fmt.Fprintln(c.err, "done")
|
|
|
|
c.abort()
|
|
var err error
|
|
for _, fn := range c.cancelfns {
|
|
err = errors.Join(err, fn(c.Context))
|
|
}
|
|
return err
|
|
}
|
|
func (c *console) AddCancel(fn func(context.Context) error) { c.cancelfns = append(c.cancelfns, fn) }
|
|
|
|
func (c *console) IfFatal(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
fmt.Fprintln(c.err, err)
|
|
c.abort()
|
|
os.Exit(1)
|
|
}
|
|
|
|
type contextKey struct{ name string }
|
|
|
|
func (c *console) Set(name string, value any) {
|
|
c.Context = context.WithValue(c.Context, contextKey{name}, value)
|
|
}
|
|
|
|
func (c *console) Get(name string) any {
|
|
return c.Context.Value(contextKey{name})
|
|
}
|
|
|
|
type args struct {
|
|
dbtype string
|
|
dbfile string
|
|
baseFeed string
|
|
Nick string
|
|
URI string
|
|
Listen string
|
|
}
|
|
|
|
func env(key, def string) string {
|
|
if v, ok := os.LookupEnv(key); ok {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func dotEnv() {
|
|
fd, err := os.Open(".env")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
scan := bufio.NewScanner(fd)
|
|
|
|
for scan.Scan() {
|
|
line := scan.Text()
|
|
|
|
if strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
key, val, ok := strings.Cut(line, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
os.Setenv(strings.TrimSpace(key), strings.TrimSpace(val))
|
|
}
|
|
}
|