feat: added logging and some routes with fmt
This commit is contained in:
@@ -4,13 +4,13 @@ import (
|
||||
"blog/internal/cache"
|
||||
"blog/internal/config"
|
||||
"blog/internal/database"
|
||||
"blog/internal/echo/logger"
|
||||
"blog/internal/echo/routes"
|
||||
"blog/internal/services"
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -38,10 +38,12 @@ func main() {
|
||||
|
||||
// Setup echo server
|
||||
e := echo.New()
|
||||
e.Use(middleware.RequestLogger())
|
||||
e.Logger = logger.NewCharmSlog()
|
||||
|
||||
// Setup routes
|
||||
routes.SetupRoutes(e, strapi_service)
|
||||
routes.SetupRoutes(e, routes.Sources{
|
||||
StrapiService: strapi_service,
|
||||
})
|
||||
|
||||
// Start server
|
||||
host := fmt.Sprintf("%s:%d", server_config.Host, server_config.Port)
|
||||
|
||||
4
go.mod
4
go.mod
@@ -34,11 +34,15 @@ require (
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/labstack/echo/v5 v5.0.4 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.20 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.31 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@@ -82,10 +82,15 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/labstack/echo/v5 v5.0.4 h1:ll3I/O8BifjMztj9dD1vx/peZQv8cR2CTUdQK6QxGGc=
|
||||
github.com/labstack/echo/v5 v5.0.4/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
@@ -112,6 +117,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
|
||||
github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
|
||||
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=
|
||||
@@ -132,6 +141,7 @@ golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/log"
|
||||
"blog/internal/config/env"
|
||||
"fmt"
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
Name string
|
||||
User string
|
||||
Host string
|
||||
Port int
|
||||
Name string
|
||||
User string
|
||||
Password string
|
||||
SSLMode string
|
||||
SSLMode string
|
||||
TimeZone string
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func (c *DatabaseConfig) LoadConfig() {
|
||||
c.SSLMode = env.GetString("DB_SSL_MODE", "disable")
|
||||
c.TimeZone = env.GetString("DB_TIME_ZONE", "America/New_York")
|
||||
|
||||
log.Info("Successfully loaded database config")
|
||||
log.Info("Successfully loaded database config")
|
||||
}
|
||||
|
||||
func (c *DatabaseConfig) GetDSN() string {
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package config
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/log"
|
||||
"blog/internal/config/env"
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
Host string
|
||||
Port int
|
||||
StrapiEndpoint string
|
||||
RedisHost string
|
||||
RedisPort int
|
||||
StrapiToken string
|
||||
CacheTTL int
|
||||
EchoMode string
|
||||
RedisHost string
|
||||
RedisPort int
|
||||
StrapiToken string
|
||||
CacheTTL int
|
||||
EchoMode string
|
||||
}
|
||||
|
||||
func NewServerConfig() *ServerConfig {
|
||||
|
||||
@@ -18,14 +18,13 @@ type DB struct {
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
|
||||
// Connect connects to database and returns DB
|
||||
func Connect(dsn string) (*DB, error) {
|
||||
// Charmbracelet's "log" as a slog handler
|
||||
charmHandler := log.NewWithOptions(os.Stdout, log.Options{
|
||||
ReportCaller: false,
|
||||
ReportCaller: false,
|
||||
ReportTimestamp: true,
|
||||
Prefix: "GORM",
|
||||
Prefix: "GORM",
|
||||
})
|
||||
|
||||
slogger := slog.New(charmHandler)
|
||||
@@ -34,12 +33,12 @@ func Connect(dsn string) (*DB, error) {
|
||||
gormLogger := logger.New(
|
||||
slog.NewLogLogger(slogger.Handler(), slog.LevelDebug),
|
||||
logger.Config{
|
||||
SlowThreshold: time.Millisecond * 200,
|
||||
LogLevel: logger.Info,
|
||||
SlowThreshold: time.Millisecond * 200,
|
||||
LogLevel: logger.Info,
|
||||
IgnoreRecordNotFoundError: false,
|
||||
Colorful: true,
|
||||
Colorful: true,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
// Connect to database
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||
|
||||
27
internal/echo/handlers/params.go
Normal file
27
internal/echo/handlers/params.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
// Helper method to get int param or default value
|
||||
func GetIntParam(c *echo.Context, name string, defaultValue int) int {
|
||||
value, err := strconv.Atoi(c.ParamOr(name, strconv.Itoa(defaultValue)))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// Helper method to get bool param or default value
|
||||
func GetBoolParam(c *echo.Context, name string, defaultValue bool) bool {
|
||||
value, err := strconv.ParseBool(c.ParamOr(name, strconv.FormatBool(defaultValue)))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
)
|
||||
|
||||
// GetAllPosts returns a list of all posts
|
||||
func GetAllPosts(c *echo.Context, s *services.StrapiService, pageSize, page int) error {
|
||||
func GetAllPosts(c *echo.Context, s *services.StrapiService) error {
|
||||
pageSize := GetIntParam(c, "pageSize", 10)
|
||||
page := GetIntParam(c, "page", 1)
|
||||
|
||||
posts, err := s.GetAllPosts(c.Request().Context(), pageSize, page)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
@@ -18,7 +21,10 @@ func GetAllPosts(c *echo.Context, s *services.StrapiService, pageSize, page int)
|
||||
}
|
||||
|
||||
// GetFeaturedPosts returns a list of featured posts
|
||||
func GetFeaturedPosts(c *echo.Context, s *services.StrapiService, pageSize, page int) error {
|
||||
func GetFeaturedPosts(c *echo.Context, s *services.StrapiService) error {
|
||||
pageSize := GetIntParam(c, "pageSize", 10)
|
||||
page := GetIntParam(c, "page", 1)
|
||||
|
||||
posts, err := s.GetFeaturedPosts(c.Request().Context(), pageSize, page)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
@@ -38,7 +44,10 @@ func GetPost(c *echo.Context, s *services.StrapiService, slug string) error {
|
||||
}
|
||||
|
||||
// GetPostSummaries returns post summaries
|
||||
func GetPostSummaries(c *echo.Context, s *services.StrapiService, pageSize, page int) error {
|
||||
func GetPostSummaries(c *echo.Context, s *services.StrapiService) error {
|
||||
pageSize := GetIntParam(c, "pageSize", 10)
|
||||
page := GetIntParam(c, "page", 1)
|
||||
|
||||
posts, err := s.GetPostSummaries(c.Request().Context(), pageSize, page)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
|
||||
23
internal/echo/logger/logger.go
Normal file
23
internal/echo/logger/logger.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
// NewCharmSlog returns a standard *slog.Logger powered by Charmbracelet
|
||||
func NewCharmSlog() *slog.Logger {
|
||||
// 1. Initialize Charmbracelet
|
||||
options := log.Options{
|
||||
ReportTimestamp: true,
|
||||
ReportCaller: true,
|
||||
Level: log.DebugLevel,
|
||||
}
|
||||
handler := log.NewWithOptions(os.Stderr, options)
|
||||
|
||||
// 2. Return as *slog.Logger
|
||||
// Charmbracelet's Logger implements the slog.Handler interface
|
||||
return slog.New(handler)
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
// "time"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
// "github.com/charmbracelet/log"
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
func ServerHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
start := time.Now()
|
||||
// start := time.Now()
|
||||
|
||||
// Process the request
|
||||
err := next(c)
|
||||
@@ -17,16 +17,16 @@ func ServerHandler(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
c.Logger().Error(err.Error())
|
||||
}
|
||||
|
||||
stop := time.Now()
|
||||
req := c.Request()
|
||||
// stop := time.Now()
|
||||
// req := c.Request()
|
||||
|
||||
// Log using charmbracelet
|
||||
log.Info("Request handled",
|
||||
"method", req.Method,
|
||||
"path", req.URL.Path,
|
||||
"latency", stop.Sub(start),
|
||||
"ip", c.RealIP(),
|
||||
)
|
||||
// // Log using charmbracelet
|
||||
// log.Info("Request handlers",
|
||||
// "method", req.Method,
|
||||
// "path", req.URL.Path,
|
||||
// "latency", stop.Sub(start),
|
||||
// "ip", c.RealIP(),
|
||||
// )
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,36 +1,53 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"blog/internal/cache"
|
||||
"blog/internal/echo/handlers"
|
||||
"blog/internal/echo/middleware"
|
||||
"blog/internal/services"
|
||||
"strconv"
|
||||
"net/http"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
// Helper method to get int param or default value
|
||||
func getIntParam(c *echo.Context, name string, defaultValue int) int {
|
||||
value, err := strconv.Atoi(c.ParamOr(name, strconv.Itoa(defaultValue)))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return value
|
||||
// Souces is a struct that has the sources for any data that the routes need
|
||||
type Sources struct {
|
||||
StrapiService *services.StrapiService
|
||||
Caches []cache.Cache
|
||||
}
|
||||
|
||||
func SetupRoutes(e *echo.Echo, s *services.StrapiService) {
|
||||
func SetupRoutes(e *echo.Echo, sources Sources) {
|
||||
if sources.StrapiService == nil {
|
||||
log.Fatal("Error", "error", "strapi service is required")
|
||||
}
|
||||
|
||||
// Global middleware
|
||||
e.Use(middleware.ServerHandler)
|
||||
|
||||
// Post routes
|
||||
posts := e.Group("/posts") // Routing group
|
||||
// Routes
|
||||
strapiRoutes(e, sources.StrapiService)
|
||||
|
||||
// GET /posts/all
|
||||
posts.GET("/all", func(c *echo.Context) error {
|
||||
pageSize := getIntParam(c, "pageSize", 10)
|
||||
page := getIntParam(c, "page", 1)
|
||||
|
||||
return handlers.GetAllPosts(c, s, pageSize, page)
|
||||
// Special routes
|
||||
e.GET("/api", func(c *echo.Context) error {
|
||||
// Load all routes
|
||||
// routes, _ := json.MarshalIndent(e.Router().Routes(), "", "")
|
||||
return c.JSON(http.StatusOK, e.Router().Routes())
|
||||
})
|
||||
}
|
||||
|
||||
// Setup Strapi routes
|
||||
func strapiRoutes(e *echo.Echo, s *services.StrapiService) {
|
||||
// Post routes
|
||||
posts := e.Group("/api/posts") // Routing group
|
||||
|
||||
// GET /api/posts/all
|
||||
posts.GET("/all", func(c *echo.Context) error {
|
||||
return handlers.GetAllPosts(c, s)
|
||||
})
|
||||
|
||||
// GET /api/posts/featured
|
||||
posts.GET("/featured", func(c *echo.Context) error {
|
||||
return handlers.GetFeaturedPosts(c, s)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package email
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package markdown
|
||||
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
package models
|
||||
|
||||
|
||||
|
||||
@@ -3,77 +3,77 @@ package models
|
||||
import "time"
|
||||
|
||||
type Post struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Content string `json:"content"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
FeaturedImage *Media `json:"featured_image"`
|
||||
Published bool `json:"published"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
MetaTitle string `json:"meta_title"`
|
||||
MetaDesc string `json:"meta_description"`
|
||||
ReadingTime int `json:"reading_time"`
|
||||
Author Author `json:"author"`
|
||||
Tags []Tag `json:"tags"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Content string `json:"content"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
FeaturedImage *Media `json:"featured_image"`
|
||||
Published bool `json:"published"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
MetaTitle string `json:"meta_title"`
|
||||
MetaDesc string `json:"meta_description"`
|
||||
ReadingTime int `json:"reading_time"`
|
||||
Author Author `json:"author"`
|
||||
Tags []Tag `json:"tags"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Color string `json:"color,omitempty"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Color string `json:"color,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Bio string `json:"bio"`
|
||||
Avatar *Media `json:"avatar"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Bio string `json:"bio"`
|
||||
Avatar *Media `json:"avatar"`
|
||||
SocialLinks map[string]string `json:"social_links"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Media struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AlternativeText string `json:"alternativeText"`
|
||||
Caption string `json:"caption"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Formats MediaFormats `json:"formats"`
|
||||
Hash string `json:"hash"`
|
||||
Ext string `json:"ext"`
|
||||
Mime string `json:"mime"`
|
||||
Size float64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
PreviewURL string `json:"previewUrl"`
|
||||
Provider string `json:"provider"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AlternativeText string `json:"alternativeText"`
|
||||
Caption string `json:"caption"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Formats MediaFormats `json:"formats"`
|
||||
Hash string `json:"hash"`
|
||||
Ext string `json:"ext"`
|
||||
Mime string `json:"mime"`
|
||||
Size float64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
PreviewURL string `json:"previewUrl"`
|
||||
Provider string `json:"provider"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type MediaFormats struct {
|
||||
Large *MediaFormat `json:"large,omitempty"`
|
||||
Medium *MediaFormat `json:"medium,omitempty"`
|
||||
Small *MediaFormat `json:"small,omitempty"`
|
||||
Thumbnail *MediaFormat `json:"thumbnail,omitempty"`
|
||||
Large *MediaFormat `json:"large,omitempty"`
|
||||
Medium *MediaFormat `json:"medium,omitempty"`
|
||||
Small *MediaFormat `json:"small,omitempty"`
|
||||
Thumbnail *MediaFormat `json:"thumbnail,omitempty"`
|
||||
}
|
||||
|
||||
type MediaFormat struct {
|
||||
Name string `json:"name"`
|
||||
Hash string `json:"hash"`
|
||||
Ext string `json:"ext"`
|
||||
Mime string `json:"mime"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Size float64 `json:"size"`
|
||||
Path string `json:"path"`
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
Hash string `json:"hash"`
|
||||
Ext string `json:"ext"`
|
||||
Mime string `json:"mime"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Size float64 `json:"size"`
|
||||
Path string `json:"path"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package repositories
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package repositories
|
||||
|
||||
|
||||
@@ -3,4 +3,3 @@ package rss
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package services
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
package services
|
||||
|
||||
|
||||
Reference in New Issue
Block a user