diff --git a/app.favicon.go b/app.favicon.go new file mode 100644 index 0000000..961d6ed --- /dev/null +++ b/app.favicon.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "embed" + "io/fs" + "net/http" + + "go.sour.is/pkg/lg" + "go.sour.is/pkg/service" +) + +var _ = apps.Register(10, func(ctx context.Context, svc *service.Harness) error { + _, span := lg.Span(ctx) + defer span.End() + + svc.Add(&favicon{}) + + return nil +}) + +//go:embed favicon/* +var faviconAsset embed.FS + +type favicon struct{} + +func (favicon) RegisterHTTP(mux *http.ServeMux) { + dir, _ := fs.Sub(faviconAsset, "favicon") + srv := http.FileServer(http.FS(dir)) + + mux.Handle("/favicon.ico", srv) + mux.Handle("/favicon.txt", srv) + mux.Handle("/favicon-16x16.png", srv) + mux.Handle("/favicon-32x32.png", srv) + mux.Handle("/android-chrome-192x192.png", srv) + mux.Handle("/android-chrome-512x512.png", srv) + mux.Handle("/apple-touch-icon.png", srv) + mux.Handle("/site.webmanifest", srv) +} diff --git a/app.image.go b/app.image.go index 72567f0..6186fef 100644 --- a/app.image.go +++ b/app.image.go @@ -15,7 +15,7 @@ var _ = apps.Register(40, func(ctx context.Context, svc *service.Harness) error store := env.Default("IMAGE_STORE", "data/") - a, err := image.New(store, -1) + a, err := image.New(ctx, store, -1) if err != nil { return err } diff --git a/favicon/android-chrome-192x192.png b/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..16b457e Binary files /dev/null and b/favicon/android-chrome-192x192.png differ diff --git a/favicon/android-chrome-512x512.png b/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..d701c0d Binary files /dev/null and b/favicon/android-chrome-512x512.png differ diff --git a/favicon/apple-touch-icon.png b/favicon/apple-touch-icon.png new file mode 100644 index 0000000..44b11b8 Binary files /dev/null and b/favicon/apple-touch-icon.png differ diff --git a/favicon/favicon-16x16.png b/favicon/favicon-16x16.png new file mode 100644 index 0000000..a64441e Binary files /dev/null and b/favicon/favicon-16x16.png differ diff --git a/favicon/favicon-32x32.png b/favicon/favicon-32x32.png new file mode 100644 index 0000000..c0261c0 Binary files /dev/null and b/favicon/favicon-32x32.png differ diff --git a/favicon/favicon.ico b/favicon/favicon.ico new file mode 100644 index 0000000..0a456a6 Binary files /dev/null and b/favicon/favicon.ico differ diff --git a/favicon/favicon.txt b/favicon/favicon.txt new file mode 100644 index 0000000..2c2e810 --- /dev/null +++ b/favicon/favicon.txt @@ -0,0 +1,6 @@ +This favicon was generated using the following graphics from Twitter Twemoji: + +- Graphics Title: 1f4cb.svg +- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji) +- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f4cb.svg +- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/) diff --git a/favicon/site.webmanifest b/favicon/site.webmanifest new file mode 100644 index 0000000..32a7512 --- /dev/null +++ b/favicon/site.webmanifest @@ -0,0 +1 @@ +{"name":"sour.is paste","short_name":"paste","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/go.mod b/go.mod index 635b234..08dab25 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.41.0 // indirect - go.opentelemetry.io/otel/metric v1.18.0 // indirect + go.opentelemetry.io/otel/metric v1.18.0 go.opentelemetry.io/otel/sdk v1.18.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.41.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect diff --git a/image/image.go b/image/image.go index f22315a..acb37bf 100644 --- a/image/image.go +++ b/image/image.go @@ -17,6 +17,7 @@ import ( "github.com/h2non/filetype" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" "golang.org/x/sys/unix" @@ -27,11 +28,15 @@ import ( type image struct { store string maxSize int64 + + m_image_get metric.Int64Counter + m_image_post metric.Int64Counter + m_image_error metric.Int64Counter } const DefaultMaxSize = 500 * 1024 * 1024 -func New(store string, maxSize int64) (a *image, err error) { +func New(ctx context.Context, store string, maxSize int64) (a *image, err error) { a = &image{ store: store, maxSize: DefaultMaxSize, @@ -45,13 +50,31 @@ func New(store string, maxSize int64) (a *image, err error) { return nil, fmt.Errorf("image Store location [%s] does not exist or is not writable", a.store) } - return a, nil + m := lg.Meter(ctx) + + var merr error + a.m_image_get, merr = m.Int64Counter("m_image_get", + metric.WithDescription("retrieve image from store"), + ) + err = errors.Join(err, merr) + + a.m_image_post, merr = m.Int64Counter("m_image_post", + metric.WithDescription("save image to store"), + ) + err = errors.Join(err, merr) + + a.m_image_error, merr = m.Int64Counter("m_image_error", + metric.WithDescription("image api error"), + ) + err = errors.Join(err, merr) + + + return a, err } func (a *image) RegisterHTTP(mux *http.ServeMux) { mux.Handle("/i", http.StripPrefix("/i", a)) mux.Handle("/i/", http.StripPrefix("/i/", a)) mux.Handle("/3/upload", a) - } func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -60,10 +83,13 @@ func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet, http.MethodHead: + a.m_image_get.Add(ctx, 1) + name := strings.TrimPrefix(r.URL.Path, "/") if name != "" { rdr, head, err := a.loadFile(ctx, name) if err != nil { + a.m_image_error.Add(ctx, 1) writeError(w, err) span.RecordError(err) return @@ -88,6 +114,8 @@ func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) { } case http.MethodPost: + a.m_image_post.Add(ctx, 1) + var err error defer r.Body.Close() @@ -121,6 +149,7 @@ func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) { } id, err := a.put(ctx, fd, length) if err != nil { + a.m_image_error.Add(ctx, 1) writeError(w, err) span.RecordError(err) return @@ -183,7 +212,7 @@ func (a *image) loadFile(ctx context.Context, name string) (io.ReadSeekCloser, * return nil, nil, err } - f.Seek(0,0) + f.Seek(0, 0) return f, &Header{Mime: mime, Modified: stat.ModTime(), ETag: name}, diff --git a/image/image_test.go b/image/image_test.go index 385f265..f1a3459 100644 --- a/image/image_test.go +++ b/image/image_test.go @@ -2,6 +2,7 @@ package image_test import ( "bytes" + "context" "encoding/base64" "fmt" "mime/multipart" @@ -21,10 +22,12 @@ var testImage = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C func TestPostImgur(t *testing.T) { is := is.New(t) + ctx := context.Background() + dir, err := os.MkdirTemp("", "image") is.NoErr(err) - im, err := image.New(dir, 0) + im, err := image.New(ctx, dir, 0) is.NoErr(err) { @@ -78,11 +81,12 @@ func TestPostImgur(t *testing.T) { func TestPost(t *testing.T) { is := is.New(t) + ctx := context.Background() dir, err := os.MkdirTemp("", "image") is.NoErr(err) - im, err := image.New(dir, 0) + im, err := image.New(ctx, dir, 0) is.NoErr(err) { @@ -107,7 +111,6 @@ func TestPost(t *testing.T) { s := base64.StdEncoding.EncodeToString(res.Body.Bytes()) is.Equal(s, testImage) - } err = os.RemoveAll(dir)