Add collection handlers, routes, feeds, sitemaps
This commit is contained in:
parent
af872127c6
commit
ebeacff43c
1036
collections.go
1036
collections.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"github.com/writeas/web-core/log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func exportPostsCSV(u *User, posts *[]PublicPost) []byte {
|
||||
var b bytes.Buffer
|
||||
|
||||
r := [][]string{
|
||||
{"id", "slug", "blog", "url", "created", "title", "body"},
|
||||
}
|
||||
for _, p := range *posts {
|
||||
var blog string
|
||||
if p.Collection != nil {
|
||||
blog = p.Collection.Alias
|
||||
}
|
||||
f := []string{p.ID, p.Slug.String, blog, p.CanonicalURL(), p.Created8601(), p.Title.String, strings.Replace(p.Content, "\n", "\\n", -1)}
|
||||
r = append(r, f)
|
||||
}
|
||||
|
||||
w := csv.NewWriter(&b)
|
||||
w.WriteAll(r) // calls Flush internally
|
||||
if err := w.Error(); err != nil {
|
||||
log.Info("error writing csv:", err)
|
||||
}
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
type exportedTxt struct {
|
||||
Name, Body string
|
||||
Mod time.Time
|
||||
}
|
||||
|
||||
func exportPostsZip(u *User, posts *[]PublicPost) []byte {
|
||||
// Create a buffer to write our archive to.
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
// Create a new zip archive.
|
||||
w := zip.NewWriter(b)
|
||||
|
||||
// Add some files to the archive.
|
||||
var filename string
|
||||
files := []exportedTxt{}
|
||||
for _, p := range *posts {
|
||||
filename = ""
|
||||
if p.Collection != nil {
|
||||
filename += p.Collection.Alias + "/"
|
||||
}
|
||||
if p.Slug.String != "" {
|
||||
filename += p.Slug.String + "_"
|
||||
}
|
||||
filename += p.ID + ".txt"
|
||||
files = append(files, exportedTxt{filename, p.Content, p.Created})
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
head := &zip.FileHeader{Name: file.Name}
|
||||
head.SetModTime(file.Mod)
|
||||
f, err := w.CreateHeader(head)
|
||||
if err != nil {
|
||||
log.Error("export zip header: %v", err)
|
||||
}
|
||||
_, err = f.Write([]byte(file.Body))
|
||||
if err != nil {
|
||||
log.Error("export zip write: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to check the error on Close.
|
||||
err := w.Close()
|
||||
if err != nil {
|
||||
log.Error("export zip close: %v", err)
|
||||
}
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func compileFullExport(app *app, u *User) *ExportUser {
|
||||
exportUser := &ExportUser{
|
||||
User: u,
|
||||
}
|
||||
|
||||
colls, err := app.db.GetCollections(u)
|
||||
if err != nil {
|
||||
log.Error("unable to fetch collections: %v", err)
|
||||
}
|
||||
|
||||
posts, err := app.db.GetAnonymousPosts(u)
|
||||
if err != nil {
|
||||
log.Error("unable to fetch anon posts: %v", err)
|
||||
}
|
||||
exportUser.AnonymousPosts = *posts
|
||||
|
||||
var collObjs []CollectionObj
|
||||
for _, c := range *colls {
|
||||
co := &CollectionObj{Collection: c}
|
||||
co.Posts, err = app.db.GetPosts(&c, 0, true)
|
||||
if err != nil {
|
||||
log.Error("unable to get collection posts: %v", err)
|
||||
}
|
||||
app.db.GetPostsCount(co, true)
|
||||
collObjs = append(collObjs, *co)
|
||||
}
|
||||
exportUser.Collections = &collObjs
|
||||
|
||||
return exportUser
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/gorilla/feeds"
|
||||
"github.com/gorilla/mux"
|
||||
stripmd "github.com/writeas/go-strip-markdown"
|
||||
"github.com/writeas/web-core/log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ViewFeed(app *app, w http.ResponseWriter, req *http.Request) error {
|
||||
alias := collectionAliasFromReq(req)
|
||||
|
||||
// Display collection if this is a collection
|
||||
var c *Collection
|
||||
var err error
|
||||
if app.cfg.App.SingleUser {
|
||||
c, err = app.db.GetCollection(alias)
|
||||
} else {
|
||||
c, err = app.db.GetCollectionByID(1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.IsPrivate() || c.IsProtected() {
|
||||
return ErrCollectionNotFound
|
||||
}
|
||||
|
||||
// Fetch extra data about the Collection
|
||||
// TODO: refactor out this logic, shared in collection.go:fetchCollection()
|
||||
coll := &DisplayCollection{CollectionObj: &CollectionObj{Collection: *c}}
|
||||
if c.PublicOwner {
|
||||
u, err := app.db.GetUserByID(coll.OwnerID)
|
||||
if err != nil {
|
||||
// Log the error and just continue
|
||||
log.Error("Error getting user for collection: %v", err)
|
||||
} else {
|
||||
coll.Owner = u
|
||||
}
|
||||
}
|
||||
|
||||
tag := mux.Vars(req)["tag"]
|
||||
if tag != "" {
|
||||
coll.Posts, _ = app.db.GetPostsTagged(c, tag, 1, false)
|
||||
} else {
|
||||
coll.Posts, _ = app.db.GetPosts(c, 1, false)
|
||||
}
|
||||
|
||||
author := ""
|
||||
if coll.Owner != nil {
|
||||
author = coll.Owner.Username
|
||||
}
|
||||
|
||||
collectionTitle := coll.DisplayTitle()
|
||||
if tag != "" {
|
||||
collectionTitle = tag + " — " + collectionTitle
|
||||
}
|
||||
|
||||
baseUrl := coll.CanonicalURL()
|
||||
basePermalinkUrl := baseUrl
|
||||
siteURL := baseUrl
|
||||
if tag != "" {
|
||||
siteURL += "tag:" + tag
|
||||
}
|
||||
|
||||
feed := &Feed{
|
||||
Title: collectionTitle,
|
||||
Link: &Link{Href: siteURL},
|
||||
Description: coll.Description,
|
||||
Author: &Author{author, ""},
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
var title, permalink string
|
||||
for _, p := range *coll.Posts {
|
||||
title = p.PlainDisplayTitle()
|
||||
permalink = fmt.Sprintf("%s%s", baseUrl, p.Slug.String)
|
||||
feed.Items = append(feed.Items, &Item{
|
||||
Id: fmt.Sprintf("%s%s", basePermalinkUrl, p.Slug.String),
|
||||
Title: title,
|
||||
Link: &Link{Href: permalink},
|
||||
Description: "<![CDATA[" + stripmd.Strip(p.Content) + "]]>",
|
||||
Content: applyMarkdown([]byte(p.Content)),
|
||||
Author: &Author{author, ""},
|
||||
Created: p.Created,
|
||||
Updated: p.Updated,
|
||||
})
|
||||
}
|
||||
|
||||
rss, err := feed.ToRss()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprint(w, rss)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package writefreely
|
||||
|
||||
import "mime"
|
||||
|
||||
func IsJSON(h string) bool {
|
||||
ct, _, _ := mime.ParseMediaType(h)
|
||||
return ct == "application/json"
|
||||
}
|
59
routes.go
59
routes.go
|
@ -35,6 +35,48 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
|
|||
write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover)))
|
||||
write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo)))
|
||||
|
||||
// Handle logged in user sections
|
||||
me := write.PathPrefix("/me").Subrouter()
|
||||
me.HandleFunc("/", handler.Redirect("/me", UserLevelUser))
|
||||
me.HandleFunc("/c", handler.Redirect("/me/c/", UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/c/", handler.User(viewCollections)).Methods("GET")
|
||||
me.HandleFunc("/c/{collection}", handler.User(viewEditCollection)).Methods("GET")
|
||||
me.HandleFunc("/c/{collection}/stats", handler.User(viewStats)).Methods("GET")
|
||||
me.HandleFunc("/posts", handler.Redirect("/me/posts/", UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/posts/", handler.User(viewArticles)).Methods("GET")
|
||||
me.HandleFunc("/posts/export.csv", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/posts/export.zip", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/posts/export.json", handler.Download(viewExportPosts, UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/export", handler.User(viewExportOptions)).Methods("GET")
|
||||
me.HandleFunc("/export.json", handler.Download(viewExportFull, UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/settings", handler.User(viewSettings)).Methods("GET")
|
||||
me.HandleFunc("/logout", handler.Web(viewLogout, UserLevelNone)).Methods("GET")
|
||||
|
||||
write.HandleFunc("/api/me", handler.All(viewMeAPI)).Methods("GET")
|
||||
apiMe := write.PathPrefix("/api/me/").Subrouter()
|
||||
apiMe.HandleFunc("/", handler.All(viewMeAPI)).Methods("GET")
|
||||
apiMe.HandleFunc("/posts", handler.UserAPI(viewMyPostsAPI)).Methods("GET")
|
||||
apiMe.HandleFunc("/collections", handler.UserAPI(viewMyCollectionsAPI)).Methods("GET")
|
||||
apiMe.HandleFunc("/password", handler.All(updatePassphrase)).Methods("POST")
|
||||
apiMe.HandleFunc("/self", handler.All(updateSettings)).Methods("POST")
|
||||
|
||||
// Sign up validation
|
||||
write.HandleFunc("/api/alias", handler.All(handleUsernameCheck)).Methods("POST")
|
||||
|
||||
// Handle collections
|
||||
write.HandleFunc("/api/collections", handler.All(newCollection)).Methods("POST")
|
||||
apiColls := write.PathPrefix("/api/collections/").Subrouter()
|
||||
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(fetchCollection)).Methods("GET")
|
||||
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(existingCollection)).Methods("POST", "DELETE")
|
||||
apiColls.HandleFunc("/{alias}/posts", handler.All(fetchCollectionPosts)).Methods("GET")
|
||||
apiColls.HandleFunc("/{alias}/posts", handler.All(newPost)).Methods("POST")
|
||||
apiColls.HandleFunc("/{alias}/posts/{post}", handler.All(fetchPost)).Methods("GET")
|
||||
apiColls.HandleFunc("/{alias}/posts/{post:[a-zA-Z0-9]{10}}", handler.All(existingPost)).Methods("POST")
|
||||
apiColls.HandleFunc("/{alias}/posts/{post}/{property}", handler.All(fetchPostProperty)).Methods("GET")
|
||||
apiColls.HandleFunc("/{alias}/collect", handler.All(addPost)).Methods("POST")
|
||||
apiColls.HandleFunc("/{alias}/pin", handler.All(pinPost)).Methods("POST")
|
||||
apiColls.HandleFunc("/{alias}/unpin", handler.All(pinPost)).Methods("POST")
|
||||
|
||||
// Handle posts
|
||||
write.HandleFunc("/api/posts", handler.All(newPost)).Methods("POST")
|
||||
posts := write.PathPrefix("/api/posts/").Subrouter()
|
||||
|
@ -56,9 +98,26 @@ func initRoutes(handler *Handler, r *mux.Router, cfg *config.Config, db *datasto
|
|||
write.HandleFunc("/{action}/meta", handler.Web(handleViewMeta, UserLevelOptional)).Methods("GET")
|
||||
// Collections
|
||||
if cfg.App.SingleUser {
|
||||
RouteCollections(handler, write.PathPrefix("/").Subrouter())
|
||||
} else {
|
||||
write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelOptional))
|
||||
write.HandleFunc("/{collection}/", handler.Web(handleViewCollection, UserLevelOptional))
|
||||
RouteCollections(handler, write.PathPrefix("/{prefix:[@~$!\\-+]?}{collection}").Subrouter())
|
||||
// Posts
|
||||
write.HandleFunc("/{post}", handler.Web(handleViewPost, UserLevelOptional))
|
||||
}
|
||||
write.HandleFunc("/", handler.Web(handleViewHome, UserLevelOptional))
|
||||
}
|
||||
|
||||
func RouteCollections(handler *Handler, r *mux.Router) {
|
||||
r.HandleFunc("/page/{page:[0-9]+}", handler.Web(handleViewCollection, UserLevelOptional))
|
||||
r.HandleFunc("/tag:{tag}", handler.Web(handleViewCollectionTag, UserLevelOptional))
|
||||
r.HandleFunc("/tag:{tag}/feed/", handler.Web(ViewFeed, UserLevelOptional))
|
||||
r.HandleFunc("/tags/{tag}", handler.Web(handleViewCollectionTag, UserLevelOptional))
|
||||
r.HandleFunc("/sitemap.xml", handler.All(handleViewSitemap))
|
||||
r.HandleFunc("/feed/", handler.All(ViewFeed))
|
||||
r.HandleFunc("/{slug}", handler.Web(viewCollectionPost, UserLevelOptional))
|
||||
r.HandleFunc("/{slug}/edit", handler.Web(handleViewPad, UserLevelUser))
|
||||
r.HandleFunc("/{slug}/edit/meta", handler.Web(handleViewMeta, UserLevelUser))
|
||||
r.HandleFunc("/{slug}/", handler.Web(handleCollectionPostRedirect, UserLevelOptional)).Methods("GET")
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ const (
|
|||
sessionLength = 180 * day
|
||||
cookieName = "wfu"
|
||||
cookieUserVal = "u"
|
||||
|
||||
blogPassCookieName = "ub"
|
||||
)
|
||||
|
||||
// initSession creates the cookie store. It depends on the keychain already
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/ikeikeikeike/go-sitemap-generator/stm"
|
||||
"github.com/writeas/web-core/log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func buildSitemap(host, alias string) *stm.Sitemap {
|
||||
sm := stm.NewSitemap()
|
||||
sm.SetDefaultHost(host)
|
||||
if alias != "/" {
|
||||
sm.SetSitemapsPath(alias)
|
||||
}
|
||||
|
||||
sm.Create()
|
||||
|
||||
// Note: Do not call `sm.Finalize()` because it flushes
|
||||
// the underlying datastructure from memory to disk.
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func handleViewSitemap(app *app, w http.ResponseWriter, r *http.Request) error {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
// Determine canonical blog URL
|
||||
alias := vars["collection"]
|
||||
subdomain := vars["subdomain"]
|
||||
isSubdomain := subdomain != ""
|
||||
if isSubdomain {
|
||||
alias = subdomain
|
||||
}
|
||||
|
||||
host := fmt.Sprintf("%s/%s/", app.cfg.App.Host, alias)
|
||||
var c *Collection
|
||||
var err error
|
||||
pre := "/"
|
||||
if app.cfg.App.SingleUser {
|
||||
c, err = app.db.GetCollectionByID(1)
|
||||
} else {
|
||||
c, err = app.db.GetCollection(alias)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isSubdomain {
|
||||
pre += alias + "/"
|
||||
}
|
||||
host = c.CanonicalURL()
|
||||
|
||||
sm := buildSitemap(host, pre)
|
||||
posts, err := app.db.GetPosts(c, 0, false)
|
||||
if err != nil {
|
||||
log.Error("Error getting posts: %v", err)
|
||||
return err
|
||||
}
|
||||
lastSiteMod := time.Now()
|
||||
for i, p := range *posts {
|
||||
if i == 0 {
|
||||
lastSiteMod = p.Updated
|
||||
}
|
||||
u := stm.URL{
|
||||
"loc": p.Slug.String,
|
||||
"changefreq": "weekly",
|
||||
"mobile": true,
|
||||
"lastmod": p.Updated,
|
||||
}
|
||||
if len(p.Images) > 0 {
|
||||
imgs := []stm.URL{}
|
||||
for _, i := range p.Images {
|
||||
imgs = append(imgs, stm.URL{"loc": i, "title": ""})
|
||||
}
|
||||
u["image"] = imgs
|
||||
}
|
||||
sm.Add(u)
|
||||
}
|
||||
|
||||
// Add top URL
|
||||
sm.Add(stm.URL{
|
||||
"loc": pre,
|
||||
"changefreq": "daily",
|
||||
"priority": "1.0",
|
||||
"lastmod": lastSiteMod,
|
||||
})
|
||||
|
||||
w.Write(sm.XMLContent())
|
||||
|
||||
return nil
|
||||
}
|
|
@ -89,7 +89,6 @@ function unpinPost(e, postID) {
|
|||
// Hide current page
|
||||
var $pinnedNavLink = $header.getElementsByTagName('nav')[0].querySelector('.pinned.selected');
|
||||
$pinnedNavLink.style.display = 'none';
|
||||
try { _paq.push(['trackEvent', 'Post', 'unpin', 'post']); } catch(e) {}
|
||||
};
|
||||
|
||||
var $pinBtn = $header.getElementsByClassName('unpin')[0];
|
||||
|
|
|
@ -365,7 +365,6 @@ H.getEl('set-now').on('click', function(e) {
|
|||
// whatevs
|
||||
}
|
||||
</script>
|
||||
<noscript><p><img src="https://analytics.write.as/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
||||
<link href="/css/icons.css" rel="stylesheet">
|
||||
</body>
|
||||
</html>{{end}}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"github.com/writeas/impart"
|
||||
"github.com/writeas/web-core/log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handleWebSignup(app *app, w http.ResponseWriter, r *http.Request) error {
|
||||
reqJSON := IsJSON(r.Header.Get("Content-Type"))
|
||||
|
||||
// Get params
|
||||
var ur userRegistration
|
||||
if reqJSON {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&ur)
|
||||
if err != nil {
|
||||
log.Error("Couldn't parse signup JSON request: %v\n", err)
|
||||
return ErrBadJSON
|
||||
}
|
||||
} else {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Error("Couldn't parse signup form request: %v\n", err)
|
||||
return ErrBadFormData
|
||||
}
|
||||
|
||||
err = app.formDecoder.Decode(&ur, r.PostForm)
|
||||
if err != nil {
|
||||
log.Error("Couldn't decode signup form request: %v\n", err)
|
||||
return ErrBadFormData
|
||||
}
|
||||
}
|
||||
ur.Web = true
|
||||
|
||||
_, err := signupWithRegistration(app, ur, w, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return impart.HTTPError{http.StatusFound, "/"}
|
||||
}
|
||||
|
||||
// { "username": "asdf" }
|
||||
// result: { code: 204 }
|
||||
func handleUsernameCheck(app *app, w http.ResponseWriter, r *http.Request) error {
|
||||
reqJSON := IsJSON(r.Header.Get("Content-Type"))
|
||||
|
||||
// Get params
|
||||
var d struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
if reqJSON {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&d)
|
||||
if err != nil {
|
||||
log.Error("Couldn't decode username check: %v\n", err)
|
||||
return ErrBadFormData
|
||||
}
|
||||
} else {
|
||||
return impart.HTTPError{http.StatusNotAcceptable, "Must be JSON request"}
|
||||
}
|
||||
|
||||
// Check if username is okay
|
||||
finalUsername := getSlug(d.Username, "")
|
||||
if finalUsername == "" {
|
||||
errMsg := "Invalid username"
|
||||
if d.Username != "" {
|
||||
// Username was provided, but didn't convert into valid latin characters
|
||||
errMsg += " - must have at least 2 letters or numbers"
|
||||
}
|
||||
return impart.HTTPError{http.StatusBadRequest, errMsg + "."}
|
||||
}
|
||||
if app.db.PostIDExists(finalUsername) {
|
||||
return impart.HTTPError{http.StatusConflict, "Username is already taken."}
|
||||
}
|
||||
var un string
|
||||
err := app.db.QueryRow("SELECT username FROM users WHERE username = ?", finalUsername).Scan(&un)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return impart.WriteSuccess(w, finalUsername, http.StatusOK)
|
||||
case err != nil:
|
||||
log.Error("Couldn't SELECT username: %v", err)
|
||||
return impart.HTTPError{http.StatusInternalServerError, "We messed up."}
|
||||
}
|
||||
|
||||
// Username was found, so it's taken
|
||||
return impart.HTTPError{http.StatusConflict, "Username is already taken."}
|
||||
}
|
||||
|
||||
func getValidUsername(app *app, reqName, prevName string) (string, *impart.HTTPError) {
|
||||
// Check if username is okay
|
||||
finalUsername := getSlug(reqName, "")
|
||||
if finalUsername == "" {
|
||||
errMsg := "Invalid username"
|
||||
if reqName != "" {
|
||||
// Username was provided, but didn't convert into valid latin characters
|
||||
errMsg += " - must have at least 2 letters or numbers"
|
||||
}
|
||||
return "", &impart.HTTPError{http.StatusBadRequest, errMsg + "."}
|
||||
}
|
||||
if finalUsername == prevName {
|
||||
return "", &impart.HTTPError{http.StatusNotModified, "Username unchanged."}
|
||||
}
|
||||
if app.db.PostIDExists(finalUsername) {
|
||||
return "", &impart.HTTPError{http.StatusConflict, "Username is already taken."}
|
||||
}
|
||||
var un string
|
||||
err := app.db.QueryRow("SELECT username FROM users WHERE username = ?", finalUsername).Scan(&un)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
return finalUsername, nil
|
||||
case err != nil:
|
||||
log.Error("Couldn't SELECT username: %v", err)
|
||||
return "", &impart.HTTPError{http.StatusInternalServerError, "We messed up."}
|
||||
}
|
||||
|
||||
// Username was found, so it's taken
|
||||
return "", &impart.HTTPError{http.StatusConflict, "Username is already taken."}
|
||||
}
|
Loading…
Reference in New Issue