feat: complete OCI registry implementation with docker push/pull support
A lightweight OCI (Open Container Initiative) registry implementation written in Go.
This commit is contained in:
56
pkg/resp/err.go
Normal file
56
pkg/resp/err.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package resp
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Error struct {
|
||||
Status int `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Err error `json:"err"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func (e *Error) _r() *res {
|
||||
data := &res{
|
||||
Status: e.Status,
|
||||
Msg: e.Msg,
|
||||
Data: e.Data,
|
||||
Err: e.Err,
|
||||
}
|
||||
|
||||
if data.Status < 0 || data.Status > 999 {
|
||||
data.Status = 500
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func NewError(err error, args ...any) *Error {
|
||||
e := &Error{
|
||||
Status: http.StatusInternalServerError,
|
||||
Err: err,
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
if status, ok := args[0].(int); ok {
|
||||
e.Status = status
|
||||
}
|
||||
}
|
||||
|
||||
e.Msg = Msg(e.Status)
|
||||
|
||||
if len(args) > 1 {
|
||||
if msg, ok := args[1].(string); ok {
|
||||
e.Msg = msg
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
e.Data = args[2]
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
5
pkg/resp/i18n.go
Normal file
5
pkg/resp/i18n.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package resp
|
||||
|
||||
func t(msg string) string {
|
||||
return msg
|
||||
}
|
||||
37
pkg/resp/msg.go
Normal file
37
pkg/resp/msg.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package resp
|
||||
|
||||
const (
|
||||
Msg200 = "操作成功"
|
||||
Msg201 = "操作需要审核, 请继续"
|
||||
Msg202 = "操作未完成, 请继续"
|
||||
Msg400 = "参数错误"
|
||||
Msg400Duplicate = "目标已存在, 请勿重复创建"
|
||||
Msg401 = "该账号登录已失效, 请重新登录"
|
||||
Msg401NoMulti = "用户已在其他地方登录"
|
||||
Msg403 = "权限不足"
|
||||
Msg404 = "资源不存在"
|
||||
Msg500 = "服务器开小差了"
|
||||
Msg501 = "服务不可用"
|
||||
Msg503 = "服务不可用或正在升级, 请联系管理员"
|
||||
)
|
||||
|
||||
func Msg(status int) string {
|
||||
switch status {
|
||||
case 400:
|
||||
return Msg400
|
||||
case 401:
|
||||
return Msg401
|
||||
case 403:
|
||||
return Msg403
|
||||
case 404:
|
||||
return Msg404
|
||||
case 500:
|
||||
return Msg500
|
||||
case 501:
|
||||
return Msg501
|
||||
case 503:
|
||||
return Msg503
|
||||
}
|
||||
|
||||
return "未知错误"
|
||||
}
|
||||
185
pkg/resp/resp.go
Normal file
185
pkg/resp/resp.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type res struct {
|
||||
Status int `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Data any `json:"data"`
|
||||
Err any `json:"err"`
|
||||
}
|
||||
|
||||
func R200(c fiber.Ctx, data any, msgs ...string) error {
|
||||
r := &res{
|
||||
Status: 200,
|
||||
Msg: Msg200,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
if len(msgs) > 0 && msgs[0] != "" {
|
||||
r.Msg = msgs[0]
|
||||
}
|
||||
|
||||
return c.JSON(r)
|
||||
}
|
||||
|
||||
func R201(c fiber.Ctx, data any, msgs ...string) error {
|
||||
r := &res{
|
||||
Status: 201,
|
||||
Msg: Msg201,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
if len(msgs) > 0 && msgs[0] != "" {
|
||||
r.Msg = msgs[0]
|
||||
}
|
||||
|
||||
return c.JSON(r)
|
||||
}
|
||||
|
||||
func R202(c fiber.Ctx, data any, msgs ...string) error {
|
||||
r := &res{
|
||||
Status: 202,
|
||||
Msg: Msg202,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
if len(msgs) > 0 && msgs[0] != "" {
|
||||
r.Msg = msgs[0]
|
||||
}
|
||||
|
||||
return c.JSON(r)
|
||||
}
|
||||
|
||||
func RC(c fiber.Ctx, status int, args ...any) error {
|
||||
return _r(c, &res{Status: status}, args...)
|
||||
}
|
||||
|
||||
func RE(c fiber.Ctx, err error) error {
|
||||
var re *Error
|
||||
|
||||
if errors.As(err, &re) {
|
||||
return RC(c, re.Status, re.Msg, re.Data, re.Err)
|
||||
}
|
||||
|
||||
estr := strings.ToLower(err.Error())
|
||||
if strings.Contains(estr, "duplicate") {
|
||||
return R400(c, Msg400Duplicate, nil, estr)
|
||||
}
|
||||
|
||||
return R500(c, "", nil, err)
|
||||
}
|
||||
|
||||
func _r(c fiber.Ctx, r *res, args ...any) error {
|
||||
length := len(args)
|
||||
|
||||
if length == 0 {
|
||||
goto END
|
||||
}
|
||||
|
||||
if length >= 1 {
|
||||
if msg, ok := args[0].(string); ok {
|
||||
r.Msg = msg
|
||||
}
|
||||
}
|
||||
|
||||
if length >= 2 {
|
||||
r.Data = args[1]
|
||||
}
|
||||
|
||||
if length >= 3 {
|
||||
if ee, ok := args[2].(error); ok {
|
||||
r.Err = ee.Error()
|
||||
} else {
|
||||
r.Err = args[2]
|
||||
}
|
||||
}
|
||||
END:
|
||||
|
||||
if r.Msg == "" {
|
||||
r.Msg = Msg(r.Status)
|
||||
}
|
||||
|
||||
// todo: i18n r.Msg
|
||||
// r.Msg = t(r.Msg)
|
||||
|
||||
return c.Status(r.Status).JSON(r)
|
||||
}
|
||||
|
||||
// R400
|
||||
//
|
||||
// args[0]: should be msg to display to user(defaulted)
|
||||
// args[1]: could be extra data to send with(no default)
|
||||
// args[2]: could be error msg to send to with debug mode
|
||||
func R400(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 400,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
|
||||
// R401
|
||||
//
|
||||
// args[0]: should be msg to display to user(defaulted)
|
||||
// args[1]: could be extra data to send with(no default)
|
||||
// args[2]: could be error msg to send to with debug mode
|
||||
func R401(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 401,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
|
||||
// R403
|
||||
//
|
||||
// args[0]: should be msg to display to user(defaulted)
|
||||
// args[1]: could be extra data to send with(no default)
|
||||
// args[2]: could be error msg to send to with debug mode
|
||||
func R403(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 403,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
|
||||
func R404(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 404,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
|
||||
// R500
|
||||
//
|
||||
// args[0]: should be msg to display to user(defaulted)
|
||||
// args[1]: could be extra data to send with(no default)
|
||||
// args[2]: could be error msg to send to with debug mode
|
||||
func R500(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 500,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
|
||||
// R501
|
||||
//
|
||||
// args[0]: should be msg to display to user(defaulted)
|
||||
// args[1]: could be extra data to send with(no default)
|
||||
// args[2]: could be error msg to send to with debug mode
|
||||
func R501(c fiber.Ctx, args ...any) error {
|
||||
r := &res{
|
||||
Status: 501,
|
||||
}
|
||||
|
||||
return _r(c, r, args...)
|
||||
}
|
||||
75
pkg/resp/sse.go
Normal file
75
pkg/resp/sse.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type SSEManager struct {
|
||||
c fiber.Ctx
|
||||
event string
|
||||
ch chan string
|
||||
id int64
|
||||
}
|
||||
|
||||
func (m *SSEManager) Send(msg string) {
|
||||
m.ch <- msg
|
||||
}
|
||||
|
||||
func (m *SSEManager) JSON(data any) {
|
||||
bs, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
m.ch <- err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
m.ch <- string(bs)
|
||||
}
|
||||
|
||||
func (m *SSEManager) Writer() func(w *bufio.Writer) {
|
||||
return func(w *bufio.Writer) {
|
||||
for msg := range m.ch {
|
||||
fmt.Fprintf(w, "event: %s\nid: %d\ntimestamp: %d\ndata: %s\n\n", m.event, m.id, time.Now().UnixMilli(), msg)
|
||||
w.Flush()
|
||||
m.id++
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SSEManager) Close() {
|
||||
close(m.ch)
|
||||
}
|
||||
|
||||
// SSE create a new SSEManager
|
||||
// example:
|
||||
//
|
||||
// func someHandler(c fiber.Ctx) error {
|
||||
// m := resp.SSE(c, "test")
|
||||
// go func() {
|
||||
// defer m.Close()
|
||||
// for i := range 10 {
|
||||
// m.Send("test" + strconv.Itoa(i))
|
||||
// time.Sleep(1 * time.Second)
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// return c.SendStreamWriter(m.Writer())
|
||||
// }
|
||||
func SSE(c fiber.Ctx, event string) *SSEManager {
|
||||
c.Set("Content-Type", "text/event-stream")
|
||||
c.Set("Cache-Control", "no-cache")
|
||||
c.Set("Connection", "keep-alive")
|
||||
c.Set("Transfer-Encoding", "chunked")
|
||||
|
||||
return &SSEManager{
|
||||
c: c,
|
||||
event: event,
|
||||
id: 0,
|
||||
ch: make(chan string, 1),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user