diff --git a/internal/database/token.go b/internal/database/token.go index f40f582..6577396 100644 --- a/internal/database/token.go +++ b/internal/database/token.go @@ -53,6 +53,8 @@ const ( // Controls whether tokens have access to sensitive account data, NOT if they can use `/accounts/@me` endpoints. TokenScopeAccountsMe TokenScope = "accounts.me" TokenScopeAccountsWrite TokenScope = "accounts.write" + TokenScopeBlogsRead TokenScope = "blogs.read" + TokenScopeBlogsWrite TokenScope = "blogs.write" ) func (s TokenScope) IsValid() bool { diff --git a/web/api/blogs/get_blog.go b/web/api/blogs/get_blog.go new file mode 100644 index 0000000..12e511f --- /dev/null +++ b/web/api/blogs/get_blog.go @@ -0,0 +1,74 @@ +package blogs + +import ( + "net/http" + + "git.sleepycat.moe/sam/mercury/internal/database/sql" + "git.sleepycat.moe/sam/mercury/web/api" + "github.com/go-chi/chi/v5" + "github.com/oklog/ulid/v2" + "github.com/rs/zerolog/log" +) + +func (app *App) GetID(w http.ResponseWriter, r *http.Request) (api.Blog, error) { + ctx := r.Context() + id, err := ulid.Parse(chi.URLParamFromCtx(ctx, "blogID")) + if err != nil { + return api.Blog{}, api.Error{Code: api.ErrBlogNotFound} + } + + conn, err := app.Database.Acquire(ctx) + if err != nil { + log.Err(err).Msg("acquiring connection") + return api.Blog{}, err + } + defer conn.Release() + + blog, err := app.Blog(conn).ByID(ctx, id) + if err != nil { + if err == sql.ErrNotFound { + return api.Blog{}, api.Error{Code: api.ErrBlogNotFound} + } + + log.Err(err).Str("id", id.String()).Msg("fetching blog from database") + return api.Blog{}, err + } + + acct, err := app.Account(conn).ByID(ctx, blog.AccountID) + if err != nil { + log.Err(err).Str("id", blog.AccountID.String()).Msg("fetching account from database") + return api.Blog{}, err + } + + return api.DBBlogToBlog(blog, acct), nil +} + +func (app *App) LookupName(w http.ResponseWriter, r *http.Request) (api.Blog, error) { + ctx := r.Context() + name := chi.URLParamFromCtx(ctx, "blogName") + + conn, err := app.Database.Acquire(ctx) + if err != nil { + log.Err(err).Msg("acquiring connection") + return api.Blog{}, err + } + defer conn.Release() + + blog, err := app.Blog(conn).ByName(ctx, name, "") + if err != nil { + if err == sql.ErrNotFound { + return api.Blog{}, api.Error{Code: api.ErrBlogNotFound} + } + + log.Err(err).Str("name", name).Msg("fetching blog from database") + return api.Blog{}, err + } + + acct, err := app.Account(conn).ByID(ctx, blog.AccountID) + if err != nil { + log.Err(err).Str("id", blog.AccountID.String()).Msg("fetching account from database") + return api.Blog{}, err + } + + return api.DBBlogToBlog(blog, acct), nil +} diff --git a/web/api/blogs/module.go b/web/api/blogs/module.go new file mode 100644 index 0000000..6d651b5 --- /dev/null +++ b/web/api/blogs/module.go @@ -0,0 +1,13 @@ +package blogs + +import "git.sleepycat.moe/sam/mercury/web/app" + +type App struct { + *app.App +} + +func New(app *app.App) *App { + return &App{ + App: app, + } +} diff --git a/web/api/error.go b/web/api/error.go index 226ffd3..92368cc 100644 --- a/web/api/error.go +++ b/web/api/error.go @@ -126,7 +126,10 @@ const ( ErrMissingScope = 1002 // Account related - ErrAccountNotFound = 1003 + ErrAccountNotFound = 2001 + + // Blog related + ErrBlogNotFound = 3001 ) func ErrCodeMessage(code int) string { @@ -145,6 +148,8 @@ var errCodeMessages = map[int]string{ ErrMissingScope: "Token is missing required scope for this endpoint", ErrAccountNotFound: "Account not found", + + ErrBlogNotFound: "Blog not found", } func ErrCodeStatus(code int) int { @@ -163,4 +168,6 @@ var errCodeStatuses = map[int]int{ ErrMissingScope: http.StatusForbidden, ErrAccountNotFound: http.StatusNotFound, + + ErrBlogNotFound: http.StatusNotFound, } diff --git a/web/routes.go b/web/routes.go index 853d58e..2c35f3e 100644 --- a/web/routes.go +++ b/web/routes.go @@ -4,6 +4,7 @@ import ( "git.sleepycat.moe/sam/mercury/internal/database" "git.sleepycat.moe/sam/mercury/web/api" "git.sleepycat.moe/sam/mercury/web/api/accounts" + "git.sleepycat.moe/sam/mercury/web/api/blogs" "git.sleepycat.moe/sam/mercury/web/app" "git.sleepycat.moe/sam/mercury/web/auth" "git.sleepycat.moe/sam/mercury/web/frontend" @@ -43,5 +44,11 @@ func Routes(app *app.App) { Get("/accounts/{accountID}", api.WrapHandlerT(accounts.GetID)) r.With(app.APIAuth(database.TokenScopeAccountsMe, false)). Get("/accounts/@me", api.WrapHandlerT(accounts.GetMe)) + + blogs := blogs.New(app) + r.With(app.APIAuth(database.TokenScopeBlogsRead, true)). + Get("/blogs/{blogID}", api.WrapHandlerT(blogs.GetID)) + r.With(app.APIAuth(database.TokenScopeBlogsRead, true)). + Get("/blogs/lookup/{blogName}", api.WrapHandlerT(blogs.LookupName)) }) }