feat: docs api design

This commit is contained in:
neo-f
2023-03-22 22:45:17 +08:00
commit 084d0de8bc
52 changed files with 3420 additions and 0 deletions

23
cmd/octopus/main.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"octopus/cmd/octopus/scripts"
"octopus/cmd/octopus/server"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "octopus-cli",
Short: "The `octopus` CLI",
}
func main() {
rootCmd.AddCommand(server.CmdRun)
rootCmd.AddCommand(scripts.CmdScripts)
if err := rootCmd.Execute(); err != nil {
log.Fatal().Err(err).Msg("Failed to execute root command")
}
}

View File

@@ -0,0 +1,10 @@
package scripts
import (
"github.com/spf13/cobra"
)
var CmdScripts = &cobra.Command{
Use: "scripts",
Short: "执行定制化脚本",
}

74
cmd/octopus/server/app.go Normal file
View File

@@ -0,0 +1,74 @@
package server
import (
"net/http"
"time"
"octopus"
"octopus/internal/config"
"octopus/internal/router"
"octopus/pkg/tools"
"octopus/statics"
"github.com/getkin/kin-openapi/openapi3"
"github.com/gofiber/fiber/v2"
"github.com/neo-f/soda"
)
var innerURLs = tools.NewSet(
config.URL_MONITOR,
config.URL_HEALTH,
config.URL_VERSION,
config.URL_OPENAPI,
config.URL_REDOC,
config.URL_SWAGGER,
config.URL_RAPIDOC,
config.URL_ELEMENTS,
config.URL_GATEWAY_CONFIG,
config.URL_METRICS,
)
var ROUTES = []func(app *soda.Soda){
RegisterBase,
router.RegisterDebuggerRouter,
router.RegisterDocRouter,
}
// startHttpServer starts configures and starts an HTTP server on the given URL.
// It shuts down the server if any error is received in the error channel.
func InitApp() *soda.Soda {
app := soda.New("Octopus", octopus.Version,
soda.EnableValidateRequest(),
soda.WithOpenAPISpec(config.URL_OPENAPI),
soda.WithStoplightElements(config.URL_ELEMENTS),
soda.WithRapiDoc(config.URL_RAPIDOC),
soda.WithRedoc(config.URL_REDOC),
soda.WithSwagger(config.URL_SWAGGER),
soda.WithFiberConfig(
fiber.Config{
ReadTimeout: time.Second * 10,
EnablePrintRoutes: true,
ErrorHandler: func(c *fiber.Ctx, err error) error {
if err == nil {
return c.Next()
}
status := http.StatusInternalServerError //default error status
if e, ok := err.(*fiber.Error); ok { // it's a custom error, so use the status in the error
status = e.Code
}
msg := map[string]interface{}{"code": status, "message": err.Error()}
return c.Status(status).JSON(msg)
},
},
),
)
app.OpenAPI().Info.Contact = &openapi3.Contact{Name: "NEO", Email: "tmpgfw@gmail.com"}
app.OpenAPI().Info.Description = statics.GenDescription()
for _, env := range config.ENVS {
app.OpenAPI().AddServer(&openapi3.Server{URL: env[1], Description: env[0]})
}
for _, route := range ROUTES {
route(app)
}
return app
}

106
cmd/octopus/server/base.go Normal file
View File

@@ -0,0 +1,106 @@
package server
import (
"octopus"
"octopus/internal/config"
"octopus/pkg/logger"
"octopus/pkg/middlewares/metrics"
"octopus/pkg/middlewares/uniform"
"runtime"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
flogger "github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/monitor"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/neo-f/soda"
"github.com/rs/xid"
"github.com/rs/zerolog/log"
)
type SystemHealth struct {
MySQL bool `json:"mysql" oai:"description=mysql 连接状态"`
Redis bool `json:"redis" oai:"description=redis 连接状态"`
AntsPool interface{} `json:"ants_pool" oai:"description=协程池状态"`
}
func HealthProbe(c *fiber.Ctx) error {
return c.Status(200).SendString("OK")
}
type SystemVersion struct {
Version string `json:"version" oai:"description=版本"`
BuildTime string `json:"build_time" oai:"description=构建时间"`
GoVersion string `json:"go_version" oai:"description=go 版本"`
}
func version(c *fiber.Ctx) error {
return c.JSON(SystemVersion{
Version: octopus.Version,
BuildTime: octopus.BuildTime,
GoVersion: octopus.GoVersion,
})
}
func defaultNextFunc(c *fiber.Ctx) bool {
return innerURLs.Has(c.Path())
}
func RegisterBase(app *soda.Soda) {
app.Use(
// CORS 跨域
cors.New(),
// Compress 压缩
compress.New(),
// recover 异常恢复
recover.New(recover.Config{
EnableStackTrace: true,
StackTraceHandler: func(c *fiber.Ctx, e interface{}) {
buf := make([]byte, 1024)
buf = buf[:runtime.Stack(buf, false)]
log.Ctx(c.UserContext()).Error().Interface("err", e).Msg(string(buf))
},
}),
// uniform 统一返回结构
uniform.New(uniform.Config{Next: defaultNextFunc}),
// requestid 请求增加trace_id
requestid.New(
requestid.Config{
Generator: func() string { return xid.New().String() },
ContextKey: "trace_id",
},
),
// kylin ball 麒麟球实现
logger.NewKylinMiddleware(),
// logger 请求日志
flogger.New(flogger.Config{
Next: func(c *fiber.Ctx) bool { return innerURLs.Has(c.Path()) },
TimeFormat: time.RFC3339,
Format: "${time} [${locals:trace_id}] ${status} - ${latency} ${method} ${path}\n",
}),
// Prometheus
metrics.NewMiddleware(metrics.Config{Next: defaultNextFunc}),
)
if config.Get().Debug {
app.Use(pprof.New())
app.Get(config.URL_MONITOR, monitor.New()).
SetSummary("系统监视器").
AddTags("System").OK()
}
app.Get(config.URL_HEALTH, HealthProbe).
SetSummary("健康检查").
SetDescription("\n - 一切正常的时候返回状态码200\n - 任意一项不成功的时候返回状态码400").
AddJSONResponse(200, SystemHealth{}).
AddJSONResponse(400, SystemHealth{}).
AddTags("System").OK()
app.Get(config.URL_VERSION, version).
SetSummary("版本信息").
AddJSONResponse(200, SystemVersion{}).
AddTags("System").OK()
}

76
cmd/octopus/server/cmd.go Normal file
View File

@@ -0,0 +1,76 @@
package server
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"octopus/internal/config"
"octopus/pkg/logger"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
var CmdRun = &cobra.Command{
Use: "run",
Short: "Run the octopus server",
Run: func(*cobra.Command, []string) {
////////////////SETTING LOGS && Sentry ///////////////////
logger.Setup()
//////////////////////////////////////////////////////////
app := InitApp()
log.Info().Msg("starting http server")
go app.Listen(fmt.Sprintf(":%d", config.Get().HTTPPort)) //nolint:errcheck
ctx, cancel := context.WithCancel(context.Background())
wg := &sync.WaitGroup{}
// Start the app
wg.Add(1)
go func() {
defer wg.Done()
<-ctx.Done()
if err := app.Shutdown(); err != nil {
log.Fatal().Err(err).Msg("failed to shutdown")
}
}()
prometheusAddr := fmt.Sprintf(":%d", config.Get().PrometheusPort)
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
server := &http.Server{Addr: prometheusAddr, Handler: mux}
wg.Add(1)
go func() {
defer wg.Done()
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("failed to start prometheus server")
}
}()
wg.Add(1)
go func() {
defer wg.Done()
<-ctx.Done()
if err := server.Shutdown(ctx); err != nil {
log.Fatal().Err(err).Msg("failed to shutdown prometheus server")
}
}()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
log.Info().Msg("initialize complete")
<-sigs // Blocks here until interrupted
// Handle shutdown
fmt.Println("Shutdown signal received")
cancel()
wg.Wait()
fmt.Println("All workers done, shutting down!")
},
}

23
cmd/octopus/version.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"fmt"
"octopus"
"github.com/spf13/cobra"
)
var cmdVersion = &cobra.Command{
Use: "version",
Short: "print the version info",
Run: func(*cobra.Command, []string) {
fmt.Println("version:", octopus.Version)
fmt.Println("build time:", octopus.BuildTime)
fmt.Println("build go version:", octopus.GoVersion)
},
}
func init() { // nolint
rootCmd.AddCommand(cmdVersion)
}