Add paginated Vimeo video list API (GET /me/videos).
Exposes the Vimeo account library for admin workflows and syncs swagger docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
9afc9a4392
commit
7f8ef3373c
555
docs/docs.go
555
docs/docs.go
|
|
@ -702,6 +702,64 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/analytics/dashboard": {
|
||||||
|
"get": {
|
||||||
|
"description": "Platform analytics with optional date filters: all-time (default), year, year+month, or custom from/to range.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"analytics"
|
||||||
|
],
|
||||||
|
"summary": "Analytics dashboard",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Calendar year (e.g. 2025)",
|
||||||
|
"name": "year",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Calendar month 1-12 (requires year)",
|
||||||
|
"name": "month",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom range start (YYYY-MM-DD or RFC3339)",
|
||||||
|
"name": "from",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom range end (YYYY-MM-DD or RFC3339, inclusive)",
|
||||||
|
"name": "to",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsDashboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/assessment/questions": {
|
"/api/v1/assessment/questions": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns all active assessment questions from the initial assessment set",
|
"description": "Returns all active assessment questions from the initial assessment set",
|
||||||
|
|
@ -8701,6 +8759,105 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/vimeo/videos": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns a paginated list of videos for the Vimeo API token (GET https://api.vimeo.com/me/videos)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Vimeo"
|
||||||
|
],
|
||||||
|
"summary": "List videos stored in the Vimeo account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "Page number (starts at 1)",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 25,
|
||||||
|
"description": "Page size (Vimeo max 100)",
|
||||||
|
"name": "per_page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search query",
|
||||||
|
"name": "query",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Sort field (e.g. date, alphabetical, plays, likes, comments, duration, relevance)",
|
||||||
|
"name": "sort",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "asc or desc",
|
||||||
|
"name": "direction",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Vimeo filter (e.g. embeddable, playable)",
|
||||||
|
"name": "filter",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Vimeo filter_type when using filter",
|
||||||
|
"name": "filter_type",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.VimeoVideoResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "#/definitions/handlers.VimeoVideosListMetadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"503": {
|
||||||
|
"description": "Service Unavailable",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/vimeo/videos/{video_id}": {
|
"/api/v1/vimeo/videos/{video_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Retrieves video details from Vimeo by video ID",
|
"description": "Retrieves video details from Vimeo by video ID",
|
||||||
|
|
@ -9455,6 +9612,375 @@ const docTemplate = `{
|
||||||
"Age55Plus"
|
"Age55Plus"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.AnalyticsContentSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"question_sets_by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions_by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_question_sets": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_questions": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsCoursesSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total_categories": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_courses": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_sub_courses": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_videos": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsDashboard": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsContentSection"
|
||||||
|
},
|
||||||
|
"courses": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsCoursesSection"
|
||||||
|
},
|
||||||
|
"date_filter": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsDateFilter"
|
||||||
|
},
|
||||||
|
"generated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsIssuesSection"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsNotificationsSection"
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsPaymentsSection"
|
||||||
|
},
|
||||||
|
"subscriptions": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsSubscriptionsSection"
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTeamSection"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsUsersSection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsDateFilter": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"range_end": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"range_start": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ref_date": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"series_end": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"series_start": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"year": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsIssuesSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolution_rate": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"resolved_issues": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_issues": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsLabelAmount": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsLabelCount": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsNotificationsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_channel": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"read_count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_sent": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"unread_count": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsPaymentsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"avg_transaction_value": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"by_method": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelAmount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelAmount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revenue_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsRevenueTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"successful_payments": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_payments": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsRevenueByPlan": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"currency": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"plan_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsRevenueTimePoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsSubscriptionsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"active_subscriptions": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_subscriptions_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_today": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_week": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"revenue_by_plan": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsRevenueByPlan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_subscriptions": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsTeamSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_role": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_members": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsTimePoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsUsersSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_age_group": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_knowledge_level": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_region": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_role": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_today": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_week": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"registrations_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_users": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.CreateCourseInput": {
|
"domain.CreateCourseInput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
@ -11198,6 +11724,35 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handlers.VimeoVideosListMetadata": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"current_page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"first": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"next": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"previous": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_pages": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handlers.addQuestionToSetReq": {
|
"handlers.addQuestionToSetReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,64 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/analytics/dashboard": {
|
||||||
|
"get": {
|
||||||
|
"description": "Platform analytics with optional date filters: all-time (default), year, year+month, or custom from/to range.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"analytics"
|
||||||
|
],
|
||||||
|
"summary": "Analytics dashboard",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Calendar year (e.g. 2025)",
|
||||||
|
"name": "year",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Calendar month 1-12 (requires year)",
|
||||||
|
"name": "month",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom range start (YYYY-MM-DD or RFC3339)",
|
||||||
|
"name": "from",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom range end (YYYY-MM-DD or RFC3339, inclusive)",
|
||||||
|
"name": "to",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsDashboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/assessment/questions": {
|
"/api/v1/assessment/questions": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns all active assessment questions from the initial assessment set",
|
"description": "Returns all active assessment questions from the initial assessment set",
|
||||||
|
|
@ -8693,6 +8751,105 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/vimeo/videos": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns a paginated list of videos for the Vimeo API token (GET https://api.vimeo.com/me/videos)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Vimeo"
|
||||||
|
],
|
||||||
|
"summary": "List videos stored in the Vimeo account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "Page number (starts at 1)",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 25,
|
||||||
|
"description": "Page size (Vimeo max 100)",
|
||||||
|
"name": "per_page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Search query",
|
||||||
|
"name": "query",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Sort field (e.g. date, alphabetical, plays, likes, comments, duration, relevance)",
|
||||||
|
"name": "sort",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "asc or desc",
|
||||||
|
"name": "direction",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Vimeo filter (e.g. embeddable, playable)",
|
||||||
|
"name": "filter",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Vimeo filter_type when using filter",
|
||||||
|
"name": "filter_type",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/domain.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.VimeoVideoResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "#/definitions/handlers.VimeoVideosListMetadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"503": {
|
||||||
|
"description": "Service Unavailable",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/vimeo/videos/{video_id}": {
|
"/api/v1/vimeo/videos/{video_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Retrieves video details from Vimeo by video ID",
|
"description": "Retrieves video details from Vimeo by video ID",
|
||||||
|
|
@ -9447,6 +9604,375 @@
|
||||||
"Age55Plus"
|
"Age55Plus"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.AnalyticsContentSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"question_sets_by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions_by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_question_sets": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_questions": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsCoursesSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total_categories": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_courses": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_sub_courses": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_videos": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsDashboard": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsContentSection"
|
||||||
|
},
|
||||||
|
"courses": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsCoursesSection"
|
||||||
|
},
|
||||||
|
"date_filter": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsDateFilter"
|
||||||
|
},
|
||||||
|
"generated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsIssuesSection"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsNotificationsSection"
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsPaymentsSection"
|
||||||
|
},
|
||||||
|
"subscriptions": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsSubscriptionsSection"
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTeamSection"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsUsersSection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsDateFilter": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"range_end": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"range_start": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ref_date": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"series_end": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"series_start": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"year": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsIssuesSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolution_rate": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"resolved_issues": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_issues": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsLabelAmount": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"amount": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsLabelCount": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsNotificationsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_channel": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_type": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"read_count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_sent": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"unread_count": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsPaymentsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"avg_transaction_value": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"by_method": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelAmount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelAmount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revenue_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsRevenueTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"successful_payments": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_payments": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsRevenueByPlan": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"currency": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"plan_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsRevenueTimePoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"revenue": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsSubscriptionsSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"active_subscriptions": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_subscriptions_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_today": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_week": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"revenue_by_plan": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsRevenueByPlan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_subscriptions": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsTeamSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_role": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_members": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsTimePoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.AnalyticsUsersSection": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"by_age_group": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_knowledge_level": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_region": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_role": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"by_status": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsLabelCount"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"new_month": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_today": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"new_week": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"registrations_last_30_days": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.AnalyticsTimePoint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total_users": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.CreateCourseInput": {
|
"domain.CreateCourseInput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
@ -11190,6 +11716,35 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handlers.VimeoVideosListMetadata": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"current_page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"first": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"next": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"previous": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_pages": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handlers.addQuestionToSetReq": {
|
"handlers.addQuestionToSetReq": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,247 @@ definitions:
|
||||||
- Age35To44
|
- Age35To44
|
||||||
- Age45To54
|
- Age45To54
|
||||||
- Age55Plus
|
- Age55Plus
|
||||||
|
domain.AnalyticsContentSection:
|
||||||
|
properties:
|
||||||
|
question_sets_by_type:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
questions_by_type:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
total_question_sets:
|
||||||
|
type: integer
|
||||||
|
total_questions:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsCoursesSection:
|
||||||
|
properties:
|
||||||
|
total_categories:
|
||||||
|
type: integer
|
||||||
|
total_courses:
|
||||||
|
type: integer
|
||||||
|
total_sub_courses:
|
||||||
|
type: integer
|
||||||
|
total_videos:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsDashboard:
|
||||||
|
properties:
|
||||||
|
content:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsContentSection'
|
||||||
|
courses:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsCoursesSection'
|
||||||
|
date_filter:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsDateFilter'
|
||||||
|
generated_at:
|
||||||
|
type: string
|
||||||
|
issues:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsIssuesSection'
|
||||||
|
notifications:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsNotificationsSection'
|
||||||
|
payments:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsPaymentsSection'
|
||||||
|
subscriptions:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsSubscriptionsSection'
|
||||||
|
team:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsTeamSection'
|
||||||
|
users:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsUsersSection'
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsDateFilter:
|
||||||
|
properties:
|
||||||
|
from:
|
||||||
|
type: string
|
||||||
|
mode:
|
||||||
|
type: string
|
||||||
|
month:
|
||||||
|
type: integer
|
||||||
|
range_end:
|
||||||
|
type: string
|
||||||
|
range_start:
|
||||||
|
type: string
|
||||||
|
ref_date:
|
||||||
|
type: string
|
||||||
|
series_end:
|
||||||
|
type: string
|
||||||
|
series_start:
|
||||||
|
type: string
|
||||||
|
to:
|
||||||
|
type: string
|
||||||
|
year:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsIssuesSection:
|
||||||
|
properties:
|
||||||
|
by_status:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_type:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
resolution_rate:
|
||||||
|
type: number
|
||||||
|
resolved_issues:
|
||||||
|
type: integer
|
||||||
|
total_issues:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsLabelAmount:
|
||||||
|
properties:
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsLabelCount:
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsNotificationsSection:
|
||||||
|
properties:
|
||||||
|
by_channel:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_type:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
read_count:
|
||||||
|
type: integer
|
||||||
|
total_sent:
|
||||||
|
type: integer
|
||||||
|
unread_count:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsPaymentsSection:
|
||||||
|
properties:
|
||||||
|
avg_transaction_value:
|
||||||
|
type: number
|
||||||
|
by_method:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelAmount'
|
||||||
|
type: array
|
||||||
|
by_status:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelAmount'
|
||||||
|
type: array
|
||||||
|
revenue_last_30_days:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsRevenueTimePoint'
|
||||||
|
type: array
|
||||||
|
successful_payments:
|
||||||
|
type: integer
|
||||||
|
total_payments:
|
||||||
|
type: integer
|
||||||
|
total_revenue:
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsRevenueByPlan:
|
||||||
|
properties:
|
||||||
|
currency:
|
||||||
|
type: string
|
||||||
|
plan_name:
|
||||||
|
type: string
|
||||||
|
revenue:
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsRevenueTimePoint:
|
||||||
|
properties:
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
revenue:
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsSubscriptionsSection:
|
||||||
|
properties:
|
||||||
|
active_subscriptions:
|
||||||
|
type: integer
|
||||||
|
by_status:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
new_month:
|
||||||
|
type: integer
|
||||||
|
new_subscriptions_last_30_days:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsTimePoint'
|
||||||
|
type: array
|
||||||
|
new_today:
|
||||||
|
type: integer
|
||||||
|
new_week:
|
||||||
|
type: integer
|
||||||
|
revenue_by_plan:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsRevenueByPlan'
|
||||||
|
type: array
|
||||||
|
total_subscriptions:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsTeamSection:
|
||||||
|
properties:
|
||||||
|
by_role:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_status:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
total_members:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsTimePoint:
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
domain.AnalyticsUsersSection:
|
||||||
|
properties:
|
||||||
|
by_age_group:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_knowledge_level:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_region:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_role:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
by_status:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsLabelCount'
|
||||||
|
type: array
|
||||||
|
new_month:
|
||||||
|
type: integer
|
||||||
|
new_today:
|
||||||
|
type: integer
|
||||||
|
new_week:
|
||||||
|
type: integer
|
||||||
|
registrations_last_30_days:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsTimePoint'
|
||||||
|
type: array
|
||||||
|
total_users:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
domain.CreateCourseInput:
|
domain.CreateCourseInput:
|
||||||
properties:
|
properties:
|
||||||
description:
|
description:
|
||||||
|
|
@ -1190,6 +1431,25 @@ definitions:
|
||||||
width:
|
width:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
handlers.VimeoVideosListMetadata:
|
||||||
|
properties:
|
||||||
|
current_page:
|
||||||
|
type: integer
|
||||||
|
first:
|
||||||
|
type: string
|
||||||
|
last:
|
||||||
|
type: string
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
next:
|
||||||
|
type: string
|
||||||
|
previous:
|
||||||
|
type: string
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
handlers.addQuestionToSetReq:
|
handlers.addQuestionToSetReq:
|
||||||
properties:
|
properties:
|
||||||
display_order:
|
display_order:
|
||||||
|
|
@ -2602,6 +2862,45 @@ paths:
|
||||||
summary: List account deletion requests
|
summary: List account deletion requests
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
|
/api/v1/analytics/dashboard:
|
||||||
|
get:
|
||||||
|
description: 'Platform analytics with optional date filters: all-time (default),
|
||||||
|
year, year+month, or custom from/to range.'
|
||||||
|
parameters:
|
||||||
|
- description: Calendar year (e.g. 2025)
|
||||||
|
in: query
|
||||||
|
name: year
|
||||||
|
type: integer
|
||||||
|
- description: Calendar month 1-12 (requires year)
|
||||||
|
in: query
|
||||||
|
name: month
|
||||||
|
type: integer
|
||||||
|
- description: Custom range start (YYYY-MM-DD or RFC3339)
|
||||||
|
in: query
|
||||||
|
name: from
|
||||||
|
type: string
|
||||||
|
- description: Custom range end (YYYY-MM-DD or RFC3339, inclusive)
|
||||||
|
in: query
|
||||||
|
name: to
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.AnalyticsDashboard'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: Analytics dashboard
|
||||||
|
tags:
|
||||||
|
- analytics
|
||||||
/api/v1/assessment/questions:
|
/api/v1/assessment/questions:
|
||||||
get:
|
get:
|
||||||
description: Returns all active assessment questions from the initial assessment
|
description: Returns all active assessment questions from the initial assessment
|
||||||
|
|
@ -7824,6 +8123,71 @@ paths:
|
||||||
summary: Create a TUS resumable upload to Vimeo
|
summary: Create a TUS resumable upload to Vimeo
|
||||||
tags:
|
tags:
|
||||||
- Vimeo
|
- Vimeo
|
||||||
|
/api/v1/vimeo/videos:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Returns a paginated list of videos for the Vimeo API token (GET
|
||||||
|
https://api.vimeo.com/me/videos)
|
||||||
|
parameters:
|
||||||
|
- default: 1
|
||||||
|
description: Page number (starts at 1)
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- default: 25
|
||||||
|
description: Page size (Vimeo max 100)
|
||||||
|
in: query
|
||||||
|
name: per_page
|
||||||
|
type: integer
|
||||||
|
- description: Search query
|
||||||
|
in: query
|
||||||
|
name: query
|
||||||
|
type: string
|
||||||
|
- description: Sort field (e.g. date, alphabetical, plays, likes, comments,
|
||||||
|
duration, relevance)
|
||||||
|
in: query
|
||||||
|
name: sort
|
||||||
|
type: string
|
||||||
|
- description: asc or desc
|
||||||
|
in: query
|
||||||
|
name: direction
|
||||||
|
type: string
|
||||||
|
- description: Vimeo filter (e.g. embeddable, playable)
|
||||||
|
in: query
|
||||||
|
name: filter
|
||||||
|
type: string
|
||||||
|
- description: Vimeo filter_type when using filter
|
||||||
|
in: query
|
||||||
|
name: filter_type
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/domain.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.VimeoVideoResponse'
|
||||||
|
type: array
|
||||||
|
metadata:
|
||||||
|
$ref: '#/definitions/handlers.VimeoVideosListMetadata'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
"503":
|
||||||
|
description: Service Unavailable
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ErrorResponse'
|
||||||
|
summary: List videos stored in the Vimeo account
|
||||||
|
tags:
|
||||||
|
- Vimeo
|
||||||
/api/v1/vimeo/videos/{video_id}:
|
/api/v1/vimeo/videos/{video_id}:
|
||||||
delete:
|
delete:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -141,6 +142,35 @@ type UpdateVideoRequest struct {
|
||||||
Privacy *PrivacyParams `json:"privacy,omitempty"`
|
Privacy *PrivacyParams `json:"privacy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListVideosParams configures GET /me/videos (authenticated user’s library).
|
||||||
|
// See https://developer.vimeo.com/api/reference/videos#get_videos
|
||||||
|
type ListVideosParams struct {
|
||||||
|
Page int // 1-based; omitted when 0
|
||||||
|
PerPage int // max 100; omitted when 0
|
||||||
|
Query string // optional search filter
|
||||||
|
Sort string // e.g. date, alphabetical, plays, likes, comments, duration, relevance
|
||||||
|
Direction string // asc or desc
|
||||||
|
Filter string // optional: embeddable, playable, playable_in_subscription, etc.
|
||||||
|
FilterType string // optional: 8 for staff picks (when using filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVideosResponse is the JSON envelope Vimeo returns for list endpoints.
|
||||||
|
type ListVideosResponse struct {
|
||||||
|
Total int `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PerPage int `json:"per_page"`
|
||||||
|
Paging PagingLinks `json:"paging"`
|
||||||
|
Data []Video `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PagingLinks contains cursor URLs for the next/previous page from Vimeo.
|
||||||
|
type PagingLinks struct {
|
||||||
|
Next string `json:"next"`
|
||||||
|
Previous string `json:"previous"`
|
||||||
|
First string `json:"first"`
|
||||||
|
Last string `json:"last"`
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) doRequest(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
|
func (c *Client) doRequest(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
|
||||||
var reqBody io.Reader
|
var reqBody io.Reader
|
||||||
if body != nil {
|
if body != nil {
|
||||||
|
|
@ -185,6 +215,55 @@ func (c *Client) GetVideo(ctx context.Context, videoID string) (*Video, error) {
|
||||||
return &video, nil
|
return &video, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListMyVideos calls GET /me/videos for the token’s Vimeo account.
|
||||||
|
func (c *Client) ListMyVideos(ctx context.Context, params ListVideosParams) (*ListVideosResponse, error) {
|
||||||
|
q := url.Values{}
|
||||||
|
if params.Page > 0 {
|
||||||
|
q.Set("page", strconv.Itoa(params.Page))
|
||||||
|
}
|
||||||
|
if params.PerPage > 0 {
|
||||||
|
q.Set("per_page", strconv.Itoa(params.PerPage))
|
||||||
|
}
|
||||||
|
if params.Query != "" {
|
||||||
|
q.Set("query", params.Query)
|
||||||
|
}
|
||||||
|
if params.Sort != "" {
|
||||||
|
q.Set("sort", params.Sort)
|
||||||
|
}
|
||||||
|
if params.Direction != "" {
|
||||||
|
q.Set("direction", params.Direction)
|
||||||
|
}
|
||||||
|
if params.Filter != "" {
|
||||||
|
q.Set("filter", params.Filter)
|
||||||
|
}
|
||||||
|
if params.FilterType != "" {
|
||||||
|
q.Set("filter_type", params.FilterType)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "/me/videos"
|
||||||
|
if enc := q.Encode(); enc != "" {
|
||||||
|
path += "?" + enc
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.doRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
return nil, fmt.Errorf("failed to list videos: status %d, body: %s", resp.StatusCode, string(bodyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
var out ListVideosResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode list videos response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) CreateUpload(ctx context.Context, req *UploadRequest) (*UploadResponse, error) {
|
func (c *Client) CreateUpload(ctx context.Context, req *UploadRequest) (*UploadResponse, error) {
|
||||||
resp, err := c.doRequest(ctx, http.MethodPost, "/me/videos", req)
|
resp, err := c.doRequest(ctx, http.MethodPost, "/me/videos", req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,7 @@ var AllPermissions = []domain.PermissionSeed{
|
||||||
{Key: "analytics.dashboard", Name: "View Dashboard", Description: "View analytics dashboard", GroupName: "Analytics"},
|
{Key: "analytics.dashboard", Name: "View Dashboard", Description: "View analytics dashboard", GroupName: "Analytics"},
|
||||||
|
|
||||||
// Vimeo
|
// Vimeo
|
||||||
|
{Key: "vimeo.videos.list", Name: "List Vimeo Videos", Description: "List videos in the Vimeo account", GroupName: "Vimeo"},
|
||||||
{Key: "vimeo.videos.get", Name: "Get Vimeo Video", Description: "Get Vimeo video details", GroupName: "Vimeo"},
|
{Key: "vimeo.videos.get", Name: "Get Vimeo Video", Description: "Get Vimeo video details", GroupName: "Vimeo"},
|
||||||
{Key: "vimeo.videos.embed", Name: "Get Embed Code", Description: "Get Vimeo embed code", GroupName: "Vimeo"},
|
{Key: "vimeo.videos.embed", Name: "Get Embed Code", Description: "Get Vimeo embed code", GroupName: "Vimeo"},
|
||||||
{Key: "vimeo.videos.status", Name: "Get Transcode Status", Description: "Get Vimeo transcode status", GroupName: "Vimeo"},
|
{Key: "vimeo.videos.status", Name: "Get Transcode Status", Description: "Get Vimeo transcode status", GroupName: "Vimeo"},
|
||||||
|
|
@ -380,7 +381,7 @@ var DefaultRolePermissions = map[string][]string{
|
||||||
"analytics.dashboard",
|
"analytics.dashboard",
|
||||||
|
|
||||||
// Vimeo
|
// Vimeo
|
||||||
"vimeo.videos.get", "vimeo.videos.embed", "vimeo.videos.status", "vimeo.videos.delete",
|
"vimeo.videos.list", "vimeo.videos.get", "vimeo.videos.embed", "vimeo.videos.status", "vimeo.videos.delete",
|
||||||
"vimeo.uploads.pull", "vimeo.uploads.tus",
|
"vimeo.uploads.pull", "vimeo.uploads.tus",
|
||||||
|
|
||||||
// Team (full access)
|
// Team (full access)
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,15 @@ type UploadResult struct {
|
||||||
Status string
|
Status string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListVideosPage is the service result for a paginated Vimeo library query.
|
||||||
|
type ListVideosPage struct {
|
||||||
|
Total int
|
||||||
|
Page int
|
||||||
|
PerPage int
|
||||||
|
Paging vimeo.PagingLinks
|
||||||
|
Videos []*VideoInfo
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo, error) {
|
func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo, error) {
|
||||||
video, err := s.client.GetVideo(ctx, videoID)
|
video, err := s.client.GetVideo(ctx, videoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -52,6 +61,18 @@ func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo,
|
||||||
return nil, fmt.Errorf("failed to get video: %w", err)
|
return nil, fmt.Errorf("failed to get video: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return s.videoModelToInfo(video, videoID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) videoModelToInfo(video *vimeo.Video, fallbackID string) *VideoInfo {
|
||||||
|
videoID := fallbackID
|
||||||
|
if videoID == "" {
|
||||||
|
videoID = vimeo.ExtractVideoID(video.URI)
|
||||||
|
}
|
||||||
|
if videoID == "" {
|
||||||
|
videoID = vimeo.ExtractVideoID(video.Link)
|
||||||
|
}
|
||||||
|
|
||||||
info := &VideoInfo{
|
info := &VideoInfo{
|
||||||
VimeoID: videoID,
|
VimeoID: videoID,
|
||||||
URI: video.URI,
|
URI: video.URI,
|
||||||
|
|
@ -66,7 +87,7 @@ func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo,
|
||||||
|
|
||||||
if video.PlayerEmbedURL != "" {
|
if video.PlayerEmbedURL != "" {
|
||||||
info.EmbedURL = video.PlayerEmbedURL
|
info.EmbedURL = video.PlayerEmbedURL
|
||||||
} else {
|
} else if videoID != "" {
|
||||||
info.EmbedURL = vimeo.GenerateEmbedURL(videoID, nil)
|
info.EmbedURL = vimeo.GenerateEmbedURL(videoID, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +103,28 @@ func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo,
|
||||||
info.TranscodeStatus = video.Transcode.Status
|
info.TranscodeStatus = video.Transcode.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
return info, nil
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ListVideos(ctx context.Context, params vimeo.ListVideosParams) (*ListVideosPage, error) {
|
||||||
|
raw, err := s.client.ListMyVideos(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to list Vimeo videos", zap.Error(err))
|
||||||
|
return nil, fmt.Errorf("failed to list videos: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &ListVideosPage{
|
||||||
|
Total: raw.Total,
|
||||||
|
Page: raw.Page,
|
||||||
|
PerPage: raw.PerPage,
|
||||||
|
Paging: raw.Paging,
|
||||||
|
Videos: make([]*VideoInfo, 0, len(raw.Data)),
|
||||||
|
}
|
||||||
|
for i := range raw.Data {
|
||||||
|
out.Videos = append(out.Videos, s.videoModelToInfo(&raw.Data[i], ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreatePullUpload(ctx context.Context, name, description, sourceURL string, fileSize int64) (*UploadResult, error) {
|
func (s *Service) CreatePullUpload(ctx context.Context, name, description, sourceURL string, fileSize int64) (*UploadResult, error) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"Yimaru-Backend/internal/domain"
|
"Yimaru-Backend/internal/domain"
|
||||||
"Yimaru-Backend/internal/pkgs/vimeo"
|
"Yimaru-Backend/internal/pkgs/vimeo"
|
||||||
|
vimeoservice "Yimaru-Backend/internal/services/vimeo"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
|
@ -62,6 +63,124 @@ type VimeoEmbedResponse struct {
|
||||||
EmbedHTML string `json:"embed_html"`
|
EmbedHTML string `json:"embed_html"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VimeoVideosListMetadata struct {
|
||||||
|
domain.Pagination
|
||||||
|
Next string `json:"next,omitempty"`
|
||||||
|
Previous string `json:"previous,omitempty"`
|
||||||
|
First string `json:"first,omitempty"`
|
||||||
|
Last string `json:"last,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func vimeoVideoInfoToResponse(info *vimeoservice.VideoInfo) VimeoVideoResponse {
|
||||||
|
return 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVimeoVideos godoc
|
||||||
|
// @Summary List videos stored in the Vimeo account
|
||||||
|
// @Description Returns a paginated list of videos for the Vimeo API token (GET https://api.vimeo.com/me/videos)
|
||||||
|
// @Tags Vimeo
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param page query int false "Page number (starts at 1)" default(1)
|
||||||
|
// @Param per_page query int false "Page size (Vimeo max 100)" default(25)
|
||||||
|
// @Param query query string false "Search query"
|
||||||
|
// @Param sort query string false "Sort field (e.g. date, alphabetical, plays, likes, comments, duration, relevance)"
|
||||||
|
// @Param direction query string false "asc or desc"
|
||||||
|
// @Param filter query string false "Vimeo filter (e.g. embeddable, playable)"
|
||||||
|
// @Param filter_type query string false "Vimeo filter_type when using filter"
|
||||||
|
// @Success 200 {object} domain.Response{data=[]handlers.VimeoVideoResponse,metadata=handlers.VimeoVideosListMetadata}
|
||||||
|
// @Failure 503 {object} domain.ErrorResponse
|
||||||
|
// @Failure 500 {object} domain.ErrorResponse
|
||||||
|
// @Router /api/v1/vimeo/videos [get]
|
||||||
|
func (h *Handler) ListVimeoVideos(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",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||||
|
perPage, _ := strconv.Atoi(c.Query("per_page", "25"))
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if perPage < 1 {
|
||||||
|
perPage = 25
|
||||||
|
}
|
||||||
|
if perPage > 100 {
|
||||||
|
perPage = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
params := vimeo.ListVideosParams{
|
||||||
|
Page: page,
|
||||||
|
PerPage: perPage,
|
||||||
|
Query: c.Query("query"),
|
||||||
|
Sort: c.Query("sort"),
|
||||||
|
Direction: c.Query("direction"),
|
||||||
|
Filter: c.Query("filter"),
|
||||||
|
FilterType: c.Query("filter_type"),
|
||||||
|
}
|
||||||
|
|
||||||
|
pageResult, err := h.vimeoSvc.ListVideos(c.Context(), params)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Failed to list Vimeo videos",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]VimeoVideoResponse, 0, len(pageResult.Videos))
|
||||||
|
for _, info := range pageResult.Videos {
|
||||||
|
if info == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
items = append(items, vimeoVideoInfoToResponse(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPages := 0
|
||||||
|
if pageResult.PerPage > 0 && pageResult.Total > 0 {
|
||||||
|
totalPages = (pageResult.Total + pageResult.PerPage - 1) / pageResult.PerPage
|
||||||
|
}
|
||||||
|
currentPage := pageResult.Page
|
||||||
|
if currentPage < 1 {
|
||||||
|
currentPage = page
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(domain.Response{
|
||||||
|
Message: "Vimeo videos listed successfully",
|
||||||
|
Data: items,
|
||||||
|
MetaData: VimeoVideosListMetadata{
|
||||||
|
Pagination: domain.Pagination{
|
||||||
|
Total: pageResult.Total,
|
||||||
|
TotalPages: totalPages,
|
||||||
|
CurrentPage: currentPage,
|
||||||
|
Limit: pageResult.PerPage,
|
||||||
|
},
|
||||||
|
Next: pageResult.Paging.Next,
|
||||||
|
Previous: pageResult.Paging.Previous,
|
||||||
|
First: pageResult.Paging.First,
|
||||||
|
Last: pageResult.Paging.Last,
|
||||||
|
},
|
||||||
|
Success: true,
|
||||||
|
StatusCode: fiber.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// GetVimeoVideo godoc
|
// GetVimeoVideo godoc
|
||||||
// @Summary Get video information from Vimeo
|
// @Summary Get video information from Vimeo
|
||||||
// @Description Retrieves video details from Vimeo by video ID
|
// @Description Retrieves video details from Vimeo by video ID
|
||||||
|
|
@ -99,21 +218,7 @@ func (h *Handler) GetVimeoVideo(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return c.JSON(domain.Response{
|
return c.JSON(domain.Response{
|
||||||
Message: "Video retrieved successfully",
|
Message: "Video retrieved successfully",
|
||||||
Data: VimeoVideoResponse{
|
Data: vimeoVideoInfoToResponse(info),
|
||||||
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,
|
|
||||||
},
|
|
||||||
Success: true,
|
Success: true,
|
||||||
StatusCode: fiber.StatusOK,
|
StatusCode: fiber.StatusOK,
|
||||||
})
|
})
|
||||||
|
|
@ -413,21 +518,7 @@ func (h *Handler) GetSampleVideo(c *fiber.Ctx) error {
|
||||||
return c.JSON(domain.Response{
|
return c.JSON(domain.Response{
|
||||||
Message: "Sample video retrieved successfully",
|
Message: "Sample video retrieved successfully",
|
||||||
Data: fiber.Map{
|
Data: fiber.Map{
|
||||||
"video": VimeoVideoResponse{
|
"video": vimeoVideoInfoToResponse(info),
|
||||||
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,
|
"iframe": iframe,
|
||||||
},
|
},
|
||||||
Success: true,
|
Success: true,
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,7 @@ func (a *App) initAppRoutes() {
|
||||||
|
|
||||||
// Vimeo
|
// Vimeo
|
||||||
vimeoGroup := groupV1.Group("/vimeo")
|
vimeoGroup := groupV1.Group("/vimeo")
|
||||||
|
vimeoGroup.Get("/videos", a.authMiddleware, a.RequirePermission("vimeo.videos.list"), h.ListVimeoVideos)
|
||||||
vimeoGroup.Get("/videos/:video_id", a.authMiddleware, a.RequirePermission("vimeo.videos.get"), h.GetVimeoVideo)
|
vimeoGroup.Get("/videos/:video_id", a.authMiddleware, a.RequirePermission("vimeo.videos.get"), h.GetVimeoVideo)
|
||||||
vimeoGroup.Get("/videos/:video_id/embed", a.authMiddleware, a.RequirePermission("vimeo.videos.embed"), h.GetEmbedCode)
|
vimeoGroup.Get("/videos/:video_id/embed", a.authMiddleware, a.RequirePermission("vimeo.videos.embed"), h.GetEmbedCode)
|
||||||
vimeoGroup.Get("/videos/:video_id/status", a.authMiddleware, a.RequirePermission("vimeo.videos.status"), h.GetTranscodeStatus)
|
vimeoGroup.Get("/videos/:video_id/status", a.authMiddleware, a.RequirePermission("vimeo.videos.status"), h.GetTranscodeStatus)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user