mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-09 18:10:29 +00:00
feat: api result add code (#96)
This commit is contained in:
1 parent
dbbbc204fb
commit
2e639e0fef
9 files changed
+130
-89
No files matched your search
+52
-32
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/GopeedLab/gopeed/pkg/base"
|
||||
"github.com/GopeedLab/gopeed/pkg/download"
|
||||
"github.com/GopeedLab/gopeed/pkg/rest/model"
|
||||
"github.com/GopeedLab/gopeed/pkg/rest/util"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -15,19 +14,19 @@ import (
|
||||
|
||||
func Resolve(w http.ResponseWriter, r *http.Request) {
|
||||
var req base.Request
|
||||
if util.ReadJson(w, r, &req) {
|
||||
if ReadJson(r, w, &req) {
|
||||
rr, err := Downloader.Resolve(&req)
|
||||
if err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, model.NewResultWithData(rr))
|
||||
WriteJson(w, model.NewOkResult(rr))
|
||||
}
|
||||
}
|
||||
|
||||
func CreateTask(w http.ResponseWriter, r *http.Request) {
|
||||
var req model.CreateTask
|
||||
if util.ReadJson(w, r, &req) {
|
||||
if ReadJson(r, w, &req) {
|
||||
var (
|
||||
taskId string
|
||||
err error
|
||||
@@ -37,64 +36,85 @@ func CreateTask(w http.ResponseWriter, r *http.Request) {
|
||||
} else if req.Req != nil {
|
||||
taskId, err = Downloader.DirectCreate(req.Req, req.Opts)
|
||||
} else {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg("param is required: rid or req"))
|
||||
WriteJson(w, model.NewErrorResult("param invalid: rid or req", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, model.NewResultWithData(taskId))
|
||||
WriteJson(w, model.NewOkResult(taskId))
|
||||
}
|
||||
}
|
||||
|
||||
func PauseTask(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
taskId := vars["id"]
|
||||
if err := Downloader.Pause(taskId); err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
if taskId == "" {
|
||||
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, nil)
|
||||
if err := Downloader.Pause(taskId); err != nil {
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
WriteJson(w, model.NewNilResult())
|
||||
}
|
||||
|
||||
func ContinueTask(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
taskId := vars["id"]
|
||||
if err := Downloader.Continue(taskId); err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
if taskId == "" {
|
||||
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, nil)
|
||||
if err := Downloader.Continue(taskId); err != nil {
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
WriteJson(w, model.NewNilResult())
|
||||
}
|
||||
|
||||
func DeleteTask(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
taskId := vars["id"]
|
||||
force := r.FormValue("force")
|
||||
if err := Downloader.Delete(taskId, force == "true"); err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
if taskId == "" {
|
||||
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, nil)
|
||||
if err := Downloader.Delete(taskId, force == "true"); err != nil {
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
WriteJson(w, model.NewNilResult())
|
||||
}
|
||||
|
||||
func GetTask(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
taskId := vars["id"]
|
||||
task := Downloader.GetTask(taskId)
|
||||
if task == nil {
|
||||
util.WriteJson(w, http.StatusNotFound, model.NewResultWithMsg("task not found"))
|
||||
if taskId == "" {
|
||||
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
util.WriteJsonOk(w, model.NewResultWithData(task))
|
||||
task := Downloader.GetTask(taskId)
|
||||
if task == nil {
|
||||
WriteJson(w, model.NewErrorResult("task not found", model.CodeTaskNotFound))
|
||||
return
|
||||
}
|
||||
WriteJson(w, model.NewOkResult(task))
|
||||
}
|
||||
|
||||
func GetTasks(w http.ResponseWriter, r *http.Request) {
|
||||
status := r.FormValue("status")
|
||||
if status == "" {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg("param is required: status"))
|
||||
return
|
||||
status = strings.Join([]string{
|
||||
string(base.DownloadStatusReady),
|
||||
string(base.DownloadStatusRunning),
|
||||
string(base.DownloadStatusPause),
|
||||
string(base.DownloadStatusError),
|
||||
string(base.DownloadStatusDone),
|
||||
}, ",")
|
||||
}
|
||||
statusArr := strings.Split(status, ",")
|
||||
tasks := Downloader.GetTasks()
|
||||
@@ -106,33 +126,33 @@ func GetTasks(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
util.WriteJsonOk(w, model.NewResultWithData(result))
|
||||
WriteJson(w, model.NewOkResult(result))
|
||||
}
|
||||
|
||||
func GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteJsonOk(w, model.NewResultWithData(getServerConfig()))
|
||||
WriteJson(w, model.NewOkResult(getServerConfig()))
|
||||
}
|
||||
|
||||
func PutConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var cfg download.DownloaderStoreConfig
|
||||
if util.ReadJson(w, r, &cfg) {
|
||||
if ReadJson(r, w, &cfg) {
|
||||
if err := Downloader.PutConfig(&cfg); err != nil {
|
||||
util.WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
util.WriteJsonOk(w, nil)
|
||||
WriteJson(w, model.NewNilResult())
|
||||
}
|
||||
|
||||
func DoProxy(w http.ResponseWriter, r *http.Request) {
|
||||
target := r.Header.Get("X-Target-Uri")
|
||||
if target == "" {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg("header is required: X-Target-Uri"))
|
||||
WriteJson(w, model.NewErrorResult("param invalid: X-Target-Uri", model.CodeInvalidParam))
|
||||
return
|
||||
}
|
||||
targetUrl, err := url.Parse(target)
|
||||
if err != nil {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
r.RequestURI = ""
|
||||
@@ -140,7 +160,7 @@ func DoProxy(w http.ResponseWriter, r *http.Request) {
|
||||
r.Host = targetUrl.Host
|
||||
resp, err := http.DefaultClient.Do(r)
|
||||
if err != nil {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
@@ -159,7 +179,7 @@ func DoProxy(w http.ResponseWriter, r *http.Request) {
|
||||
reader = resp.Body
|
||||
}
|
||||
if _, err := io.Copy(w, reader); err != nil {
|
||||
util.WriteJson(w, http.StatusBadRequest, model.NewResultWithMsg(err.Error()))
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,47 @@
|
||||
package model
|
||||
|
||||
type RespCode int
|
||||
|
||||
const (
|
||||
CodeOk RespCode = 0
|
||||
// CodeError is the common error code
|
||||
CodeError RespCode = 1000
|
||||
// CodeUnauthorized is the error code for unauthorized
|
||||
CodeUnauthorized RespCode = 1002
|
||||
// CodeInvalidParam is the error code for invalid parameter
|
||||
CodeInvalidParam RespCode = 1003
|
||||
// CodeTaskNotFound is the error code for task not found
|
||||
CodeTaskNotFound RespCode = 2001
|
||||
)
|
||||
|
||||
type Result[T any] struct {
|
||||
Msg string `json:"msg"`
|
||||
Data T `json:"data"`
|
||||
Code RespCode `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data T `json:"data"`
|
||||
}
|
||||
|
||||
func NewResult[T any](msg string, data T) *Result[T] {
|
||||
func NewOkResult[T any](data T) *Result[T] {
|
||||
return &Result[T]{
|
||||
Msg: msg,
|
||||
Code: CodeOk,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func NewResultWithMsg(msg string) *Result[any] {
|
||||
return NewResult[any](msg, nil)
|
||||
func NewNilResult() *Result[any] {
|
||||
return &Result[any]{
|
||||
Code: CodeOk,
|
||||
}
|
||||
}
|
||||
|
||||
func NewResultWithData[T any](data T) *Result[T] {
|
||||
return NewResult[T]("", data)
|
||||
func NewErrorResult(msg string, code ...RespCode) *Result[any] {
|
||||
// if code is not provided, the default code is CodeError
|
||||
c := CodeError
|
||||
if len(code) > 0 {
|
||||
c = code[0]
|
||||
}
|
||||
|
||||
return &Result[any]{
|
||||
Code: c,
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -2,9 +2,9 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/GopeedLab/gopeed/pkg/download"
|
||||
"github.com/GopeedLab/gopeed/pkg/rest/model"
|
||||
restUtil "github.com/GopeedLab/gopeed/pkg/rest/util"
|
||||
"github.com/GopeedLab/gopeed/pkg/util"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
@@ -98,7 +98,7 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
|
||||
r.Use(func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Api-Token") != startCfg.ApiToken {
|
||||
restUtil.WriteJson(w, http.StatusUnauthorized, model.NewResultWithMsg("invalid token"))
|
||||
WriteJson(w, model.NewErrorResult("invalid token", model.CodeUnauthorized))
|
||||
return
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
@@ -113,3 +113,17 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
|
||||
)(r)}
|
||||
return srv, listener, nil
|
||||
}
|
||||
|
||||
func ReadJson(r *http.Request, w http.ResponseWriter, v any) bool {
|
||||
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
|
||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func WriteJson(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
+13
-11
@@ -150,7 +150,7 @@ func TestDeleteTask(t *testing.T) {
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
httpRequestCheckOk[any](http.MethodDelete, "/api/v1/tasks/"+taskId, nil)
|
||||
code, _ := httpRequest[*download.Task](http.MethodGet, "/api/v1/tasks/"+taskId, nil)
|
||||
checkCode(code, http.StatusNotFound)
|
||||
checkCode(code, model.CodeTaskNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func TestDeleteTaskForce(t *testing.T) {
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
httpRequestCheckOk[any](http.MethodDelete, "/api/v1/tasks/"+taskId+"?force=true", nil)
|
||||
code, _ := httpRequest[*download.Task](http.MethodGet, "/api/v1/tasks/"+taskId, nil)
|
||||
checkCode(code, http.StatusNotFound)
|
||||
checkCode(code, model.CodeTaskNotFound)
|
||||
if _, err := os.Stat(test.DownloadFile); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Errorf("DeleteTaskForce() got = %v, want %v", err, os.ErrNotExist)
|
||||
}
|
||||
@@ -179,8 +179,7 @@ func TestGetTasks(t *testing.T) {
|
||||
|
||||
httpRequestCheckOk[string](http.MethodPost, fmt.Sprintf("/api/v1/tasks?status=%s,%s",
|
||||
base.DownloadStatusReady, base.DownloadStatusRunning), createReq)
|
||||
code, _ := httpRequest[[]*download.Task](http.MethodGet, "/api/v1/tasks", nil)
|
||||
checkCode(code, http.StatusBadRequest)
|
||||
httpRequestCheckOk[[]*download.Task](http.MethodGet, "/api/v1/tasks", nil)
|
||||
|
||||
wg.Wait()
|
||||
r := httpRequestCheckOk[[]*download.Task](http.MethodGet, fmt.Sprintf("/api/v1/tasks?status=%s",
|
||||
@@ -245,12 +244,12 @@ func TestAuthorization(t *testing.T) {
|
||||
}()
|
||||
|
||||
code, _ := doHttpRequest[any](http.MethodGet, "/api/v1/config", nil, nil)
|
||||
checkCode(code, http.StatusUnauthorized)
|
||||
checkCode(code, model.CodeUnauthorized)
|
||||
|
||||
code, _ = doHttpRequest[any](http.MethodGet, "/api/v1/config", map[string]string{
|
||||
"X-Api-Token": cfg.ApiToken,
|
||||
}, nil)
|
||||
checkCode(code, http.StatusOK)
|
||||
checkCode(code, model.CodeOk)
|
||||
|
||||
}
|
||||
|
||||
@@ -308,17 +307,20 @@ func doHttpRequest0(method string, path string, headers map[string]string, body
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
panic(fmt.Sprintf("http request failed, status code: %d", response.StatusCode))
|
||||
}
|
||||
return response.StatusCode, respBody
|
||||
}
|
||||
|
||||
func doHttpRequest[T any](method string, path string, headers map[string]string, body any) (int, *model.Result[T]) {
|
||||
code, respBody := doHttpRequest0(method, path, headers, body)
|
||||
_, respBody := doHttpRequest0(method, path, headers, body)
|
||||
|
||||
var r model.Result[T]
|
||||
if err := json.Unmarshal(respBody, &r); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return code, &r
|
||||
return int(r.Code), &r
|
||||
}
|
||||
|
||||
func httpRequest[T any](method string, path string, body any) (int, *model.Result[T]) {
|
||||
@@ -332,11 +334,11 @@ func httpRequestCheckOk[T any](method string, path string, body any) T {
|
||||
}
|
||||
|
||||
func checkOk(code int) {
|
||||
checkCode(code, 200)
|
||||
checkCode(code, model.CodeOk)
|
||||
}
|
||||
|
||||
func checkCode(code int, exceptCode int) {
|
||||
if code != exceptCode {
|
||||
func checkCode(code int, exceptCode model.RespCode) {
|
||||
if code != int(exceptCode) {
|
||||
panic(fmt.Sprintf("code got = %d, want %d", code, exceptCode))
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/GopeedLab/gopeed/pkg/rest/model"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ReadJson(w http.ResponseWriter, r *http.Request, v any) bool {
|
||||
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
|
||||
WriteJson(w, http.StatusInternalServerError, model.NewResultWithMsg(err.Error()))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func WriteJsonOk(w http.ResponseWriter, v any) {
|
||||
if v == nil {
|
||||
WriteJson(w, http.StatusOK, model.NewResultWithData[any](nil))
|
||||
} else {
|
||||
WriteJson(w, http.StatusOK, v)
|
||||
}
|
||||
}
|
||||
|
||||
func WriteJson(w http.ResponseWriter, code int, v any) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(code)
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
@@ -70,15 +70,17 @@ Future<T> _parse<T>(
|
||||
try {
|
||||
var resp = await fetch();
|
||||
if (fromJsonT != null) {
|
||||
return Result<T>.fromJson(resp.data, fromJsonT).data as T;
|
||||
final result = Result<T>.fromJson(resp.data, fromJsonT);
|
||||
if (result.code == 0) {
|
||||
return result.data as T;
|
||||
} else {
|
||||
throw Exception(result);
|
||||
}
|
||||
} else {
|
||||
return null as T;
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
if (e.response == null) {
|
||||
throw Exception("Server error");
|
||||
}
|
||||
throw Exception(Result.fromJson(e.response?.data, (_) => null).msg);
|
||||
throw Exception(Result(code: 1000, msg: e.message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ part 'result.g.dart';
|
||||
|
||||
@JsonSerializable(genericArgumentFactories: true)
|
||||
class Result<T> {
|
||||
int code;
|
||||
String? msg;
|
||||
T? data;
|
||||
|
||||
Result({
|
||||
required this.code,
|
||||
this.msg,
|
||||
this.data,
|
||||
});
|
||||
@@ -18,6 +20,7 @@ class Result<T> {
|
||||
) =>
|
||||
_$ResultFromJson(json, fromJsonT);
|
||||
Map<String, dynamic> toJson() => {
|
||||
'code': code,
|
||||
'msg': msg,
|
||||
'data': data is List
|
||||
? (data as dynamic)?.map((e) => e.toJson()).toList()
|
||||
|
||||
@@ -11,6 +11,7 @@ Result<T> _$ResultFromJson<T>(
|
||||
T Function(Object? json) fromJsonT,
|
||||
) =>
|
||||
Result<T>(
|
||||
code: json['code'] as int,
|
||||
msg: json['msg'] as String?,
|
||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||
);
|
||||
@@ -19,7 +20,9 @@ Map<String, dynamic> _$ResultToJson<T>(
|
||||
Result<T> instance,
|
||||
Object? Function(T value) toJsonT,
|
||||
) {
|
||||
final val = <String, dynamic>{};
|
||||
final val = <String, dynamic>{
|
||||
'code': instance.code,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
|
||||
@@ -1004,5 +1004,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.18.0 <4.0.0"
|
||||
dart: ">=2.18.0 <3.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
Reference in new issue
Block a user