package app import ( "database/sql" "encoding/base64" "encoding/json" "errors" "fmt" "io" "log" "net/http" "strconv" "github.com/go-chi/chi/v5" ) type AddUserReq struct { Username string `json:"username"` Password string `json:"password"` } type AddUserRsp struct { ID int `json:"id"` Username string `json:"username"` } func AddUser(w http.ResponseWriter, r *http.Request) { log.Println("AddUser called") req, err := ReadBody[AddUserReq](r) if err != nil { WriteError(w, err) return } if !CheckToken(r) { WriteError(w, errors.New("token required")) return } rep := NewUserRepository(db) user, err := rep.AddUser(req.Username, req.Password) if err != nil { WriteError(w, err) return } WriteResponseCode(w, user, http.StatusCreated) } type DeleteUserReq struct { ID int `json:"id"` } func DeleteUser(w http.ResponseWriter, r *http.Request) { if !CheckToken(r) { WriteError(w, errors.New("token required")) return } req, err := ReadBody[DeleteUserReq](r) if err != nil { WriteError(w, err) return } rep := NewUserRepository(db) err = rep.DeleteUser(req.ID) if err != nil { WriteError(w, err) return } w.WriteHeader(http.StatusNoContent) } func AllUsers(w http.ResponseWriter, r *http.Request) { fmt.Println("AllUsers called") if !CheckToken(r) { WriteError(w, errors.New("invalid token")) return } rep := NewUserRepository(db) users, err := rep.GetUsers() if err != nil { WriteError(w, err) return } WriteResponse(w, users) } type GetConnectURLReq struct { ID int `json:"id"` Pass string `json:"pass"` } func GetUserURL(w http.ResponseWriter, r *http.Request) { req, err := ReadBody[GetConnectURLReq](r) if err != nil { WriteError(w, err) return } rep := NewUserRepository(db) user, err := rep.GetById(req.ID) if err != nil { WriteError(w, err) return } //if user.Password != req.Pass { // WriteError(w, errors.New("invalid password")) // return //} urlTemplate := "hysteria2://%s@%s%s?obfs=salamander&obfs-password=%s&type=hysteria&mport&security=tls&sni=%s&alpn=h3&fp=chrome&allowInsecure=0#%s" authString := encodeURL(user) u := fmt.Sprintf( urlTemplate, authString, cfg.Host, hysteriaCfg.Listen, hysteriaCfg.Obfs.Salamander.Password, cfg.SNI, formatConfigName(cfg.NameFormat, user), ) WriteResponse(w, u) } func Sub(w http.ResponseWriter, r *http.Request) { idS := chi.URLParam(r, "id") id, err := strconv.Atoi(idS) if err != nil { WriteError(w, err) return } rep := NewUserRepository(db) user, err := rep.GetById(id) if err != nil { WriteError(w, err) return } urlTemplate := "hysteria2://%s@%s%s?obfs=salamander&obfs-password=%s&type=hysteria&mport&security=tls&sni=%s&alpn=h3&fp=chrome&allowInsecure=0#%s" authString := encodeURL(user) u := fmt.Sprintf( urlTemplate, authString, cfg.Host, hysteriaCfg.Listen, hysteriaCfg.Obfs.Salamander.Password, cfg.SNI, formatConfigName(cfg.NameFormat, user), ) w.Write([]byte(base64.StdEncoding.EncodeToString([]byte(u)))) } type AuthReq struct { Addr string `json:"addr"` Auth string `json:"auth"` Tx int `json:"tx"` } type AuthRsp struct { Ok bool `json:"ok"` ID string `json:"id"` } func DoAuth(w http.ResponseWriter, r *http.Request) { data, err := io.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) log.Println(err) return } req := new(AuthReq) if err = json.Unmarshal(data, req); err != nil { w.WriteHeader(http.StatusBadRequest) log.Println(err) return } log.Printf("New auth request from %s, data %s", req.Addr, string(data)) reqUser, err := decodeURL(req.Auth) if err != nil { w.WriteHeader(http.StatusBadRequest) log.Println(err) return } rep := NewUserRepository(db) user, err := rep.GetById(reqUser.ID) if err != nil { if errors.Is(err, sql.ErrNoRows) { w.WriteHeader(http.StatusNotFound) return } log.Println(err) w.WriteHeader(http.StatusBadRequest) return } res := &AuthRsp{true, user.Username} err = json.NewEncoder(w).Encode(res) if err != nil { w.WriteHeader(http.StatusBadRequest) } else { w.WriteHeader(http.StatusOK) } }