Skip to content

Commit 7f6b4a6

Browse files
committed
feat: added nginx serve file
1 parent 93e1e19 commit 7f6b4a6

File tree

14 files changed

+169
-139
lines changed

14 files changed

+169
-139
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ log/
88
build/
99
dist/
1010
tmp/
11+
build.sh

config_sample.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ api_key: API3Q37hsqU
77
api_secret: hZBTzWScAVPgVVasZshXwwypxoDzVLeU45xb
88
## It can be any local path
99
path: ./examples/files
10+
nginx_file_serve_path: "/examples/"
1011
debug: false
1112
compress: false
1213
delete_empty_dir: false

examples/generate.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package main
22

33
import (
44
"fmt"
5+
"time"
6+
57
"github.com/go-jose/go-jose/v4"
68
"github.com/go-jose/go-jose/v4/jwt"
7-
"time"
89
)
910

1011
func main() {

go.mod

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
module github.com/jibon57/secure-file-download-server
22

3-
go 1.23
3+
go 1.25.3
44

55
require (
6-
github.com/go-jose/go-jose/v4 v4.0.4
7-
github.com/gofiber/fiber/v2 v2.52.6
6+
github.com/go-jose/go-jose/v4 v4.1.3
7+
github.com/gofiber/fiber/v2 v2.52.9
88
gopkg.in/yaml.v3 v3.0.1
99
)
1010

1111
require (
12-
github.com/andybalholm/brotli v1.1.1 // indirect
12+
github.com/andybalholm/brotli v1.2.0 // indirect
13+
github.com/clipperhouse/stringish v0.1.1 // indirect
14+
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
1315
github.com/google/uuid v1.6.0 // indirect
14-
github.com/klauspost/compress v1.17.11 // indirect
16+
github.com/klauspost/compress v1.18.1 // indirect
1517
github.com/mattn/go-colorable v0.1.14 // indirect
1618
github.com/mattn/go-isatty v0.0.20 // indirect
17-
github.com/mattn/go-runewidth v0.0.16 // indirect
18-
github.com/rivo/uniseg v0.4.7 // indirect
19+
github.com/mattn/go-runewidth v0.0.19 // indirect
1920
github.com/valyala/bytebufferpool v1.0.0 // indirect
20-
github.com/valyala/fasthttp v1.58.0 // indirect
21-
github.com/valyala/tcplisten v1.0.0 // indirect
22-
golang.org/x/crypto v0.32.0 // indirect
23-
golang.org/x/sys v0.29.0 // indirect
21+
github.com/valyala/fasthttp v1.68.0 // indirect
22+
golang.org/x/sys v0.38.0 // indirect
2423
)

go.sum

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,32 @@
1-
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
2-
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
3-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5-
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
6-
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
7-
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
8-
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
9-
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
10-
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1+
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
2+
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
3+
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
4+
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
5+
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
6+
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
7+
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
8+
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
9+
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
10+
github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
1111
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1212
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
13-
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
14-
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
13+
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
14+
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
1515
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
1616
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
1717
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
1818
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
19-
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
20-
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
21-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23-
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
24-
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
25-
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
26-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
27-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
19+
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
20+
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
2821
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
2922
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
30-
github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE=
31-
github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw=
32-
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
33-
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
23+
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
24+
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
3425
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
3526
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
36-
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
37-
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
3827
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39-
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
40-
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
28+
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
29+
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
4130
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
4231
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4332
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

config.go renamed to internal/config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package internal
22

33
import (
44
"os"
@@ -11,6 +11,7 @@ type AppConfig struct {
1111
ApiKey string `yaml:"api_key"`
1212
ApiSecret string `yaml:"api_secret"`
1313
Path string `yaml:"path"`
14+
NginxFileServePath string `yaml:"nginx_file_serve_path"`
1415
Debug bool `yaml:"debug"`
1516
Compress bool `yaml:"compress"`
1617
DeleteEmptyDir bool `yaml:"delete_empty_dir"`
@@ -19,7 +20,9 @@ type AppConfig struct {
1920
DelFileBackupDuration time.Duration `yaml:"del_file_backup_duration"`
2021
}
2122

22-
func createOrUpdateDirs() error {
23+
var AppCnf AppConfig
24+
25+
func CreateOrUpdateDirs() error {
2326
err := os.MkdirAll(AppCnf.Path, 0755)
2427
if err != nil {
2528
return err

router.go renamed to internal/file_del.go

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,17 @@
1-
package main
1+
package internal
22

33
import (
44
"errors"
55
"fmt"
6-
"github.com/go-jose/go-jose/v4"
7-
"github.com/go-jose/go-jose/v4/jwt"
8-
"github.com/gofiber/fiber/v2"
9-
"github.com/gofiber/fiber/v2/middleware/logger"
10-
"github.com/gofiber/fiber/v2/middleware/recover"
116
"io/fs"
127
"log"
138
"os"
149
"path"
1510
"strings"
1611
"time"
17-
)
18-
19-
func Router() *fiber.App {
20-
app := fiber.New(fiber.Config{
21-
AppName: "File download server version: " + Version,
22-
})
23-
if AppCnf.Debug {
24-
app.Use(logger.New())
25-
}
26-
app.Use(recover.New())
27-
28-
// format: http://ip:port/download/token
29-
// make sure token is urlencoded
30-
app.Get("/download/:token", HandleDownloadFile)
31-
app.Post("/delete", HandleDeleteFile)
32-
33-
return app
34-
}
35-
36-
func HandleDownloadFile(c *fiber.Ctx) error {
37-
token := c.Params("token")
3812

39-
if len(token) == 0 {
40-
return sendResponse(c, fiber.StatusUnauthorized, false, "token require or invalid url")
41-
}
42-
43-
file, status, err := verifyToken(token)
44-
if err != nil {
45-
return sendResponse(c, status, false, err.Error())
46-
}
47-
48-
c.Attachment(file)
49-
return c.SendFile(file, AppCnf.Compress)
50-
}
13+
"github.com/gofiber/fiber/v2"
14+
)
5115

5216
type DeleteFileReq struct {
5317
FilePath *string `json:"file_path,omitempty" xml:"file_path,omitempty" form:"file_path,omitempty"`
@@ -128,36 +92,3 @@ func HandleDeleteFile(c *fiber.Ctx) error {
12892

12993
return sendResponse(c, fiber.StatusOK, true, "file deleted")
13094
}
131-
132-
func verifyToken(token string) (string, int, error) {
133-
tok, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.HS256})
134-
if err != nil {
135-
return "", fiber.StatusUnauthorized, err
136-
}
137-
138-
out := jwt.Claims{}
139-
if err = tok.Claims([]byte(AppCnf.ApiSecret), &out); err != nil {
140-
return "", fiber.StatusUnauthorized, err
141-
}
142-
143-
if err = out.Validate(jwt.Expected{Issuer: AppCnf.ApiKey, Time: time.Now().UTC()}); err != nil {
144-
return "", fiber.StatusUnauthorized, err
145-
}
146-
147-
file := fmt.Sprintf("%s/%s", AppCnf.Path, out.Subject)
148-
_, err = os.Lstat(file)
149-
150-
if err != nil {
151-
ms := strings.SplitN(err.Error(), "/", -1)
152-
return "", fiber.StatusNotFound, errors.New(ms[len(ms)-1])
153-
}
154-
155-
return file, fiber.StatusOK, nil
156-
}
157-
158-
func sendResponse(c *fiber.Ctx, statusCode int, status bool, msg string) error {
159-
return c.Status(statusCode).JSON(fiber.Map{
160-
"status": status,
161-
"msg": msg,
162-
})
163-
}

internal/file_serve.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/gofiber/fiber/v2"
9+
)
10+
11+
func HandleDownloadFile(c *fiber.Ctx) error {
12+
token := c.Params("token")
13+
14+
if len(token) == 0 {
15+
return sendResponse(c, fiber.StatusUnauthorized, false, "token require or invalid url")
16+
}
17+
18+
out, err := verifyToken(token)
19+
if err != nil {
20+
return sendResponse(c, fiber.StatusUnauthorized, false, err.Error())
21+
}
22+
23+
file := fmt.Sprintf("%s/%s", AppCnf.Path, out.Subject)
24+
_, err = os.Lstat(file)
25+
26+
if err != nil {
27+
ms := strings.SplitN(err.Error(), "/", -1)
28+
return sendResponse(c, fiber.StatusNotFound, false, ms[len(ms)-1])
29+
}
30+
31+
c.Attachment(file)
32+
return c.SendFile(file, AppCnf.Compress)
33+
}
34+
35+
func HandleServeFile(c *fiber.Ctx) error {
36+
token := c.Params("token")
37+
38+
if len(token) == 0 {
39+
return sendResponse(c, fiber.StatusUnauthorized, false, "token require or invalid url")
40+
}
41+
42+
out, err := verifyToken(token)
43+
if err != nil {
44+
return sendResponse(c, fiber.StatusUnauthorized, false, err.Error())
45+
}
46+
ms := strings.SplitN(out.Subject, "/", -1)
47+
48+
c.Attachment(ms[len(ms)-1])
49+
c.Set("X-Accel-Redirect", fmt.Sprintf("%s%s", AppCnf.NginxFileServePath, out.Subject))
50+
return c.SendStatus(fiber.StatusOK)
51+
}

internal/router.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package internal
2+
3+
import (
4+
"time"
5+
6+
"github.com/go-jose/go-jose/v4"
7+
"github.com/go-jose/go-jose/v4/jwt"
8+
"github.com/gofiber/fiber/v2"
9+
"github.com/gofiber/fiber/v2/middleware/logger"
10+
"github.com/gofiber/fiber/v2/middleware/recover"
11+
)
12+
13+
func Router(version string) *fiber.App {
14+
app := fiber.New(fiber.Config{
15+
AppName: "File download server version: " + version,
16+
})
17+
if AppCnf.Debug {
18+
app.Use(logger.New())
19+
}
20+
app.Use(recover.New())
21+
22+
// format: http://ip:port/download/token
23+
// make sure token is urlencoded
24+
app.Get("/download/:token", HandleDownloadFile)
25+
app.Get("/serveFile/:token", HandleServeFile)
26+
app.Post("/delete", HandleDeleteFile)
27+
28+
return app
29+
}
30+
31+
func verifyToken(token string) (*jwt.Claims, error) {
32+
tok, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.HS256})
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
out := jwt.Claims{}
38+
if err = tok.Claims([]byte(AppCnf.ApiSecret), &out); err != nil {
39+
return nil, err
40+
}
41+
42+
if err = out.Validate(jwt.Expected{Issuer: AppCnf.ApiKey, Time: time.Now().UTC()}); err != nil {
43+
return nil, err
44+
}
45+
46+
return &out, nil
47+
}
48+
49+
func sendResponse(c *fiber.Ctx, statusCode int, status bool, msg string) error {
50+
return c.Status(statusCode).JSON(fiber.Map{
51+
"status": status,
52+
"msg": msg,
53+
})
54+
}

scheduler.go renamed to internal/scheduler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package internal
22

33
import (
44
"log"
@@ -9,7 +9,7 @@ import (
99

1010
var closeTicker chan bool
1111

12-
func startScheduler() {
12+
func StartScheduler() {
1313
closeTicker = make(chan bool)
1414
hourlyChecker := time.NewTicker(1 * time.Hour)
1515

0 commit comments

Comments
 (0)