mirror of
https://github.com/zoriya/vex.git
synced 2026-06-03 02:41:31 +00:00
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (h *Handler) GetEntries(c echo.Context) error {
|
||||
ret, err := h.entries.ListEntries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(200, ret)
|
||||
}
|
||||
|
||||
func (h *Handler) RegisterEntriesRoutes(e *echo.Echo, r *echo.Group) {
|
||||
e.GET("/entries", h.GetEntries)
|
||||
}
|
||||
+10
-1
@@ -12,6 +12,14 @@ type AddFeedDto struct {
|
||||
Tags []string `json:"tags" validate:"required"`
|
||||
}
|
||||
|
||||
func (h *Handler) GetFeeds(c echo.Context) error {
|
||||
ret, err := h.feeds.ListFeeds()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(200, ret)
|
||||
}
|
||||
|
||||
func (h *Handler) AddFeed(c echo.Context) error {
|
||||
user, err := GetCurrentUserId(c)
|
||||
if err != nil {
|
||||
@@ -35,6 +43,7 @@ func (h *Handler) AddFeed(c echo.Context) error {
|
||||
return c.JSON(201, feed)
|
||||
}
|
||||
|
||||
func (h *Handler) RegisterFeedsRoutes(echo *echo.Echo, r *echo.Group) {
|
||||
func (h *Handler) RegisterFeedsRoutes(e *echo.Echo, r *echo.Group) {
|
||||
e.GET("/feeds", h.GetFeeds)
|
||||
r.POST("/feeds", h.AddFeed)
|
||||
}
|
||||
|
||||
+3
-5
@@ -17,15 +17,11 @@ import (
|
||||
|
||||
type Handler struct {
|
||||
feeds vex.FeedService
|
||||
entries vex.EntryService
|
||||
users vex.UserService
|
||||
jwtSecret []byte
|
||||
}
|
||||
|
||||
func (h *Handler) GetEntries(c echo.Context) error {
|
||||
ret := make([]interface{}, 0)
|
||||
return c.JSON(200, ret)
|
||||
}
|
||||
|
||||
type Validator struct {
|
||||
validator *validator.Validate
|
||||
}
|
||||
@@ -53,6 +49,7 @@ func main() {
|
||||
}
|
||||
h := Handler{
|
||||
feeds: vex.NewFeedService(db),
|
||||
entries: vex.NewEntryService(db),
|
||||
users: vex.NewUserService(db),
|
||||
jwtSecret: []byte(os.Getenv("JWT_SECRET")),
|
||||
}
|
||||
@@ -68,6 +65,7 @@ func main() {
|
||||
|
||||
e.GET("/entries", h.GetEntries)
|
||||
h.RegisterLoginRoutes(e, r)
|
||||
h.RegisterEntriesRoutes(e, r)
|
||||
h.RegisterFeedsRoutes(e, r)
|
||||
|
||||
e.Start(":1597")
|
||||
|
||||
+1
-11
@@ -20,12 +20,6 @@ type RegisterDto struct {
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
type UserDto struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (h *Handler) Login(c echo.Context) error {
|
||||
var req LoginDto
|
||||
err := c.Bind(&req)
|
||||
@@ -86,11 +80,7 @@ func (h *Handler) GetMe(c echo.Context) error {
|
||||
if user == nil {
|
||||
return echo.NewHTTPError(500, "Internal server error")
|
||||
}
|
||||
return c.JSON(200, UserDto{
|
||||
Id: user.Id,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
})
|
||||
return c.JSON(200, user)
|
||||
}
|
||||
|
||||
func GetCurrentUserId(c echo.Context) (uuid.UUID, error) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package vex
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Date time.Time `json:"date"`
|
||||
Content string `json:"content"`
|
||||
Author *string `json:"author"`
|
||||
FeedId uuid.UUID `json:"feedId"`
|
||||
Feed Feed `json:"feed,omitempty"`
|
||||
}
|
||||
|
||||
type EntryDao struct {
|
||||
Id uuid.UUID
|
||||
Title string
|
||||
Link string
|
||||
Date time.Time
|
||||
Content string
|
||||
Author *string
|
||||
FeedId uuid.UUID `db:"feed_id"`
|
||||
Feed FeedDao `db:"feed"`
|
||||
}
|
||||
|
||||
func (e *EntryDao) ToEntry() Entry {
|
||||
return Entry{
|
||||
Id: e.Id,
|
||||
Title: e.Title,
|
||||
Link: e.Link,
|
||||
Date: e.Date,
|
||||
Content: e.Content,
|
||||
Author: e.Author,
|
||||
FeedId: e.FeedId,
|
||||
Feed: e.Feed.ToFeed(),
|
||||
}
|
||||
}
|
||||
|
||||
type EntryService struct {
|
||||
database *sqlx.DB
|
||||
}
|
||||
|
||||
func NewEntryService(db *sqlx.DB) EntryService {
|
||||
return EntryService{database: db}
|
||||
}
|
||||
|
||||
func (s EntryService) ListEntries() ([]Entry, error) {
|
||||
ret := []EntryDao{}
|
||||
err := s.database.Select(
|
||||
&ret,
|
||||
`select e.*, f.id as "feed.id", f.name as "feed.name", f.link as "feed.link", f.favicon_url as "feed.favicon_url",
|
||||
f.tags as "feed.tags", f.submitter_id as "feed.submitter_id", f.added_date as "feed.added_date"
|
||||
from entries as e
|
||||
left join feeds as f on f.id = e.feed_id
|
||||
order by e.date`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Map(ret, func(e EntryDao, _ int) Entry { return e.ToEntry() }), nil
|
||||
}
|
||||
+46
-4
@@ -1,18 +1,46 @@
|
||||
package vex
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
type Feed struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Link string `json:"link"`
|
||||
FaviconUrl string `json:"faviconUrl"`
|
||||
Tags []string `json:"tags"`
|
||||
SubmitterId uuid.UUID `json:"submitterId"`
|
||||
Submitter User `json:"submitter,omitempty"`
|
||||
AddedDate time.Time `json:"addedDate"`
|
||||
}
|
||||
|
||||
type FeedDao struct {
|
||||
Id uuid.UUID
|
||||
Name string
|
||||
Link string
|
||||
FaviconUrl string `db:"favicon_url"`
|
||||
Tags pq.StringArray
|
||||
SubmitterId uuid.UUID `db:"submitter_id"`
|
||||
Submitter User `db:"submitter"`
|
||||
AddedDate time.Time `db:"added_date"`
|
||||
}
|
||||
|
||||
func (f *FeedDao) ToFeed() Feed {
|
||||
return Feed{
|
||||
Id: f.Id,
|
||||
Name: f.Name,
|
||||
Link: f.Name,
|
||||
FaviconUrl: f.FaviconUrl,
|
||||
Tags: f.Tags,
|
||||
SubmitterId: f.SubmitterId,
|
||||
Submitter: f.Submitter,
|
||||
AddedDate: f.AddedDate,
|
||||
}
|
||||
}
|
||||
|
||||
type FeedService struct {
|
||||
@@ -24,7 +52,7 @@ func NewFeedService(db *sqlx.DB) FeedService {
|
||||
}
|
||||
|
||||
func (s FeedService) AddFeed(link string, tags []string, submitter uuid.UUID) (Feed, error) {
|
||||
feed := Feed{
|
||||
feed := FeedDao{
|
||||
Id: uuid.New(),
|
||||
Name: link,
|
||||
Link: link,
|
||||
@@ -34,12 +62,26 @@ func (s FeedService) AddFeed(link string, tags []string, submitter uuid.UUID) (F
|
||||
}
|
||||
|
||||
_, err := s.database.NamedExec(
|
||||
`insert into feeds (id, name, link, favicon_url, tags, submitter_id)
|
||||
values (:id, :name, :link, :favicon_url, :tags, :submitter_id)`,
|
||||
`insert into feeds (id, name, link, favicon_url, tags, submitter_id, added_date)
|
||||
values (:id, :name, :link, :favicon_url, :tags, :submitter_id, :added_date)`,
|
||||
feed,
|
||||
)
|
||||
if err != nil {
|
||||
return Feed{}, err
|
||||
}
|
||||
return feed, nil
|
||||
return feed.ToFeed(), nil
|
||||
}
|
||||
|
||||
func (s FeedService) ListFeeds() ([]Feed, error) {
|
||||
ret := []FeedDao{}
|
||||
err := s.database.Select(
|
||||
&ret,
|
||||
`select f.*, u.id as "submitter.id", u.name as "submitter.name", u.email as "submitter.email", u.password as "submitter.password" from feeds
|
||||
as f left join users as u on u.id = f.submitter_id
|
||||
order by added_date`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Map(ret, func(f FeedDao, _ int) Feed { return f.ToFeed() }), nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ go 1.22.1
|
||||
|
||||
require (
|
||||
github.com/go-playground/validator/v10 v10.20.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/labstack/echo-jwt/v4 v4.2.0
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
||||
+4
-4
@@ -7,10 +7,10 @@ import (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id uuid.UUID
|
||||
Name string
|
||||
Email string
|
||||
Password []byte
|
||||
Id uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Password []byte `json:"-"`
|
||||
}
|
||||
|
||||
type UserService struct {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package vex
|
||||
|
||||
func Map[T, U any](ts []T, f func(T, int) U) []U {
|
||||
us := make([]U, len(ts))
|
||||
for i := range ts {
|
||||
us[i] = f(ts[i], i)
|
||||
}
|
||||
return us
|
||||
}
|
||||
+2
-1
@@ -11,7 +11,8 @@ create table if not exists feeds(
|
||||
link text not null unique,
|
||||
favicon_url text not null,
|
||||
tags text[] not null,
|
||||
submitter_id uuid not null references users(id)
|
||||
submitter_id uuid not null references users(id),
|
||||
added_date timestamp with time zone not null,
|
||||
);
|
||||
|
||||
create table if not exists entries(
|
||||
|
||||
Reference in New Issue
Block a user