package routes import ( "net/http" "net/url" "regexp" "time" "github.com/gorilla/mux" "github.com/patrickmn/go-cache" "sour.is/x/toolbox/httpsrv" "sour.is/x/toolbox/uuid" ) func init() { s := NewShortManager(365 * 24 * time.Hour) httpsrv.HttpRegister("short", httpsrv.HttpRoutes{ {Name: "getShort", Method: "GET", Pattern: "/s/{id}", HandlerFunc: s.getShort}, {Name: "putShort", Method: "PUT", Pattern: "/s/{id}", HandlerFunc: s.putShort}, }) } type shortManager struct { defaultExpire time.Duration db *cache.Cache } type shortURL struct { ID string URL string Secret string } func NewShortManager(defaultExpire time.Duration) *shortManager { return &shortManager{ defaultExpire: defaultExpire, db: cache.New(defaultExpire, defaultExpire/10), } } func (s *shortManager) GetURL(id string) *shortURL { if u, ok := s.db.Get(id); ok { if url, ok := u.(*shortURL); ok { return url } } return nil } func (s *shortManager) PutURL(id string, url *shortURL) { s.db.SetDefault(id, url) } func (s *shortManager) getShort(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] url := s.GetURL(id) if url == nil { httpsrv.WriteError(w, 404, "not found") return } w.Header().Set("Location", url.URL) w.WriteHeader(http.StatusFound) } func (s *shortManager) putShort(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() vars := mux.Vars(r) id := vars["id"] secret := r.FormValue("secret") u, err := url.Parse(r.FormValue("url")) if err != nil { httpsrv.WriteError(w, 400, "bad url") return } short := s.GetURL(id) if short == nil { short = newshort(id, secret, u.String()) s.PutURL(short.ID, short) httpsrv.WriteObject(w, 200, short) return } if secret == "" { httpsrv.WriteError(w, 401, "no auth") return } if secret != short.Secret { httpsrv.WriteError(w, 403, "forbidden") return } short.URL = u.String() s.PutURL(short.ID, short) httpsrv.WriteObject(w, 200, short) } func newshort(id, secret, u string) *shortURL { m, err := regexp.MatchString("[a-z-]{1,64}", id) if id == "" || !m || err != nil { id = uuid.V4() } m, err = regexp.MatchString("[a-z-]{1,64}", secret) if secret == "" || !m || err != nil { secret = uuid.V4() } return &shortURL{ID: id, Secret: secret, URL: u} }