Compare commits

..

9 Commits

Author SHA1 Message Date
f2a73056c8 fix: github action 2024-07-14 17:49:43 +08:00
52ec9a0686 chore: nfctl version, check cmd 2024-07-14 17:26:48 +08:00
cbb959a31e chore: add github workflow to build nfctl 2024-07-13 22:37:05 +08:00
b267cc7a2e style: cmd.new add success tip 2024-07-12 16:33:35 +08:00
0f139cda98 feat: add nfctl(ctl to start nf project) 2024-07-12 16:00:15 +08:00
8a423c2887 dev: nft/ctl 2024-07-09 18:08:49 +08:00
bf1c5ad92f feat: c.IP add use-proxy-header arg 2024-07-04 17:37:27 +08:00
9b7c8d9d24 fix: writer 2024-06-25 16:36:43 +08:00
c13263fe0d fix: logger missing status while 404 2024-06-17 14:31:49 +08:00
21 changed files with 1128 additions and 22 deletions

62
.github/workflows/nfctl.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: Auto Build
on:
push:
branches:
- 'master'
env:
RELEASE_VERSION: v24.07.14-r2
jobs:
build-job:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
pull-requests: write
repository-projects: write
steps:
- name: checkout repository
uses: actions/checkout@v4
- name: install golang
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: build linux amd64
run: CGO_ENABLE=0 GOOS=linux GOARCH=amd64 go build -ldflags='-s -w' -o "dist/nfctl-linux_amd64-${{ env.RELEASE_VERSION }}" nft/nfctl/main.go
- name: build linux arm64
run: CGO_ENABLE=0 GOOS=linux GOARCH=arm64 go build -ldflags='-s -w' -o "dist/nfctl-linux_arm64-${{ env.RELEASE_VERSION }}" nft/nfctl/main.go
- name: build windows amd64
run: CGO_ENABLE=0 GOOS=windows GOARCH=amd64 go build -ldflags='-s -w' -o "dist/nfctl-win_amd64-${{ env.RELEASE_VERSION }}.exe" nft/nfctl/main.go
- name: build windows arm64
run: CGO_ENABLE=0 GOOS=windows GOARCH=arm64 go build -ldflags='-s -w' -o "dist/nfctl-win_arm64-${{ env.RELEASE_VERSION }}.exe" nft/nfctl/main.go
- name: build darwin amd64
run: CGO_ENABLE=0 GOOS=darwin GOARCH=amd64 go build -ldflags='-s -w' -o "dist/nfctl-darwin_amd64-${{ env.RELEASE_VERSION }}" nft/nfctl/main.go
- name: build darwin arm64
run: CGO_ENABLE=0 GOOS=darwin GOARCH=arm64 go build -ldflags='-s -w' -o "dist/nfctl-darwin_arm64-${{ env.RELEASE_VERSION }}" nft/nfctl/main.go
- name: show all builds
run: ls -lash dist
- name: create releases
id: create_releases
uses: "marvinpinto/action-automatic-releases@latest"
with:
automatic_release_tag: "Release-nfctl-${{ env.RELEASE_VERSION }}"
repo_token: "${{ secrets.GITHUB_TOKEN }}"
title: "Release_${{ env.RELEASE_VERSION }}"
prerelease: false
files: |
dist/nfctl-linux_amd64-${{ env.RELEASE_VERSION }}
dist/nfctl-linux_arm64-${{ env.RELEASE_VERSION }}
dist/nfctl-win_amd64-${{ env.RELEASE_VERSION }}.exe
dist/nfctl-win_arm64-${{ env.RELEASE_VERSION }}.exe
dist/nfctl-darwin_amd64-${{ env.RELEASE_VERSION }}
dist/nfctl-darwin_arm64-${{ env.RELEASE_VERSION }}

12
app.go
View File

@ -202,17 +202,19 @@ func (a *App) handleHTTPRequest(c *Ctx) {
} }
if len(allowed) > 0 { if len(allowed) > 0 {
c.handlers = a.combineHandlers() c.handlers = a.combineHandlers(a.config.MethodNotAllowedHandler)
serveError(c, a.config.MethodNotAllowedHandler) _ = c.Next()
return return
} }
} }
c.handlers = a.combineHandlers() c.handlers = a.combineHandlers(a.config.NotFoundHandler)
serveError(c, a.config.NotFoundHandler) _ = c.Next()
return
} }
func errorHandler(c *Ctx) error { func errorHandler(c *Ctx) error {
@ -271,6 +273,6 @@ func redirectRequest(c *Ctx) {
//debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL) //debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
http.Redirect(c.writer, req, rURL, code) http.Redirect(c.Writer, req, rURL, code)
c.writermem.WriteHeaderNow() c.writermem.WriteHeaderNow()
} }

36
ctx.go
View File

@ -14,10 +14,14 @@ import (
"sync" "sync"
) )
var (
forwardHeaders = []string{"CF-Connecting-IP", "X-Forwarded-For", "X-Real-Ip"}
)
type Ctx struct { type Ctx struct {
lock sync.Mutex lock sync.Mutex
writermem responseWriter writermem responseWriter
writer http.ResponseWriter Writer ResponseWriter
Request *http.Request Request *http.Request
path string path string
method string method string
@ -39,7 +43,6 @@ func newContext(app *App, writer http.ResponseWriter, request *http.Request) *Ct
ctx := &Ctx{ ctx := &Ctx{
lock: sync.Mutex{}, lock: sync.Mutex{},
writer: writer,
Request: request, Request: request,
path: request.URL.Path, path: request.URL.Path,
method: request.Method, method: request.Method,
@ -54,11 +57,13 @@ func newContext(app *App, writer http.ResponseWriter, request *http.Request) *Ct
} }
ctx.writermem = responseWriter{ ctx.writermem = responseWriter{
ResponseWriter: ctx.writer, ResponseWriter: writer,
size: -1, size: -1,
status: 0, status: 0,
} }
ctx.Writer = &ctx.writermem
return ctx return ctx
} }
@ -190,11 +195,24 @@ func (c *Ctx) Get(key string, defaultValue ...string) string {
return value return value
} }
func (c *Ctx) IP() string { func (c *Ctx) IP(useProxyHeader ...bool) string {
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
if err != nil { if err != nil {
return "" return ""
} }
if len(useProxyHeader) > 0 && useProxyHeader[0] {
for _, h := range forwardHeaders {
for _, rip := range strings.Split(c.Request.Header.Get(h), ",") {
realIP := net.ParseIP(strings.Replace(rip, " ", "", -1))
if check := net.ParseIP(realIP.String()); check != nil {
ip = realIP.String()
break
}
}
}
}
return ip return ip
} }
@ -303,11 +321,11 @@ func (c *Ctx) SSEvent(event string, data interface{}) error {
c.Set("Cache-Control", "no-cache") c.Set("Cache-Control", "no-cache")
c.Set("Transfer-Encoding", "chunked") c.Set("Transfer-Encoding", "chunked")
return sse.Encode(c.writer, sse.Event{Event: event, Data: data}) return sse.Encode(c.Writer, sse.Event{Event: event, Data: data})
} }
func (c *Ctx) Flush() error { func (c *Ctx) Flush() error {
if f, ok := c.writer.(http.Flusher); ok { if f, ok := c.Writer.(http.Flusher); ok {
f.Flush() f.Flush()
return nil return nil
} }
@ -315,13 +333,9 @@ func (c *Ctx) Flush() error {
return errors.New("http.Flusher is not implemented") return errors.New("http.Flusher is not implemented")
} }
func (c *Ctx) RawWriter() http.ResponseWriter {
return c.writer
}
func (c *Ctx) HTML(html string) error { func (c *Ctx) HTML(html string) error {
c.SetHeader("Content-Type", "text/html") c.SetHeader("Content-Type", "text/html")
_, err := c.writer.Write([]byte(html)) _, err := c.Write([]byte(html))
return err return err
} }

31
go.mod
View File

@ -4,11 +4,40 @@ go 1.20
require ( require (
github.com/fatih/color v1.17.0 github.com/fatih/color v1.17.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-resty/resty/v2 v2.13.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/jedib0t/go-pretty/v6 v6.5.9
github.com/savioxavier/termlink v1.3.0
github.com/spf13/cobra v1.8.1
) )
require ( require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.18.0 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
) )

162
go.sum
View File

@ -1,13 +1,171 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU=
github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4=
github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

36
nft/nfctl/clone/clone.go Normal file
View File

@ -0,0 +1,36 @@
package clone
import (
"fmt"
"github.com/go-git/go-git/v5"
_ "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/loveuer/nf/nft/log"
"net/url"
)
func Clone(pwd string, ins *url.URL) error {
uri := fmt.Sprintf("%s://%s%s", ins.Scheme, ins.Host, ins.Path)
opt := &git.CloneOptions{
URL: uri,
Depth: 1,
InsecureSkipTLS: true,
SingleBranch: true,
}
if ins.User != nil {
password, _ := ins.User.Password()
opt.Auth = &http.BasicAuth{
Username: ins.User.Username(),
Password: password,
}
}
log.Info("start clone %s", uri)
_, err := git.PlainClone(pwd, false, opt)
if err != nil {
return err
}
return nil
}

16
nft/nfctl/cmd/check.go Normal file
View File

@ -0,0 +1,16 @@
package cmd
import (
"github.com/loveuer/nf/nft/nfctl/version"
"github.com/spf13/cobra"
)
var (
checkCmd = &cobra.Command{
Use: "check",
Short: "nfctl new version check",
Run: func(cmd *cobra.Command, args []string) {
version.Check(true, true, 30)
},
}
)

30
nft/nfctl/cmd/cmd.go Normal file
View File

@ -0,0 +1,30 @@
package cmd
import (
"github.com/loveuer/nf/nft/log"
"github.com/loveuer/nf/nft/nfctl/opt"
"github.com/spf13/cobra"
)
var (
Root = &cobra.Command{
Use: "nfctl",
Short: "nfctl: easy start your nf backend work",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if opt.Debug == true {
log.SetLogLevel(log.LogLevelDebug)
}
},
}
)
func init() {
initNew()
Root.PersistentFlags().BoolVar(&opt.Debug, "debug", false, "debug mode")
Root.AddCommand(
versionCmd,
checkCmd,
cmdNew,
)
}

131
nft/nfctl/cmd/new.go Normal file
View File

@ -0,0 +1,131 @@
package cmd
import (
"errors"
"fmt"
"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 (
cmdNew = &cobra.Command{
Use: "new",
Short: "nfctl new: start new project",
Example: `nfctl new {project} -t ultone [recommend]
nfctl new {project} -t https://github.com/loveuer/ultone.git
nfctl new {project} --template http://username:token@my.gitlab.com/my-zone/my-repo.git
`,
SilenceUsage: true,
}
template string
disableInit bool
preTemplateMap = map[string]string{
"ultone": "https://gitcode.com/loveuer/ultone.git",
}
)
func initNew() {
cmdNew.Flags().StringVarP(&template, "template", "t", "", "template name/url[example:ultone, https://github.com/xxx/yyy.git]")
cmdNew.Flags().BoolVar(&disableInit, "without-init", false, "don't run template init script")
cmdNew.RunE = func(cmd *cobra.Command, args []string) error {
version.Check(true, false, 5)
var (
err error
urlIns *url.URL
pwd string
projectDir string
initBs []byte
renderBs []byte
scripts []tp.Cmd
)
if len(args) == 0 {
return fmt.Errorf("project name required")
}
if pwd, err = os.Getwd(); err != nil {
return fmt.Errorf("get work dir err")
}
projectDir = path.Join(pwd, args[0])
if _, err = os.Stat(projectDir); !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("project folder already exist")
}
if err = os.MkdirAll(projectDir, 0750); err != nil {
return fmt.Errorf("create project dir err: %v", err)
}
defer func() {
if err != nil {
_ = os.RemoveAll(projectDir)
}
}()
if template == "" {
// todo no template new project
return fmt.Errorf("😥create basic project(without template) comming soon...")
}
cloneUrl := template
if ptUrl, ok := preTemplateMap[cloneUrl]; ok {
cloneUrl = ptUrl
}
if urlIns, err = url.Parse(cloneUrl); err != nil {
return fmt.Errorf("invalid clone url: %v", err)
}
if err = clone.Clone(projectDir, urlIns); err != nil {
return fmt.Errorf("clone template err: %v", err)
}
if initBs, err = os.ReadFile(path.Join(projectDir, ".nfctl")); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return fmt.Errorf("read nfctl script file err: %v", err)
}
if renderBs, err = tp.RenderVar(initBs, map[string]any{
"PROJECT_NAME": args[0],
}); err != nil {
return fmt.Errorf("render template init script err: %v", err)
}
if scripts, err = tp.ParseCmd(projectDir, renderBs); err != nil {
return fmt.Errorf("parse template init script err: %v", err)
}
for _, script := range scripts {
if opt.Debug {
log.Debug("start script:\n%s\n", script.String())
}
if err = script.Execute(); err != nil {
return fmt.Errorf("execute template init script err: %v", err)
}
}
if err = os.RemoveAll(path.Join(projectDir, ".git")); err != nil {
log.Warn("remove .git folder err: %s", err.Error())
}
log.Info("🎉 create project [%s] 成功!!!", args[0])
return nil
}
}

18
nft/nfctl/cmd/version.go Normal file
View File

@ -0,0 +1,18 @@
package cmd
import (
"github.com/fatih/color"
"github.com/loveuer/nf/nft/nfctl/version"
"github.com/spf13/cobra"
)
var (
versionCmd = &cobra.Command{
Use: "version",
Short: "print nfctl version and exit",
Run: func(cmd *cobra.Command, args []string) {
color.Cyan("nfctl - version: %s", version.Version)
version.Check(true, false, 5)
},
}
)

19
nft/nfctl/main.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"context"
"github.com/loveuer/nf/nft/nfctl/cmd"
"os/signal"
"syscall"
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
defer cancel()
//if !(len(os.Args) >= 2 && os.Args[1] == "version") {
// version.Check(5)
//}
_ = cmd.Root.ExecuteContext(ctx)
}

5
nft/nfctl/opt/var.go Normal file
View File

@ -0,0 +1,5 @@
package opt
var (
Debug bool
)

23
nft/nfctl/readme.md Normal file
View File

@ -0,0 +1,23 @@
# nfctl
# 通过 nfctl 快速开启后台项目
### 1. Installation
-`go install github.com/loveuer/nf/nft/nfctl@latest`
- ② download prebuild binary [release](https://github.com/loveuer/nf/releases)
### 2. Usage
- `nfctl new {project}`
- `nfctl new project -t ultone`
- `nfctl new project -t https://github.com/xxx/yyy.git`
- `nfctl new project --template https://gitcode/loveuer/ultone.git`
- `nfctl new project --template https://{username}:{password/token}@my.gitlab.com/name/project.git`
### 3. nfctl init script
- `为方便模版的初始化, 可以采用 nfctl init script, 当 nfctl new project -t xxx 从模版开始项目时会自动执行`
- `具体的编写规则如下:`
* [init 脚本规则](https://github.com/loveuer/nf/nft/nfctl/script.md) 或者
* [国内](https://gitcode.com/loveuer/nf/nft/nfctl/script.md)

80
nft/nfctl/tp/parse.go Normal file
View File

@ -0,0 +1,80 @@
package tp
import (
"bufio"
"bytes"
"fmt"
"strings"
)
func ParseCmd(pwd string, content []byte) ([]Cmd, error) {
var (
err error
cmds = make([]Cmd, 0)
start = false
)
scanner := bufio.NewScanner(bytes.NewReader(content))
scanner.Buffer(make([]byte, 1024), 1024*1024*10)
record := make([]string, 0)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) == 0 {
continue
}
if !start && strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "!") {
if start {
return nil, fmt.Errorf("invalid content: unEOF cmd block found")
}
start = true
record = append(record, line)
continue
}
if strings.HasPrefix(line, "EOF") {
start = false
if len(record) == 0 {
continue
}
var cmd Cmd
if cmd, err = ParseBlock(pwd, record); err != nil {
return nil, err
}
cmds = append(cmds, cmd)
record = record[:0]
continue
}
if start {
record = append(record, line)
}
}
if err = scanner.Err(); err != nil {
return nil, err
}
return cmds, err
}
func ParseBlock(pwd string, lines []string) (Cmd, error) {
switch lines[0] {
case "!replace content":
return newReplaceContent(pwd, lines[1:])
case "!replace name":
return newReplaceName(pwd, lines[1:])
case "!generate":
return newGenerate(pwd, lines[1:])
}
return nil, fmt.Errorf("invalid cmd block: unknown type: %s", lines[0])
}

View File

@ -0,0 +1,37 @@
package tp
import (
"github.com/loveuer/nf/nft/log"
"os"
"testing"
)
func TestParseInitFile(t *testing.T) {
bs, err := os.ReadFile("xtest")
if err != nil {
log.Fatal(err.Error())
}
data := map[string]any{
"PROJECT_NAME": "myproject",
}
result, err := RenderVar(bs, data)
if err != nil {
log.Fatal(err.Error())
}
pwd, _ := os.Getwd()
cmds, err := ParseCmd(pwd, result)
if err != nil {
log.Fatal(err.Error())
}
for _, item := range cmds {
log.Info("one cmd => %s\n\n", item.String())
if err = item.Execute(); err != nil {
log.Fatal(err.Error())
}
}
}

29
nft/nfctl/tp/render.go Normal file
View File

@ -0,0 +1,29 @@
package tp
import (
"bytes"
"text/template"
)
var (
_t *template.Template
)
func init() {
_t = template.New("tp")
}
func RenderVar(t []byte, data map[string]any) ([]byte, error) {
tr, err := _t.Parse(string(t))
if err != nil {
return nil, err
}
var buf bytes.Buffer
if err = tr.Execute(&buf, data); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

316
nft/nfctl/tp/tp.go Normal file
View File

@ -0,0 +1,316 @@
package tp
import (
"bufio"
"bytes"
"fmt"
"github.com/loveuer/nf/nft/log"
"io/fs"
"os"
"path"
"path/filepath"
"regexp"
"strings"
)
type Cmd interface {
String() string
Execute() error
}
var (
_ Cmd = (*Generate)(nil)
_ Cmd = (*ReplaceContent)(nil)
_ Cmd = (*ReplaceName)(nil)
)
type Generate struct {
pwd string
filename string
content []string
}
func (t *Generate) String() string {
return fmt.Sprintf("!generate\n%s\n%s\n", t.filename, strings.Join(t.content, "\n"))
}
func (t *Generate) Execute() error {
var (
err error
location = t.filename
input *os.File
)
log.Debug("[Generate] generate[%s]", t.filename)
if !path.IsAbs(t.filename) {
location = path.Join(t.pwd, t.filename)
}
if err = os.MkdirAll(path.Dir(location), 0644); err != nil {
return err
}
if !strings.HasSuffix(location, "/") {
if input, err = os.OpenFile(location, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0744); err != nil {
return err
}
if len(t.content) > 0 {
content := strings.Join(t.content, "\n")
_, err = input.WriteString(content)
return err
}
}
return nil
}
func newGenerate(pwd string, lines []string) (*Generate, error) {
if len(lines) == 0 {
return nil, fmt.Errorf("generate cmd require file/folder name")
}
return &Generate{
pwd: pwd,
filename: lines[0],
content: lines[1:],
}, nil
}
type replaceNameMatchType int
const (
replaceNameMatchReg replaceNameMatchType = iota + 1
replaceNameMatchExact
replaceNameMatchPrefix
replaceNameMatchSuffix
)
func (rm replaceNameMatchType) Label() string {
switch rm {
case replaceNameMatchReg:
return "reg"
case replaceNameMatchExact:
return "exact"
case replaceNameMatchPrefix:
return "prefix"
case replaceNameMatchSuffix:
return "suffix"
}
log.Panic("unknown replace match type: %v", rm)
return ""
}
type ReplaceContent struct {
pwd string
name string
content string
targetName string
matchType replaceNameMatchType
fromContent string
targetEmpty bool
targetContent string
}
func (t *ReplaceContent) String() string {
return fmt.Sprintf("!replace content\n%s\n%s\n", t.name, t.content)
}
func (t *ReplaceContent) Execute() error {
var (
fn filepath.WalkFunc
handler = func(location string) error {
bs, err := os.ReadFile(location)
if err != nil {
return err
}
log.Debug("[ReplaceContent] handle[%s] replace [%s] => [%s]", location, t.fromContent, t.targetContent)
newbs, err := t.executeFile(bs)
if err != nil {
return err
}
return os.WriteFile(location, newbs, 0644)
}
)
switch t.matchType {
case replaceNameMatchExact:
fn = func(location string, info fs.FileInfo, err error) error {
if location == path.Join(t.pwd, t.targetName) {
log.Debug("[ReplaceContent] exact match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchPrefix:
fn = func(location string, info fs.FileInfo, err error) error {
if strings.HasPrefix(path.Base(location), t.targetName) {
log.Debug("[ReplaceContent] prefix match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchSuffix:
fn = func(location string, info fs.FileInfo, err error) error {
if strings.HasSuffix(location, t.targetName) {
log.Debug("[ReplaceContent] suffix match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchReg:
fn = func(location string, info fs.FileInfo, err error) error {
if match, err := regexp.MatchString(t.targetName, location); err == nil && match {
log.Debug("[ReplaceContent] reg match: %s", location)
return handler(location)
}
return nil
}
}
return filepath.Walk(t.pwd, fn)
}
func (t *ReplaceContent) executeFile(raw []byte) ([]byte, error) {
scanner := bufio.NewScanner(bytes.NewReader(raw))
scanner.Buffer(make([]byte, 1024), 1024*1024)
lines := make([]string, 0)
for scanner.Scan() {
line := scanner.Text()
lines = append(
lines,
strings.ReplaceAll(line, t.fromContent, t.targetContent),
)
}
return []byte(strings.Join(lines, "\n")), nil
}
func newReplaceContent(pwd string, lines []string) (*ReplaceContent, error) {
if len(lines) != 2 {
return nil, fmt.Errorf("invalid replace_content cmd: required 2 lines params")
}
var (
name = lines[0]
content = lines[1]
matchType replaceNameMatchType
)
names := strings.SplitN(name, " ", 2)
if len(names) != 2 {
return nil, fmt.Errorf("invalid replace_content cmd: name line, required: [reg/exact/prefix/shuffix] {filename}")
}
switch names[0] {
case "exact":
matchType = replaceNameMatchExact
case "reg":
matchType = replaceNameMatchReg
case "prefix":
matchType = replaceNameMatchPrefix
case "suffix":
matchType = replaceNameMatchSuffix
default:
return nil, fmt.Errorf("invalid replace_content name match type, example: [reg *.go] [exact go.mod]")
}
var (
targetName string = names[1]
targetEmpty = false
targetContent string
)
contents := strings.SplitN(content, "=>", 2)
fromContent := strings.TrimSpace(contents[0])
if len(contents) == 1 {
targetEmpty = true
} else {
if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` {
targetEmpty = true
}
}
return &ReplaceContent{
pwd: pwd,
name: name,
content: content,
matchType: matchType,
targetName: targetName,
fromContent: fromContent,
targetEmpty: targetEmpty,
targetContent: targetContent,
}, nil
}
type ReplaceName struct {
pwd string
line string
targetEmpty bool
fromContent string
targetContent string
}
func (t *ReplaceName) String() string {
return fmt.Sprintf("!replace name\n%s\n", t.line)
}
func (t *ReplaceName) Execute() error {
fullpath := path.Join(t.pwd, t.fromContent)
if t.targetEmpty {
return os.RemoveAll(fullpath)
}
ftpath := path.Join(t.pwd, t.targetContent)
return os.Rename(fullpath, ftpath)
}
func newReplaceName(pwd string, lines []string) (*ReplaceName, error) {
if len(lines) != 1 {
return nil, fmt.Errorf("replace_name need one line param, for example: mian.go => main.go")
}
var (
content = lines[0]
targetEmpty = false
fromContent string
targetContent string
)
contents := strings.SplitN(content, "=>", 2)
fromContent = strings.TrimSpace(contents[0])
if len(contents) == 1 {
targetEmpty = true
} else {
if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` {
targetEmpty = true
}
}
if !targetEmpty {
if (strings.HasPrefix(targetContent, `"`) && strings.HasSuffix(targetContent, `"`)) || (strings.HasPrefix(targetContent, `'`) && strings.HasSuffix(targetContent, `'`)) {
targetContent = targetContent[1 : len(targetContent)-1]
}
}
return &ReplaceName{
pwd: pwd,
line: content,
targetEmpty: targetEmpty,
fromContent: fromContent,
targetContent: targetContent,
}, nil
}

3
nft/nfctl/version/var.go Normal file
View File

@ -0,0 +1,3 @@
package version
const Version = "v24.07.14-r2"

View File

@ -0,0 +1,81 @@
package version
import (
"context"
"crypto/tls"
"fmt"
"github.com/fatih/color"
"github.com/go-resty/resty/v2"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/loveuer/nf/nft/log"
"github.com/savioxavier/termlink"
"strings"
"time"
)
var (
client = resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
uri = "https://raw.gitcode.com/loveuer/nf/raw/master/nft/nfctl/version/var.go"
prefix = "const Version = "
)
func UpgradePrint(newVersion string) {
t := table.NewWriter()
t.AppendRows([]table.Row{
{color.GreenString("New Version Found: %s", newVersion)},
{color.CyanString("Upgrade it with: [go install github.com/loveuer/nf/nft/nfctl@master]")},
{fmt.Sprint("Or Download by: ")},
{color.CyanString(termlink.Link("Releases", "https://github.com/loveuer/nf/releases"))},
{color.CyanString(termlink.Link("Releases", "https://gitcode.com/loveuer/nf/releases"))},
})
fmt.Println(t.Render())
}
func Check(printUpgradable bool, printNoNeedUpgrade bool, timeout ...int) string {
var (
v string
)
defer func() {
if printUpgradable {
if v > Version {
UpgradePrint(v)
}
}
if printNoNeedUpgrade {
if v == Version {
color.Cyan("Your Version: %s is Newest", Version)
}
}
}()
ctx, _ := context.WithTimeout(context.Background(), time.Duration(30)*time.Second)
if len(timeout) > 0 && timeout[0] > 0 {
ctx, _ = context.WithTimeout(context.Background(), time.Duration(timeout[0])*time.Second)
}
resp, err := client.R().SetContext(ctx).
Get(uri)
if err != nil {
log.Debug("[Check] http get[%s] err: %v", uri, err.Error())
return ""
}
log.Debug("[Check] http get[%s] body:\n%s", uri, resp.String())
for _, line := range strings.Split(resp.String(), "\n") {
log.Debug("[Check] version.go line: %s", line)
if strings.HasPrefix(line, prefix) {
may := strings.TrimPrefix(line, prefix)
if len(may) > 2 {
v = may[1 : len(may)-1]
}
return v
}
}
return ""
}

View File

@ -0,0 +1,16 @@
package version
import (
"github.com/loveuer/nf/nft/log"
"testing"
)
func TestUpgradePrint(t *testing.T) {
UpgradePrint("v24.07.14-r5")
}
func TestCheck(t *testing.T) {
log.SetLogLevel(log.LogLevelDebug)
v := Check(15)
t.Logf("got version: %s", v)
}

View File

@ -42,8 +42,9 @@ type ResponseWriter interface {
type responseWriter struct { type responseWriter struct {
http.ResponseWriter http.ResponseWriter
size int written bool
status int size int
status int
} }
var _ ResponseWriter = (*responseWriter)(nil) var _ ResponseWriter = (*responseWriter)(nil)
@ -103,7 +104,7 @@ func (w *responseWriter) Size() int {
} }
func (w *responseWriter) Written() bool { func (w *responseWriter) Written() bool {
return w.size != noWritten return w.size != noWritten || w.written
} }
// Hijack implements the http.Hijacker interface. // Hijack implements the http.Hijacker interface.