minor fixes
This commit is contained in:
parent
0226275d47
commit
f9da45da62
|
|
@ -376,6 +376,13 @@ SET
|
|||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: GetUserSummary :one
|
||||
SELECT
|
||||
COUNT(*) AS total_users,
|
||||
COUNT(*) FILTER (WHERE status = 'ACTIVE') AS active_users,
|
||||
COUNT(*) FILTER (WHERE created_at >= date_trunc('month', CURRENT_DATE)) AS joined_this_month
|
||||
FROM users;
|
||||
|
||||
-- name: UpdateUserKnowledgeLevel :exec
|
||||
UPDATE users
|
||||
SET
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ services:
|
|||
container_name: yimaru-backend-postgres-1
|
||||
image: postgres:16-alpine
|
||||
ports:
|
||||
- "5432:5422"
|
||||
- "5592:5422"
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=secret
|
||||
- POSTGRES_USER=root
|
||||
|
|
|
|||
|
|
@ -764,6 +764,27 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
|||
return i, err
|
||||
}
|
||||
|
||||
const GetUserSummary = `-- name: GetUserSummary :one
|
||||
SELECT
|
||||
COUNT(*) AS total_users,
|
||||
COUNT(*) FILTER (WHERE status = 'ACTIVE') AS active_users,
|
||||
COUNT(*) FILTER (WHERE created_at >= date_trunc('month', CURRENT_DATE)) AS joined_this_month
|
||||
FROM users
|
||||
`
|
||||
|
||||
type GetUserSummaryRow struct {
|
||||
TotalUsers int64 `json:"total_users"`
|
||||
ActiveUsers int64 `json:"active_users"`
|
||||
JoinedThisMonth int64 `json:"joined_this_month"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserSummary(ctx context.Context) (GetUserSummaryRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetUserSummary)
|
||||
var i GetUserSummaryRow
|
||||
err := row.Scan(&i.TotalUsers, &i.ActiveUsers, &i.JoinedThisMonth)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const IsUserNameUnique = `-- name: IsUserNameUnique :one
|
||||
SELECT
|
||||
CASE WHEN COUNT(*) = 0 THEN true ELSE false END AS is_unique
|
||||
|
|
|
|||
|
|
@ -181,6 +181,12 @@ type UpdateUserStatusReq struct {
|
|||
UserID int64
|
||||
}
|
||||
|
||||
type UserSummary struct {
|
||||
TotalUsers int64 `json:"total_users"`
|
||||
ActiveUsers int64 `json:"active_users"`
|
||||
JoinedThisMonth int64 `json:"joined_this_month"`
|
||||
}
|
||||
|
||||
type UpdateUserReq struct {
|
||||
UserID int64 `json:"-"`
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ type UserStore interface {
|
|||
limit, offset int32,
|
||||
) ([]domain.User, int64, error)
|
||||
GetTotalUsers(ctx context.Context, role *string) (int64, error)
|
||||
GetUserSummary(ctx context.Context) (domain.UserSummary, error)
|
||||
SearchUserByNameOrPhone(
|
||||
ctx context.Context,
|
||||
search string,
|
||||
|
|
|
|||
|
|
@ -515,6 +515,19 @@ func (s *Store) GetAllUsers(
|
|||
}
|
||||
|
||||
// GetTotalUsers counts users with optional filters
|
||||
func (s *Store) GetUserSummary(ctx context.Context) (domain.UserSummary, error) {
|
||||
res, err := s.queries.GetUserSummary(ctx)
|
||||
if err != nil {
|
||||
return domain.UserSummary{}, err
|
||||
}
|
||||
|
||||
return domain.UserSummary{
|
||||
TotalUsers: res.TotalUsers,
|
||||
ActiveUsers: res.ActiveUsers,
|
||||
JoinedThisMonth: res.JoinedThisMonth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetTotalUsers(ctx context.Context, role *string) (int64, error) {
|
||||
count, err := s.queries.GetTotalUsers(ctx, *role)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ func (s *Service) GetAllUsers(
|
|||
)
|
||||
}
|
||||
|
||||
func (s *Service) GetUserSummary(ctx context.Context) (domain.UserSummary, error) {
|
||||
return s.userStore.GetUserSummary(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateUserStatus(ctx context.Context, req domain.UpdateUserStatusReq) error {
|
||||
return s.userStore.UpdateUserStatus(ctx, req)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,3 +195,19 @@ func (s *Service) GetOEmbed(ctx context.Context, vimeoURL string, width, height
|
|||
func (s *Service) GeneratePlayerURL(videoID string, opts *vimeo.EmbedOptions) string {
|
||||
return vimeo.GenerateEmbedURL(videoID, opts)
|
||||
}
|
||||
|
||||
// GetSampleVideo fetches a public Vimeo video by ID and returns its info along with an embeddable iframe.
|
||||
func (s *Service) GetSampleVideo(ctx context.Context, videoID string, width, height int) (*VideoInfo, string, error) {
|
||||
info, err := s.GetVideoInfo(ctx, videoID)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to get sample video: %w", err)
|
||||
}
|
||||
|
||||
iframe := vimeo.GenerateIframeEmbed(videoID, width, height, &vimeo.EmbedOptions{
|
||||
Title: true,
|
||||
Byline: true,
|
||||
Portrait: true,
|
||||
})
|
||||
|
||||
return info, iframe, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,33 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// GetUserSummary godoc
|
||||
// @Summary Get user summary statistics
|
||||
// @Description Returns total users, active users, and users who joined this month
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} domain.Response{data=domain.UserSummary}
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/users/summary [get]
|
||||
func (h *Handler) GetUserSummary(c *fiber.Ctx) error {
|
||||
summary, err := h.userSvc.GetUserSummary(c.Context())
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to get user summary",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "User summary retrieved successfully",
|
||||
Data: summary,
|
||||
Success: true,
|
||||
StatusCode: fiber.StatusOK,
|
||||
})
|
||||
}
|
||||
|
||||
// CheckProfileCompleted godoc
|
||||
// @Summary Check if user profile is completed
|
||||
// @Description Returns the profile completion status and percentage for the specified user
|
||||
|
|
|
|||
|
|
@ -378,6 +378,63 @@ func (h *Handler) GetTranscodeStatus(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
// GetSampleVideo godoc
|
||||
// @Summary Get a sample Vimeo video with iframe embed
|
||||
// @Description Fetches a sample video from Vimeo and returns video details along with an embeddable iframe for client-side integration
|
||||
// @Tags Vimeo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param video_id query string false "Vimeo Video ID to use as sample" default(76979871)
|
||||
// @Param width query int false "Player width" default(640)
|
||||
// @Param height query int false "Player height" default(360)
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/vimeo/sample [get]
|
||||
func (h *Handler) GetSampleVideo(c *fiber.Ctx) error {
|
||||
if h.vimeoSvc == nil {
|
||||
return c.Status(fiber.StatusServiceUnavailable).JSON(domain.ErrorResponse{
|
||||
Message: "Vimeo service is not configured",
|
||||
Error: "Vimeo service is not enabled or missing access token",
|
||||
})
|
||||
}
|
||||
|
||||
videoID := c.Query("video_id", "76979871")
|
||||
width, _ := strconv.Atoi(c.Query("width", "640"))
|
||||
height, _ := strconv.Atoi(c.Query("height", "360"))
|
||||
|
||||
info, iframe, err := h.vimeoSvc.GetSampleVideo(c.Context(), videoID, width, height)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to get sample video",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Sample video retrieved successfully",
|
||||
Data: fiber.Map{
|
||||
"video": VimeoVideoResponse{
|
||||
VimeoID: info.VimeoID,
|
||||
URI: info.URI,
|
||||
Name: info.Name,
|
||||
Description: info.Description,
|
||||
Duration: info.Duration,
|
||||
Width: info.Width,
|
||||
Height: info.Height,
|
||||
Link: info.Link,
|
||||
EmbedURL: info.EmbedURL,
|
||||
EmbedHTML: info.EmbedHTML,
|
||||
ThumbnailURL: info.ThumbnailURL,
|
||||
Status: info.Status,
|
||||
TranscodeStatus: info.TranscodeStatus,
|
||||
},
|
||||
"iframe": iframe,
|
||||
},
|
||||
Success: true,
|
||||
StatusCode: fiber.StatusOK,
|
||||
})
|
||||
}
|
||||
|
||||
// GetOEmbed godoc
|
||||
// @Summary Get oEmbed data for a Vimeo URL
|
||||
// @Description Fetches oEmbed metadata for a Vimeo video URL
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ func (a *App) initAppRoutes() {
|
|||
// User Routes
|
||||
groupV1.Get("/user/:user_id/is-profile-completed", a.authMiddleware, a.RequirePermission("users.profile_completed"), h.CheckProfileCompleted)
|
||||
groupV1.Get("/users", a.authMiddleware, a.RequirePermission("users.list"), h.GetAllUsers)
|
||||
groupV1.Get("/users/summary", a.authMiddleware, a.RequirePermission("users.summary"), h.GetUserSummary)
|
||||
groupV1.Put("/user", a.authMiddleware, a.RequirePermission("users.update_self"), h.UpdateUser)
|
||||
groupV1.Patch("/user/status", a.authMiddleware, a.RequirePermission("users.update_status"), h.UpdateUserStatus)
|
||||
groupV1.Put("/user/knowledge-level", h.UpdateUserKnowledgeLevel)
|
||||
|
|
@ -292,6 +293,7 @@ func (a *App) initAppRoutes() {
|
|||
vimeoGroup.Post("/uploads/pull", a.authMiddleware, a.RequirePermission("vimeo.uploads.pull"), h.CreatePullUpload)
|
||||
vimeoGroup.Post("/uploads/tus", a.authMiddleware, a.RequirePermission("vimeo.uploads.tus"), h.CreateTusUpload)
|
||||
vimeoGroup.Get("/oembed", h.GetOEmbed)
|
||||
vimeoGroup.Get("/sample", h.GetSampleVideo)
|
||||
|
||||
// Team Management
|
||||
teamGroup := groupV1.Group("/team")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user