add-imgur-api #1
39
app.favicon.go
Normal file
39
app.favicon.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ var _ = apps.Register(40, func(ctx context.Context, svc *service.Harness) error
|
||||||
|
|
||||||
store := env.Default("IMAGE_STORE", "data/")
|
store := env.Default("IMAGE_STORE", "data/")
|
||||||
|
|
||||||
a, err := image.New(store, -1)
|
a, err := image.New(ctx, store, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
BIN
favicon/android-chrome-192x192.png
Normal file
BIN
favicon/android-chrome-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
favicon/android-chrome-512x512.png
Normal file
BIN
favicon/android-chrome-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
favicon/apple-touch-icon.png
Normal file
BIN
favicon/apple-touch-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
BIN
favicon/favicon-16x16.png
Normal file
BIN
favicon/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 472 B |
BIN
favicon/favicon-32x32.png
Normal file
BIN
favicon/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 847 B |
BIN
favicon/favicon.ico
Normal file
BIN
favicon/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
6
favicon/favicon.txt
Normal file
6
favicon/favicon.txt
Normal file
|
@ -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/)
|
1
favicon/site.webmanifest
Normal file
1
favicon/site.webmanifest
Normal file
|
@ -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"}
|
2
go.mod
2
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 v1.18.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp 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/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 v1.18.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.41.0 // indirect
|
go.opentelemetry.io/otel/sdk/metric v0.41.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
@ -27,11 +28,15 @@ import (
|
||||||
type image struct {
|
type image struct {
|
||||||
store string
|
store string
|
||||||
maxSize int64
|
maxSize int64
|
||||||
|
|
||||||
|
m_image_get metric.Int64Counter
|
||||||
|
m_image_post metric.Int64Counter
|
||||||
|
m_image_error metric.Int64Counter
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultMaxSize = 500 * 1024 * 1024
|
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{
|
a = &image{
|
||||||
store: store,
|
store: store,
|
||||||
maxSize: DefaultMaxSize,
|
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 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) {
|
func (a *image) RegisterHTTP(mux *http.ServeMux) {
|
||||||
mux.Handle("/i", http.StripPrefix("/i", a))
|
mux.Handle("/i", http.StripPrefix("/i", a))
|
||||||
mux.Handle("/i/", http.StripPrefix("/i/", a))
|
mux.Handle("/i/", http.StripPrefix("/i/", a))
|
||||||
mux.Handle("/3/upload", a)
|
mux.Handle("/3/upload", a)
|
||||||
|
|
||||||
}
|
}
|
||||||
func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
@ -60,10 +83,13 @@ func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet, http.MethodHead:
|
case http.MethodGet, http.MethodHead:
|
||||||
|
a.m_image_get.Add(ctx, 1)
|
||||||
|
|
||||||
name := strings.TrimPrefix(r.URL.Path, "/")
|
name := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
if name != "" {
|
if name != "" {
|
||||||
rdr, head, err := a.loadFile(ctx, name)
|
rdr, head, err := a.loadFile(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
a.m_image_error.Add(ctx, 1)
|
||||||
writeError(w, err)
|
writeError(w, err)
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
return
|
return
|
||||||
|
@ -88,6 +114,8 @@ func (a *image) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
|
a.m_image_post.Add(ctx, 1)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
defer r.Body.Close()
|
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)
|
id, err := a.put(ctx, fd, length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
a.m_image_error.Add(ctx, 1)
|
||||||
writeError(w, err)
|
writeError(w, err)
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,6 +2,7 @@ package image_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
@ -21,10 +22,12 @@ var testImage = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C
|
||||||
func TestPostImgur(t *testing.T) {
|
func TestPostImgur(t *testing.T) {
|
||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "image")
|
dir, err := os.MkdirTemp("", "image")
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
im, err := image.New(dir, 0)
|
im, err := image.New(ctx, dir, 0)
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -78,11 +81,12 @@ func TestPostImgur(t *testing.T) {
|
||||||
func TestPost(t *testing.T) {
|
func TestPost(t *testing.T) {
|
||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "image")
|
dir, err := os.MkdirTemp("", "image")
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
im, err := image.New(dir, 0)
|
im, err := image.New(ctx, dir, 0)
|
||||||
is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -107,7 +111,6 @@ func TestPost(t *testing.T) {
|
||||||
|
|
||||||
s := base64.StdEncoding.EncodeToString(res.Body.Bytes())
|
s := base64.StdEncoding.EncodeToString(res.Body.Bytes())
|
||||||
is.Equal(s, testImage)
|
is.Equal(s, testImage)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.RemoveAll(dir)
|
err = os.RemoveAll(dir)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user