Files
xt/app.go

193 lines
3.4 KiB
Go
Raw Normal View History

2024-11-10 13:23:00 -07:00
package main
import (
2025-02-15 16:12:42 -07:00
"context"
2024-11-10 13:23:00 -07:00
"database/sql"
"fmt"
2025-03-27 16:52:01 -06:00
"io"
"net/http"
2024-11-10 13:23:00 -07:00
"os"
2025-03-13 22:36:24 -06:00
"runtime/debug"
"strconv"
2024-11-10 13:23:00 -07:00
"strings"
_ "embed"
_ "github.com/mattn/go-sqlite3"
2025-02-24 17:28:09 -07:00
"github.com/uptrace/opentelemetry-go-extra/otelsql"
2025-03-13 22:36:24 -06:00
"go.opentelemetry.io/otel/attribute"
2025-02-24 17:28:09 -07:00
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
2025-03-13 22:36:24 -06:00
"go.opentelemetry.io/otel/trace"
2025-03-27 16:35:05 -06:00
"go.sour.is/xt/internal/console"
2025-02-24 17:28:09 -07:00
"go.sour.is/xt/internal/otel"
2024-11-10 13:23:00 -07:00
"go.yarn.social/lextwt"
2024-11-11 19:13:34 -07:00
"go.yarn.social/types"
2025-02-24 17:28:09 -07:00
"golang.org/x/sync/errgroup"
2024-11-10 13:23:00 -07:00
)
2025-03-27 16:35:05 -06:00
func run(ctx context.Context, c *console.C[args]) error {
ctx, span := otel.Span(ctx)
2025-02-24 17:28:09 -07:00
defer span.End()
2025-03-13 22:36:24 -06:00
bi, _ := debug.ReadBuildInfo()
span.AddEvent(name, trace.WithAttributes(attribute.String("version", bi.Main.Version)))
2024-11-10 13:23:00 -07:00
a := c.Args()
2025-02-15 16:12:42 -07:00
app := &appState{
2025-03-27 16:35:05 -06:00
args: a,
C: c,
2025-02-15 16:12:42 -07:00
queue: FibHeap(func(a, b *Feed) bool {
2025-03-13 22:36:24 -06:00
return a.NextScanOn.Time.Before(b.NextScanOn.Time)
2025-02-15 16:12:42 -07:00
}),
2024-11-10 13:23:00 -07:00
}
2025-02-15 16:12:42 -07:00
// Setup DB
err := func(ctx context.Context) error {
2025-02-24 17:28:09 -07:00
ctx, span := otel.Span(ctx)
defer span.End()
2025-03-13 22:36:24 -06:00
db, err := app.DB(ctx)
2024-11-10 13:23:00 -07:00
if err != nil {
2025-02-15 16:12:42 -07:00
return err
2024-11-10 13:23:00 -07:00
}
2025-02-15 16:12:42 -07:00
defer db.Close()
2024-11-10 13:23:00 -07:00
2025-02-15 16:12:42 -07:00
for _, stmt := range strings.Split(initSQL, ";") {
_, err = db.ExecContext(ctx, stmt)
2024-11-10 13:23:00 -07:00
if err != nil {
2025-02-15 16:12:42 -07:00
return err
2024-11-10 13:23:00 -07:00
}
}
2025-02-15 16:12:42 -07:00
return nil
2025-02-24 17:28:09 -07:00
}(ctx)
2025-02-15 16:12:42 -07:00
if err != nil {
return err
}
2024-11-11 19:13:34 -07:00
2025-02-15 16:12:42 -07:00
// Seed File
2025-02-24 17:28:09 -07:00
err = func(ctx context.Context) error {
ctx, span := otel.Span(ctx)
defer span.End()
2025-03-29 17:09:18 -06:00
db, err := app.DB(ctx)
if err != nil {
return err
}
defer db.Close()
2025-03-27 16:52:01 -06:00
var inFile io.Reader
if a.baseFeed != "" {
f, err := os.Open(a.baseFeed)
if err != nil {
return err
}
defer f.Close()
2025-03-29 17:09:18 -06:00
err = storeRegistry(ctx, db, f)
if err != nil {
return err
}
}
2025-03-31 10:49:36 -06:00
2025-03-29 17:09:18 -06:00
if a.URI != "" {
2025-03-27 16:52:01 -06:00
res, err := http.Get(a.URI)
if err != nil {
return err
}
inFile = res.Body
defer res.Body.Close()
2025-03-29 17:09:18 -06:00
twtfile, err := lextwt.ParseFile(inFile, &types.Twter{
Nick: a.Nick,
URI: a.URI,
})
if err != nil {
return fmt.Errorf("%w: %w", ErrParseFailed, err)
}
2025-03-31 10:49:36 -06:00
2025-03-29 17:09:18 -06:00
return storeFeed(ctx, db, twtfile)
2024-11-20 09:05:05 -07:00
}
2025-03-29 17:09:18 -06:00
return nil
2025-02-24 17:28:09 -07:00
}(ctx)
2024-11-20 09:05:05 -07:00
if err != nil {
return err
}
2025-02-24 17:28:09 -07:00
wg, ctx := errgroup.WithContext(ctx)
2025-03-13 22:36:24 -06:00
wg.Go(func() error {
2025-03-27 16:35:05 -06:00
return feedRefreshProcessor(ctx, app)
2025-03-13 22:36:24 -06:00
})
2025-03-27 16:35:05 -06:00
go httpServer(ctx, app)
2024-11-20 09:05:05 -07:00
2025-03-13 22:36:24 -06:00
err = wg.Wait()
if err != nil {
return err
}
2025-03-27 16:35:05 -06:00
return ctx.Err()
2024-11-20 09:05:05 -07:00
}
2025-02-15 16:12:42 -07:00
type appState struct {
args args
queue *fibHeap[Feed]
2025-03-27 16:35:05 -06:00
*console.C[args]
2025-02-15 16:12:42 -07:00
}
2024-11-11 19:13:34 -07:00
2025-03-13 22:36:24 -06:00
type db struct {
*sql.DB
Version string
Params map[string]string
MaxLength int
MaxVariableNumber int
}
func (app *appState) DB(ctx context.Context) (db, error) {
2025-02-24 17:28:09 -07:00
// return sql.Open(app.args.dbtype, app.args.dbfile)
2025-03-13 22:36:24 -06:00
var err error
db := db{Params: make(map[string]string)}
db.DB, err = otelsql.Open(app.args.dbtype, app.args.dbfile,
2025-02-24 17:28:09 -07:00
otelsql.WithAttributes(semconv.DBSystemSqlite),
otelsql.WithDBName("xt"))
2025-03-13 22:36:24 -06:00
if err != nil {
return db, err
}
rows, err := db.DB.QueryContext(ctx, `select sqlite_version()`)
if err != nil {
return db, err
}
if rows.Next() {
rows.Scan(&db.Version)
}
if err = rows.Err(); err != nil {
return db, err
}
rows, err = db.DB.QueryContext(ctx, `pragma compile_options`)
if err != nil {
return db, err
}
for rows.Next() {
var key, value string
rows.Scan(&key)
key, value, _ = strings.Cut(key, "=")
db.Params[key] = value
}
if err = rows.Err(); err != nil {
return db, err
}
if m, ok := db.Params["MAX_VARIABLE_NUMBER"]; ok {
db.MaxVariableNumber, _ = strconv.Atoi(m)
}
if m, ok := db.Params["MAX_LENGTH"]; ok {
db.MaxLength, _ = strconv.Atoi(m)
}
return db, err
2025-02-15 16:12:42 -07:00
}