mirror of
https://github.com/1f349/site-hosting.git
synced 2025-01-20 22:26:37 +00:00
Add database storage
This commit is contained in:
parent
88236f19d4
commit
d8ccd87e9b
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@
|
|||||||
go.work
|
go.work
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
.data/
|
||||||
|
@ -4,12 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"github.com/1f349/bluebell"
|
||||||
"github.com/1f349/bluebell/conf"
|
"github.com/1f349/bluebell/conf"
|
||||||
"github.com/1f349/bluebell/logger"
|
"github.com/1f349/bluebell/logger"
|
||||||
"github.com/1f349/bluebell/serve"
|
"github.com/1f349/bluebell/serve"
|
||||||
"github.com/1f349/bluebell/upload"
|
"github.com/1f349/bluebell/upload"
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/cloudflare/tableflip"
|
"github.com/cloudflare/tableflip"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -86,18 +88,30 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
db, err := bluebell.InitDB(config.DB)
|
||||||
|
if err != nil {
|
||||||
|
logger.Logger.Fatal("Failed to open database", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Listen must be called before Ready
|
// Listen must be called before Ready
|
||||||
ln, err := upg.Listen("tcp", config.Listen)
|
ln, err := upg.Listen("tcp", config.Listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Logger.Fatal("Listen failed", "err", err)
|
logger.Logger.Fatal("Listen failed", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadHandler := upload.New(sitesFs)
|
uploadHandler := upload.New(sitesFs, db)
|
||||||
serveHandler := serve.New(sitesFs)
|
serveHandler := serve.New(sitesFs, db, config.Domain)
|
||||||
|
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
router.POST("/u/:site", uploadHandler.Handle)
|
router.POST("/u/:site", uploadHandler.Handle)
|
||||||
router.GET("/*filepath", serveHandler.Handle)
|
router.GET("/*filepath", serveHandler.Handle)
|
||||||
|
router.POST("/sites/:host", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||||
|
|
||||||
|
})
|
||||||
|
router.DELETE("/sites/:host", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
@ -105,7 +119,7 @@ func main() {
|
|||||||
ReadHeaderTimeout: 1 * time.Minute,
|
ReadHeaderTimeout: 1 * time.Minute,
|
||||||
WriteTimeout: 1 * time.Minute,
|
WriteTimeout: 1 * time.Minute,
|
||||||
IdleTimeout: 1 * time.Minute,
|
IdleTimeout: 1 * time.Minute,
|
||||||
MaxHeaderBytes: 4_096_000,
|
MaxHeaderBytes: 4 * humanize.MiByte,
|
||||||
}
|
}
|
||||||
logger.Logger.Info("HTTP server listening on", "addr", config.Listen)
|
logger.Logger.Info("HTTP server listening on", "addr", config.Listen)
|
||||||
go func() {
|
go func() {
|
14
conf/conf.go
14
conf/conf.go
@ -1,20 +1,12 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import "github.com/mrmelon54/trie"
|
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
Listen string `yaml:"listen"`
|
Listen string `yaml:"listen"`
|
||||||
//fs afero.Fs
|
DB string `yaml:"db"`
|
||||||
//l *sync.RWMutex
|
Domain string `yaml:"domain"`
|
||||||
m *trie.Trie[SiteConf]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SiteConf struct {
|
func SlugFromDomain(domain string) string {
|
||||||
Domain string `json:"domain"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conf) slugFromDomain(domain string) string {
|
|
||||||
a := []byte(domain)
|
a := []byte(domain)
|
||||||
for i := range a {
|
for i := range a {
|
||||||
switch {
|
switch {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
CREATE TABLE sites
|
CREATE TABLE sites
|
||||||
(
|
(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
slug TEXT(8) NOT NULL,
|
||||||
domain TEXT NOT NULL,
|
domain TEXT NOT NULL,
|
||||||
token TEXT NOT NULL
|
token TEXT NOT NULL,
|
||||||
|
enable BOOL NOT NULL
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,8 @@ import ()
|
|||||||
|
|
||||||
type Site struct {
|
type Site struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
Enable bool `json:"enable"`
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,20 @@
|
|||||||
|
-- name: GetSiteBySlug :one
|
||||||
|
SELECT *
|
||||||
|
FROM sites
|
||||||
|
WHERE slug = ?
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: GetSiteByDomain :one
|
-- name: GetSiteByDomain :one
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM sites
|
FROM sites
|
||||||
WHERE domain = ?
|
WHERE domain = ?
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: EnableDomain :exec
|
||||||
|
INSERT INTO sites (slug, domain, token)
|
||||||
|
VALUES (?, ?, ?);
|
||||||
|
|
||||||
|
-- name: DeleteDomain :exec
|
||||||
|
UPDATE sites
|
||||||
|
SET enable = false
|
||||||
|
WHERE domain = ?;
|
||||||
|
@ -9,8 +9,35 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const deleteDomain = `-- name: DeleteDomain :exec
|
||||||
|
UPDATE sites
|
||||||
|
SET enable = false
|
||||||
|
WHERE domain = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteDomain(ctx context.Context, domain string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteDomain, domain)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const enableDomain = `-- name: EnableDomain :exec
|
||||||
|
INSERT INTO sites (slug, domain, token)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type EnableDomainParams struct {
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) EnableDomain(ctx context.Context, arg EnableDomainParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, enableDomain, arg.Slug, arg.Domain, arg.Token)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const getSiteByDomain = `-- name: GetSiteByDomain :one
|
const getSiteByDomain = `-- name: GetSiteByDomain :one
|
||||||
SELECT id, domain, token
|
SELECT id, slug, domain, token, enable
|
||||||
FROM sites
|
FROM sites
|
||||||
WHERE domain = ?
|
WHERE domain = ?
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
@ -19,6 +46,32 @@ LIMIT 1
|
|||||||
func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (Site, error) {
|
func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (Site, error) {
|
||||||
row := q.db.QueryRowContext(ctx, getSiteByDomain, domain)
|
row := q.db.QueryRowContext(ctx, getSiteByDomain, domain)
|
||||||
var i Site
|
var i Site
|
||||||
err := row.Scan(&i.ID, &i.Domain, &i.Token)
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Domain,
|
||||||
|
&i.Token,
|
||||||
|
&i.Enable,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSiteBySlug = `-- name: GetSiteBySlug :one
|
||||||
|
SELECT id, slug, domain, token, enable
|
||||||
|
FROM sites
|
||||||
|
WHERE slug = ?
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSiteBySlug(ctx context.Context, slug string) (Site, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getSiteBySlug, slug)
|
||||||
|
var i Site
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Slug,
|
||||||
|
&i.Domain,
|
||||||
|
&i.Token,
|
||||||
|
&i.Enable,
|
||||||
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.23.4
|
|||||||
require (
|
require (
|
||||||
github.com/charmbracelet/log v0.4.0
|
github.com/charmbracelet/log v0.4.0
|
||||||
github.com/cloudflare/tableflip v1.2.3
|
github.com/cloudflare/tableflip v1.2.3
|
||||||
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/mrmelon54/trie v0.0.3
|
github.com/mrmelon54/trie v0.0.3
|
||||||
|
2
go.sum
2
go.sum
@ -26,6 +26,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
|
|||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/1f349/bluebell/database"
|
"github.com/1f349/bluebell/database"
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
"github.com/golang-migrate/migrate/v4/database/mysql"
|
"github.com/golang-migrate/migrate/v4/database/mysql"
|
||||||
"github.com/golang-migrate/migrate/v4/source/iofs"
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package serve
|
package serve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/1f349/bluebell/conf"
|
"github.com/1f349/bluebell/conf"
|
||||||
|
"github.com/1f349/bluebell/database"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -24,78 +27,59 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(config conf.Conf, storage afero.Fs) *Handler {
|
type sitesQueries interface {
|
||||||
return &Handler{config, storage}
|
GetSiteByDomain(ctx context.Context, domain string) (database.Site, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(storage afero.Fs, db sitesQueries, domain string) *Handler {
|
||||||
|
return &Handler{storage, db, domain}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
conf conf.Conf
|
|
||||||
storageFs afero.Fs
|
storageFs afero.Fs
|
||||||
|
db sitesQueries
|
||||||
|
domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
|
||||||
site, branch, subdomain, ok := h.findSiteBranchSubdomain(req.Host)
|
host, _, err := net.SplitHostPort(req.Host)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "Bad Gateway", http.StatusBadGateway)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
site, ok := strings.CutSuffix(host, "."+h.domain)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(rw, "Bad Gateway", http.StatusBadGateway)
|
http.Error(rw, "Bad Gateway", http.StatusBadGateway)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
site = conf.SlugFromDomain(site)
|
||||||
|
branch := req.URL.User.Username()
|
||||||
if branch == "" {
|
if branch == "" {
|
||||||
for _, i := range indexBranches {
|
for _, i := range indexBranches {
|
||||||
if h.tryServePath(rw, site, i, subdomain, req.URL.Path) {
|
if h.tryServePath(rw, site, i, req.URL.Path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if h.tryServePath(rw, site, branch, subdomain, req.URL.Path) {
|
} else if h.tryServePath(rw, site, branch, req.URL.Path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Error(rw, "404 Not Found", http.StatusNotFound)
|
http.Error(rw, "404 Not Found", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) findSiteBranchSubdomain(host string) (site, branch, subdomain string, ok bool) {
|
func (h *Handler) tryServePath(rw http.ResponseWriter, site, branch, p string) bool {
|
||||||
var siteN int
|
|
||||||
siteN, site = h.findSite(host)
|
|
||||||
if site == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if host[siteN] != '-' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
host = host[siteN+1:]
|
|
||||||
|
|
||||||
strings.LastIndexByte(host, '-')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) findSite(host string) (int, string) {
|
|
||||||
siteVal, siteN, siteOk := h.conf.Get(host)
|
|
||||||
if !siteOk || siteVal == nil {
|
|
||||||
return -1, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// so I used less than or equal here that's to prevent a bug where the prefix
|
|
||||||
// found is longer than the string obviously that sounds impossible, and it is,
|
|
||||||
// but I would rather the program not crash if some other bug allows this weird
|
|
||||||
// event to happen
|
|
||||||
if siteN <= len(host) {
|
|
||||||
return -1, ""
|
|
||||||
}
|
|
||||||
return siteN, siteVal.Domain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) tryServePath(rw http.ResponseWriter, site, branch, subdomain, p string) bool {
|
|
||||||
for _, i := range indexFiles {
|
for _, i := range indexFiles {
|
||||||
if h.tryServeFile(rw, site, branch, subdomain, i(p)) {
|
if h.tryServeFile(rw, site, branch, i(p)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) tryServeFile(rw http.ResponseWriter, site, branch, subdomain, p string) bool {
|
func (h *Handler) tryServeFile(rw http.ResponseWriter, site, branch, p string) bool {
|
||||||
// if there is a subdomain then load files from inside the subdomain folder
|
// prevent path traversal
|
||||||
if subdomain != "" {
|
if strings.Contains(site, "..") || strings.Contains(branch, "..") || strings.Contains(p, "..") {
|
||||||
p = filepath.Join("_subdomain", subdomain, p)
|
http.Error(rw, "400 Bad Request", http.StatusBadRequest)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
open, err := h.storageFs.Open(filepath.Join(site, branch, p))
|
open, err := h.storageFs.Open(filepath.Join(site, branch, p))
|
||||||
switch {
|
switch {
|
||||||
|
@ -3,8 +3,10 @@ package upload
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1f349/bluebell/conf"
|
"github.com/1f349/bluebell/database"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"io"
|
"io"
|
||||||
@ -12,25 +14,35 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(storage afero.Fs) *Handler {
|
type sitesQueries interface {
|
||||||
return &Handler{storage, conf}
|
GetSiteBySlug(ctx context.Context, slug string) (database.Site, error)
|
||||||
|
GetSiteByDomain(ctx context.Context, domain string) (database.Site, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New(storage afero.Fs, db sitesQueries) *Handler {
|
||||||
|
return &Handler{storage, db}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxFileSize = 1 * humanize.GiByte
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
storageFs afero.Fs
|
storageFs afero.Fs
|
||||||
conf *conf.Conf
|
db sitesQueries
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
site := q.Get("site")
|
site := q.Get("site")
|
||||||
branch := q.Get("branch")
|
branch := q.Get("branch")
|
||||||
|
|
||||||
siteConf, siteN, siteOk := h.conf.Get(site)
|
site = strings.ReplaceAll(site, "*", "")
|
||||||
if !siteOk || siteN != len(site) || siteConf == nil {
|
|
||||||
http.Error(rw, "400 Bad Request", http.StatusBadRequest)
|
siteConf, err := h.db.GetSiteByDomain(req.Context(), "*"+site)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if "Bearer "+siteConf.Token != req.Header.Get("Authorization") {
|
if "Bearer "+siteConf.Token != req.Header.Get("Authorization") {
|
||||||
@ -45,7 +57,7 @@ func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httpr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if file is bigger than 1GiB
|
// if file is bigger than 1GiB
|
||||||
if fileHeader.Size > 1074000000 {
|
if fileHeader.Size > maxFileSize {
|
||||||
http.Error(rw, "File too big", http.StatusBadRequest)
|
http.Error(rw, "File too big", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user