Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e66a221e0 | |||
df318682fa | |||
af1e58bce9 | |||
940e86bd8d | |||
5263cba44a | |||
63f7516667 | |||
9b7f1e4413 |
4
.github/workflows/nfctl.yml
vendored
4
.github/workflows/nfctl.yml
vendored
@ -2,10 +2,10 @@ name: Auto Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/nfctl/*'
|
||||
|
||||
env:
|
||||
RELEASE_VERSION: v24.07.14-r3
|
||||
RELEASE_VERSION: v24.09.23-r1
|
||||
|
||||
jobs:
|
||||
build-job:
|
||||
|
41
ctx.go
41
ctx.go
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf/internal/sse"
|
||||
"html/template"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
@ -51,11 +52,10 @@ func newContext(app *App, writer http.ResponseWriter, request *http.Request) *Ct
|
||||
}
|
||||
|
||||
c := context.WithValue(request.Context(), TraceKey, traceId)
|
||||
request.WithContext(c)
|
||||
|
||||
ctx := &Ctx{
|
||||
lock: sync.Mutex{},
|
||||
Request: request,
|
||||
Request: request.WithContext(c),
|
||||
path: request.URL.Path,
|
||||
method: request.Method,
|
||||
StatusCode: 200,
|
||||
@ -75,6 +75,7 @@ func newContext(app *App, writer http.ResponseWriter, request *http.Request) *Ct
|
||||
}
|
||||
|
||||
ctx.Writer = &ctx.writermem
|
||||
ctx.writermem.Header().Set(TraceKey, traceId)
|
||||
|
||||
return ctx
|
||||
}
|
||||
@ -289,23 +290,30 @@ func (c *Ctx) Status(code int) *Ctx {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.writermem.WriteHeader(code)
|
||||
c.Writer.WriteHeader(code)
|
||||
c.StatusCode = c.writermem.status
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Set set response header
|
||||
func (c *Ctx) Set(key string, value string) {
|
||||
c.writermem.Header().Set(key, value)
|
||||
c.Writer.Header().Set(key, value)
|
||||
}
|
||||
|
||||
// AddHeader add response header
|
||||
func (c *Ctx) AddHeader(key string, value string) {
|
||||
c.Writer.Header().Add(key, value)
|
||||
}
|
||||
|
||||
// SetHeader set response header
|
||||
func (c *Ctx) SetHeader(key string, value string) {
|
||||
c.writermem.Header().Set(key, value)
|
||||
c.Writer.Header().Set(key, value)
|
||||
}
|
||||
|
||||
func (c *Ctx) SendStatus(code int) error {
|
||||
c.Status(code)
|
||||
c.writermem.WriteHeaderNow()
|
||||
c.Writer.WriteHeaderNow()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -323,7 +331,7 @@ func (c *Ctx) Writef(format string, values ...interface{}) (int, error) {
|
||||
func (c *Ctx) JSON(data interface{}) error {
|
||||
c.SetHeader("Content-Type", MIMEApplicationJSON)
|
||||
|
||||
encoder := json.NewEncoder(&c.writermem)
|
||||
encoder := json.NewEncoder(c.Writer)
|
||||
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return err
|
||||
@ -355,6 +363,21 @@ func (c *Ctx) HTML(html string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Ctx) Write(data []byte) (int, error) {
|
||||
return c.writermem.Write(data)
|
||||
func (c *Ctx) RenderHTML(name, html string, obj any) error {
|
||||
c.SetHeader("Content-Type", "text/html")
|
||||
t, err := template.New(name).Parse(html)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.Execute(c.Writer, obj)
|
||||
}
|
||||
|
||||
func (c *Ctx) Redirect(url string, code int) error {
|
||||
http.Redirect(c.Writer, c.Request, url, code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Ctx) Write(data []byte) (int, error) {
|
||||
return c.Writer.Write(data)
|
||||
}
|
||||
|
@ -2,11 +2,9 @@ package nf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/loveuer/nf/nft/log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -29,33 +27,19 @@ func NewRecover(enableStackTrace bool) HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func NewLogger(traceHeader ...string) HandlerFunc {
|
||||
Header := "X-Trace-ID"
|
||||
if len(traceHeader) > 0 && traceHeader[0] != "" {
|
||||
Header = traceHeader[0]
|
||||
}
|
||||
func NewLogger() HandlerFunc {
|
||||
|
||||
return func(c *Ctx) error {
|
||||
var (
|
||||
now = time.Now()
|
||||
trace = c.Get(Header)
|
||||
logFn func(msg string, data ...any)
|
||||
ip = c.IP()
|
||||
)
|
||||
|
||||
if trace == "" {
|
||||
trace = uuid.Must(uuid.NewV7()).String()
|
||||
}
|
||||
|
||||
c.SetHeader(Header, trace)
|
||||
|
||||
traces := strings.Split(trace, "-")
|
||||
shortTrace := traces[len(traces)-1]
|
||||
|
||||
err := c.Next()
|
||||
duration := time.Since(now)
|
||||
|
||||
msg := fmt.Sprintf("NF | %s | %15s | %3d | %s | %6s | %s", shortTrace, ip, c.StatusCode, HumanDuration(duration.Nanoseconds()), c.Method(), c.Path())
|
||||
msg := fmt.Sprintf("NF | %v | %15s | %3d | %s | %6s | %s", c.Context().Value(TraceKey), ip, c.StatusCode, HumanDuration(duration.Nanoseconds()), c.Method(), c.Path())
|
||||
|
||||
switch {
|
||||
case c.StatusCode >= 500:
|
||||
|
@ -21,7 +21,7 @@ var (
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
defaultLogger = &logger{
|
||||
DefaultLogger = &logger{
|
||||
Mutex: sync.Mutex{},
|
||||
timeFormat: "2006-01-02T15:04:05",
|
||||
writer: os.Stdout,
|
||||
@ -36,32 +36,32 @@ var (
|
||||
)
|
||||
|
||||
func SetTimeFormat(format string) {
|
||||
defaultLogger.SetTimeFormat(format)
|
||||
DefaultLogger.SetTimeFormat(format)
|
||||
}
|
||||
|
||||
func SetLogLevel(level LogLevel) {
|
||||
defaultLogger.SetLogLevel(level)
|
||||
DefaultLogger.SetLogLevel(level)
|
||||
}
|
||||
|
||||
func Debug(msg string, data ...any) {
|
||||
defaultLogger.Debug(msg, data...)
|
||||
DefaultLogger.Debug(msg, data...)
|
||||
}
|
||||
func Info(msg string, data ...any) {
|
||||
defaultLogger.Info(msg, data...)
|
||||
DefaultLogger.Info(msg, data...)
|
||||
}
|
||||
|
||||
func Warn(msg string, data ...any) {
|
||||
defaultLogger.Warn(msg, data...)
|
||||
DefaultLogger.Warn(msg, data...)
|
||||
}
|
||||
|
||||
func Error(msg string, data ...any) {
|
||||
defaultLogger.Error(msg, data...)
|
||||
DefaultLogger.Error(msg, data...)
|
||||
}
|
||||
|
||||
func Panic(msg string, data ...any) {
|
||||
defaultLogger.Panic(msg, data...)
|
||||
DefaultLogger.Panic(msg, data...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, data ...any) {
|
||||
defaultLogger.Fatal(msg, data...)
|
||||
DefaultLogger.Fatal(msg, data...)
|
||||
}
|
||||
|
@ -3,15 +3,16 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/loveuer/nf/nft/log"
|
||||
"github.com/loveuer/nf/nft/nfctl/clone"
|
||||
"github.com/loveuer/nf/nft/nfctl/opt"
|
||||
"github.com/loveuer/nf/nft/nfctl/tp"
|
||||
"github.com/loveuer/nf/nft/nfctl/version"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -44,6 +45,7 @@ func initNew() {
|
||||
err error
|
||||
urlIns *url.URL
|
||||
pwd string
|
||||
moduleName string
|
||||
projectDir string
|
||||
initBs []byte
|
||||
renderBs []byte
|
||||
@ -58,7 +60,8 @@ func initNew() {
|
||||
return fmt.Errorf("get work dir err")
|
||||
}
|
||||
|
||||
projectDir = path.Join(pwd, args[0])
|
||||
moduleName = args[0]
|
||||
projectDir = path.Join(pwd, path.Base(args[0]))
|
||||
|
||||
if _, err = os.Stat(projectDir); !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("project folder already exist")
|
||||
@ -101,7 +104,8 @@ func initNew() {
|
||||
}
|
||||
|
||||
if renderBs, err = tp.RenderVar(initBs, map[string]any{
|
||||
"PROJECT_NAME": args[0],
|
||||
"PROJECT_NAME": projectDir,
|
||||
"MODULE_NAME": moduleName,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("render template init script err: %v", err)
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ package resp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/loveuer/nf"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/loveuer/nf"
|
||||
)
|
||||
|
||||
func handleEmptyMsg(status uint32, msg string) string {
|
||||
@ -102,6 +103,18 @@ func Resp403(c *nf.Ctx, data any, msgs ...string) error {
|
||||
return Resp(c, 403, msg, err, data)
|
||||
}
|
||||
|
||||
func Resp418(c *nf.Ctx, data any, msgs ...string) error {
|
||||
msg := MSG418
|
||||
err := ""
|
||||
|
||||
if len(msgs) > 0 && msgs[0] != "" {
|
||||
msg = fmt.Sprintf("%s: %s", msg, strings.Join(msgs, "; "))
|
||||
err = ""
|
||||
}
|
||||
|
||||
return Resp(c, 418, msg, err, data)
|
||||
}
|
||||
|
||||
func Resp429(c *nf.Ctx, data any, msgs ...string) error {
|
||||
msg := MSG429
|
||||
err := ""
|
||||
|
@ -7,6 +7,7 @@ const (
|
||||
MSG401 = "登录已过期, 请重新登录"
|
||||
MSG403 = "请求权限不足"
|
||||
MSG404 = "请求资源未找到"
|
||||
MSG418 = "请求条件不满足, 请稍后再试"
|
||||
MSG429 = "请求过于频繁, 请稍后再试"
|
||||
MSG500 = "服务器开小差了, 请稍后再试"
|
||||
MSG501 = "功能开发中, 尽情期待"
|
||||
|
135
readme.md
135
readme.md
@ -5,63 +5,98 @@
|
||||
##### basic usage
|
||||
|
||||
- get param
|
||||
```go
|
||||
func main() {
|
||||
app := nf.New()
|
||||
|
||||
app.Get("/hello/:name", func(c *nf.Ctx) error {
|
||||
name := c.Param("name")
|
||||
return c.JSON(nf.Map{"status": 200, "data": "hello, " + name})
|
||||
})
|
||||
```go
|
||||
func main() {
|
||||
app := nf.New()
|
||||
|
||||
log.Fatal(app.Run("0.0.0.0:80"))
|
||||
}
|
||||
```
|
||||
app.Get("/hello/:name", func(c *nf.Ctx) error {
|
||||
name := c.Param("name")
|
||||
return c.JSON(nf.Map{"status": 200, "data": "hello, " + name})
|
||||
})
|
||||
|
||||
log.Fatal(app.Run("0.0.0.0:80"))
|
||||
}
|
||||
```
|
||||
|
||||
- parse request query
|
||||
```go
|
||||
func handleQuery(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Name string `query:"name"`
|
||||
Addr []string `query:"addr"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = Req{}
|
||||
)
|
||||
```go
|
||||
func handleQuery(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Name string `query:"name"`
|
||||
Addr []string `query:"addr"`
|
||||
}
|
||||
|
||||
if err = c.QueryParser(&req); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
var (
|
||||
err error
|
||||
req = Req{}
|
||||
)
|
||||
|
||||
return c.JSON(nf.Map{"query": req})
|
||||
}
|
||||
```
|
||||
if err = c.QueryParser(&req); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(nf.Map{"query": req})
|
||||
}
|
||||
```
|
||||
|
||||
- parse application/json body
|
||||
```go
|
||||
func handlePost(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Name string `json:"name"`
|
||||
Addr []string `json:"addr"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = Req{}
|
||||
reqMap = make(map[string]interface{})
|
||||
)
|
||||
|
||||
if err = c.BodyParser(&req); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
|
||||
// can parse body multi times
|
||||
if err = c.BodyParser(&reqMap); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(nf.Map{"struct": req, "map": reqMap})
|
||||
}
|
||||
```
|
||||
```go
|
||||
func handlePost(c *nf.Ctx) error {
|
||||
type Req struct {
|
||||
Name string `json:"name"`
|
||||
Addr []string `json:"addr"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
req = Req{}
|
||||
reqMap = make(map[string]interface{})
|
||||
)
|
||||
|
||||
if err = c.BodyParser(&req); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
|
||||
// can parse body multi times
|
||||
if err = c.BodyParser(&reqMap); err != nil {
|
||||
return nf.NewNFError(400, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(nf.Map{"struct": req, "map": reqMap})
|
||||
}
|
||||
```
|
||||
|
||||
- pass local value
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
Id int
|
||||
Username string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := nf.New()
|
||||
app.Use(auth())
|
||||
|
||||
app.Get("/item/list", list)
|
||||
}
|
||||
|
||||
func auth() nf.HandlerFunc {
|
||||
return func(c *nf.Ctx) error {
|
||||
c.Locals("user", &User{Id: 1, Username:"user"})
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func list(c *nf.Ctx) error {
|
||||
user, ok := c.Locals("user").(*User)
|
||||
if !ok {
|
||||
return c.Status(401).SendString("login required")
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
Reference in New Issue
Block a user