fix some endpoints
This commit is contained in:
parent
7aee99ac42
commit
6f1b94c040
10 changed files with 47 additions and 41 deletions
|
@ -4,9 +4,14 @@
|
||||||
|
|
||||||
create table account_follows (
|
create table account_follows (
|
||||||
account_id text not null references accounts (id) on delete cascade,
|
account_id text not null references accounts (id) on delete cascade,
|
||||||
blog_id text not null references blogs (id) on delete cascade
|
blog_id text not null references blogs (id) on delete cascade,
|
||||||
|
|
||||||
|
primary key (account_id, blog_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create index account_follows_account_id_idx on account_follows (account_id);
|
||||||
|
|
||||||
-- +migrate Down
|
-- +migrate Down
|
||||||
|
|
||||||
|
drop index account_follows_account_id_idx;
|
||||||
drop table account_follows;
|
drop table account_follows;
|
||||||
|
|
|
@ -19,22 +19,26 @@ func NewTimelineStore(q Querier) *TimelineStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimelinePost struct {
|
type TimelinePost struct {
|
||||||
database.Post
|
Post database.Post `db:"p"`
|
||||||
database.Blog
|
Blog database.Blog `db:"b"`
|
||||||
|
Account database.Account `db:"a"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimelineStore) Home(ctx context.Context, accountID ulid.ULID, limit int, before, after *ulid.ULID) ([]TimelinePost, error) {
|
func (s *TimelineStore) Home(ctx context.Context, accountID ulid.ULID, limit int, before, after *ulid.ULID) ([]TimelinePost, error) {
|
||||||
q := sqlf.Sprintf("SELECT p.*, b.account_id, b.name, b.domain FROM posts p JOIN blogs b ON b.id = p.blog_id")
|
q := sqlf.Sprintf(`SELECT p.id as "p.id", p.blog_id as "p.blog_id", p.content as "p.content", p.source as "p.source", p.visibility as "p.visibility",
|
||||||
|
b.id as "b.id", b.name as "b.name", b.domain as "b.domain", b.bio as "b.bio", b.account_id as "b.account_id",
|
||||||
|
a.id as "a.id", a.username as "a.username", a.domain as "a.domain"
|
||||||
|
FROM posts p JOIN blogs b ON b.id = p.blog_id JOIN accounts a on a.id = b.account_id`)
|
||||||
|
|
||||||
q = sqlf.Sprintf("%v WHERE (blog_id IN (%s) OR blog_id IN (%s))", q,
|
q = sqlf.Sprintf("%v WHERE (blog_id IN (%s) OR blog_id IN (%s))", q,
|
||||||
sqlf.Sprintf("SELECT id FROM blogs WHERE account_id = %s", accountID),
|
sqlf.Sprintf("SELECT id FROM blogs WHERE account_id = %s", accountID),
|
||||||
sqlf.Sprintf("SELECT blog_id FROM account_follows WHERE account_id = %s", accountID))
|
sqlf.Sprintf("SELECT blog_id FROM account_follows WHERE account_id = %s", accountID))
|
||||||
|
|
||||||
if before != nil {
|
if before != nil {
|
||||||
q = sqlf.Sprintf("%v AND id < %s", q, *before)
|
q = sqlf.Sprintf("%v AND p.id < %s", q, *before)
|
||||||
}
|
}
|
||||||
if after != nil {
|
if after != nil {
|
||||||
q = sqlf.Sprintf("%v AND id > %s", q, *after)
|
q = sqlf.Sprintf("%v AND p.id > %s", q, *after)
|
||||||
}
|
}
|
||||||
|
|
||||||
q = sqlf.Sprintf("%v AND (visibility != %s OR (b.account_id = %s OR %s IN (%s)))", q, database.DirectVisibility, accountID, accountID,
|
q = sqlf.Sprintf("%v AND (visibility != %s OR (b.account_id = %s OR %s IN (%s)))", q, database.DirectVisibility, accountID, accountID,
|
||||||
|
@ -43,7 +47,7 @@ func (s *TimelineStore) Home(ctx context.Context, accountID ulid.ULID, limit int
|
||||||
if limit <= 0 || limit > 100 {
|
if limit <= 0 || limit > 100 {
|
||||||
limit = 100
|
limit = 100
|
||||||
}
|
}
|
||||||
q = sqlf.Sprintf("%v ORDER BY id DESC LIMIT %d", q, limit)
|
q = sqlf.Sprintf("%v ORDER BY p.id DESC LIMIT %d", q, limit)
|
||||||
|
|
||||||
return Select[TimelinePost](ctx, s.q, q)
|
return Select[TimelinePost](ctx, s.q, q)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,7 @@ type Blog struct {
|
||||||
Domain *string `json:"domain"`
|
Domain *string `json:"domain"`
|
||||||
Bio string `json:"bio"`
|
Bio string `json:"bio"`
|
||||||
|
|
||||||
Account blogPartialAccount `json:"account"`
|
Account Account `json:"account"`
|
||||||
}
|
|
||||||
|
|
||||||
type blogPartialAccount struct {
|
|
||||||
ID ulid.ULID `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Domain *string `json:"domain"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DBBlogToBlog(b database.Blog, a database.Account) Blog {
|
func DBBlogToBlog(b database.Blog, a database.Account) Blog {
|
||||||
|
@ -27,10 +21,6 @@ func DBBlogToBlog(b database.Blog, a database.Account) Blog {
|
||||||
Name: b.Name,
|
Name: b.Name,
|
||||||
Domain: b.Domain,
|
Domain: b.Domain,
|
||||||
Bio: b.Bio,
|
Bio: b.Bio,
|
||||||
Account: blogPartialAccount{
|
Account: DBAccountToAccount(a),
|
||||||
ID: a.ID,
|
|
||||||
Username: a.Username,
|
|
||||||
Domain: a.Domain,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,25 +11,15 @@ type Post struct {
|
||||||
Source *string `json:"source"`
|
Source *string `json:"source"`
|
||||||
Visibility database.PostVisibility `json:"visibility"`
|
Visibility database.PostVisibility `json:"visibility"`
|
||||||
|
|
||||||
Blog postPartialBlog `json:"blog"`
|
Blog Blog `json:"blog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type postPartialBlog struct {
|
func DBPostToPost(p database.Post, b database.Blog, a database.Account) Post {
|
||||||
ID ulid.ULID `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Domain *string `json:"domain"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func DBPostToPost(p database.Post, b database.Blog) Post {
|
|
||||||
return Post{
|
return Post{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
Content: p.Content,
|
Content: p.Content,
|
||||||
Source: p.Source,
|
Source: p.Source,
|
||||||
Visibility: p.Visibility,
|
Visibility: p.Visibility,
|
||||||
Blog: postPartialBlog{
|
Blog: DBBlogToBlog(b, a),
|
||||||
ID: p.BlogID,
|
|
||||||
Name: b.Name,
|
|
||||||
Domain: b.Domain,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,12 @@ func (app *App) Create(w http.ResponseWriter, r *http.Request) (api.Post, error)
|
||||||
return api.Post{}, err
|
return api.Post{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acct, err := app.Account(conn).ByID(ctx, blog.AccountID)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("fetching account")
|
||||||
|
return api.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
if blog.AccountID != token.UserID {
|
if blog.AccountID != token.UserID {
|
||||||
return api.Post{}, api.Error{Code: api.ErrNotYourObject}
|
return api.Post{}, api.Error{Code: api.ErrNotYourObject}
|
||||||
}
|
}
|
||||||
|
@ -87,5 +93,5 @@ func (app *App) Create(w http.ResponseWriter, r *http.Request) (api.Post, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: federate post + push to websockets
|
// TODO: federate post + push to websockets
|
||||||
return api.DBPostToPost(post, blog), nil
|
return api.DBPostToPost(post, blog, acct), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,5 +40,11 @@ func (app *App) GetID(w http.ResponseWriter, r *http.Request) (api.Post, error)
|
||||||
return api.Post{}, err
|
return api.Post{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.DBPostToPost(post, blog), nil
|
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.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.DBPostToPost(post, blog, acct), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,12 @@ func (app *App) Home(w http.ResponseWriter, r *http.Request) (timelineResponse,
|
||||||
posts, err := app.Timeline().Home(ctx, token.UserID, limit, before, after)
|
posts, err := app.Timeline().Home(ctx, token.UserID, limit, before, after)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("getting posts from database")
|
log.Err(err).Msg("getting posts from database")
|
||||||
|
return timelineResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := timelineResponse{Posts: make([]api.Post, len(posts))}
|
resp := timelineResponse{Posts: make([]api.Post, len(posts))}
|
||||||
for i := range posts {
|
for i := range posts {
|
||||||
resp.Posts[i] = api.DBPostToPost(posts[i].Post, posts[i].Blog)
|
resp.Posts[i] = api.DBPostToPost(posts[i].Post, posts[i].Blog, posts[i].Account)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
|
@ -61,9 +61,11 @@ func (app *App) APIAuth(scope database.TokenScope, anonAccess bool) func(next ht
|
||||||
render.JSON(w, r, api.Error{
|
render.JSON(w, r, api.Error{
|
||||||
Code: api.ErrInvalidToken,
|
Code: api.ErrInvalidToken,
|
||||||
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
||||||
|
Details: "No token supplied",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
header = cookie.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := app.ParseToken(r.Context(), header)
|
token, err := app.ParseToken(r.Context(), header)
|
||||||
|
@ -72,6 +74,7 @@ func (app *App) APIAuth(scope database.TokenScope, anonAccess bool) func(next ht
|
||||||
render.JSON(w, r, api.Error{
|
render.JSON(w, r, api.Error{
|
||||||
Code: api.ErrInvalidToken,
|
Code: api.ErrInvalidToken,
|
||||||
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
||||||
|
Details: "Could not parse token",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -81,6 +84,7 @@ func (app *App) APIAuth(scope database.TokenScope, anonAccess bool) func(next ht
|
||||||
render.JSON(w, r, api.Error{
|
render.JSON(w, r, api.Error{
|
||||||
Code: api.ErrInvalidToken,
|
Code: api.ErrInvalidToken,
|
||||||
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
Message: api.ErrCodeMessage(api.ErrInvalidToken),
|
||||||
|
Details: "Token is expired",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
{{.Vue.RenderTags}}
|
{{.Vue.RenderTags}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="bg-background-200 dark:bg-background-900 text-textLight dark:text-textDark">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
<script type="text/json" id="accountData">{{.AccountData}}</script>
|
<script type="text/json" id="accountData">{{.AccountData}}</script>
|
||||||
|
|
|
@ -40,8 +40,8 @@ func New(app *app.App) *Frontend {
|
||||||
glue, err := vueglue.NewVueGlue(&vueglue.ViteConfig{
|
glue, err := vueglue.NewVueGlue(&vueglue.ViteConfig{
|
||||||
Environment: "development",
|
Environment: "development",
|
||||||
AssetsPath: "frontend",
|
AssetsPath: "frontend",
|
||||||
EntryPoint: "src/main.ts",
|
EntryPoint: "src/main.tsx",
|
||||||
Platform: "svelte",
|
Platform: "vue",
|
||||||
FS: os.DirFS("frontend"),
|
FS: os.DirFS("frontend"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,8 +55,8 @@ func New(app *app.App) *Frontend {
|
||||||
Environment: "production",
|
Environment: "production",
|
||||||
URLPrefix: "/assets/",
|
URLPrefix: "/assets/",
|
||||||
AssetsPath: "dist",
|
AssetsPath: "dist",
|
||||||
EntryPoint: "src/main.ts",
|
EntryPoint: "src/main.tsx",
|
||||||
Platform: "svelte",
|
Platform: "vue",
|
||||||
FS: frontend.Embed,
|
FS: frontend.Embed,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue