feat: docs api design
This commit is contained in:
23
cmd/octopus/main.go
Normal file
23
cmd/octopus/main.go
Normal 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")
|
||||
}
|
||||
}
|
||||
10
cmd/octopus/scripts/cmd.go
Normal file
10
cmd/octopus/scripts/cmd.go
Normal 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
74
cmd/octopus/server/app.go
Normal 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
106
cmd/octopus/server/base.go
Normal 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
76
cmd/octopus/server/cmd.go
Normal 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
23
cmd/octopus/version.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user