1
0
Fork 0

Split out templates, add readiness endpoints

upload-progress
Ambrose Chua 2021-05-23 11:54:30 +08:00
parent bf94a2e2bf
commit 7a2bea5969
15 changed files with 6938 additions and 91 deletions

View File

@ -12,7 +12,8 @@ type credential struct {
SecretKey string
// Region is critical when signing requests.
Region string
// Endpoint is the base URL of the bucket, including the bucket name (in either the domain or path).
// Endpoint is the base URL of the bucket, including the bucket name (in
// either the domain or path).
//
// Example:
// https://bucketname.s3.us-west-2.amazonaws.com

View File

@ -2,7 +2,6 @@ package main
import (
"errors"
"log"
"net/http"
)
@ -11,7 +10,6 @@ var errBadRequest = errors.New("bad request")
var errInternalServerError = errors.New("internal server error")
func errorResponseStatus(w http.ResponseWriter, req *http.Request, err error) {
errorMessage := err.Error()
errorStatus := http.StatusInternalServerError
if errors.Is(err, errNotFound) {
@ -22,10 +20,13 @@ func errorResponseStatus(w http.ResponseWriter, req *http.Request, err error) {
errorStatus = http.StatusInternalServerError
}
log.Printf("%s %s: %s", req.Method, req.RequestURI, errorMessage)
w.WriteHeader(errorStatus)
}
// errorResponse prints the error message in the response body.
//
// Do not use this function when the error message might contain sensitive
// information.
func errorResponse(w http.ResponseWriter, req *http.Request, err error) {
errorResponseStatus(w, req, err)
w.Write([]byte(err.Error()))

View File

@ -5,11 +5,20 @@ import (
"net/http"
)
type loggerResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (w *loggerResponseWriter) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
func middlewareLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here
log.Printf("%s: %s", r.Method, r.RequestURI)
// Call the next handler, which can be another middleware in the chain, or the final handler.
next.ServeHTTP(w, r)
lw := &loggerResponseWriter{w, http.StatusOK}
next.ServeHTTP(lw, r)
log.Printf("%s %s: %d", r.Method, r.RequestURI, lw.statusCode)
})
}

View File

@ -27,8 +27,8 @@ func main() {
router := mux.NewRouter()
router.Use(middlewareLogger)
router.Methods(http.MethodGet).Path("/readyz").HandlerFunc(readyz)
router.Methods(http.MethodGet).PathPrefix("/assets").Handler(http.FileServer(http.FS(assetsWeb)))
router.Methods(http.MethodGet).Path("/favicon.ico").Handler(http.FileServer(http.FS(assetsWeb)))
router.Methods(http.MethodGet).Path("/").HandlerFunc(handleCreate)
uploadRouter := router.PathPrefix("/{id}").Subrouter()

23
readyz.go Normal file
View File

@ -0,0 +1,23 @@
package main
import (
"bytes"
"fmt"
"net/http"
)
func readyz(w http.ResponseWriter, req *http.Request) {
status := http.StatusOK
var msg bytes.Buffer
storeErr := globalStore.ping()
if storeErr != nil {
status = http.StatusServiceUnavailable
fmt.Fprintf(&msg, "store: %v\n", storeErr)
} else {
fmt.Fprintf(&msg, "store: ok\n")
}
w.WriteHeader(status)
w.Write(msg.Bytes())
}

24
s3.go
View File

@ -60,7 +60,10 @@ type initiateMultipartUploadResult struct {
UploadID string `xml:"UploadId"`
}
func initiateMultipartUpload(key string, cred credential) (initiateMultipartUploadResult, error) {
func initiateMultipartUpload(
key string,
cred credential,
) (initiateMultipartUploadResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
@ -119,7 +122,11 @@ type listPartsResult struct {
Parts []part `xml:"Part"`
}
func listParts(key, uploadID string, cred credential, partNumberMarker uint32) (listPartsResult, error) {
func listParts(
key, uploadID string,
cred credential,
partNumberMarker uint32,
) (listPartsResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
@ -151,7 +158,7 @@ func listParts(key, uploadID string, cred credential, partNumberMarker uint32) (
}
for i := range result.Parts {
result.Parts[i].ETag = strings.TrimSuffix(strings.TrimPrefix(result.Parts[i].ETag, "\""), "\"")
result.Parts[i].ETag = stripETag(result.Parts[i].ETag)
}
return result, nil
@ -186,7 +193,11 @@ type completeMultipartUploadResult struct {
ETag string
}
func completeMultipartUpload(key, uploadID string, parts []completePart, cred credential) (completeMultipartUploadResult, error) {
func completeMultipartUpload(
key, uploadID string,
parts []completePart,
cred credential,
) (completeMultipartUploadResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
@ -230,7 +241,10 @@ func completeMultipartUpload(key, uploadID string, parts []completePart, cred cr
/* abortMultipartUpload */
func abortMultipartUpload(key, uploadID string, cred credential) error {
func abortMultipartUpload(
key, uploadID string,
cred credential,
) error {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

View File

@ -17,6 +17,7 @@ var errInvalidConnectionType = errors.New("invalid connection type")
type store interface {
put(key string, data []byte, expire time.Duration) error
get(key string) ([]byte, error)
ping() error
}
type redisStore struct {
@ -51,8 +52,23 @@ func newRedisStore(connection string) (*redisStore, error) {
return &redisStore{client}, nil
}
func (s *redisStore) ping() error {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
var pong string
err := s.client.Do(ctx, radix.Cmd(&pong, "PING"))
if err != nil {
return err
}
if pong != "PONG" {
return errInternalServerError
}
return nil
}
func (s *redisStore) put(key string, data []byte, expire time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
exists := 0
@ -74,7 +90,7 @@ func (s *redisStore) put(key string, data []byte, expire time.Duration) error {
}
func (s *redisStore) get(key string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
var data []byte

3
web/foot.tmpl Normal file
View File

@ -0,0 +1,3 @@
</body>
</html>

10
web/head.tmpl Normal file
View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.}}</title>
<link rel="icon" href="assets/favicon.png" type="image/png">
<link rel="stylesheet" href="assets/bundle.css">
</head>
<body>

6855
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,8 @@
"@uppy/status-bar": "^1.9.3",
"filesize": "^6.3.0",
"rollup": "^2.48.0",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-terser": "^7.0.2"
}
}

View File

@ -1,6 +1,7 @@
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import css from 'rollup-plugin-css-only';
import postcss from 'rollup-plugin-postcss';
import copy from 'rollup-plugin-copy';
import { terser } from 'rollup-plugin-terser';
export default {
@ -15,6 +16,9 @@ export default {
plugins: [
commonjs(),
resolve({ browser: true }),
css({ output: 'bundle.css' }),
postcss({ extract: true, minimize: true }),
copy({ targets: [
{ src: 'static/favicon.png', dest: 'assets' },
] }),
],
};

BIN
web/static/favicon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@ -1,16 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dropbox Not Found</title>
<link rel="stylesheet" href="assets/bundle.css">
</head>
<body>
<div class="upload-wrapper">
<div class="upload">
<h1>Dropbox Not Found</h1>
<p>The dropbox you are looking for doesn't exist or has expired.</p>
</div>
{{template "head.tmpl" "Dropbox Not Found"}}
<div class="upload-wrapper">
<div class="upload">
<h1>Dropbox Not Found</h1>
<p>The dropbox you are looking for doesn't exist or has expired.</p>
</div>
</body>
</div>
{{template "foot.tmpl"}}

View File

@ -1,23 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload</title>
<link rel="stylesheet" href="assets/bundle.css">
</head>
<body>
<div class="upload-wrapper">
<div class="upload">
<div id="drop-area"></div>
<div id="status-area"></div>
<div id="log-header">
<h4>Completed</h4>
<button id="log-clear">Clear</button>
</div>
<div id="log-area"></div>
{{template "head.tmpl" "Dropbox Not Found"}}
<div class="upload-wrapper">
<div class="upload">
<div id="drop-area"></div>
<div id="status-area"></div>
<div id="log-header">
<h4>Completed</h4>
<button id="log-clear">Clear</button>
</div>
<div id="log-area"></div>
</div>
</div>
<script src="assets/bundle.js"></script>
</body>
<script src="assets/bundle.js"></script>
{{template "foot.tmpl"}}