mirror of
https://github.com/parkan/go-hauk.git
synced 2026-05-08 16:47:46 +02:00
add api endpoints
This commit is contained in:
90
api/adopt.go
Normal file
90
api/adopt.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
func (s *Server) handleAdopt(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sid := r.FormValue("sid")
|
||||
nickname := r.FormValue("nic")
|
||||
aid := r.FormValue("aid")
|
||||
pinStr := r.FormValue("pin")
|
||||
|
||||
if sid == "" || nickname == "" || aid == "" || pinStr == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := model.LoadSession(ctx, s.store, sid, s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
shareType, err := model.LoadShareType(ctx, s.store, aid)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Share not found!")
|
||||
return
|
||||
}
|
||||
|
||||
if shareType != model.ShareTypeAlone {
|
||||
fmt.Fprintln(w, "Group shares cannot be adopted!")
|
||||
return
|
||||
}
|
||||
|
||||
share, err := model.LoadSoloShare(ctx, s.store, aid, s.cfg.PublicURL)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Share not found!")
|
||||
return
|
||||
}
|
||||
|
||||
if !share.Adoptable() {
|
||||
fmt.Fprintln(w, "Share adoption not allowed!")
|
||||
return
|
||||
}
|
||||
|
||||
hostSession, err := model.LoadSession(ctx, s.store, share.Host(), s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
if hostSession.Encrypted() {
|
||||
fmt.Fprintln(w, "End-to-end encrypted shares cannot be adopted!")
|
||||
return
|
||||
}
|
||||
|
||||
pin, _ := strconv.Atoi(pinStr)
|
||||
target, err := model.LoadGroupShareByPin(ctx, s.store, pin, s.cfg.PublicURL)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
target.AddHost(nickname, share.Host())
|
||||
if err := target.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
hostSession.AddTarget(target.ID())
|
||||
if err := hostSession.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_ = session
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
}
|
||||
211
api/create.go
Normal file
211
api/create.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
const (
|
||||
shareModeCreateAlone = 0
|
||||
shareModeCreateGroup = 1
|
||||
shareModeJoinGroup = 2
|
||||
)
|
||||
|
||||
var linkIDRe = regexp.MustCompile(`^[\w-]+$`)
|
||||
|
||||
func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
dur := r.FormValue("dur")
|
||||
interval := r.FormValue("int")
|
||||
if dur == "" || interval == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
user := r.FormValue("usr")
|
||||
pass := r.FormValue("pwd")
|
||||
if err := s.auth.Authenticate(user, pass); err != nil {
|
||||
fmt.Fprintln(w, "Incorrect password!")
|
||||
return
|
||||
}
|
||||
|
||||
d, _ := strconv.Atoi(dur)
|
||||
i, _ := strconv.ParseFloat(interval, 64)
|
||||
mod, _ := strconv.Atoi(r.FormValue("mod"))
|
||||
adoptable := r.FormValue("ado") == "1"
|
||||
encrypted := r.FormValue("e2e") == "1"
|
||||
salt := r.FormValue("salt")
|
||||
customLink := r.FormValue("lid")
|
||||
nickname := r.FormValue("nic")
|
||||
pin, _ := strconv.Atoi(r.FormValue("pin"))
|
||||
|
||||
if d > s.cfg.MaxDuration {
|
||||
fmt.Fprintln(w, "Share duration exceeds maximum configured!")
|
||||
return
|
||||
}
|
||||
if i > float64(s.cfg.MaxDuration) {
|
||||
fmt.Fprintln(w, "Interval exceeds maximum configured!")
|
||||
return
|
||||
}
|
||||
if i < s.cfg.MinInterval {
|
||||
fmt.Fprintln(w, "Interval is too short!")
|
||||
return
|
||||
}
|
||||
|
||||
if (mod == shareModeCreateGroup || mod == shareModeJoinGroup) && encrypted {
|
||||
fmt.Fprintln(w, "End-to-end encryption is not supported for group shares.")
|
||||
return
|
||||
}
|
||||
|
||||
if (mod == shareModeCreateGroup || mod == shareModeJoinGroup) && nickname == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
if mod == shareModeJoinGroup && pin == 0 {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
if encrypted && salt == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
expire := time.Now().Add(time.Duration(d) * time.Second)
|
||||
|
||||
session, err := model.NewSession(s.store, s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
session.SetExpire(expire)
|
||||
session.SetInterval(i)
|
||||
if encrypted {
|
||||
session.SetEncrypted(true, salt)
|
||||
}
|
||||
|
||||
linkGen := func() (string, error) {
|
||||
return s.linkgen.Generate(ctx)
|
||||
}
|
||||
|
||||
switch mod {
|
||||
case shareModeCreateAlone:
|
||||
share, err := model.NewSoloShare(s.store, s.cfg.PublicURL, linkGen)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if customLink != "" && linkIDRe.MatchString(customLink) {
|
||||
if err := s.tryCustomLink(ctx, share, customLink); err == nil {
|
||||
share.SetID(customLink)
|
||||
}
|
||||
}
|
||||
|
||||
share.SetAdoptable(adoptable)
|
||||
share.SetHost(session.ID())
|
||||
share.SetExpire(expire)
|
||||
if err := share.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session.AddTarget(share.ID())
|
||||
if err := session.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
fmt.Fprintln(w, session.ID())
|
||||
fmt.Fprintln(w, share.ViewLink())
|
||||
fmt.Fprintln(w, share.ID())
|
||||
|
||||
case shareModeCreateGroup:
|
||||
share, err := model.NewGroupShare(s.store, s.cfg.PublicURL, linkGen)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if customLink != "" && linkIDRe.MatchString(customLink) {
|
||||
if err := s.tryCustomLink(ctx, share, customLink); err == nil {
|
||||
share.SetID(customLink)
|
||||
}
|
||||
}
|
||||
|
||||
share.AddHost(nickname, session.ID())
|
||||
share.SetExpire(expire)
|
||||
if err := share.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session.AddTarget(share.ID())
|
||||
if err := session.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
fmt.Fprintln(w, session.ID())
|
||||
fmt.Fprintln(w, share.ViewLink())
|
||||
fmt.Fprintln(w, share.Pin())
|
||||
fmt.Fprintln(w, share.ID())
|
||||
|
||||
case shareModeJoinGroup:
|
||||
share, err := model.LoadGroupShareByPin(ctx, s.store, pin, s.cfg.PublicURL)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Invalid group PIN!")
|
||||
return
|
||||
}
|
||||
|
||||
share.AddHost(nickname, session.ID())
|
||||
if err := share.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session.AddTarget(share.ID())
|
||||
if err := session.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
fmt.Fprintln(w, session.ID())
|
||||
fmt.Fprintln(w, share.ViewLink())
|
||||
fmt.Fprintln(w, share.ID())
|
||||
|
||||
default:
|
||||
fmt.Fprintln(w, "Unsupported share mode!")
|
||||
}
|
||||
}
|
||||
|
||||
type customLinkSetter interface {
|
||||
SetID(string)
|
||||
}
|
||||
|
||||
func (s *Server) tryCustomLink(ctx context.Context, _ customLinkSetter, link string) error {
|
||||
exists, err := s.store.Exists(ctx, "locdata-"+link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("link already exists")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
api/dynamic.go
Normal file
50
api/dynamic.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/parkan/go-hauk/config"
|
||||
)
|
||||
|
||||
type velocityUnit struct {
|
||||
MpsMultiplier float64 `json:"mpsMultiplier"`
|
||||
Unit string `json:"unit"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDynamic(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
|
||||
|
||||
var velUnit velocityUnit
|
||||
switch s.cfg.VelocityUnit {
|
||||
case config.MilesPerHour:
|
||||
velUnit = velocityUnit{MpsMultiplier: 3.6 * 0.6213712, Unit: "mph"}
|
||||
case config.MetersPerSecond:
|
||||
velUnit = velocityUnit{MpsMultiplier: 1, Unit: "m/s"}
|
||||
default:
|
||||
velUnit = velocityUnit{MpsMultiplier: 3.6, Unit: "km/h"}
|
||||
}
|
||||
|
||||
tileURI, _ := json.Marshal(s.cfg.MapTileURI)
|
||||
attribution, _ := json.Marshal(s.cfg.MapAttribution)
|
||||
defaultZoom, _ := json.Marshal(s.cfg.DefaultZoom)
|
||||
maxZoom, _ := json.Marshal(s.cfg.MaxZoom)
|
||||
maxPoints, _ := json.Marshal(s.cfg.MaxShownPts)
|
||||
velDelta, _ := json.Marshal(s.cfg.VelocityDataPts)
|
||||
trailColor, _ := json.Marshal(s.cfg.TrailColor)
|
||||
velUnitJSON, _ := json.Marshal(velUnit)
|
||||
offlineTimeout, _ := json.Marshal(s.cfg.OfflineTimeout)
|
||||
requestTimeout, _ := json.Marshal(s.cfg.RequestTimeout)
|
||||
|
||||
fmt.Fprintf(w, "var TILE_URI = %s;\n", tileURI)
|
||||
fmt.Fprintf(w, "var ATTRIBUTION = %s;\n", attribution)
|
||||
fmt.Fprintf(w, "var DEFAULT_ZOOM = %s;\n", defaultZoom)
|
||||
fmt.Fprintf(w, "var MAX_ZOOM = %s;\n", maxZoom)
|
||||
fmt.Fprintf(w, "var MAX_POINTS = %s;\n", maxPoints)
|
||||
fmt.Fprintf(w, "var VELOCITY_DELTA_TIME = %s;\n", velDelta)
|
||||
fmt.Fprintf(w, "var TRAIL_COLOR = %s;\n", trailColor)
|
||||
fmt.Fprintf(w, "var VELOCITY_UNIT = %s;\n", velUnitJSON)
|
||||
fmt.Fprintf(w, "var OFFLINE_TIMEOUT = %s;\n", offlineTimeout)
|
||||
fmt.Fprintf(w, "var REQUEST_TIMEOUT = %s;\n", requestTimeout)
|
||||
}
|
||||
108
api/fetch.go
Normal file
108
api/fetch.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
type soloResponse struct {
|
||||
Type int `json:"type"`
|
||||
Expire int64 `json:"expire"`
|
||||
ServerTime float64 `json:"serverTime"`
|
||||
Interval float64 `json:"interval"`
|
||||
Points [][]any `json:"points"`
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
type groupResponse struct {
|
||||
Type int `json:"type"`
|
||||
Expire int64 `json:"expire"`
|
||||
ServerTime float64 `json:"serverTime"`
|
||||
Interval float64 `json:"interval"`
|
||||
Points map[string][][]any `json:"points"`
|
||||
}
|
||||
|
||||
func (s *Server) handleFetch(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
id := r.URL.Query().Get("id")
|
||||
if id == "" {
|
||||
fmt.Fprintln(w, "Invalid session!")
|
||||
return
|
||||
}
|
||||
|
||||
sinceStr := r.URL.Query().Get("since")
|
||||
since := float64(0)
|
||||
if sinceStr != "" {
|
||||
since, _ = strconv.ParseFloat(sinceStr, 64)
|
||||
}
|
||||
|
||||
shareType, err := model.LoadShareType(ctx, s.store, id)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintln(w, "Invalid session!")
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/json")
|
||||
now := float64(time.Now().UnixNano()) / 1e9
|
||||
|
||||
switch shareType {
|
||||
case model.ShareTypeAlone:
|
||||
share, err := model.LoadSoloShare(ctx, s.store, id, s.cfg.PublicURL)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintln(w, "Invalid session!")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := model.LoadSession(ctx, s.store, share.Host(), s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintln(w, "Invalid session!")
|
||||
return
|
||||
}
|
||||
|
||||
resp := soloResponse{
|
||||
Type: share.Type(),
|
||||
Expire: share.Expire().Unix(),
|
||||
ServerTime: now,
|
||||
Interval: session.Interval(),
|
||||
Points: session.GetPoints(since),
|
||||
Encrypted: session.Encrypted(),
|
||||
Salt: session.Salt(),
|
||||
}
|
||||
if resp.Points == nil {
|
||||
resp.Points = [][]any{}
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
|
||||
case model.ShareTypeGroup:
|
||||
share, err := model.LoadGroupShare(ctx, s.store, id, s.cfg.PublicURL)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintln(w, "Invalid session!")
|
||||
return
|
||||
}
|
||||
|
||||
points, _ := share.GetAllPoints(ctx, since, s.cfg.MaxCachedPts)
|
||||
if points == nil {
|
||||
points = make(map[string][][]any)
|
||||
}
|
||||
|
||||
resp := groupResponse{
|
||||
Type: share.Type(),
|
||||
Expire: share.Expire().Unix(),
|
||||
ServerTime: now,
|
||||
Interval: share.GetAutoInterval(ctx, s.cfg.MaxCachedPts),
|
||||
Points: points,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
}
|
||||
60
api/newlink.go
Normal file
60
api/newlink.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
func (s *Server) handleNewLink(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sid := r.FormValue("sid")
|
||||
adoptable := r.FormValue("ado") == "1"
|
||||
|
||||
if sid == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := model.LoadSession(ctx, s.store, sid, s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
linkGen := func() (string, error) {
|
||||
return s.linkgen.Generate(ctx)
|
||||
}
|
||||
|
||||
share, err := model.NewSoloShare(s.store, s.cfg.PublicURL, linkGen)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
share.SetAdoptable(adoptable)
|
||||
share.SetHost(session.ID())
|
||||
share.SetExpire(session.Expire())
|
||||
|
||||
if err := share.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session.AddTarget(share.ID())
|
||||
if err := session.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
fmt.Fprintln(w, share.ViewLink())
|
||||
fmt.Fprintln(w, share.ID())
|
||||
}
|
||||
99
api/post.go
Normal file
99
api/post.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
func (s *Server) handlePost(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sid := r.FormValue("sid")
|
||||
lat := r.FormValue("lat")
|
||||
lon := r.FormValue("lon")
|
||||
ts := r.FormValue("time")
|
||||
|
||||
if sid == "" || lat == "" || lon == "" || ts == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := model.LoadSession(ctx, s.store, sid, s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
var point []any
|
||||
|
||||
if !session.Encrypted() {
|
||||
latF, _ := strconv.ParseFloat(lat, 64)
|
||||
lonF, _ := strconv.ParseFloat(lon, 64)
|
||||
timeF, _ := strconv.ParseFloat(ts, 64)
|
||||
|
||||
if latF < -90 || latF > 90 || lonF < -180 || lonF > 180 {
|
||||
fmt.Fprintln(w, "Invalid location!")
|
||||
return
|
||||
}
|
||||
|
||||
var speed, acc *float64
|
||||
if spd := r.FormValue("spd"); spd != "" {
|
||||
v, _ := strconv.ParseFloat(spd, 64)
|
||||
speed = &v
|
||||
}
|
||||
if a := r.FormValue("acc"); a != "" {
|
||||
v, _ := strconv.ParseFloat(a, 64)
|
||||
acc = &v
|
||||
}
|
||||
|
||||
prv := 0
|
||||
if r.FormValue("prv") == "1" {
|
||||
prv = 1
|
||||
}
|
||||
|
||||
point = []any{latF, lonF, timeF, prv, acc, speed}
|
||||
} else {
|
||||
iv := r.FormValue("iv")
|
||||
if iv == "" {
|
||||
fmt.Fprintln(w, "Missing data!")
|
||||
return
|
||||
}
|
||||
|
||||
var speed, acc, prv any
|
||||
if spd := r.FormValue("spd"); spd != "" {
|
||||
speed = spd
|
||||
}
|
||||
if a := r.FormValue("acc"); a != "" {
|
||||
acc = a
|
||||
}
|
||||
if p := r.FormValue("prv"); p != "" {
|
||||
prv = p
|
||||
}
|
||||
|
||||
point = []any{iv, lat, lon, ts, prv, acc, speed}
|
||||
}
|
||||
|
||||
session.AddPoint(point)
|
||||
if err := session.Save(ctx); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if session.HasExpired() {
|
||||
fmt.Fprintln(w, "Session expired!")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
fmt.Fprintf(w, "%s?%%s\n", s.cfg.PublicURL)
|
||||
fmt.Fprintln(w, strings.Join(session.Targets(), ","))
|
||||
}
|
||||
61
api/server.go
Normal file
61
api/server.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
"github.com/parkan/go-hauk/auth"
|
||||
"github.com/parkan/go-hauk/config"
|
||||
"github.com/parkan/go-hauk/frontend"
|
||||
"github.com/parkan/go-hauk/linkgen"
|
||||
"github.com/parkan/go-hauk/store"
|
||||
)
|
||||
|
||||
const backendVersion = "1.6.2-go"
|
||||
|
||||
type Server struct {
|
||||
mux *http.ServeMux
|
||||
cfg *config.Config
|
||||
store store.Store
|
||||
auth auth.Authenticator
|
||||
linkgen *linkgen.Generator
|
||||
}
|
||||
|
||||
func NewServer(cfg *config.Config, s store.Store) *Server {
|
||||
srv := &Server{
|
||||
mux: http.NewServeMux(),
|
||||
cfg: cfg,
|
||||
store: s,
|
||||
linkgen: linkgen.New(s, cfg.LinkStyle),
|
||||
}
|
||||
|
||||
switch cfg.AuthMethod {
|
||||
case config.AuthHtpasswd:
|
||||
srv.auth = auth.NewHtpasswdAuth(cfg.HtpasswdPath)
|
||||
case config.AuthLDAP:
|
||||
srv.auth = auth.NewLDAPAuth(
|
||||
cfg.LDAPUri, cfg.LDAPBaseDN, cfg.LDAPBindDN,
|
||||
cfg.LDAPBindPass, cfg.LDAPUserFilter, cfg.LDAPStartTLS,
|
||||
)
|
||||
default:
|
||||
srv.auth = auth.NewPasswordAuth(cfg.PasswordHash)
|
||||
}
|
||||
|
||||
srv.mux.HandleFunc("POST /api/create.php", srv.handleCreate)
|
||||
srv.mux.HandleFunc("POST /api/post.php", srv.handlePost)
|
||||
srv.mux.HandleFunc("GET /api/fetch.php", srv.handleFetch)
|
||||
srv.mux.HandleFunc("POST /api/stop.php", srv.handleStop)
|
||||
srv.mux.HandleFunc("POST /api/adopt.php", srv.handleAdopt)
|
||||
srv.mux.HandleFunc("POST /api/new-link.php", srv.handleNewLink)
|
||||
srv.mux.HandleFunc("GET /dynamic.js.php", srv.handleDynamic)
|
||||
|
||||
staticFS, _ := fs.Sub(frontend.Files, ".")
|
||||
srv.mux.Handle("/", http.FileServer(http.FS(staticFS)))
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Hauk-Version", backendVersion)
|
||||
s.mux.ServeHTTP(w, r)
|
||||
}
|
||||
96
api/stop.go
Normal file
96
api/stop.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/parkan/go-hauk/model"
|
||||
)
|
||||
|
||||
func (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sid := r.FormValue("sid")
|
||||
if sid == "" {
|
||||
fmt.Fprintln(w, "OK")
|
||||
return
|
||||
}
|
||||
|
||||
lid := r.FormValue("lid")
|
||||
|
||||
session, err := model.LoadSession(ctx, s.store, sid, s.cfg.MaxCachedPts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, "OK")
|
||||
return
|
||||
}
|
||||
|
||||
if lid != "" {
|
||||
found := false
|
||||
for _, t := range session.Targets() {
|
||||
if t == lid {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Fprintln(w, "OK")
|
||||
return
|
||||
}
|
||||
|
||||
shareType, err := model.LoadShareType(ctx, s.store, lid)
|
||||
if err == nil {
|
||||
switch shareType {
|
||||
case model.ShareTypeAlone:
|
||||
share, err := model.LoadSoloShare(ctx, s.store, lid, s.cfg.PublicURL)
|
||||
if err == nil {
|
||||
share.Delete(ctx)
|
||||
}
|
||||
case model.ShareTypeGroup:
|
||||
share, err := model.LoadGroupShare(ctx, s.store, lid, s.cfg.PublicURL)
|
||||
if err == nil {
|
||||
share.RemoveHost(sid)
|
||||
if len(share.Hosts()) == 0 {
|
||||
share.Delete(ctx)
|
||||
} else {
|
||||
share.Save(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
session.RemoveTarget(lid)
|
||||
session.Save(ctx)
|
||||
} else {
|
||||
for _, t := range session.Targets() {
|
||||
shareType, err := model.LoadShareType(ctx, s.store, t)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch shareType {
|
||||
case model.ShareTypeAlone:
|
||||
share, err := model.LoadSoloShare(ctx, s.store, t, s.cfg.PublicURL)
|
||||
if err == nil {
|
||||
share.Delete(ctx)
|
||||
}
|
||||
case model.ShareTypeGroup:
|
||||
share, err := model.LoadGroupShare(ctx, s.store, t, s.cfg.PublicURL)
|
||||
if err == nil {
|
||||
share.RemoveHost(sid)
|
||||
if len(share.Hosts()) == 0 {
|
||||
share.Delete(ctx)
|
||||
} else {
|
||||
share.Save(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
session.Delete(ctx)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "OK")
|
||||
}
|
||||
Reference in New Issue
Block a user