seed data clearer API
This commit is contained in:
parent
36134f32a2
commit
05cb8715f9
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE question_sets
|
||||
DROP COLUMN IF EXISTS intro_video_url;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE question_sets
|
||||
ADD COLUMN intro_video_url TEXT;
|
||||
|
|
@ -44,7 +44,7 @@ WHERE sub_course_id = $1 AND status = 'PUBLISHED'
|
|||
ORDER BY display_order, id;
|
||||
|
||||
-- name: GetSubCoursePracticesForLearningPath :many
|
||||
SELECT id, title, description, persona, status,
|
||||
SELECT id, title, description, persona, status, intro_video_url,
|
||||
(SELECT COUNT(*) FROM question_set_items WHERE set_id = qs.id) AS question_count
|
||||
FROM question_sets qs
|
||||
WHERE qs.owner_type = 'SUB_COURSE' AND qs.owner_id = $1
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ INSERT INTO question_sets (
|
|||
passing_score,
|
||||
shuffle_questions,
|
||||
status,
|
||||
sub_course_video_id
|
||||
sub_course_video_id,
|
||||
intro_video_url
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, COALESCE($10, false), COALESCE($11, 'DRAFT'), $12)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, COALESCE($10, false), COALESCE($11, 'DRAFT'), $12, $13)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetQuestionSetByID :one
|
||||
|
|
@ -59,9 +60,10 @@ SET
|
|||
passing_score = COALESCE($6, passing_score),
|
||||
shuffle_questions = COALESCE($7, shuffle_questions),
|
||||
status = COALESCE($8, status),
|
||||
sub_course_video_id = COALESCE($9, sub_course_video_id),
|
||||
intro_video_url = COALESCE($9, intro_video_url),
|
||||
sub_course_video_id = COALESCE($10, sub_course_video_id),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $10;
|
||||
WHERE id = $11;
|
||||
|
||||
-- name: ArchiveQuestionSet :exec
|
||||
UPDATE question_sets
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ func (q *Queries) GetFullLearningTree(ctx context.Context) ([]GetFullLearningTre
|
|||
}
|
||||
|
||||
const GetSubCoursePracticesForLearningPath = `-- name: GetSubCoursePracticesForLearningPath :many
|
||||
SELECT id, title, description, persona, status,
|
||||
SELECT id, title, description, persona, status, intro_video_url,
|
||||
(SELECT COUNT(*) FROM question_set_items WHERE set_id = qs.id) AS question_count
|
||||
FROM question_sets qs
|
||||
WHERE qs.owner_type = 'SUB_COURSE' AND qs.owner_id = $1
|
||||
|
|
@ -160,6 +160,7 @@ type GetSubCoursePracticesForLearningPathRow struct {
|
|||
Description pgtype.Text `json:"description"`
|
||||
Persona pgtype.Text `json:"persona"`
|
||||
Status string `json:"status"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
QuestionCount int64 `json:"question_count"`
|
||||
}
|
||||
|
||||
|
|
@ -178,6 +179,7 @@ func (q *Queries) GetSubCoursePracticesForLearningPath(ctx context.Context, owne
|
|||
&i.Description,
|
||||
&i.Persona,
|
||||
&i.Status,
|
||||
&i.IntroVideoUrl,
|
||||
&i.QuestionCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ type QuestionSet struct {
|
|||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SubCourseVideoID pgtype.Int8 `json:"sub_course_video_id"`
|
||||
DisplayOrder int32 `json:"display_order"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
}
|
||||
|
||||
type QuestionSetItem struct {
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ func (q *Queries) GetQuestionSetItems(ctx context.Context, setID int64) ([]GetQu
|
|||
}
|
||||
|
||||
const GetQuestionSetsContainingQuestion = `-- name: GetQuestionSetsContainingQuestion :many
|
||||
SELECT qs.id, qs.title, qs.description, qs.set_type, qs.owner_type, qs.owner_id, qs.banner_image, qs.persona, qs.time_limit_minutes, qs.passing_score, qs.shuffle_questions, qs.status, qs.created_at, qs.updated_at, qs.sub_course_video_id, qs.display_order
|
||||
SELECT qs.id, qs.title, qs.description, qs.set_type, qs.owner_type, qs.owner_id, qs.banner_image, qs.persona, qs.time_limit_minutes, qs.passing_score, qs.shuffle_questions, qs.status, qs.created_at, qs.updated_at, qs.sub_course_video_id, qs.display_order, qs.intro_video_url
|
||||
FROM question_sets qs
|
||||
JOIN question_set_items qsi ON qsi.set_id = qs.id
|
||||
WHERE qsi.question_id = $1
|
||||
|
|
@ -231,6 +231,7 @@ func (q *Queries) GetQuestionSetsContainingQuestion(ctx context.Context, questio
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,10 +64,11 @@ INSERT INTO question_sets (
|
|||
passing_score,
|
||||
shuffle_questions,
|
||||
status,
|
||||
sub_course_video_id
|
||||
sub_course_video_id,
|
||||
intro_video_url
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, COALESCE($10, false), COALESCE($11, 'DRAFT'), $12)
|
||||
RETURNING id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, COALESCE($10, false), COALESCE($11, 'DRAFT'), $12, $13)
|
||||
RETURNING id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
`
|
||||
|
||||
type CreateQuestionSetParams struct {
|
||||
|
|
@ -83,6 +84,7 @@ type CreateQuestionSetParams struct {
|
|||
Column10 interface{} `json:"column_10"`
|
||||
Column11 interface{} `json:"column_11"`
|
||||
SubCourseVideoID pgtype.Int8 `json:"sub_course_video_id"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateQuestionSet(ctx context.Context, arg CreateQuestionSetParams) (QuestionSet, error) {
|
||||
|
|
@ -99,6 +101,7 @@ func (q *Queries) CreateQuestionSet(ctx context.Context, arg CreateQuestionSetPa
|
|||
arg.Column10,
|
||||
arg.Column11,
|
||||
arg.SubCourseVideoID,
|
||||
arg.IntroVideoUrl,
|
||||
)
|
||||
var i QuestionSet
|
||||
err := row.Scan(
|
||||
|
|
@ -118,6 +121,7 @@ func (q *Queries) CreateQuestionSet(ctx context.Context, arg CreateQuestionSetPa
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -133,7 +137,7 @@ func (q *Queries) DeleteQuestionSet(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const GetInitialAssessmentSet = `-- name: GetInitialAssessmentSet :one
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
FROM question_sets
|
||||
WHERE set_type = 'INITIAL_ASSESSMENT'
|
||||
AND status = 'PUBLISHED'
|
||||
|
|
@ -161,12 +165,13 @@ func (q *Queries) GetInitialAssessmentSet(ctx context.Context) (QuestionSet, err
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetPublishedQuestionSetsByOwner = `-- name: GetPublishedQuestionSetsByOwner :many
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
FROM question_sets
|
||||
WHERE owner_type = $1
|
||||
AND owner_id = $2
|
||||
|
|
@ -205,6 +210,7 @@ func (q *Queries) GetPublishedQuestionSetsByOwner(ctx context.Context, arg GetPu
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -217,7 +223,7 @@ func (q *Queries) GetPublishedQuestionSetsByOwner(ctx context.Context, arg GetPu
|
|||
}
|
||||
|
||||
const GetQuestionSetByID = `-- name: GetQuestionSetByID :one
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
FROM question_sets
|
||||
WHERE id = $1
|
||||
`
|
||||
|
|
@ -242,12 +248,13 @@ func (q *Queries) GetQuestionSetByID(ctx context.Context, id int64) (QuestionSet
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetQuestionSetsByOwner = `-- name: GetQuestionSetsByOwner :many
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
FROM question_sets
|
||||
WHERE owner_type = $1
|
||||
AND owner_id = $2
|
||||
|
|
@ -286,6 +293,7 @@ func (q *Queries) GetQuestionSetsByOwner(ctx context.Context, arg GetQuestionSet
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -300,7 +308,7 @@ func (q *Queries) GetQuestionSetsByOwner(ctx context.Context, arg GetQuestionSet
|
|||
const GetQuestionSetsByType = `-- name: GetQuestionSetsByType :many
|
||||
SELECT
|
||||
COUNT(*) OVER () AS total_count,
|
||||
qs.id, qs.title, qs.description, qs.set_type, qs.owner_type, qs.owner_id, qs.banner_image, qs.persona, qs.time_limit_minutes, qs.passing_score, qs.shuffle_questions, qs.status, qs.created_at, qs.updated_at, qs.sub_course_video_id, qs.display_order
|
||||
qs.id, qs.title, qs.description, qs.set_type, qs.owner_type, qs.owner_id, qs.banner_image, qs.persona, qs.time_limit_minutes, qs.passing_score, qs.shuffle_questions, qs.status, qs.created_at, qs.updated_at, qs.sub_course_video_id, qs.display_order, qs.intro_video_url
|
||||
FROM question_sets qs
|
||||
WHERE set_type = $1
|
||||
AND status != 'ARCHIVED'
|
||||
|
|
@ -333,6 +341,7 @@ type GetQuestionSetsByTypeRow struct {
|
|||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
SubCourseVideoID pgtype.Int8 `json:"sub_course_video_id"`
|
||||
DisplayOrder int32 `json:"display_order"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetQuestionSetsByType(ctx context.Context, arg GetQuestionSetsByTypeParams) ([]GetQuestionSetsByTypeRow, error) {
|
||||
|
|
@ -362,6 +371,7 @@ func (q *Queries) GetQuestionSetsByType(ctx context.Context, arg GetQuestionSets
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -374,7 +384,7 @@ func (q *Queries) GetQuestionSetsByType(ctx context.Context, arg GetQuestionSets
|
|||
}
|
||||
|
||||
const GetSubCourseInitialAssessmentSet = `-- name: GetSubCourseInitialAssessmentSet :one
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order
|
||||
SELECT id, title, description, set_type, owner_type, owner_id, banner_image, persona, time_limit_minutes, passing_score, shuffle_questions, status, created_at, updated_at, sub_course_video_id, display_order, intro_video_url
|
||||
FROM question_sets
|
||||
WHERE set_type = 'INITIAL_ASSESSMENT'
|
||||
AND owner_type = 'SUB_COURSE'
|
||||
|
|
@ -404,6 +414,7 @@ func (q *Queries) GetSubCourseInitialAssessmentSet(ctx context.Context, ownerID
|
|||
&i.UpdatedAt,
|
||||
&i.SubCourseVideoID,
|
||||
&i.DisplayOrder,
|
||||
&i.IntroVideoUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -507,9 +518,10 @@ SET
|
|||
passing_score = COALESCE($6, passing_score),
|
||||
shuffle_questions = COALESCE($7, shuffle_questions),
|
||||
status = COALESCE($8, status),
|
||||
sub_course_video_id = COALESCE($9, sub_course_video_id),
|
||||
intro_video_url = COALESCE($9, intro_video_url),
|
||||
sub_course_video_id = COALESCE($10, sub_course_video_id),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $10
|
||||
WHERE id = $11
|
||||
`
|
||||
|
||||
type UpdateQuestionSetParams struct {
|
||||
|
|
@ -521,6 +533,7 @@ type UpdateQuestionSetParams struct {
|
|||
PassingScore pgtype.Int4 `json:"passing_score"`
|
||||
ShuffleQuestions bool `json:"shuffle_questions"`
|
||||
Status string `json:"status"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
SubCourseVideoID pgtype.Int8 `json:"sub_course_video_id"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
|
@ -535,6 +548,7 @@ func (q *Queries) UpdateQuestionSet(ctx context.Context, arg UpdateQuestionSetPa
|
|||
arg.PassingScore,
|
||||
arg.ShuffleQuestions,
|
||||
arg.Status,
|
||||
arg.IntroVideoUrl,
|
||||
arg.SubCourseVideoID,
|
||||
arg.ID,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ type LearningPathPractice struct {
|
|||
Description *string `json:"description,omitempty"`
|
||||
Persona *string `json:"persona,omitempty"`
|
||||
Status string `json:"status"`
|
||||
IntroVideoURL *string `json:"intro_video_url,omitempty"`
|
||||
QuestionCount int64 `json:"question_count"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ type QuestionSet struct {
|
|||
ShuffleQuestions bool
|
||||
Status string
|
||||
SubCourseVideoID *int64
|
||||
IntroVideoURL *string
|
||||
UserPersonas []UserPersona
|
||||
CreatedAt time.Time
|
||||
UpdatedAt *time.Time
|
||||
|
|
@ -170,6 +171,7 @@ type CreateQuestionSetInput struct {
|
|||
ShuffleQuestions *bool
|
||||
Status *string
|
||||
SubCourseVideoID *int64
|
||||
IntroVideoURL *string
|
||||
}
|
||||
|
||||
// UserPersona represents a user acting as a persona in a practice session
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ func (s *Store) getSubCoursePracticesForPath(ctx context.Context, subCourseID in
|
|||
Description: ptrString(row.Description),
|
||||
Persona: ptrString(row.Persona),
|
||||
Status: row.Status,
|
||||
IntroVideoURL: ptrString(row.IntroVideoUrl),
|
||||
QuestionCount: row.QuestionCount,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ func questionSetToDomain(qs dbgen.QuestionSet) domain.QuestionSet {
|
|||
ShuffleQuestions: qs.ShuffleQuestions,
|
||||
Status: qs.Status,
|
||||
SubCourseVideoID: fromPgInt8(qs.SubCourseVideoID),
|
||||
IntroVideoURL: fromPgText(qs.IntroVideoUrl),
|
||||
CreatedAt: qs.CreatedAt.Time,
|
||||
UpdatedAt: timePtr(qs.UpdatedAt),
|
||||
}
|
||||
|
|
@ -542,6 +543,7 @@ func (s *Store) CreateQuestionSet(ctx context.Context, input domain.CreateQuesti
|
|||
Column10: shuffleQuestions,
|
||||
Column11: status,
|
||||
SubCourseVideoID: toPgInt8(input.SubCourseVideoID),
|
||||
IntroVideoUrl: toPgText(input.IntroVideoURL),
|
||||
})
|
||||
if err != nil {
|
||||
return domain.QuestionSet{}, err
|
||||
|
|
@ -603,6 +605,7 @@ func (s *Store) GetQuestionSetsByType(ctx context.Context, setType string, limit
|
|||
ShuffleQuestions: r.ShuffleQuestions,
|
||||
Status: r.Status,
|
||||
SubCourseVideoID: fromPgInt8(r.SubCourseVideoID),
|
||||
IntroVideoURL: fromPgText(r.IntroVideoUrl),
|
||||
CreatedAt: r.CreatedAt.Time,
|
||||
UpdatedAt: timePtr(r.UpdatedAt),
|
||||
}
|
||||
|
|
@ -688,6 +691,7 @@ func (s *Store) UpdateQuestionSet(ctx context.Context, id int64, input domain.Cr
|
|||
PassingScore: toPgInt4(input.PassingScore),
|
||||
ShuffleQuestions: shuffleQuestions,
|
||||
Status: status,
|
||||
IntroVideoUrl: toPgText(input.IntroVideoURL),
|
||||
SubCourseVideoID: toPgInt8(input.SubCourseVideoID),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ type resetAndReseedReq struct {
|
|||
Confirm string `json:"confirm"`
|
||||
}
|
||||
|
||||
type clearCourseManagementReq struct {
|
||||
Confirm string `json:"confirm"`
|
||||
}
|
||||
|
||||
func extractInsertStatement(sqlContent string, tableName string) (string, bool) {
|
||||
pattern := fmt.Sprintf(`(?is)INSERT\s+INTO\s+%s\b.*?;`, regexp.QuoteMeta(tableName))
|
||||
re := regexp.MustCompile(pattern)
|
||||
|
|
@ -191,3 +195,71 @@ func (h *Handler) ResetAndReseedDatabase(c *fiber.Ctx) error {
|
|||
StatusCode: fiber.StatusOK,
|
||||
})
|
||||
}
|
||||
|
||||
// ClearCourseManagementData godoc
|
||||
// @Summary Clear course management hierarchy data only
|
||||
// @Description Truncates course_categories, courses, and sub_courses (same scope as reset-reseed) without re-inserting seed SQL.
|
||||
// @Tags internal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Seed-Reset-Token header string false "Optional token when DB_RESET_RESEED_TOKEN is set"
|
||||
// @Param body body clearCourseManagementReq true "Confirmation payload"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 403 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/internal/db/clear-course-management [post]
|
||||
func (h *Handler) ClearCourseManagementData(c *fiber.Ctx) error {
|
||||
if h.Cfg == nil || !h.Cfg.DBResetReseedEnabled {
|
||||
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
|
||||
Message: "Operation is disabled",
|
||||
Error: "internal course management maintenance is disabled",
|
||||
})
|
||||
}
|
||||
|
||||
var req clearCourseManagementReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid request body",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
if strings.TrimSpace(req.Confirm) != "CLEAR_COURSE_MANAGEMENT" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Confirmation required",
|
||||
Error: `set confirm to "CLEAR_COURSE_MANAGEMENT"`,
|
||||
})
|
||||
}
|
||||
|
||||
expectedToken := strings.TrimSpace(h.Cfg.DBResetReseedToken)
|
||||
if expectedToken != "" {
|
||||
providedToken := strings.TrimSpace(c.Get("X-Seed-Reset-Token"))
|
||||
if subtle.ConstantTimeCompare([]byte(providedToken), []byte(expectedToken)) != 1 {
|
||||
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid reset token",
|
||||
Error: "missing or invalid X-Seed-Reset-Token",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
tableNames := []string{"course_categories", "courses", "sub_courses"}
|
||||
sql := `BEGIN;
|
||||
TRUNCATE TABLE sub_courses, courses, course_categories RESTART IDENTITY CASCADE;
|
||||
COMMIT;`
|
||||
|
||||
if _, err := h.analyticsDB.ExecRaw(c.Context(), sql); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to clear course management data",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Course management hierarchy cleared successfully (no re-seed)",
|
||||
Data: map[string]interface{}{
|
||||
"tables": tableNames,
|
||||
},
|
||||
Success: true,
|
||||
StatusCode: fiber.StatusOK,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ type createQuestionSetReq struct {
|
|||
ShuffleQuestions *bool `json:"shuffle_questions"`
|
||||
Status *string `json:"status"`
|
||||
SubCourseVideoID *int64 `json:"sub_course_video_id"`
|
||||
IntroVideoURL *string `json:"intro_video_url"`
|
||||
}
|
||||
|
||||
type questionSetRes struct {
|
||||
|
|
@ -541,6 +542,7 @@ type questionSetRes struct {
|
|||
ShuffleQuestions bool `json:"shuffle_questions"`
|
||||
Status string `json:"status"`
|
||||
SubCourseVideoID *int64 `json:"sub_course_video_id,omitempty"`
|
||||
IntroVideoURL *string `json:"intro_video_url,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
QuestionCount *int64 `json:"question_count,omitempty"`
|
||||
}
|
||||
|
|
@ -626,6 +628,7 @@ func (h *Handler) CreateQuestionSet(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: req.ShuffleQuestions,
|
||||
Status: req.Status,
|
||||
SubCourseVideoID: req.SubCourseVideoID,
|
||||
IntroVideoURL: req.IntroVideoURL,
|
||||
}
|
||||
|
||||
set, err := h.questionsSvc.CreateQuestionSet(c.Context(), input)
|
||||
|
|
@ -659,6 +662,7 @@ func (h *Handler) CreateQuestionSet(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: set.ShuffleQuestions,
|
||||
Status: set.Status,
|
||||
SubCourseVideoID: set.SubCourseVideoID,
|
||||
IntroVideoURL: set.IntroVideoURL,
|
||||
CreatedAt: set.CreatedAt.String(),
|
||||
},
|
||||
})
|
||||
|
|
@ -711,6 +715,7 @@ func (h *Handler) GetSubCourseEntryAssessmentSet(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: set.ShuffleQuestions,
|
||||
Status: set.Status,
|
||||
SubCourseVideoID: set.SubCourseVideoID,
|
||||
IntroVideoURL: set.IntroVideoURL,
|
||||
CreatedAt: set.CreatedAt.String(),
|
||||
QuestionCount: &count,
|
||||
},
|
||||
|
|
@ -774,6 +779,7 @@ func (h *Handler) GetQuestionSetByID(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: set.ShuffleQuestions,
|
||||
Status: set.Status,
|
||||
SubCourseVideoID: set.SubCourseVideoID,
|
||||
IntroVideoURL: set.IntroVideoURL,
|
||||
CreatedAt: set.CreatedAt.String(),
|
||||
QuestionCount: &count,
|
||||
},
|
||||
|
|
@ -829,6 +835,7 @@ func (h *Handler) GetQuestionSetsByType(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: s.ShuffleQuestions,
|
||||
Status: s.Status,
|
||||
SubCourseVideoID: s.SubCourseVideoID,
|
||||
IntroVideoURL: s.IntroVideoURL,
|
||||
CreatedAt: s.CreatedAt.String(),
|
||||
})
|
||||
}
|
||||
|
|
@ -895,6 +902,7 @@ func (h *Handler) GetQuestionSetsByOwner(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: s.ShuffleQuestions,
|
||||
Status: s.Status,
|
||||
SubCourseVideoID: s.SubCourseVideoID,
|
||||
IntroVideoURL: s.IntroVideoURL,
|
||||
CreatedAt: s.CreatedAt.String(),
|
||||
})
|
||||
}
|
||||
|
|
@ -915,6 +923,7 @@ type updateQuestionSetReq struct {
|
|||
ShuffleQuestions *bool `json:"shuffle_questions"`
|
||||
Status *string `json:"status"`
|
||||
SubCourseVideoID *int64 `json:"sub_course_video_id"`
|
||||
IntroVideoURL *string `json:"intro_video_url"`
|
||||
}
|
||||
|
||||
// UpdateQuestionSet godoc
|
||||
|
|
@ -962,6 +971,7 @@ func (h *Handler) UpdateQuestionSet(c *fiber.Ctx) error {
|
|||
ShuffleQuestions: req.ShuffleQuestions,
|
||||
Status: req.Status,
|
||||
SubCourseVideoID: req.SubCourseVideoID,
|
||||
IntroVideoURL: req.IntroVideoURL,
|
||||
}
|
||||
|
||||
err = h.questionsSvc.UpdateQuestionSet(c.Context(), id, input)
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/user/me/deletion/cancel", a.authMiddleware, a.RequirePermission("users.cancel_delete_self"), h.CancelMyUserAccountDeletion)
|
||||
groupV1.Post("/internal/users/purge-due-deletions", a.authMiddleware, a.RequirePermission("users.purge_due_deletions"), h.PurgeDueDeletedUsers)
|
||||
groupV1.Post("/internal/db/reset-reseed", a.authMiddleware, a.RequirePermission("internal.db.reset_reseed"), h.ResetAndReseedDatabase)
|
||||
groupV1.Post("/internal/db/clear-course-management", a.authMiddleware, a.RequirePermission("internal.db.reset_reseed"), h.ClearCourseManagementData)
|
||||
groupV1.Get("/user/single/:id", a.authMiddleware, a.RequirePermission("users.get"), h.GetUserByID)
|
||||
groupV1.Delete("/user/delete/:id", a.authMiddleware, a.RequirePermission("users.delete"), h.DeleteUser)
|
||||
groupV1.Post("/user/search", a.authMiddleware, a.RequirePermission("users.search"), h.SearchUserByNameOrPhone)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user