Split out templates, add readiness endpoints
parent
bf94a2e2bf
commit
7a2bea5969
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
|
|
17
logger.go
17
logger.go
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
2
main.go
2
main.go
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
24
s3.go
|
@ -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()
|
||||
|
||||
|
|
20
store.go
20
store.go
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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' },
|
||||
] }),
|
||||
],
|
||||
};
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 234 B |
|
@ -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"}}
|
||||
|
|
|
@ -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"}}
|
||||
|
|
Loading…
Reference in New Issue