mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-08 04:20:59 -05:00
Add 'thumbnails/' from commit '2ebf7b2f23af96b3b499c401fe5498a9349403d8'
git-subtree-dir: thumbnails git-subtree-mainline:ccc651a11egit-subtree-split:2ebf7b2f23
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/metrics"
|
||||
v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// NewInstrument returns a service that instruments metrics.
|
||||
func NewInstrument(next v0proto.ThumbnailServiceHandler, metrics *metrics.Metrics) v0proto.ThumbnailServiceHandler {
|
||||
return instrument{
|
||||
next: next,
|
||||
metrics: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
type instrument struct {
|
||||
next v0proto.ThumbnailServiceHandler
|
||||
metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// GetThumbnail implements the ThumbnailServiceHandler interface.
|
||||
func (i instrument) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error {
|
||||
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) {
|
||||
us := v * 1000_000
|
||||
i.metrics.Latency.WithLabelValues().Observe(us)
|
||||
i.metrics.Duration.WithLabelValues().Observe(v)
|
||||
}))
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
err := i.next.GetThumbnail(ctx, req, rsp)
|
||||
|
||||
if err != nil {
|
||||
i.metrics.Counter.WithLabelValues().Inc()
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0"
|
||||
)
|
||||
|
||||
// NewLogging returns a service that logs messages.
|
||||
func NewLogging(next v0proto.ThumbnailServiceHandler, logger log.Logger) v0proto.ThumbnailServiceHandler {
|
||||
return logging{
|
||||
next: next,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type logging struct {
|
||||
next v0proto.ThumbnailServiceHandler
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// GetThumbnail implements the ThumbnailServiceHandler interface.
|
||||
func (l logging) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error {
|
||||
start := time.Now()
|
||||
err := l.next.GetThumbnail(ctx, req, rsp)
|
||||
|
||||
logger := l.logger.With().
|
||||
Str("method", "Thumbnails.GetThumbnail").
|
||||
Dur("duration", time.Since(start)).
|
||||
Logger()
|
||||
|
||||
if err != nil {
|
||||
logger.Warn().
|
||||
Err(err).
|
||||
Msg("Failed to execute")
|
||||
} else {
|
||||
logger.Debug().
|
||||
Msg("")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/config"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnail/storage"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
ThumbnailStorage storage.Storage
|
||||
ImageSource imgsource.Source
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware provides a function to set the middleware option.
|
||||
func Middleware(val ...func(http.Handler) http.Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.Middleware = val
|
||||
}
|
||||
}
|
||||
|
||||
// ThumbnailStorage provides a function to set the thumbnail storage option.
|
||||
func ThumbnailStorage(val storage.Storage) Option {
|
||||
return func(o *Options) {
|
||||
o.ThumbnailStorage = val
|
||||
}
|
||||
}
|
||||
|
||||
// ThumbnailSource provides a function to set the image source option.
|
||||
func ThumbnailSource(val imgsource.Source) Option {
|
||||
return func(o *Options) {
|
||||
o.ImageSource = val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
|
||||
merrors "github.com/micro/go-micro/v2/errors"
|
||||
"github.com/owncloud/ocis-pkg/v2/log"
|
||||
v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnail"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnail/imgsource"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnail/resolution"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewService returns a service implementation for Service.
|
||||
func NewService(opts ...Option) v0proto.ThumbnailServiceHandler {
|
||||
options := newOptions(opts...)
|
||||
logger := options.Logger
|
||||
resolutions, err := resolution.New(options.Config.Thumbnail.Resolutions)
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msg("resolutions not configured correctly")
|
||||
}
|
||||
svc := Thumbnail{
|
||||
serviceID: options.Config.Server.Namespace + "." + options.Config.Server.Name,
|
||||
manager: thumbnail.NewSimpleManager(
|
||||
options.ThumbnailStorage,
|
||||
logger,
|
||||
),
|
||||
resolutions: resolutions,
|
||||
source: options.ImageSource,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
// Thumbnail implements the GRPC handler.
|
||||
type Thumbnail struct {
|
||||
serviceID string
|
||||
manager thumbnail.Manager
|
||||
resolutions resolution.Resolutions
|
||||
source imgsource.Source
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// GetThumbnail retrieves a thumbnail for an image
|
||||
func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error {
|
||||
encoder := thumbnail.EncoderForType(req.Filetype.String())
|
||||
if encoder == nil {
|
||||
g.logger.Debug().Str("filetype", req.Filetype.String()).Msg("unsupported filetype")
|
||||
return nil
|
||||
}
|
||||
r := g.resolutions.ClosestMatch(int(req.Width), int(req.Height))
|
||||
|
||||
auth := req.Authorization
|
||||
if auth == "" {
|
||||
return merrors.BadRequest(g.serviceID, "authorization is missing")
|
||||
}
|
||||
username, err := usernameFromAuthorization(auth)
|
||||
if err != nil {
|
||||
return merrors.InternalServerError(g.serviceID, "could not get username: %v", err.Error())
|
||||
}
|
||||
|
||||
tr := thumbnail.Request{
|
||||
Resolution: r,
|
||||
ImagePath: req.Filepath,
|
||||
Encoder: encoder,
|
||||
ETag: req.Etag,
|
||||
Username: username,
|
||||
}
|
||||
|
||||
thumbnail := g.manager.GetStored(tr)
|
||||
if thumbnail != nil {
|
||||
rsp.Thumbnail = thumbnail
|
||||
rsp.Mimetype = tr.Encoder.MimeType()
|
||||
return nil
|
||||
}
|
||||
|
||||
sCtx := imgsource.WithAuthorization(ctx, auth)
|
||||
img, err := g.source.Get(sCtx, tr.ImagePath)
|
||||
if err != nil {
|
||||
return merrors.InternalServerError(g.serviceID, "could not get image from source: %v", err.Error())
|
||||
}
|
||||
if img == nil {
|
||||
return merrors.InternalServerError(g.serviceID, "could not get image from source")
|
||||
}
|
||||
thumbnail, err = g.manager.Get(tr, img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsp.Thumbnail = thumbnail
|
||||
rsp.Mimetype = tr.Encoder.MimeType()
|
||||
return nil
|
||||
}
|
||||
|
||||
func usernameFromAuthorization(auth string) (string, error) {
|
||||
tokenString := auth[len("Bearer "):] // strip the bearer prefix
|
||||
|
||||
var claims map[string]interface{}
|
||||
token, err := jwt.ParseSigned(tokenString)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not parse auth token")
|
||||
}
|
||||
err = token.UnsafeClaimsWithoutVerification(&claims)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not get claims from auth token")
|
||||
}
|
||||
|
||||
identityMap := claims["kc.identity"].(map[string]interface{})
|
||||
username := identityMap["kc.i.un"].(string)
|
||||
|
||||
return username, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v0proto "github.com/owncloud/ocis-thumbnails/pkg/proto/v0"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// NewTracing returns a service that instruments traces.
|
||||
func NewTracing(next v0proto.ThumbnailServiceHandler) v0proto.ThumbnailServiceHandler {
|
||||
return tracing{
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
type tracing struct {
|
||||
next v0proto.ThumbnailServiceHandler
|
||||
}
|
||||
|
||||
// GetThumbnail implements the ThumbnailServiceHandler interface.
|
||||
func (t tracing) GetThumbnail(ctx context.Context, req *v0proto.GetRequest, rsp *v0proto.GetResponse) error {
|
||||
ctx, span := trace.StartSpan(ctx, "Thumbnails.GetThumbnail")
|
||||
defer span.End()
|
||||
|
||||
span.Annotate([]trace.Attribute{
|
||||
trace.StringAttribute("filepath", req.Filepath),
|
||||
trace.StringAttribute("filetype", req.Filetype.String()),
|
||||
trace.StringAttribute("etag", req.Etag),
|
||||
trace.Int64Attribute("width", int64(req.Width)),
|
||||
trace.Int64Attribute("height", int64(req.Height)),
|
||||
}, "Execute Thumbnails.GetThumbnail handler")
|
||||
|
||||
return t.next.GetThumbnail(ctx, req, rsp)
|
||||
}
|
||||
Reference in New Issue
Block a user