feat: 引入签名认证、加密工具包及大量goctl代码生成模板,并更新API、Admin和Node服务逻辑。

This commit is contained in:
2026-02-27 08:49:37 -08:00
parent 9dacd85a89
commit e1896f677c
166 changed files with 2913 additions and 489 deletions
+12
View File
@@ -0,0 +1,12 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package config
import {{.authImport}}
type Config struct {
rest.RestConf
{{.auth}}
{{.jwtTrans}}
}
+20
View File
@@ -0,0 +1,20 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package svc
import (
{{.configImport}}
)
type ServiceContext struct {
Config {{.config}}
{{.middleware}}
}
func NewServiceContext(c {{.config}}) *ServiceContext {
return &ServiceContext{
Config: c,
{{.middlewareAssignment}}
}
}
+3
View File
@@ -0,0 +1,3 @@
Name: {{.serviceName}}
Host: {{.host}}
Port: {{.port}}
+26
View File
@@ -0,0 +1,26 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.PkgName}}
import (
"net/http"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
{{.ImportPackages}}
)
{{if .HasDoc}}{{.Doc}}{{end}}
func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
{{if .HasRequest}}var req types.{{.RequestType}}
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
{{if .HasResp}}result.HttpResult(r, w, resp, err){{else}}result.HttpResult(r, w, nil, err){{end}}
}
}
+84
View File
@@ -0,0 +1,84 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.PkgName}}
import (
"bytes"
{{if .HasRequest}}"encoding/json"{{end}}
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
{{.ImportPackages}}
)
{{if .HasDoc}}{{.Doc}}{{end}}
func Test{{.HandlerName}}(t *testing.T) {
// new service context
c := config.Config{}
svcCtx := svc.NewServiceContext(c)
// init mock service context here
tests := []struct {
name string
reqBody interface{}
wantStatus int
wantResp string
setupMocks func()
}{
{
name: "invalid request body",
reqBody: "invalid",
wantStatus: http.StatusBadRequest,
wantResp: "unsupported type", // Adjust based on actual error response
setupMocks: func() {
// No setup needed for this test case
},
},
{
name: "handler error",
{{if .HasRequest}}reqBody: types.{{.RequestType}}{
//TODO: add fields here
},
{{end}}wantStatus: http.StatusBadRequest,
wantResp: "error", // Adjust based on actual error response
setupMocks: func() {
// Mock login logic to return an error
},
},
{
name: "handler successful",
{{if .HasRequest}}reqBody: types.{{.RequestType}}{
//TODO: add fields here
},
{{end}}wantStatus: http.StatusOK,
wantResp: `{"code":0,"msg":"success","data":{}}`, // Adjust based on actual success response
setupMocks: func() {
// Mock login logic to return success
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setupMocks()
var reqBody []byte
{{if .HasRequest}}var err error
reqBody, err = json.Marshal(tt.reqBody)
require.NoError(t, err){{end}}
req, err := http.NewRequest("POST", "/ut", bytes.NewBuffer(reqBody))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
handler := {{.HandlerName}}(svcCtx)
handler.ServeHTTP(rr, req)
t.Log(rr.Body.String())
assert.Equal(t, tt.wantStatus, rr.Code)
assert.Contains(t, rr.Body.String(), tt.wantResp)
})
}
}
+120
View File
@@ -0,0 +1,120 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package main
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"{{.projectPkg}}/internal/config"
"{{.projectPkg}}/internal/handler"
"{{.projectPkg}}/internal/svc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zeromicro/go-zero/rest"
)
func TestMain(m *testing.M) {
// TODO: Add setup/teardown logic here if needed
m.Run()
}
func TestServerIntegration(t *testing.T) {
// Create test server
c := config.Config{
RestConf: rest.RestConf{
Host: "127.0.0.1",
Port: 0, // Use random available port
},
}
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
// Start server in background
go func() {
server.Start()
}()
// Wait for server to start
time.Sleep(100 * time.Millisecond)
tests := []struct {
name string
method string
path string
body string
expectedStatus int
setup func()
}{
{
name: "health check",
method: "GET",
path: "/health",
expectedStatus: http.StatusNotFound, // Adjust based on actual routes
setup: func() {},
},
{{if .hasRoutes}}{{range .routes}}{
name: "{{.Method}} {{.Path}}",
method: "{{.Method}}",
path: "{{.Path}}",
expectedStatus: http.StatusOK, // TODO: Adjust expected status
setup: func() {
// TODO: Add setup logic for this endpoint
},
},
{{end}}{{end}}{
name: "not found route",
method: "GET",
path: "/nonexistent",
expectedStatus: http.StatusNotFound,
setup: func() {},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setup()
req, err := http.NewRequest(tt.method, tt.path, nil)
require.NoError(t, err)
rr := httptest.NewRecorder()
server.ServeHTTP(rr, req)
assert.Equal(t, tt.expectedStatus, rr.Code)
// TODO: Add response body assertions
t.Logf("Response: %s", rr.Body.String())
})
}
}
func TestServerLifecycle(t *testing.T) {
c := config.Config{
RestConf: rest.RestConf{
Host: "127.0.0.1",
Port: 0,
},
}
server := rest.MustNewServer(c.RestConf)
// Test server can start and stop without errors
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
// In a real integration test, you might start the server in a goroutine
// and test actual HTTP requests, but for scaffolding we keep it simple
server.Stop()
// TODO: Add more lifecycle tests as needed
assert.True(t, true, "Server lifecycle test passed")
}
+29
View File
@@ -0,0 +1,29 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.pkgName}}
import (
{{.imports}}
)
type {{.logic}} struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
{{if .hasDoc}}{{.doc}}{{end}}
func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} {
return &{{.logic}}{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
// todo: add your logic here and delete this line
{{.returnString}}
}
+72
View File
@@ -0,0 +1,72 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.pkgName}}
import (
"context"
"testing"
{{.imports}}
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test{{.logic}}_{{.function}}(t *testing.T) {
c := config.Config{}
mockSvcCtx := svc.NewServiceContext(c)
// init mock service context here
tests := []struct {
name string
ctx context.Context
setupMocks func()
{{if .hasRequest}}req *{{.requestType}}{{end}}
wantErr bool
checkResp func{{if .hasResponse}}{{.responseType}}{{else}}(err error){{end}}
}{
{
name: "response error",
ctx: context.Background(),
setupMocks: func() {
// mock data for this test case
},
{{if .hasRequest}}req: &{{.requestType}}{
// TODO: init your request here
},{{end}}
wantErr: true,
checkResp: func{{if .hasResponse}}{{.responseType}}{{else}}(err error){{end}} {
// TODO: Add your check logic here
},
},
{
name: "successful",
ctx: context.Background(),
setupMocks: func() {
// Mock data for this test case
},
{{if .hasRequest}}req: &{{.requestType}}{
// TODO: init your request here
},{{end}}
wantErr: false,
checkResp: func{{if .hasResponse}}{{.responseType}}{{else}}(err error){{end}} {
// TODO: Add your check logic here
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setupMocks()
l := New{{.logic}}(tt.ctx, mockSvcCtx)
{{if .hasResponse}}resp, {{end}}err := l.{{.function}}({{if .hasRequest}}tt.req{{end}})
if tt.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
{{if .hasResponse}}assert.NotNil(t, resp){{end}}
}
tt.checkResp({{if .hasResponse}}resp, {{end}}err)
})
}
}
+29
View File
@@ -0,0 +1,29 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package main
import (
"flag"
"fmt"
{{.importPackages}}
)
var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
+22
View File
@@ -0,0 +1,22 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package middleware
import "net/http"
type {{.name}} struct {
}
func New{{.name}}() *{{.name}} {
return &{{.name}}{}
}
func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}
+4
View File
@@ -0,0 +1,4 @@
server.AddRoutes(
{{.routes}} {{.jwt}}{{.signature}} {{.prefix}} {{.timeout}} {{.maxBytes}} {{.sse}}
)
+15
View File
@@ -0,0 +1,15 @@
// Code generated by goctl. DO NOT EDIT.
// goctl {{.version}}
package handler
import (
"net/http"{{if .hasTimeout}}
"time"{{end}}
{{.importPackages}}
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
{{.routesAdditions}}
}
+68
View File
@@ -0,0 +1,68 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.PkgName}}
import (
"encoding/json"
"fmt"
"net/http"
"github.com/zeromicro/go-zero/core/logc"
"github.com/zeromicro/go-zero/core/threading"
{{if .HasRequest}}"github.com/zeromicro/go-zero/rest/httpx"{{end}}
{{.ImportPackages}}
)
{{if .HasDoc}}{{.Doc}}{{end}}
func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
{{if .HasRequest}}var req types.{{.RequestType}}
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
{{end}}// Buffer size of 16 is chosen as a reasonable default to balance throughput and memory usage.
// You can change this based on your application's needs.
// if your go-zero version less than 1.8.1, you need to add 3 lines below.
// w.Header().Set("Content-Type", "text/event-stream")
// w.Header().Set("Cache-Control", "no-cache")
// w.Header().Set("Connection", "keep-alive")
client := make(chan {{.ResponseType}}, 16)
l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
threading.GoSafeCtx(r.Context(), func() {
defer close(client)
err := l.{{.Call}}({{if .HasRequest}}&req, {{end}}client)
if err != nil {
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
return
}
})
for {
select {
case data, ok := <-client:
if !ok {
return
}
output, err := json.Marshal(data)
if err != nil {
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
continue
}
if _, err := fmt.Fprintf(w, "data: %s\n\n", string(output)); err != nil {
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
return
}
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
case <-r.Context().Done():
return
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package {{.pkgName}}
import (
{{.imports}}
)
type {{.logic}} struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
{{if .hasDoc}}{{.doc}}{{end}}
func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} {
return &{{.logic}}{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
// todo: add your logic here and delete this line
{{.returnString}}
}
+60
View File
@@ -0,0 +1,60 @@
// Code scaffolded by goctl. Safe to edit.
// goctl {{.version}}
package svc
import (
"testing"
"{{.projectPkg}}/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewServiceContext(t *testing.T) {
tests := []struct {
name string
config config.Config
setup func() config.Config
}{
{
name: "default config",
setup: func() config.Config {
return config.Config{}
},
},
{
name: "valid config",
setup: func() config.Config {
return config.Config{
// TODO: Add valid config values here
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := tt.setup()
svcCtx := NewServiceContext(c)
// Basic assertions
require.NotNil(t, svcCtx)
assert.Equal(t, c, svcCtx.Config)
// TODO: Add additional assertions for middleware and dependencies
})
}
}
func TestServiceContext_Initialization(t *testing.T) {
c := config.Config{}
svcCtx := NewServiceContext(c)
// Verify service context is properly initialized
assert.NotNil(t, svcCtx)
assert.Equal(t, c, svcCtx.Config)
// TODO: Add tests for middleware initialization if any
// TODO: Add tests for external dependencies if any
}
+24
View File
@@ -0,0 +1,24 @@
syntax = "v1"
info (
title: // TODO: add title
desc: // TODO: add description
author: "{{.gitUser}}"
email: "{{.gitEmail}}"
)
type request {
// TODO: add members here and delete this comment
}
type response {
// TODO: add members here and delete this comment
}
service {{.serviceName}} {
@handler GetUser // TODO: set handler name and delete this comment
get /users/id/:userId(request) returns(response)
@handler CreateUser // TODO: set handler name and delete this comment
post /users/create(request)
}
+8
View File
@@ -0,0 +1,8 @@
// Code generated by goctl. DO NOT EDIT.
// goctl {{.version}}
package types{{if .containsTime}}
import (
"time"
){{end}}
{{.types}}