rewrite url serfivce

This commit is contained in:
Asim Aslam
2021-12-11 20:23:24 +00:00
parent bba608b405
commit 088f3f7a52
3 changed files with 118 additions and 141 deletions

View File

@@ -2,59 +2,47 @@ package handler
import (
"context"
"errors"
"strings"
"time"
"github.com/micro/micro/v3/service/config"
"github.com/micro/micro/v3/service/logger"
"github.com/micro/micro/v3/service/model"
cache "github.com/patrickmn/go-cache"
"github.com/teris-io/shortid"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/store"
"github.com/micro/services/pkg/tenant"
url "github.com/micro/services/url/proto"
cache "github.com/patrickmn/go-cache"
"github.com/teris-io/shortid"
)
const hostPrefix = "https://m3o.one/u/"
type Url struct {
pairs model.Model
ownerIndex model.Index
cache *cache.Cache
hostPrefix string
}
func NewUrl() *Url {
var hp string
cfg, err := config.Get("micro.url.host_prefix")
if err != nil {
hp = cfg.String(hostPrefix)
}
if len(strings.TrimSpace(hp)) == 0 {
hp = hostPrefix
}
ownerIndex := model.ByEquality("owner")
ownerIndex.Order.Type = model.OrderTypeUnordered
m := model.NewModel(
model.WithKey("shortURL"),
model.WithIndexes(ownerIndex),
)
m.Register(&url.URLPair{})
return &Url{
pairs: m,
ownerIndex: ownerIndex,
hostPrefix: hp,
cache: cache.New(cache.NoExpiration, cache.NoExpiration),
hostPrefix: hp,
}
}
func (e *Url) Shorten(ctx context.Context, req *url.ShortenRequest, rsp *url.ShortenResponse) error {
tenantID, ok := tenant.FromContext(ctx)
tenantId, ok := tenant.FromContext(ctx)
if !ok {
return errors.New("Not authorized")
return errors.Unauthorized("url.shorten", "not authorized")
}
sid, err := shortid.New(1, shortid.DefaultABC, 2342)
if err != nil {
@@ -66,75 +54,83 @@ func (e *Url) Shorten(ctx context.Context, req *url.ShortenRequest, rsp *url.Sho
return err
}
p := &url.URLPair{
val := &url.URLPair{
DestinationURL: req.DestinationURL,
ShortURL: id,
Owner: tenantID,
Created: time.Now().Unix(),
ShortURL: e.hostPrefix + id,
Created: time.Now().Format(time.RFC3339Nano),
}
rsp.ShortURL = e.hostPrefix + id
return e.pairs.Create(p)
// write a global key
key := "url/" + id
if err := store.Write(store.NewRecord(key, val)); err != nil {
return err
}
// write per owner key
key = "urlOwner/" + tenantId + "/" + id
if err := store.Write(store.NewRecord(key, val)); err != nil {
return err
}
return nil
}
func (e *Url) List(ctx context.Context, req *url.ListRequest, rsp *url.ListResponse) error {
tenantID, ok := tenant.FromContext(ctx)
tenantId, ok := tenant.FromContext(ctx)
if !ok {
return errors.New("Not authorized")
return errors.Unauthorized("url.shorten", "not authorized")
}
rsp.UrlPairs = []*url.URLPair{}
rsp.Urls = []*url.URLPair{}
var err error
if req.ShortURL != "" {
err = e.pairs.Read(model.QueryEquals("shortURL", req.ShortURL), &rsp.UrlPairs)
key := "urlOwner/" + tenantId + "/"
var opts []store.ReadOption
if len(req.ShortURL) > 0 {
id := strings.Replace(req.ShortURL, e.hostPrefix, "", -1)
key += id
} else {
err = e.pairs.Read(e.ownerIndex.ToQuery(tenantID), &rsp.UrlPairs)
opts = append(opts, store.ReadPrefix())
}
records, err := store.Read(key, opts...)
if err != nil {
return err
}
for _, v := range rsp.UrlPairs {
// get the counter and add it to db value to improve
// accuracy
// A thing to keep in mind is that in memory cache hits
// from other nodes wont be added to this. Still, hopefully good enough
count, ok := e.cache.Get(v.ShortURL)
if ok {
v.HitCount += count.(int64)
for _, rec := range records {
uri := new(url.URLPair)
if err := rec.Decode(uri); err != nil {
continue
}
v.ShortURL = e.hostPrefix + v.ShortURL
rsp.Urls = append(rsp.Urls, uri)
}
return nil
}
func (e *Url) Proxy(ctx context.Context, req *url.ProxyRequest, rsp *url.ProxyResponse) error {
var pair url.URLPair
id := strings.Replace(req.ShortURL, e.hostPrefix, "", -1)
err := e.pairs.Read(model.QueryEquals("shortURL", id), &pair)
records, err := store.Read("url/" + id)
if err != nil {
return err
}
v, found := e.cache.Get(id)
// @todo there is an ABA problem with this solution
// when it comes to the hit counter
if !found {
e.cache.Set(id, int64(1), cache.NoExpiration)
} else {
// we null out the counter
e.cache.Set(id, int64(0), cache.NoExpiration)
if v.(int64)%10 == 0 {
go func() {
// We add instead of set in case the service runs in multiple
// instances
pair.HitCount += v.(int64) + int64(1)
err = e.pairs.Update(pair)
if err != nil {
logger.Error(err)
}
}()
}
if len(records) == 0 {
return errors.NotFound("url.proxy", "not found")
}
rsp.DestinationURL = pair.DestinationURL
uri := new(url.URLPair)
if err := records[0].Decode(uri); err != nil {
return err
}
rsp.DestinationURL = uri.DestinationURL
return nil
}