This commit is contained in:
195
api/api.go
Normal file
195
api/api.go
Normal file
@ -0,0 +1,195 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.sati.ac/sati.ac/bridge/config"
|
||||
"git.sati.ac/sati.ac/sati-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type TaskState uint
|
||||
|
||||
const (
|
||||
StateProcessing TaskState = iota
|
||||
StateSuccess
|
||||
StateError
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
State TaskState
|
||||
Entity *sati.TaskEntity
|
||||
Result any
|
||||
CreateTime int64
|
||||
EndTime int64
|
||||
}
|
||||
|
||||
type RegistryStats struct {
|
||||
Total uint32
|
||||
Success uint32
|
||||
Error uint32
|
||||
Processing uint32
|
||||
}
|
||||
|
||||
type TaskRegistry struct {
|
||||
mu *sync.RWMutex
|
||||
lifetime time.Duration
|
||||
tasks map[uint32]*Task
|
||||
idCounter uint32
|
||||
api *sati.Api
|
||||
stats RegistryStats
|
||||
}
|
||||
|
||||
func NewTaskRegistry(api *sati.Api, lifetime time.Duration) *TaskRegistry {
|
||||
return &TaskRegistry{
|
||||
mu: &sync.RWMutex{},
|
||||
tasks: map[uint32]*Task{},
|
||||
api: api,
|
||||
lifetime: lifetime,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TaskRegistry) CreateTask(task sati.AnyTask) uint32 {
|
||||
t.mu.Lock()
|
||||
t.idCounter++
|
||||
id := t.idCounter
|
||||
entry := &Task{
|
||||
State: StateProcessing,
|
||||
CreateTime: time.Now().Unix(),
|
||||
}
|
||||
t.stats.Total++
|
||||
t.stats.Processing++
|
||||
t.tasks[id] = entry
|
||||
t.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
result := task.Result()
|
||||
entity, err := t.api.Solve(task, result)
|
||||
|
||||
t.mu.Lock()
|
||||
t.stats.Processing--
|
||||
if err != nil {
|
||||
t.stats.Error++
|
||||
entry.State = StateError
|
||||
} else {
|
||||
t.stats.Success++
|
||||
entry.State = StateSuccess
|
||||
entry.Entity = entity
|
||||
entry.Result = result
|
||||
}
|
||||
entry.EndTime = time.Now().Unix()
|
||||
t.mu.Unlock()
|
||||
|
||||
time.Sleep(t.lifetime)
|
||||
t.mu.Lock()
|
||||
delete(t.tasks, id)
|
||||
t.mu.Unlock()
|
||||
}()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (t *TaskRegistry) Stats() RegistryStats {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.stats
|
||||
}
|
||||
|
||||
func (t *TaskRegistry) Get(id uint32) *Task {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
task := t.tasks[id]
|
||||
if task == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := *task
|
||||
return &cloned
|
||||
}
|
||||
|
||||
type ApiContext struct {
|
||||
Config *config.Config
|
||||
Api *sati.Api
|
||||
Registry *TaskRegistry
|
||||
Logger *logrus.Logger
|
||||
Server *ApiServer
|
||||
}
|
||||
|
||||
type ApiHandler interface {
|
||||
Name() string
|
||||
Domains() []string
|
||||
ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type ApiServer struct {
|
||||
handlers map[string]ApiHandler
|
||||
domains map[string]ApiHandler
|
||||
ctx *ApiContext
|
||||
}
|
||||
|
||||
func (a *ApiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
a.ctx.Logger.WithFields(logrus.Fields{
|
||||
"method": r.Method,
|
||||
"host": r.Host,
|
||||
"path": r.URL.Path,
|
||||
}).Debug("request")
|
||||
|
||||
handler, ok := a.domains[r.Host]
|
||||
if !ok {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("domain not found"))
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (a *ApiServer) GetDomains() []string {
|
||||
domains := make([]string, 0, len(a.domains))
|
||||
|
||||
for domain := range a.domains {
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
func NewApiServer(ctx *ApiContext) *ApiServer {
|
||||
handlers := []ApiHandler{
|
||||
newStatsApi(ctx),
|
||||
newAntigateV2Api(ctx),
|
||||
newRuCaptchaApi(ctx),
|
||||
}
|
||||
|
||||
server := &ApiServer{
|
||||
handlers: map[string]ApiHandler{},
|
||||
domains: map[string]ApiHandler{},
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
for _, handler := range handlers {
|
||||
server.handlers[handler.Name()] = handler
|
||||
for _, domain := range handler.Domains() {
|
||||
server.domains[domain] = handler
|
||||
}
|
||||
}
|
||||
|
||||
for domain, handlerName := range ctx.Config.ExtraDomains {
|
||||
handler, ok := server.handlers[handlerName]
|
||||
if !ok {
|
||||
ctx.Logger.WithFields(logrus.Fields{
|
||||
"domain": domain,
|
||||
"handler": handlerName,
|
||||
}).Warn("extraDomains: handler not found, ignoring it")
|
||||
continue
|
||||
}
|
||||
|
||||
server.domains[domain] = handler
|
||||
}
|
||||
|
||||
ctx.Server = server
|
||||
return server
|
||||
}
|
||||
Reference in New Issue
Block a user