Replaced all other referenced to twt/twtxt

This commit is contained in:
James Mills
2021-01-30 15:06:58 +10:00
parent e0501407b5
commit 0454b9b62b
14 changed files with 138 additions and 1728 deletions

2
.envrc Normal file
View File

@@ -0,0 +1,2 @@
GOPRIVATE=git.mills.io/prologic/spyda
export GOPRIVATE

View File

@@ -16,7 +16,6 @@ import (
"git.mills.io/prologic/spyda"
"git.mills.io/prologic/spyda/internal"
"git.mills.io/prologic/spyda/types/retwt"
)
var (
@@ -34,22 +33,14 @@ var (
// Pod Oeprator
adminUser string
adminPass string
adminName string
adminEmail string
// Pod Settings
openProfiles bool
openRegistrations bool
// Limits
resultsPerpage int
// Pod Limits
twtsPerPage int
maxTwtLength int
maxUploadSize int64
maxFetchLimit int64
maxCacheTTL time.Duration
maxCacheItems int
// Pod Secrets
// Secrets
apiSigningKey string
cookieSecret string
magiclinkSecret string
@@ -61,19 +52,10 @@ var (
smtpPass string
smtpFrom string
// Messaging Settings
smtpBind string
pop3Bind string
// Timeouts
sessionExpiry time.Duration
sessionCacheTTL time.Duration
apiSessionTime time.Duration
transcoderTimeout time.Duration
// Whitelists, Sources
feedSources []string
whitelistedDomains []string
)
func init() {
@@ -89,48 +71,19 @@ func init() {
flag.StringVarP(&theme, "theme", "t", internal.DefaultTheme, "set the default theme")
flag.StringVarP(&baseURL, "base-url", "u", internal.DefaultBaseURL, "base url to use")
// Pod Oeprator
// Administration
flag.StringVarP(&adminName, "admin-name", "N", internal.DefaultAdminName, "default admin user name")
flag.StringVarP(&adminEmail, "admin-email", "E", internal.DefaultAdminEmail, "default admin user email")
flag.StringVarP(&adminUser, "admin-user", "A", internal.DefaultAdminUser, "default admin user to use")
flag.StringVarP(&adminPass, "admin-pass", "P", internal.DefaultAdminName, "default admin user name")
// Pod Settings
flag.BoolVarP(
&openRegistrations, "open-registrations", "R", internal.DefaultOpenRegistrations,
"whether or not to have open user registgration",
)
flag.BoolVarP(
&openProfiles, "open-profiles", "O", internal.DefaultOpenProfiles,
"whether or not to have open user profiles",
// Limits
flag.IntVarP(
&resultsPerPage, "results-per-page", "T", internal.DefaultResultsPerPage,
"maximum results per page to display",
)
// Pod Limits
flag.IntVarP(
&twtsPerPage, "twts-per-page", "T", internal.DefaultTwtsPerPage,
"maximum twts per page to display",
)
flag.IntVarP(
&maxTwtLength, "max-twt-length", "L", internal.DefaultMaxTwtLength,
"maximum length of posts",
)
flag.Int64VarP(
&maxUploadSize, "max-upload-size", "U", internal.DefaultMaxUploadSize,
"maximum upload size of media",
)
flag.Int64VarP(
&maxFetchLimit, "max-fetch-limit", "F", internal.DefaultMaxFetchLimit,
"maximum feed fetch limit in bytes",
)
flag.DurationVarP(
&maxCacheTTL, "max-cache-ttl", "C", internal.DefaultMaxCacheTTL,
"maximum cache ttl (time-to-live) of cached twts in memory",
)
flag.IntVarP(
&maxCacheItems, "max-cache-items", "I", internal.DefaultMaxCacheItems,
"maximum cache items (per feed source) of cached twts in memory",
)
// Pod Secrets
// Secrets
flag.StringVar(
&apiSigningKey, "api-signing-key", internal.DefaultAPISigningKey,
"secret to use for signing api tokens",
@@ -151,10 +104,6 @@ func init() {
flag.StringVar(&smtpPass, "smtp-pass", internal.DefaultSMTPPass, "SMTP Pass to use for email sending")
flag.StringVar(&smtpFrom, "smtp-from", internal.DefaultSMTPFrom, "SMTP From to use for email sending")
// Messaging Settings
flag.StringVar(&smtpBind, "smtp-bind", internal.DefaultSMTPBind, "SMTP interface and port to bind to")
flag.StringVar(&pop3Bind, "pop3-bind", internal.DefaultPOP3Bind, "POP3 interface and port to bind to")
// Timeouts
flag.DurationVar(
&sessionExpiry, "session-expiry", internal.DefaultSessionExpiry,
@@ -168,20 +117,6 @@ func init() {
&apiSessionTime, "api-session-time", internal.DefaultAPISessionTime,
"timeout for api tokens to expire",
)
flag.DurationVar(
&transcoderTimeout, "transcoder-timeout", internal.DefaultTranscoderTimeout,
"timeout for the video transcoder",
)
// Whitelists, Sources
flag.StringSliceVar(
&feedSources, "feed-sources", internal.DefaultFeedSources,
"external feed sources for discovery of other feeds",
)
flag.StringSliceVar(
&whitelistedDomains, "whitelist-domain", internal.DefaultWhitelistedDomains,
"whitelist of external domains to permit for display of inline images",
)
}
func flagNameFromEnvironmentName(s string) string {
@@ -232,8 +167,6 @@ func main() {
log.SetLevel(log.InfoLevel)
}
retwt.DefaultTwtManager()
svr, err := internal.NewServer(bind,
// Debug mode
internal.WithDebug(debug),
@@ -246,24 +179,16 @@ func main() {
internal.WithTheme(theme),
internal.WithBaseURL(baseURL),
// Pod Oeprator
// Administration
internal.WithAdminUser(adminUser),
internal.WithAdminPass(adminPass),
internal.WithAdminName(adminName),
internal.WithAdminEmail(adminEmail),
// Pod Settings
internal.WithOpenProfiles(openProfiles),
internal.WithOpenRegistrations(openRegistrations),
// Limits
internal.WithResultsPerPage(resultsPerPage),
// Pod Limits
internal.WithTwtsPerPage(twtsPerPage),
internal.WithMaxTwtLength(maxTwtLength),
internal.WithMaxUploadSize(maxUploadSize),
internal.WithMaxFetchLimit(maxFetchLimit),
internal.WithMaxCacheTTL(maxCacheTTL),
internal.WithMaxCacheItems(maxCacheItems),
// Pod Secrets
// Secrets
internal.WithAPISigningKey(apiSigningKey),
internal.WithCookieSecret(cookieSecret),
internal.WithMagicLinkSecret(magiclinkSecret),
@@ -275,19 +200,10 @@ func main() {
internal.WithSMTPPass(smtpPass),
internal.WithSMTPFrom(smtpFrom),
// Messaging Settings
internal.WithSMTPBind(smtpBind),
internal.WithPOP3Bind(pop3Bind),
// Timeouts
internal.WithSessionExpiry(sessionExpiry),
internal.WithSessionCacheTTL(sessionCacheTTL),
internal.WithAPISessionTime(apiSessionTime),
internal.WithTranscoderTimeout(transcoderTimeout),
// Whitelists, Sources
internal.WithFeedSources(feedSources),
internal.WithWhitelistedDomains(whitelistedDomains),
)
if err != nil {
log.WithError(err).Fatal("error creating server")

3
go.mod
View File

@@ -3,12 +3,14 @@ module veri-index
go 1.13
require (
git.mills.io/prologic/spyda v0.0.0-00010101000000-000000000000
github.com/GeertJohan/go.rice v1.0.2
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.6.1
github.com/andreadipersio/securecookie v0.0.0-20131119095127-e3c3b33544ec // indirect
github.com/andyleap/microformats v0.0.0-20150523144534-25ae286f528b
github.com/audiolion/ipip v1.0.0
github.com/bakape/thumbnailer/v2 v2.6.6
@@ -20,6 +22,7 @@ require (
github.com/disintegration/gift v1.2.1
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec
github.com/dustin/go-humanize v1.0.0
github.com/elithrar/simple-scrypt v1.3.0 // indirect
github.com/gabstv/merger v1.0.1
github.com/goccy/go-yaml v1.8.6
github.com/gomarkdown/markdown v0.0.0-20201113031856-722100d81a8e

427
go.sum
View File

@@ -1,427 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andyleap/microformats v0.0.0-20150523144534-25ae286f528b/go.mod h1:I3yyaN+QdpdChOtQg3ApgY01JRmFsXJASweq6Ye5A3s=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/audiolion/ipip v1.0.0/go.mod h1:dQtLacQAC8IPK3CTQAwhm2B+I0403irUHgit3r2IylM=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bakape/thumbnailer/v2 v2.6.6/go.mod h1:+yOYrfZmQ3VO7uqVHxTr3p5J74WRjP5MeQQXaU6GBjY=
github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOYvd3Ow=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creasty/defaults v1.5.1/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/disintegration/gift v1.1.2/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec/go.mod h1:K0KBFIr1gWu/C1Gp10nFAcAE4hsB7JxE6OgLijrJ8Sk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabstv/merger v1.0.1/go.mod h1:oQKCbAX4P6q0jk4s9Is144NojOE/HggFPb5qjPNZjq8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-yaml v1.8.6/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomarkdown/markdown v0.0.0-20201113031856-722100d81a8e/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/goware/urlx v0.3.1/go.mod h1:h8uwbJy68o+tQXCGZNa9D73WN8n0r9OBae5bUnLcgjw=
github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/james4k/fmatter v0.0.0-20150827042251-377c8ea6259d/go.mod h1:lxdGBh4Mr76rBen37GEal03CF0eF1qF5DSk2qfrrdo0=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lithammer/shortuuid/v3 v3.0.5/go.mod h1:2QdoCtD4SBzugx2qs3gdR3LXY6McxZYCNEHwDmYvOAE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022/go.mod h1:x4NsS+uc7ecH/Cbm9xKQ6XzmJM57rWTkjywjfB2yQ18=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/plar/go-adaptive-radix-tree v1.0.4/go.mod h1:Ot8d28EII3i7Lv4PSvBlF8ejiD/CtRYDuPsySJbSaK8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prologic/bitcask v0.3.10/go.mod h1:8RKJdbHLE7HFGLYSGu9slnYXSV7DMIucwVkaIYOk9GY=
github.com/prologic/observe v0.0.0-20181231082615-747b185a0928/go.mod h1:tEdBKdkpsOZCgueJIZwZREodFg5oRhLkTWWNiQ5y84E=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
github.com/rickb777/accept v0.0.0-20170318132422-d5183c44530d/go.mod h1:sv64uV+hMk2K4qwURvESkYmF8QyMYF/9nJpxF8UPQb8=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/securisec/go-keywords v0.0.0-20200619134240-769e7273f2ed/go.mod h1:ewJJMApUajQGvQOaQb/QyzTLoL619B5D02XOZlGnlNo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/steambap/captcha v1.3.1/go.mod h1:OPtfgZrvs/bolQ9JIZXsejxNzc98ETqaiMCVg8kqQGM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/theplant-retired/timezones v0.0.0-20150304063004-f9bd3c0ef9db/go.mod h1:vXWFQa6TAgTMbDqs8os5Wy6sTryvHNC39l0M3WHl8EQ=
github.com/tidwall/btree v0.2.2/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/redcon v1.4.0/go.mod h1:IGzxyoKE3Ea5AWIXo/ZHP+hzY8sWXaMKr7KlFgcWSZU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/unrolled/logger v0.0.0-20201216141554-31a3694fe979/go.mod h1:X5DBNY1yIVkuLwJP3BXlCoQCa5mGg7hPJPIA0Blwc44=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vcraescu/go-paginator v1.0.0/go.mod h1:caZCjjt2qcA1O2aDzW7lwAcK4Rxw3LNvdEVF/ONxZWw=
github.com/wblakecaldwell/profiler v0.0.0-20150908040756-6111ef1313a1/go.mod h1:3+0F8oLB1rQlbIcRAuqDgGdzNi9X69un/aPz4cUAFV4=
github.com/writeas/slug v1.2.0/go.mod h1:RE8shOqQP3YhsfsQe0L3RnuejfQ4Mk+JjY5YJQFubfQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191118222007-07fc4c7f2b98/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.53.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@@ -7,13 +7,11 @@ import (
"math/rand"
"net/url"
"os"
"regexp"
"strings"
"time"
"github.com/gabstv/merger"
"github.com/goccy/go-yaml"
"git.mills.io/prologic/spyda/types"
log "github.com/sirupsen/logrus"
)
@@ -25,11 +23,6 @@ var (
type Settings struct {
Name string `yaml:"pod_name"`
Description string `yaml:"pod_description"`
MaxTwtLength int `yaml:"max_twt_length"`
OpenProfiles bool `yaml:"open_profiles"`
OpenRegistrations bool `yaml:"open_registrations"`
}
// Config contains the server configuration parameters
@@ -43,50 +36,30 @@ type Config struct {
Theme string
BaseURL string
AdminUser string
AdminPass string
AdminName string
AdminEmail string
FeedSources []string
RegisterMessage string
CookieSecret string
TwtPrompts []string
TwtsPerPage int
MaxUploadSize int64
MaxTwtLength int
MaxCacheTTL time.Duration
MaxCacheItems int
MsgsPerPage int
OpenProfiles bool
OpenRegistrations bool
SearchPrompts []string
ResultsPerPage int
APISessionTime time.Duration
SessionExpiry time.Duration
SessionCacheTTL time.Duration
TranscoderTimeout time.Duration
APISigningKey string
CookieSecret string
MagicLinkSecret string
SMTPBind string
POP3Bind string
SMTPHost string
SMTPPort int
SMTPUser string
SMTPPass string
SMTPFrom string
MaxFetchLimit int64
APISessionTime time.Duration
APISigningKey string
baseURL *url.URL
whitelistedDomains []*regexp.Regexp
WhitelistedDomains []string
// path string
}
var _ types.FmtOpts = (*Config)(nil)
func (c *Config) IsLocalURL(url string) bool {
if NormalizeURL(url) == "" {
return false
@@ -94,8 +67,6 @@ func (c *Config) IsLocalURL(url string) bool {
return strings.HasPrefix(NormalizeURL(url), NormalizeURL(c.BaseURL))
}
func (c *Config) LocalURL() *url.URL { return c.baseURL }
func (c *Config) ExternalURL(nick, uri string) string { return URLForExternalProfile(c, nick, uri) }
func (c *Config) UserURL(url string) string { return UserURL(url) }
// Settings returns a `Settings` struct containing pod settings that can
// then be persisted to disk to override some configuration options.
@@ -109,28 +80,10 @@ func (c *Config) Settings() *Settings {
return settings
}
// WhitelistedDomain returns true if the domain provided is a whiltelisted
// domain as per the configuration
func (c *Config) WhitelistedDomain(domain string) (bool, bool) {
// Always per mit our own domain
ourDomain := strings.TrimPrefix(strings.ToLower(c.baseURL.Hostname()), "www.")
if domain == ourDomain {
return true, true
}
// Check against list of whitelistedDomains (regexes)
for _, re := range c.whitelistedDomains {
if re.MatchString(domain) {
return true, false
}
}
return false, false
}
// RandomTwtPrompt returns a random Twt Prompt for display by the UI
func (c *Config) RandomTwtPrompt() string {
n := rand.Int() % len(c.TwtPrompts)
return c.TwtPrompts[n]
// RandomSearchPrompt returns a random Search Prompt for display by the UI
func (c *Config) RandomSearchPrompt() string {
n := rand.Int() % len(c.SearchPrompts)
return c.SearchPrompts[n]
}
// Validate validates the configuration is valid which for the most part

View File

@@ -1,7 +1,6 @@
package internal
import (
"fmt"
"html/template"
"net/http"
"strings"
@@ -13,7 +12,6 @@ import (
"git.mills.io/prologic/spyda/internal/session"
"git.mills.io/prologic/spyda/types"
"github.com/justinas/nosurf"
"github.com/theplant-retired/timezones"
)
type Meta struct {
@@ -34,21 +32,12 @@ type Context struct {
BaseURL string
InstanceName string
SoftwareVersion string
TwtsPerPage int
TwtPrompt string
MaxTwtLength int
RegisterDisabled bool
OpenProfiles bool
RegisterDisabledMessage string
Timezones []*timezones.Zoneinfo
SearchPrompt string
Reply string
Username string
User *User
Tokens []*Token
LastTwt types.Twt
Profile types.Profile
Authenticated bool
IsAdmin bool
@@ -65,19 +54,9 @@ type Context struct {
Links types.Links
Alternatives types.Alternatives
Messages Messages
NewMessages int
Twter types.Twter
Twts types.Twts
BlogPost *BlogPost
BlogPosts BlogPosts
Feeds []*Feed
FeedSources FeedSourceMap
Pager *paginator.Paginator
// Report abuse
ReportNick string
ReportURL string
// Reset Password Token
@@ -94,18 +73,12 @@ func NewContext(conf *Config, db Store, req *http.Request) *Context {
BaseURL: conf.BaseURL,
InstanceName: conf.Name,
SoftwareVersion: spyda.FullVersion(),
TwtsPerPage: conf.TwtsPerPage,
TwtPrompt: conf.RandomTwtPrompt(),
MaxTwtLength: conf.MaxTwtLength,
RegisterDisabled: !conf.OpenRegistrations,
OpenProfiles: conf.OpenProfiles,
LastTwt: types.NilTwt,
SearchPrompt: conf.RandomSearchPrompt(),
Commit: spyda.Commit,
Theme: conf.Theme,
Timezones: timezones.AllZones,
Title: "",
Meta: Meta{
Title: DefaultMetaTitle,
@@ -113,14 +86,6 @@ func NewContext(conf *Config, db Store, req *http.Request) *Context {
Keywords: DefaultMetaKeywords,
Description: conf.Description,
},
Alternatives: types.Alternatives{
types.Alternative{
Type: "application/atom+xml",
Title: fmt.Sprintf("%s local feed", conf.Name),
URL: fmt.Sprintf("%s/atom.xml", conf.BaseURL),
},
},
}
ctx.CSRFToken = nosurf.Token(req)
@@ -138,11 +103,6 @@ func NewContext(conf *Config, db Store, req *http.Request) *Context {
log.WithError(err).Warnf("error loading user object for %s", ctx.Username)
}
ctx.Twter = types.Twter{
Nick: user.Username,
URL: URLForUser(conf, user.Username),
}
ctx.User = user
tokens, err := db.GetUserTokens(user)
@@ -153,7 +113,6 @@ func NewContext(conf *Config, db Store, req *http.Request) *Context {
} else {
ctx.User = &User{}
ctx.Twter = types.Twter{}
}
if ctx.Username == conf.AdminUser {

View File

@@ -2,72 +2,22 @@ package internal
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/creasty/defaults"
"git.mills.io/prologic/spyda/types"
log "github.com/sirupsen/logrus"
)
const (
maxUserFeeds = 5 // 5 is < 7 and humans can only really handle ~7 things
)
var (
ErrFeedAlreadyExists = errors.New("error: feed already exists by that name")
ErrAlreadyFollows = errors.New("error: you already follow this feed")
ErrTooManyFeeds = errors.New("error: you have too many feeds")
)
// Feed ...
type Feed struct {
Name string
Description string
URL string
CreatedAt time.Time
Followers map[string]string `default:"{}"`
remotes map[string]string
}
// User ...
type User struct {
Username string
Password string
Tagline string
Email string // DEPRECATED: In favor of storing a Hashed Email
URL string
Email string
CreatedAt time.Time
Theme string `default:"auto"`
Recovery string `default:"auto"`
DisplayDatesInTimezone string `default:"UTC"`
IsFollowersPubliclyVisible bool `default:"true"`
IsFollowingPubliclyVisible bool `default:"true"`
IsBookmarksPubliclyVisible bool `default:"true"`
Feeds []string `default:"[]"`
Tokens []string `default:"[]"`
SMTPToken string `default:""`
POP3Token string `default:""`
Bookmarks map[string]string `default:"{}"`
Followers map[string]string `default:"{}"`
Following map[string]string `default:"{}"`
Muted map[string]string `default:"{}"`
muted map[string]string
remotes map[string]string
sources map[string]string
}
// Token ...
@@ -100,124 +50,6 @@ func (t *Token) Bytes() ([]byte, error) {
return data, nil
}
func CreateFeed(conf *Config, db Store, user *User, name string, force bool) error {
if user != nil {
if !force && len(user.Feeds) > maxUserFeeds {
return ErrTooManyFeeds
}
}
fn := filepath.Join(conf.Data, feedsDir, name)
stat, err := os.Stat(fn)
if err == nil && !force {
return ErrFeedAlreadyExists
}
if stat == nil {
if err := ioutil.WriteFile(fn, []byte{}, 0644); err != nil {
return err
}
}
if user != nil {
if !user.OwnsFeed(name) {
user.Feeds = append(user.Feeds, name)
}
}
followers := make(map[string]string)
if user != nil {
followers[user.Username] = user.URL
}
feed := NewFeed()
feed.Name = name
feed.URL = URLForUser(conf, name)
feed.Followers = followers
feed.CreatedAt = time.Now()
if err := db.SetFeed(name, feed); err != nil {
return err
}
if user != nil {
user.Follow(name, feed.URL)
}
return nil
}
func DetachFeedFromOwner(db Store, user *User, feed *Feed) (err error) {
delete(user.Following, feed.Name)
delete(user.sources, feed.URL)
user.Feeds = RemoveString(user.Feeds, feed.Name)
if err = db.SetUser(user.Username, user); err != nil {
return
}
delete(feed.Followers, user.Username)
if err = db.SetFeed(feed.Name, feed); err != nil {
return
}
return nil
}
func RemoveFeedOwnership(db Store, user *User, feed *Feed) (err error) {
user.Feeds = RemoveString(user.Feeds, feed.Name)
if err = db.SetUser(user.Username, user); err != nil {
return
}
return nil
}
func AddFeedOwnership(db Store, user *User, feed *Feed) (err error) {
user.Feeds = append(user.Feeds, feed.Name)
if err = db.SetUser(user.Username, user); err != nil {
return
}
return nil
}
// NewFeed ...
func NewFeed() *Feed {
feed := &Feed{}
if err := defaults.Set(feed); err != nil {
log.WithError(err).Error("error creating new feed object")
}
return feed
}
// LoadFeed ...
func LoadFeed(data []byte) (feed *Feed, err error) {
feed = &Feed{}
if err := defaults.Set(feed); err != nil {
return nil, err
}
if err = json.Unmarshal(data, &feed); err != nil {
return nil, err
}
if feed.Followers == nil {
feed.Followers = make(map[string]string)
}
feed.remotes = make(map[string]string)
for n, u := range feed.Followers {
if u = NormalizeURL(u); u == "" {
continue
}
feed.remotes[u] = n
}
return
}
// NewUser ...
func NewUser() *User {
user := &User{}
@@ -237,104 +69,9 @@ func LoadUser(data []byte) (user *User, err error) {
return nil, err
}
if user.SMTPToken == "" {
user.SMTPToken = GenerateRandomToken()
}
if user.POP3Token == "" {
user.POP3Token = GenerateRandomToken()
}
if user.Bookmarks == nil {
user.Bookmarks = make(map[string]string)
}
if user.Followers == nil {
user.Followers = make(map[string]string)
}
if user.Following == nil {
user.Following = make(map[string]string)
}
user.muted = make(map[string]string)
for n, u := range user.Muted {
if u = NormalizeURL(u); u == "" {
continue
}
user.muted[u] = n
}
user.remotes = make(map[string]string)
for n, u := range user.Followers {
if u = NormalizeURL(u); u == "" {
continue
}
user.remotes[u] = n
}
user.sources = make(map[string]string)
for n, u := range user.Following {
if u = NormalizeURL(u); u == "" {
continue
}
user.sources[u] = n
}
return
}
func (f *Feed) AddFollower(nick, url string) {
url = NormalizeURL(url)
f.Followers[nick] = url
f.remotes[url] = nick
}
func (f *Feed) FollowedBy(url string) bool {
_, ok := f.remotes[NormalizeURL(url)]
return ok
}
func (f *Feed) Source() types.Feeds {
feeds := make(types.Feeds)
feeds[types.Feed{Nick: f.Name, URL: f.URL}] = true
return feeds
}
func (f *Feed) Profile(baseURL string, viewer *User) types.Profile {
var (
follows bool
followedBy bool
muted bool
)
if viewer != nil {
follows = viewer.Follows(f.URL)
followedBy = viewer.FollowedBy(f.URL)
muted = viewer.HasMuted(f.URL)
}
return types.Profile{
Type: "Feed",
Username: f.Name,
Tagline: f.Description,
URL: f.URL,
BlogsURL: URLForBlogs(baseURL, f.Name),
Follows: follows,
FollowedBy: followedBy,
Muted: muted,
Followers: f.Followers,
}
}
func (f *Feed) Bytes() ([]byte, error) {
data, err := json.Marshal(f)
if err != nil {
return nil, err
}
return data, nil
}
func (u *User) String() string {
url, err := url.Parse(u.URL)
if err != nil {
@@ -361,190 +98,6 @@ func (u *User) HasToken(token string) bool {
return false
}
func (u *User) OwnsFeed(name string) bool {
name = NormalizeFeedName(name)
for _, feed := range u.Feeds {
if NormalizeFeedName(feed) == name {
return true
}
}
return false
}
func (u *User) Is(url string) bool {
if NormalizeURL(url) == "" {
return false
}
return u.URL == NormalizeURL(url)
}
func (u *User) Bookmark(hash string) {
if _, ok := u.Bookmarks[hash]; !ok {
u.Bookmarks[hash] = ""
} else {
delete(u.Bookmarks, hash)
}
}
func (u *User) Bookmarked(hash string) bool {
_, ok := u.Bookmarks[hash]
return ok
}
func (u *User) AddFollower(nick, url string) {
url = NormalizeURL(url)
u.Followers[nick] = url
u.remotes[url] = nick
}
func (u *User) FollowedBy(url string) bool {
_, ok := u.remotes[NormalizeURL(url)]
return ok
}
func (u *User) Mute(nick, url string) {
if !u.HasMuted(url) {
u.Muted[nick] = url
u.muted[url] = nick
}
}
func (u *User) Unmute(nick string) {
url, ok := u.Muted[nick]
if ok {
delete(u.Muted, nick)
delete(u.muted, url)
}
}
func (u *User) Follow(nick, url string) {
if !u.Follows(url) {
u.Following[nick] = url
u.sources[url] = nick
}
}
func (u *User) FollowAndValidate(conf *Config, nick, url string) error {
if err := ValidateFeed(conf, nick, url); err != nil {
return err
}
if u.Follows(url) {
return ErrAlreadyFollows
}
u.Following[nick] = url
u.sources[url] = nick
return nil
}
func (u *User) Follows(url string) bool {
_, ok := u.sources[NormalizeURL(url)]
return ok
}
func (u *User) HasMuted(url string) bool {
_, ok := u.muted[NormalizeURL(url)]
return ok
}
func (u *User) Source() types.Feeds {
feeds := make(types.Feeds)
feeds[types.Feed{Nick: u.Username, URL: u.URL}] = true
return feeds
}
func (u *User) Sources() types.Feeds {
// Ensure we fetch the user's own posts in the cache
feeds := u.Source()
for url, nick := range u.sources {
feeds[types.Feed{Nick: nick, URL: url}] = true
}
return feeds
}
func (u *User) Profile(baseURL string, viewer *User) types.Profile {
var (
follows bool
followedBy bool
muted bool
)
if viewer != nil {
if viewer.Is(u.URL) {
follows = true
followedBy = true
} else {
follows = viewer.Follows(u.URL)
followedBy = viewer.FollowedBy(u.URL)
}
muted = viewer.HasMuted(u.URL)
}
return types.Profile{
Type: "User",
Username: u.Username,
Tagline: u.Tagline,
URL: u.URL,
BlogsURL: URLForBlogs(baseURL, u.Username),
Follows: follows,
FollowedBy: followedBy,
Muted: muted,
Followers: u.Followers,
Following: u.Following,
Bookmarks: u.Bookmarks,
}
}
func (u *User) Twter() types.Twter {
return types.Twter{Nick: u.Username, URL: u.URL}
}
func (u *User) Filter(twts []types.Twt) (filtered []types.Twt) {
// fast-path
if len(u.muted) == 0 {
return twts
}
for _, twt := range twts {
if u.HasMuted(twt.Twter().URL) {
continue
}
filtered = append(filtered, twt)
}
return
}
func (u *User) Reply(twt types.Twt) string {
mentionsSet := make(map[string]bool)
for _, m := range twt.Mentions() {
twter := m.Twter()
if _, ok := mentionsSet[twter.Nick]; !ok && twter.Nick != u.Username {
mentionsSet[twter.Nick] = true
}
}
mentions := []string{fmt.Sprintf("@%s", twt.Twter().Nick)}
for nick := range mentionsSet {
mentions = append(mentions, fmt.Sprintf("@%s", nick))
}
mentions = UniqStrings(mentions)
subject := twt.Subject()
if subject != "" {
subject = FormatMentionsAndTagsForSubject(subject)
return fmt.Sprintf("%s %s ", strings.Join(mentions, " "), subject)
}
return fmt.Sprintf("%s ", strings.Join(mentions, " "))
}
func (u *User) Bytes() ([]byte, error) {
data, err := json.Marshal(u)
if err != nil {

View File

@@ -26,8 +26,9 @@ const (
// DefaultAdminXXX is the default admin user / pod operator
DefaultAdminUser = "admin"
DefaultAdminPass = "admiN"
DefaultAdminName = "Administrator"
DefaultAdminEmail = "support@twt.social"
DefaultAdminEmail = "support@spyda.dev"
// DefaultName is the default instance name
DefaultName = "spyda.dev"
@@ -41,36 +42,11 @@ const (
// DefaultTheme is the default theme to use ('light' or 'dark')
DefaultTheme = "dark"
// DefaultOpenRegistrations is the default for open user registrations
DefaultOpenRegistrations = false
// DefaultRegisterMessage is the default message displayed when registrations are disabled
DefaultRegisterMessage = ""
// DefaultCookieSecret is the server's default cookie secret
DefaultCookieSecret = InvalidConfigValue
// DefaultTwtsPerPage is the server's default twts per page to display
DefaultTwtsPerPage = 50
// DefaultMaxTwtLength is the default maximum length of posts permitted
DefaultMaxTwtLength = 288
// DefaultMaxCacheTTL is the default maximum cache ttl of twts in memory
DefaultMaxCacheTTL = time.Hour * 24 * 10 // 10 days 28 days 28 days 28 days
// DefaultMaxCacheItems is the default maximum cache items (per feed source)
// of twts in memory
DefaultMaxCacheItems = DefaultTwtsPerPage * 3 // We get bored after paging thorughh > 3 pages :D
// DefaultMsgPerPage is the server's default msgs per page to display
DefaultMsgsPerPage = 20
// DefaultOpenProfiles is the default for whether or not to have open user profiles
DefaultOpenProfiles = false
// DefaultMaxUploadSize is the default maximum upload size permitted
DefaultMaxUploadSize = 1 << 24 // ~16MB (enough for high-res photos)
// DefaultResultsPerPage is the server's default results per page to display
DefaultResultsPerPage = 10
// DefaultSessionCacheTTL is the server's default session cache ttl
DefaultSessionCacheTTL = 1 * time.Hour
@@ -78,16 +54,9 @@ const (
// DefaultSessionExpiry is the server's default session expiry time
DefaultSessionExpiry = 240 * time.Hour // 10 days
// DefaultTranscoderTimeout is the default vodeo transcoding timeout
DefaultTranscoderTimeout = 10 * time.Minute // 10mins
// DefaultMagicLinkSecret is the jwt magic link secret
DefaultMagicLinkSecret = InvalidConfigValue
// Default Messaging settings
DefaultSMTPBind = "0.0.0.0:8025"
DefaultPOP3Bind = "0.0.0.0:8110"
// Default SMTP configuration
DefaultSMTPHost = "smtp.gmail.com"
DefaultSMTPPort = 587
@@ -95,9 +64,6 @@ const (
DefaultSMTPPass = InvalidConfigValue
DefaultSMTPFrom = InvalidConfigValue
// DefaultMaxFetchLimit is the maximum fetch fetch limit in bytes
DefaultMaxFetchLimit = 1 << 21 // ~2MB (or more than enough for a year)
// DefaultAPISessionTime is the server's default session time for API tokens
DefaultAPISessionTime = 240 * time.Hour // 10 days
@@ -127,17 +93,16 @@ func NewConfig() *Config {
Theme: DefaultTheme,
BaseURL: DefaultBaseURL,
AdminUser: DefaultAdminUser,
FeedSources: DefaultFeedSources,
RegisterMessage: DefaultRegisterMessage,
AdminPass: DefaultAdminPass,
CookieSecret: DefaultCookieSecret,
TwtPrompts: DefaultTwtPrompts,
TwtsPerPage: DefaultTwtsPerPage,
MaxTwtLength: DefaultMaxTwtLength,
MsgsPerPage: DefaultMsgsPerPage,
OpenProfiles: DefaultOpenProfiles,
OpenRegistrations: DefaultOpenRegistrations,
SessionExpiry: DefaultSessionExpiry,
MagicLinkSecret: DefaultMagicLinkSecret,
SearchPrompts: DefaultSearchPrompts,
ResultsPerPage: DefaultResultsPerPage,
SessionExpiry: DefaultSessionExpiry,
SMTPHost: DefaultSMTPHost,
SMTPPort: DefaultSMTPPort,
SMTPUser: DefaultSMTPUser,
@@ -257,50 +222,10 @@ func WithCookieSecret(secret string) Option {
}
}
// WithTwtsPerPage sets the server's twts per page
func WithTwtsPerPage(twtsPerPage int) Option {
// WithResultsPerPage sets the server's results per page
func WithResultsPerPage(resultsPerPage int) Option {
return func(cfg *Config) error {
cfg.TwtsPerPage = twtsPerPage
return nil
}
}
// WithMaxTwtLength sets the maximum length of posts permitted on the server
func WithMaxTwtLength(maxTwtLength int) Option {
return func(cfg *Config) error {
cfg.MaxTwtLength = maxTwtLength
return nil
}
}
// WithMaxCacheTTL sets the maximum cache ttl of twts in memory
func WithMaxCacheTTL(maxCacheTTL time.Duration) Option {
return func(cfg *Config) error {
cfg.MaxCacheTTL = maxCacheTTL
return nil
}
}
// WithMaxCacheItems sets the maximum cache items (per feed source) of twts in memory
func WithMaxCacheItems(maxCacheItems int) Option {
return func(cfg *Config) error {
cfg.MaxCacheItems = maxCacheItems
return nil
}
}
// WithOpenProfiles sets whether or not to have open user profiles
func WithOpenProfiles(openProfiles bool) Option {
return func(cfg *Config) error {
cfg.OpenProfiles = openProfiles
return nil
}
}
// WithMaxUploadSize sets the maximum upload size permitted by the server
func WithMaxUploadSize(maxUploadSize int64) Option {
return func(cfg *Config) error {
cfg.MaxUploadSize = maxUploadSize
cfg.ResultsPerPage = resultsPerPage
return nil
}
}

38
internal/pages/abuse.md Normal file
View File

@@ -0,0 +1,38 @@
---
title: Spyda Abuse Policy
---
# Abuse Policy
> Help us build and maintain a quality search experience.
At Spyda.search, we believe that the Web should be freely searchable without
having to track you or collect data about your searches or behavior. Everyone
should be able to benefit from an open search experience without worrying about
privacy.
That being said there are a basic set of guidelines that help ensure that
search is of the highest quality and safe to use and considered acceptable
by everyone from all backgrounds and walks of live.
Therefore the following outlines the types of content that Spyda will not
permit and if found will be immediately deleted and potenaitlly blacklisted
from future indexing.
**If you have a website or know of a website that has been crawled and indexed
that violates these guidelines, please contact us immediately at /support so
we can remove the offending data.**
- **Hate speech:** Any website found to be hosting content that attacks other people on the basis of race, ethnicity, national origin, sexual orientation, gender, gender identity, religious affiliation, age, disability, or disease. We will not index websites whose primary purpose is inciting harm towards others on the basis of these categories.
- **Threats of violence:** Any websites found to be treating, planning or otherwise of a treating nature towards others to organize, promote, or incite acts of real-world violence or terrorism.
- **Illegal activities**: Any websites found to be hosting content for unlawful purposes or in furtherance of illegal activities. International webmasters agree to comply with all local laws regarding online conduct and acceptable content.
## What Happens If a Website Violates These Guidelines
- It will be immediately removed from our indexes.
- We _may_ attempt to reach the webmaster to correct the situation.
- We _may_ blacklist the website from future indexing.
Thanks for helping us build and maintain a safe and quality search experience for all!
We reserve the right to update these guidelines as needed.

File diff suppressed because one or more lines are too long

View File

@@ -11,18 +11,11 @@ import (
const robotsTpl = `User-Agent: *
Disallow: /
Allow: /
Allow: /twt
Allow: /user
Allow: /feed
Allow: /about
Allow: /abuse
Allow: /help
Allow: /blogs
Allow: /privacy
Allow: /support
Allow: /search
Allow: /external
Allow: /atom.xml
Allow: /media
`
// RobotsHandler ...

View File

@@ -12,7 +12,6 @@ import (
rice "github.com/GeertJohan/go.rice"
"github.com/NYTimes/gziphandler"
humanize "github.com/dustin/go-humanize"
"github.com/gabstv/merger"
"github.com/justinas/nosurf"
"github.com/prologic/observe"
@@ -33,7 +32,7 @@ var (
)
func init() {
metrics = observe.NewMetrics("twtd")
metrics = observe.NewMetrics("spyda")
}
// Server ...
@@ -234,45 +233,6 @@ func (s *Server) setupMetrics() {
"Number of unique feeds in the global feed cache",
)
// feed cache size
metrics.NewGauge(
"cache", "twts",
"Number of active twts in the global feed cache",
)
// blogs cache size
metrics.NewGaugeFunc(
"cache", "blogs",
"Number of blogs in the blogs cache",
func() float64 {
return float64(s.blogs.Count())
},
)
// feed cache processing time
metrics.NewGauge(
"cache", "last_processed_seconds",
"Number of seconds for a feed cache cycle",
)
// feed cache limited fetch (feed exceeded MaxFetchLImit or unknown size)
metrics.NewCounter(
"cache", "limited",
"Number of feed cache fetches affected by MaxFetchLimit",
)
// archive size
metrics.NewCounter(
"archive", "size",
"Number of items inserted into the global feed archive",
)
// archive errors
metrics.NewCounter(
"archive", "error",
"Number of items errored inserting into the global feed archive",
)
// server info
metrics.NewGaugeVec(
"server", "info",
@@ -353,140 +313,40 @@ func (s *Server) initRoutes() {
s.router.GET("/privacy", s.PageHandler("privacy"))
s.router.GET("/abuse", s.PageHandler("abuse"))
s.router.GET("/", s.TimelineHandler())
s.router.HEAD("/", s.TimelineHandler())
s.router.GET("/", s.IndexHandler())
s.router.HEAD("/", s.IndexHandler())
s.router.GET("/robots.txt", s.RobotsHandler())
s.router.HEAD("/robots.txt", s.RobotsHandler())
s.router.GET("/discover", s.am.MustAuth(s.DiscoverHandler()))
s.router.GET("/mentions", s.am.MustAuth(s.MentionsHandler()))
s.router.GET("/search", s.SearchHandler())
s.router.HEAD("/twt/:hash", s.PermalinkHandler())
s.router.GET("/twt/:hash", s.PermalinkHandler())
s.router.GET("/bookmark/:hash", s.BookmarkHandler())
s.router.POST("/bookmark/:hash", s.BookmarkHandler())
s.router.HEAD("/conv/:hash", s.ConversationHandler())
s.router.GET("/conv/:hash", s.ConversationHandler())
s.router.GET("/feeds", s.am.MustAuth(s.FeedsHandler()))
s.router.POST("/feed", s.am.MustAuth(s.FeedHandler()))
s.router.POST("/post", s.am.MustAuth(s.PostHandler()))
s.router.PATCH("/post", s.am.MustAuth(s.PostHandler()))
s.router.DELETE("/post", s.am.MustAuth(s.PostHandler()))
// Private Messages
s.router.GET("/messages", s.am.MustAuth(s.ListMessagesHandler()))
s.router.GET("/messages/:msgid", s.am.MustAuth(s.ViewMessageHandler()))
s.router.POST("/messages/send", s.am.MustAuth(s.SendMessageHandler()))
s.router.POST("/messages/delete", s.am.MustAuth(s.DeleteMessagesHandler()))
s.router.POST("/blog", s.am.MustAuth(s.CreateOrUpdateBlogHandler()))
s.router.GET("/blogs/:author", s.ListBlogsHandler())
s.router.GET("/blog/:author/:year/:month/:date/:slug", s.ViewBlogHandler())
s.router.HEAD("/blog/:author/:year/:month/:date/:slug", s.ViewBlogHandler())
s.router.GET("/blog/:author/:year/:month/:date/:slug/edit", s.EditBlogHandler())
s.router.GET("/blog/:author/:year/:month/:date/:slug/delete", s.DeleteBlogHandler())
s.router.GET("/blog/:author/:year/:month/:date/:slug/publish", s.PublishBlogHandler())
if s.config.OpenProfiles {
s.router.GET("/user/:nick/", s.ProfileHandler())
s.router.GET("/user/:nick/config.yaml", s.UserConfigHandler())
} else {
s.router.GET("/user/:nick/", s.am.MustAuth(s.ProfileHandler()))
s.router.GET("/user/:nick/config.yaml", s.am.MustAuth(s.UserConfigHandler()))
}
s.router.GET("/user/:nick/avatar", s.AvatarHandler())
s.router.HEAD("/user/:nick/avatar", s.AvatarHandler())
s.router.GET("/user/:nick/followers", s.FollowersHandler())
s.router.GET("/user/:nick/following", s.FollowingHandler())
s.router.GET("/user/:nick/bookmarks", s.BookmarksHandler())
s.router.GET("/pod/avatar", s.PodAvatarHandler())
// WebMentions
s.router.POST("/user/:nick/webmention", s.WebMentionHandler())
// External Feeds
s.router.GET("/external", s.ExternalHandler())
s.router.GET("/externalAvatar", s.ExternalAvatarHandler())
s.router.HEAD("/externalAvatar", s.ExternalAvatarHandler())
// External Queries (protected by a short-lived token)
s.router.GET("/whoFollows", s.WhoFollowsHandler())
// Syndication Formats (RSS, Atom, JSON Feed)
s.router.HEAD("/atom.xml", s.SyndicationHandler())
s.router.HEAD("/user/:nick/atom.xml", s.SyndicationHandler())
s.router.GET("/atom.xml", s.SyndicationHandler())
s.router.GET("/user/:nick/atom.xml", s.SyndicationHandler())
s.router.GET("/feed/:name/manage", s.am.MustAuth(s.ManageFeedHandler()))
s.router.POST("/feed/:name/manage", s.am.MustAuth(s.ManageFeedHandler()))
s.router.POST("/feed/:name/archive", s.am.MustAuth(s.ArchiveFeedHandler()))
s.router.GET("/login", s.am.HasAuth(s.LoginHandler()))
s.router.POST("/login", s.LoginHandler())
s.router.GET("/logout", s.LogoutHandler())
s.router.POST("/logout", s.LogoutHandler())
s.router.GET("/register", s.am.HasAuth(s.RegisterHandler()))
s.router.POST("/register", s.RegisterHandler())
// Reset Password
s.router.GET("/resetPassword", s.ResetPasswordHandler())
s.router.POST("/resetPassword", s.ResetPasswordHandler())
s.router.GET("/newPassword", s.ResetPasswordMagicLinkHandler())
s.router.POST("/newPassword", s.NewPasswordHandler())
s.router.GET("/pwreset", s.ResetPasswordHandler())
s.router.POST("/pwreset", s.ResetPasswordHandler())
s.router.GET("/chpasswd", s.ResetPasswordMagicLinkHandler())
s.router.POST("/chpasswd", s.NewPasswordHandler())
// Media Handling
s.router.GET("/media/:name", s.MediaHandler())
s.router.HEAD("/media/:name", s.MediaHandler())
s.router.POST("/upload", s.am.MustAuth(s.UploadMediaHandler()))
// Task State
s.router.GET("/task/:uuid", s.TaskHandler())
// User/Feed Lookups
s.router.GET("/lookup", s.am.MustAuth(s.LookupHandler()))
s.router.GET("/follow", s.am.MustAuth(s.FollowHandler()))
s.router.POST("/follow", s.am.MustAuth(s.FollowHandler()))
s.router.GET("/import", s.am.MustAuth(s.ImportHandler()))
s.router.POST("/import", s.am.MustAuth(s.ImportHandler()))
s.router.GET("/unfollow", s.am.MustAuth(s.UnfollowHandler()))
s.router.POST("/unfollow", s.am.MustAuth(s.UnfollowHandler()))
s.router.GET("/mute", s.am.MustAuth(s.MuteHandler()))
s.router.POST("/mute", s.am.MustAuth(s.MuteHandler()))
s.router.GET("/unmute", s.am.MustAuth(s.UnmuteHandler()))
s.router.POST("/unmute", s.am.MustAuth(s.UnmuteHandler()))
s.router.GET("/transferFeed/:name", s.TransferFeedHandler())
s.router.GET("/transferFeed/:name/:transferTo", s.TransferFeedHandler())
s.router.GET("/add", s.AddHandler())
s.router.POST("/add", s.AddHandler())
s.router.GET("/settings", s.am.MustAuth(s.SettingsHandler()))
s.router.POST("/settings", s.am.MustAuth(s.SettingsHandler()))
s.router.POST("/token/delete/:signature", s.am.MustAuth(s.DeleteTokenHandler()))
s.router.GET("/config", s.am.MustAuth(s.PodConfigHandler()))
s.router.GET("/manage/pod", s.ManagePodHandler())
s.router.POST("/manage/pod", s.ManagePodHandler())
s.router.GET("/manage", s.ManageHandler())
s.router.POST("/manage", s.ManageHandler())
s.router.GET("/manage/users", s.ManageUsersHandler())
s.router.POST("/manage/adduser", s.AddUserHandler())
s.router.POST("/manage/deluser", s.DelUserHandler())
s.router.GET("/deleteFeeds", s.DeleteAccountHandler())
s.router.POST("/delete", s.am.MustAuth(s.DeleteAllHandler()))
// Support / Report Abuse handlers
s.router.GET("/support", s.SupportHandler())
@@ -687,25 +547,12 @@ func NewServer(bind string, options ...Option) (*Server, error) {
log.Infof("Admin User: %s", server.config.AdminUser)
log.Infof("Admin Name: %s", server.config.AdminName)
log.Infof("Admin Email: %s", server.config.AdminEmail)
log.Infof("Max Twts per Page: %d", server.config.TwtsPerPage)
log.Infof("Max Cache TTL: %s", server.config.MaxCacheTTL)
log.Infof("Max Cache Items: %d", server.config.MaxCacheItems)
log.Infof("Maximum length of Posts: %d", server.config.MaxTwtLength)
log.Infof("Open User Profiles: %t", server.config.OpenProfiles)
log.Infof("Open Registrations: %t", server.config.OpenRegistrations)
log.Infof("SMTP Host: %s", server.config.SMTPHost)
log.Infof("SMTP Port: %d", server.config.SMTPPort)
log.Infof("SMTP User: %s", server.config.SMTPUser)
log.Infof("SMTP From: %s", server.config.SMTPFrom)
log.Infof("Max Fetch Limit: %s", humanize.Bytes(uint64(server.config.MaxFetchLimit)))
log.Infof("Max Upload Size: %s", humanize.Bytes(uint64(server.config.MaxUploadSize)))
log.Infof("API Session Time: %s", server.config.APISessionTime)
// Warn about user registration being disabled.
if !server.config.OpenRegistrations {
log.Warn("Open Registrations are disabled as per configuration (no -R/--open-registrations)")
}
server.initRoutes()
api.initRoutes()

View File

@@ -40,11 +40,7 @@ func NewTemplateManager(conf *Config, blogs *BlogsCache, cache *Cache) (*Templat
funcMap["hostnameFromURL"] = HostnameFromURL
funcMap["prettyURL"] = PrettyURL
funcMap["isLocalURL"] = IsLocalURLFactory(conf)
funcMap["formatTwt"] = FormatTwtFactory(conf)
funcMap["unparseTwt"] = UnparseTwtFactory(conf)
funcMap["formatForDateTime"] = FormatForDateTime
funcMap["urlForBlog"] = URLForBlogFactory(conf, blogs)
funcMap["urlForConv"] = URLForConvFactory(conf, cache)
funcMap["isAdminUser"] = IsAdminUserFactory(conf)
m := &TemplateManager{debug: conf.Debug, templates: templates, funcMap: funcMap}

View File

@@ -11,7 +11,7 @@
</li>
</ul>
<ul>
<li><small>Page {{ .Page }}/{{ .PageNums }} of {{ .Nums }} Twts</small></li>
<li><small>Page {{ .Page }}/{{ .PageNums }} of {{ .Nums }} Results</small></li>
</ul>
<ul>
<li>