201 lines
3.7 KiB
Go
201 lines
3.7 KiB
Go
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
|
|
config *config.Config
|
|
tasks map[uint32]*Task
|
|
idCounter uint32
|
|
api *sati.Api
|
|
stats RegistryStats
|
|
}
|
|
|
|
func NewTaskRegistry(api *sati.Api, config *config.Config) *TaskRegistry {
|
|
return &TaskRegistry{
|
|
mu: &sync.RWMutex{},
|
|
tasks: map[uint32]*Task{},
|
|
api: api,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
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(time.Millisecond * time.Duration(t.config.TaskLifetime))
|
|
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 {
|
|
if t.config.TaskGetDelay != 0 {
|
|
time.Sleep(time.Millisecond * time.Duration(t.config.TaskGetDelay))
|
|
}
|
|
|
|
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),
|
|
newCapSolverApi(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
|
|
}
|