232 lines
5.8 KiB
Go
232 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"slices"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.sour.is/xt/internal/otel"
|
|
"go.sour.is/xt/internal/uuid"
|
|
"go.yarn.social/lextwt"
|
|
)
|
|
|
|
type API struct {
|
|
app *appState
|
|
db db
|
|
hostname string
|
|
}
|
|
|
|
func (a *API) plain(w http.ResponseWriter, r *http.Request) {
|
|
reg := lextwt.NewTwtRegistry(mkPreambleDocs(a.hostname), nil)
|
|
reg.WriteTo(w)
|
|
}
|
|
|
|
func (a *API) conv(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := otel.Span(r.Context())
|
|
defer span.End()
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
hash := r.PathValue("hash")
|
|
// if (len(hash) < 6 || len(hash) > 8) && !notAny(hash, "abcdefghijklmnopqrstuvwxyz234567") {
|
|
// w.WriteHeader(http.StatusBadRequest)
|
|
// return
|
|
// }
|
|
|
|
limit := 100
|
|
if v, ok := strconv.Atoi(r.URL.Query().Get("limit")); ok == nil {
|
|
limit = v
|
|
}
|
|
|
|
var offset int64 = 0
|
|
if v, ok := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64); ok == nil {
|
|
offset = v
|
|
}
|
|
|
|
twts, offset, end, err := fetchConv(ctx, a.db, hash, limit, offset)
|
|
span.RecordError(err)
|
|
if err != nil {
|
|
http.Error(w, "ERR", 500)
|
|
return
|
|
}
|
|
|
|
preamble := mkPreamble(a.hostname, "", "/api/plain/conv/"+hash, limit, int64(len(twts)), offset, end)
|
|
reg := lextwt.NewTwtRegistry(preamble, twts)
|
|
reg.WriteTo(w)
|
|
}
|
|
|
|
func (a *API) mentions(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := otel.Span(r.Context())
|
|
defer span.End()
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
uri := r.URL.Query().Get("uri")
|
|
if uri == "" {
|
|
reg := lextwt.NewTwtRegistry(mkPreambleDocs(a.hostname), nil)
|
|
reg.WriteTo(w)
|
|
|
|
return
|
|
}
|
|
mention := uuid.UrlNS.UUID5(uri)
|
|
|
|
limit := 100
|
|
if v, ok := strconv.Atoi(r.URL.Query().Get("limit")); ok == nil {
|
|
limit = v
|
|
}
|
|
|
|
var offset int64 = 0
|
|
if v, ok := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64); ok == nil {
|
|
offset = v
|
|
}
|
|
|
|
twts, offset, end, err := fetchMentions(ctx, a.db, mention, limit, offset)
|
|
span.RecordError(err)
|
|
if err != nil {
|
|
http.Error(w, "ERR", 500)
|
|
return
|
|
}
|
|
|
|
preamble := mkPreamble(a.hostname, uri, "/api/plain/mentions", limit, int64(len(twts)), offset, end)
|
|
reg := lextwt.NewTwtRegistry(preamble, twts)
|
|
reg.WriteTo(w)
|
|
}
|
|
|
|
func (a *API) twt(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := otel.Span(r.Context())
|
|
defer span.End()
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
uri := r.URL.Query().Get("uri")
|
|
|
|
limit := 100
|
|
if v, ok := strconv.Atoi(r.URL.Query().Get("limit")); ok == nil {
|
|
limit = v
|
|
}
|
|
limit = min(100, max(1, limit))
|
|
|
|
var offset int64 = 0
|
|
if v, ok := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64); ok == nil {
|
|
offset = v
|
|
}
|
|
|
|
twts, offset, end, err := fetchTwts(ctx, a.db, uri, limit, offset)
|
|
span.RecordError(err)
|
|
if err != nil {
|
|
http.Error(w, "ERR", 500)
|
|
return
|
|
}
|
|
|
|
preamble := mkPreamble(a.hostname, uri, "/api/plain/twt", limit, int64(len(twts)), offset, end)
|
|
|
|
reg := lextwt.NewTwtRegistry(preamble, twts)
|
|
reg.WriteTo(w)
|
|
}
|
|
|
|
func (a *API) users(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := otel.Span(r.Context())
|
|
defer span.End()
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
uri := r.URL.Query().Get("uri")
|
|
q := r.URL.Query().Get("q")
|
|
|
|
twts, err := fetchUsers(ctx, a.db, uri, q)
|
|
if err != nil {
|
|
http.Error(w, "ERR", 500)
|
|
return
|
|
}
|
|
|
|
reg := lextwt.NewTwtRegistry(mkPreambleDocs(a.hostname), twts)
|
|
reg.WriteTo(w)
|
|
}
|
|
|
|
func (a *API) queue(w http.ResponseWriter, r *http.Request) {
|
|
lis := slices.Collect(a.app.queue.Iter())
|
|
sort.Slice(lis, func(i, j int) bool {
|
|
return lis[i].NextScanOn.Time.Before(lis[j].LastScanOn.Time)
|
|
})
|
|
for _, feed := range lis {
|
|
fmt.Fprintln(w, feed.State, feed.NextScanOn.Time.Format(time.RFC3339), feed.Nick, feed.URI)
|
|
}
|
|
}
|
|
|
|
func notAny(s string, chars string) bool {
|
|
for _, c := range s {
|
|
if !strings.ContainsRune(chars, c) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func mkqry(uri string, limit int, offset int64) string {
|
|
qry := make([]string, 0, 3)
|
|
|
|
if uri != "" {
|
|
qry = append(qry, "uri="+uri)
|
|
}
|
|
|
|
if limit != 100 {
|
|
qry = append(qry, fmt.Sprint("limit=", limit))
|
|
}
|
|
|
|
if offset != 0 {
|
|
qry = append(qry, fmt.Sprint("offset=", offset))
|
|
}
|
|
|
|
if len(qry) == 0 {
|
|
return ""
|
|
}
|
|
|
|
return "?" + strings.Join(qry, "&")
|
|
}
|
|
|
|
func add(preamble lextwt.Comments, text string, v ...any) lextwt.Comments {
|
|
if len(v) > 0 {
|
|
text = fmt.Sprintf(text, v...)
|
|
}
|
|
return append(preamble, lextwt.NewComment("# "+text))
|
|
}
|
|
func addKey(preamble lextwt.Comments, key, value string, v ...any) lextwt.Comments {
|
|
if len(v) > 0 {
|
|
value = fmt.Sprintf(value, v...)
|
|
}
|
|
comment := fmt.Sprintf("# %s = %s", key, value)
|
|
return append(preamble, lextwt.NewCommentValue(comment, key, value))
|
|
}
|
|
|
|
func mkPreamble(hostname, uri, path string, limit int, length, offset, end int64) lextwt.Comments {
|
|
preamble := addKey(mkPreambleDocs(hostname), "twt range", "1 %d", end)
|
|
preamble = addKey(preamble, "self", "%s%s%s", hostname, path, mkqry(uri, limit, offset))
|
|
if next := offset + length; next < end {
|
|
preamble = addKey(preamble, "next", "%s%s%s", hostname, path, mkqry(uri, limit, next))
|
|
}
|
|
if prev := offset - int64(limit); prev > 0 {
|
|
preamble = addKey(preamble, "prev", "%s%s%s", hostname, path, mkqry(uri, limit, prev))
|
|
}
|
|
return preamble
|
|
}
|
|
|
|
var mkPreambleDocs = func(hostname string) lextwt.Comments {
|
|
c := add(nil, iAmTheWatcher)
|
|
c = add(c, "")
|
|
c = add(c, "Usage:")
|
|
c = add(c, " %s/api/plain/users View list of users and latest twt date.", hostname)
|
|
c = add(c, " %s/api/plain/twt View all twts.", hostname)
|
|
c = add(c, " %s/api/plain/mentions?uri=:uri View all mentions for uri.", hostname)
|
|
c = add(c, " %s/api/plain/conv/:hash View all twts for a conversation subject.", hostname)
|
|
c = add(c, "")
|
|
c = add(c, "Options:")
|
|
c = add(c, " uri Filter to show a specific users twts.")
|
|
c = add(c, " offset Start index for quey.")
|
|
c = add(c, " limit Count of items to return (going back in time).")
|
|
return add(c, "")
|
|
}
|