Skip to content

Commit 325849b

Browse files
authored
Implementing guest upload / file requests (#337)
* Add API call to download files and optionally not increasing counter, added Download API permission * Added option for presigned URLs and add download button in main menu * Breaking: Check that chunks are at least 5MB * Added docs * A lot of refactoring and minor fixes
1 parent e2ec083 commit 325849b

File tree

101 files changed

+7533
-998
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+7533
-998
lines changed

build/go-generate/minifyStaticContent.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,16 @@ func getPaths() []converter {
7272
Name: "wasm_exec JS",
7373
})
7474
result = append(result, converter{
75-
InputPath: pathPrefix + "js/dateformat.js",
76-
OutputPath: pathPrefix + "js/min/dateformat.min.js",
75+
InputPath: pathPrefix + "js/all_public.js",
76+
OutputPath: pathPrefix + "js/min/all_public.min.js",
7777
Type: "text/javascript",
78-
Name: "Dateformat JS",
78+
Name: "Public functions JS",
7979
})
8080
result = append(result, converter{
81-
InputPath: pathPrefix + "js/uuid.js",
82-
OutputPath: pathPrefix + "js/min/uuid.min.js",
81+
InputPath: pathPrefix + "js/public_upload.js",
82+
OutputPath: pathPrefix + "js/min/public_upload.min.js",
8383
Type: "text/javascript",
84-
Name: "UUID JS",
84+
Name: "Public upload JS",
8585
})
8686
return result
8787
}

build/go-generate/updateApiRouting.go

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,28 @@ func hasRequiredTag(tags []string) bool {
8181
return false
8282
}
8383

84-
func headerExists(headerName string, required, isString bool) string {
84+
func hasBase64Tag(tags []string) bool {
85+
// Check if the tag contains "supportBase64:true"
86+
for _, tag := range tags {
87+
if strings.HasPrefix(tag, "supportBase64") {
88+
return true
89+
}
90+
}
91+
return false
92+
}
93+
94+
func headerExists(headerName string, required, isString, base64Support bool) string {
95+
base64SupportEntry := ""
96+
if base64Support {
97+
base64SupportEntry = ", has base64support"
98+
}
8599
return fmt.Sprintf("\n"+`
86-
// RequestParser header value %s, required: %v
100+
// RequestParser header value %s, required: %v%s
87101
exists, err = checkHeaderExists(r, %s, %v, %v)
88102
if err != nil {
89103
return err
90104
}
91-
p.foundHeaders[%s] = exists`, headerName, required, headerName, required, isString, headerName)
105+
p.foundHeaders[%s] = exists`, headerName, required, base64SupportEntry, headerName, required, isString, headerName)
92106
}
93107

94108
func generateParseRequestMethod(typeName string, fields []*ast.Field) string {
@@ -122,25 +136,41 @@ func generateParseRequestMethod(typeName string, fields []*ast.Field) string {
122136
// Check if the tag has the "header" key and extract its value
123137
tagParts := strings.Split(tag, " ")
124138
required := hasRequiredTag(tagParts)
139+
base64Support := hasBase64Tag(tagParts)
125140
for _, part := range tagParts {
126141
if strings.HasPrefix(part, "header:") {
127-
// Extract header name after 'header:'
142+
// Extract the header name after 'header:'
128143
headerName := strings.TrimPrefix(part, "header:")
129144

130145
fieldType := field.Type.(*ast.Ident).Name
131146

132-
// Use appropriate parsing function based on the field type
147+
// Use the appropriate parsing function based on the field type
133148
switch fieldType {
134149
case "string":
135-
method += headerExists(headerName, required, true)
136-
method += fmt.Sprintf(`
137-
if (exists) {
138-
p.%s = r.Header.Get(%s)
150+
method += headerExists(headerName, required, true, base64Support)
151+
if !base64Support {
152+
method += fmt.Sprintf(`
153+
if (exists) {
154+
p.%s = r.Header.Get(%s)
155+
}
156+
`, field.Names[0].Name, headerName)
157+
} else {
158+
method += fmt.Sprintf(`
159+
if (exists) {
160+
p.%s = r.Header.Get(%s)
161+
if strings.HasPrefix(p.%s, "base64:") {
162+
decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(p.%s, "base64:"))
163+
if err != nil {
164+
return err
165+
}
166+
p.%s = string(decoded)
167+
}
168+
}
169+
`, field.Names[0].Name, headerName, field.Names[0].Name, field.Names[0].Name, field.Names[0].Name)
139170
}
140-
`, field.Names[0].Name, headerName)
141171

142172
case "bool":
143-
method += headerExists(headerName, required, false)
173+
method += headerExists(headerName, required, false, false)
144174
method += fmt.Sprintf(`
145175
if (exists) {
146176
p.%s, err = parseHeaderBool(r, %s)
@@ -151,7 +181,7 @@ func generateParseRequestMethod(typeName string, fields []*ast.Field) string {
151181
`, field.Names[0].Name, headerName, strings.Replace(headerName, "\"", "", -1))
152182

153183
case "int":
154-
method += headerExists(headerName, required, false)
184+
method += headerExists(headerName, required, false, false)
155185
method += fmt.Sprintf(`
156186
if (exists) {
157187
p.%s, err = parseHeaderInt(r, %s)
@@ -162,7 +192,7 @@ func generateParseRequestMethod(typeName string, fields []*ast.Field) string {
162192
`, field.Names[0].Name, headerName, strings.Replace(headerName, "\"", "", -1))
163193

164194
case "int64":
165-
method += headerExists(headerName, required, false)
195+
method += headerExists(headerName, required, false, false)
166196
method += fmt.Sprintf(`
167197
if (exists) {
168198
p.%s, err = parseHeaderInt64(r, %s)
@@ -236,8 +266,10 @@ func main() {
236266
package api
237267
238268
import (
269+
"encoding/base64"
239270
"fmt"
240271
"net/http"
272+
"strings"
241273
)
242274
243275
// Do not modify: This is an automatically generated file created by updateApiRouting.go

build/go-generate/updateEnvVariables.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,15 @@ func extractEnvVars() ([]envVar, error) {
124124
})
125125

126126
result = append(result,
127-
envVar{
128-
Name: "DOCKER_NONROOT",
129-
Action: "DEPRECATED.\n\nDocker only: Runs the binary in the container as a non-root user, if set to \"true\"",
130-
Default: "false",
131-
},
132127
envVar{
133128
Name: "TMPDIR",
134129
Action: "Sets the path which contains temporary files",
135-
Default: "Non-Docker: Default OS path\n\nDocker: [DATA_DIR]",
130+
Default: "Non-Docker: Default OS path\nDocker: [DATA_DIR]",
131+
},
132+
envVar{
133+
Name: "DOCKER_NONROOT",
134+
Action: "DEPRECATED.\nDocker only: Runs the binary in the container as a non-root user, if set to \"true\"",
135+
Default: "false",
136136
},
137137
)
138138

build/go.mod

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,30 @@ require (
1919
golang.org/x/oauth2 v0.27.0
2020
golang.org/x/sync v0.11.0
2121
golang.org/x/term v0.37.0
22+
golang.org/x/time v0.14.0
2223
gopkg.in/yaml.v3 v3.0.1
2324
modernc.org/sqlite v1.35.0
2425
)
2526

2627
require (
2728
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
29+
github.com/djherbis/atime v1.1.0 // indirect
2830
github.com/dustin/go-humanize v1.0.1 // indirect
31+
github.com/fsnotify/fsnotify v1.9.0 // indirect
2932
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
3033
github.com/google/uuid v1.6.0 // indirect
3134
github.com/jmespath/go-jmespath v0.4.0 // indirect
35+
github.com/jmoiron/sqlx v1.4.0 // indirect
3236
github.com/mattn/go-isatty v0.0.20 // indirect
3337
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
3438
github.com/ncruces/go-strftime v0.1.9 // indirect
39+
github.com/pelletier/go-toml v1.9.5 // indirect
3540
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
3641
github.com/rivo/uniseg v0.4.7 // indirect
3742
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect
38-
github.com/tdewolff/minify/v2 v2.24.2 // indirect
39-
github.com/tdewolff/parse/v2 v2.8.3 // indirect
43+
github.com/tdewolff/argp v0.0.0-20250430135133-0f54527d2b1e // indirect
44+
github.com/tdewolff/minify/v2 v2.24.8 // indirect
45+
github.com/tdewolff/parse/v2 v2.8.5 // indirect
4046
github.com/yuin/gopher-lua v1.1.1 // indirect
4147
go.shabbyrobe.org/gocovmerge v0.0.0-20230507111327-fa4f82cfbf4d // indirect
4248
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect

build/go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
12
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
23
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
34
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
@@ -8,21 +9,28 @@ github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5
89
github.com/cevatbarisyilmaz/ara v0.0.4/go.mod h1:BfFOxnUd6Mj6xmcvRxHN3Sr21Z1T3U2MYkYOmoQe4Ts=
910
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
1011
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12+
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
1113
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
14+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
1215
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
16+
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
1317
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
1418
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1519
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
1620
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
1721
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
22+
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
1823
github.com/johannesboyne/gofakes3 v0.0.0-20250106100439-5c39aecd6999/go.mod h1:t6osVdP++3g4v2awHz4+HFccij23BbdT1rX3W7IijqQ=
1924
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
2025
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
2126
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
2227
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
28+
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
2329
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
30+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
2431
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
2532
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
33+
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
2634
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
2735
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2836
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -34,12 +42,15 @@ github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
3442
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
3543
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
3644
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
45+
github.com/tdewolff/argp v0.0.0-20250430135133-0f54527d2b1e/go.mod h1:xw2b1X81m4zY1OGytzHNr/YKXbf/STHkK5idoNamlYE=
3746
github.com/tdewolff/minify/v2 v2.23.11 h1:cZqTVCtuVvPC8/GbCvYgIcdAQGmoxEObZzKeKIUixTE=
3847
github.com/tdewolff/minify/v2 v2.23.11/go.mod h1:vmkbfGQ5hp/eYB+TswNWKma67S0a+32HBL+mFWxjZ2Q=
3948
github.com/tdewolff/minify/v2 v2.24.2/go.mod h1:1JrCtoZXaDbqioQZfk3Jdmr0GPJKiU7c1Apmb+7tCeE=
49+
github.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=
4050
github.com/tdewolff/parse/v2 v2.8.2-0.20250806174018-50048bb39781 h1:2qicgFovKg1XtX7Wf6GwexUdpb7q/jMIE2IgkYsVAvE=
4151
github.com/tdewolff/parse/v2 v2.8.2-0.20250806174018-50048bb39781/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
4252
github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
53+
github.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
4354
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
4455
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
4556
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -96,6 +107,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
96107
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
97108
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
98109
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
110+
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
99111
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
100112
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
101113
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

cmd/cli-uploader/Main.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"io"
9+
"os"
10+
"path/filepath"
11+
812
"github.com/forceu/gokapi/cmd/cli-uploader/cliapi"
913
"github.com/forceu/gokapi/cmd/cli-uploader/cliconfig"
1014
"github.com/forceu/gokapi/cmd/cli-uploader/cliconstants"
1115
"github.com/forceu/gokapi/cmd/cli-uploader/cliflags"
1216
"github.com/forceu/gokapi/internal/environment"
1317
"github.com/forceu/gokapi/internal/helper"
1418
"github.com/schollz/progressbar/v3"
15-
"io"
16-
"os"
17-
"path/filepath"
1819
)
1920

2021
func main() {
@@ -25,9 +26,11 @@ func main() {
2526
case cliflags.ModeLogout:
2627
doLogout()
2728
case cliflags.ModeUpload:
28-
processUpload(false)
29+
processUpload(cliflags.ModeUpload)
2930
case cliflags.ModeArchive:
30-
processUpload(true)
31+
processUpload(cliflags.ModeArchive)
32+
case cliflags.ModeDownload:
33+
processDownload()
3134
case cliflags.ModeInvalid:
3235
os.Exit(3)
3336
}
@@ -38,11 +41,11 @@ func doLogin() {
3841
cliconfig.CreateLogin()
3942
}
4043

41-
func processUpload(isArchive bool) {
44+
func processUpload(mode int) {
4245
cliconfig.Load()
43-
uploadParam := cliflags.GetUploadParameters(isArchive)
46+
uploadParam := cliflags.GetUploadParameters(mode)
4447

45-
if isArchive {
48+
if mode == cliflags.ModeArchive {
4649
zipPath, err := zipFolder(uploadParam.Directory, uploadParam.TmpFolder, !uploadParam.JsonOutput)
4750
if err != nil {
4851
fmt.Println(err)
@@ -57,7 +60,7 @@ func processUpload(isArchive bool) {
5760
if err != nil {
5861
fmt.Println()
5962
if errors.Is(cliapi.ErrUnauthorised, err) {
60-
fmt.Println("ERROR: Unauthorised API key. Please re-run login.")
63+
fmt.Println("ERROR: Unauthorised API key. Please re-run login or make sure that the API key has the permission to upload files.")
6164
} else {
6265
fmt.Println("ERROR: Could not upload file")
6366
fmt.Println(err)
@@ -77,6 +80,24 @@ func processUpload(isArchive bool) {
7780
fmt.Println("File Download URL: " + result.UrlDownload)
7881
}
7982

83+
func processDownload() {
84+
cliconfig.Load()
85+
uploadParam := cliflags.GetUploadParameters(cliflags.ModeDownload)
86+
87+
// Perform the download
88+
err := cliapi.DownloadFile(uploadParam)
89+
if err != nil {
90+
fmt.Println()
91+
if errors.Is(cliapi.ErrUnauthorised, err) {
92+
fmt.Println("ERROR: Unauthorised API key. Please re-run login or make sure that the API key has the permission to download files.")
93+
} else {
94+
fmt.Println("ERROR: Could not download file")
95+
fmt.Println(err)
96+
}
97+
os.Exit(1)
98+
}
99+
}
100+
80101
func doLogout() {
81102
err := cliconfig.Delete()
82103
if err != nil {

0 commit comments

Comments
 (0)