|
|
@@ -79,7 +79,7 @@ func (s *Store) CreateUser(ctx context.Context, nu NewUser) (*User, error) {
|
|
|
|
PasswordHash: nu.PasswordHash,
|
|
|
|
PasswordHash: nu.PasswordHash,
|
|
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
|
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`INSERT INTO users (id, username, name, password_hash, role, avatar_url, state, created_at) VALUES (?, ?, ?, ?, ?, '', '', ?)`),
|
|
|
|
_, err := s.db.ExecContext(ctx, `INSERT INTO users (id, username, name, password_hash, role, avatar_url, state, created_at) VALUES ($1, $2, $3, $4, $5, '', '', $6)`,
|
|
|
|
u.ID, u.Username, u.Name, u.PasswordHash, string(u.Role), u.CreatedAt)
|
|
|
|
u.ID, u.Username, u.Name, u.PasswordHash, string(u.Role), u.CreatedAt)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
@@ -89,12 +89,12 @@ func (s *Store) CreateUser(ctx context.Context, nu NewUser) (*User, error) {
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) CountAdmins(ctx context.Context) (int, error) {
|
|
|
|
func (s *Store) CountAdmins(ctx context.Context) (int, error) {
|
|
|
|
var n int
|
|
|
|
var n int
|
|
|
|
err := s.db.QueryRowContext(ctx, s.q(`SELECT COUNT(*) FROM users WHERE role = ?`), string(RoleAdmin)).Scan(&n)
|
|
|
|
err := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users WHERE role = $1`, string(RoleAdmin)).Scan(&n)
|
|
|
|
return n, err
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) ListUsers(ctx context.Context) ([]User, error) {
|
|
|
|
func (s *Store) ListUsers(ctx context.Context) ([]User, error) {
|
|
|
|
rows, err := s.db.QueryContext(ctx, s.q(`SELECT id, username, name, role, avatar_url, state, created_at FROM users ORDER BY created_at ASC`))
|
|
|
|
rows, err := s.db.QueryContext(ctx, `SELECT id, username, name, role, avatar_url, state, created_at FROM users ORDER BY created_at ASC`)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -123,20 +123,20 @@ func (s *Store) SetRole(ctx context.Context, userID string, role Role) error {
|
|
|
|
defer tx.Rollback()
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
|
|
|
|
var current string
|
|
|
|
var current string
|
|
|
|
err = tx.QueryRowContext(ctx, s.q(`SELECT role FROM users WHERE id = ?`), userID).Scan(¤t)
|
|
|
|
err = tx.QueryRowContext(ctx, `SELECT role FROM users WHERE id = $1`, userID).Scan(¤t)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if Role(current) == RoleAdmin && role == RoleUser {
|
|
|
|
if Role(current) == RoleAdmin && role == RoleUser {
|
|
|
|
var n int
|
|
|
|
var n int
|
|
|
|
if err := tx.QueryRowContext(ctx, s.q(`SELECT COUNT(*) FROM users WHERE role = ?`), string(RoleAdmin)).Scan(&n); err != nil {
|
|
|
|
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM users WHERE role = $1`, string(RoleAdmin)).Scan(&n); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if n <= 1 {
|
|
|
|
if n <= 1 {
|
|
|
|
return ErrLastAdmin
|
|
|
|
return ErrLastAdmin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res, err := tx.ExecContext(ctx, s.q(`UPDATE users SET role = ? WHERE id = ?`), string(role), userID)
|
|
|
|
res, err := tx.ExecContext(ctx, `UPDATE users SET role = $1 WHERE id = $2`, string(role), userID)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -151,11 +151,11 @@ func (s *Store) SetRole(ctx context.Context, userID string, role Role) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) UserByID(ctx context.Context, id string) (*User, error) {
|
|
|
|
func (s *Store) UserByID(ctx context.Context, id string) (*User, error) {
|
|
|
|
return scanUser(s.db.QueryRowContext(ctx, s.q(`SELECT id, username, name, role, avatar_url, state, created_at FROM users WHERE id = ?`), id), false)
|
|
|
|
return scanUser(s.db.QueryRowContext(ctx, `SELECT id, username, name, role, avatar_url, state, created_at FROM users WHERE id = $1`, id), false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) UserByUsername(ctx context.Context, username string) (*User, error) {
|
|
|
|
func (s *Store) UserByUsername(ctx context.Context, username string) (*User, error) {
|
|
|
|
return scanUser(s.db.QueryRowContext(ctx, s.q(`SELECT id, username, name, role, avatar_url, state, created_at, password_hash FROM users WHERE username = ?`), NormalizeUsername(username)), true)
|
|
|
|
return scanUser(s.db.QueryRowContext(ctx, `SELECT id, username, name, role, avatar_url, state, created_at, password_hash FROM users WHERE username = $1`, NormalizeUsername(username)), true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func scanUser(row *sql.Row, withSecrets bool) (*User, error) {
|
|
|
|
func scanUser(row *sql.Row, withSecrets bool) (*User, error) {
|
|
|
@@ -188,7 +188,7 @@ func (s *Store) CreateQuestion(ctx context.Context, authorID, title, body, city
|
|
|
|
HuntDate: pacific.Today(),
|
|
|
|
HuntDate: pacific.Today(),
|
|
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
|
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`INSERT INTO questions (id, author_id, title, body, city, hunt_date, hidden, created_at) VALUES (?, ?, ?, ?, ?, ?, 0, ?)`),
|
|
|
|
_, err := s.db.ExecContext(ctx, `INSERT INTO questions (id, author_id, title, body, city, hunt_date, hidden, created_at) VALUES ($1, $2, $3, $4, $5, $6, 0, $7)`,
|
|
|
|
q.ID, q.AuthorID, q.Title, q.Body, q.City, q.HuntDate, q.CreatedAt)
|
|
|
|
q.ID, q.AuthorID, q.Title, q.Body, q.City, q.HuntDate, q.CreatedAt)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
@@ -197,18 +197,18 @@ func (s *Store) CreateQuestion(ctx context.Context, authorID, title, body, city
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) ListHunt(ctx context.Context, huntDate, viewerID string) ([]RankedQuestion, error) {
|
|
|
|
func (s *Store) ListHunt(ctx context.Context, huntDate, viewerID string) ([]RankedQuestion, error) {
|
|
|
|
rows, err := s.db.QueryContext(ctx, s.q(`
|
|
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
COALESCE(SUM(v.value), 0) AS score,
|
|
|
|
COALESCE(SUM(v.value), 0) AS score,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
|
COALESCE((SELECT value FROM votes WHERE user_id = ? AND question_id = q.id), 0) AS user_vote
|
|
|
|
COALESCE((SELECT value FROM votes WHERE user_id = $1 AND question_id = q.id), 0) AS user_vote
|
|
|
|
FROM questions q
|
|
|
|
FROM questions q
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
LEFT JOIN votes v ON v.question_id = q.id
|
|
|
|
LEFT JOIN votes v ON v.question_id = q.id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
WHERE q.hunt_date = ? AND q.hidden = 0
|
|
|
|
WHERE q.hunt_date = $2 AND q.hidden = 0
|
|
|
|
GROUP BY q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at, a.question_id
|
|
|
|
GROUP BY q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at, a.question_id
|
|
|
|
ORDER BY score DESC, q.created_at ASC`), viewerID, huntDate)
|
|
|
|
ORDER BY score DESC, q.created_at ASC`, viewerID, huntDate)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -225,15 +225,15 @@ ORDER BY score DESC, q.created_at ASC`), viewerID, huntDate)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) GetQuestion(ctx context.Context, id, viewerID string) (*RankedQuestion, error) {
|
|
|
|
func (s *Store) GetQuestion(ctx context.Context, id, viewerID string) (*RankedQuestion, error) {
|
|
|
|
row := s.db.QueryRowContext(ctx, s.q(`
|
|
|
|
row := s.db.QueryRowContext(ctx, `
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
|
COALESCE((SELECT value FROM votes WHERE user_id = ? AND question_id = q.id), 0) AS user_vote
|
|
|
|
COALESCE((SELECT value FROM votes WHERE user_id = $1 AND question_id = q.id), 0) AS user_vote
|
|
|
|
FROM questions q
|
|
|
|
FROM questions q
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
WHERE q.id = ?`), viewerID, id)
|
|
|
|
WHERE q.id = $2`, viewerID, id)
|
|
|
|
q, err := scanRankedRow(row)
|
|
|
|
q, err := scanRankedRow(row)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
@@ -268,15 +268,15 @@ func (s *Store) Vote(ctx context.Context, userID, questionID string, value int)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
defer tx.Rollback()
|
|
|
|
var current sql.NullInt64
|
|
|
|
var current sql.NullInt64
|
|
|
|
err = tx.QueryRowContext(ctx, s.q(`SELECT value FROM votes WHERE user_id = ? AND question_id = ?`), userID, questionID).Scan(¤t)
|
|
|
|
err = tx.QueryRowContext(ctx, `SELECT value FROM votes WHERE user_id = $1 AND question_id = $2`, userID, questionID).Scan(¤t)
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err == nil && current.Valid && int(current.Int64) == value {
|
|
|
|
if err == nil && current.Valid && int(current.Int64) == value {
|
|
|
|
_, err = tx.ExecContext(ctx, s.q(`DELETE FROM votes WHERE user_id = ? AND question_id = ?`), userID, questionID)
|
|
|
|
_, err = tx.ExecContext(ctx, `DELETE FROM votes WHERE user_id = $1 AND question_id = $2`, userID, questionID)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
_, err = tx.ExecContext(ctx, s.q(`INSERT INTO votes (user_id, question_id, value) VALUES (?, ?, ?)
|
|
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO votes (user_id, question_id, value) VALUES ($1, $2, $3)
|
|
|
|
ON CONFLICT (user_id, question_id) DO UPDATE SET value = excluded.value`), userID, questionID, value)
|
|
|
|
ON CONFLICT (user_id, question_id) DO UPDATE SET value = excluded.value`, userID, questionID, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@@ -286,11 +286,11 @@ ON CONFLICT (user_id, question_id) DO UPDATE SET value = excluded.value`), userI
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) GetAnswer(ctx context.Context, questionID string) (*Answer, error) {
|
|
|
|
func (s *Store) GetAnswer(ctx context.Context, questionID string) (*Answer, error) {
|
|
|
|
var a Answer
|
|
|
|
var a Answer
|
|
|
|
err := s.db.QueryRowContext(ctx, s.q(`
|
|
|
|
err := s.db.QueryRowContext(ctx, `
|
|
|
|
SELECT a.question_id, a.author_id, u.name, a.body, a.created_at, a.updated_at
|
|
|
|
SELECT a.question_id, a.author_id, u.name, a.body, a.created_at, a.updated_at
|
|
|
|
FROM answers a
|
|
|
|
FROM answers a
|
|
|
|
JOIN users u ON u.id = a.author_id
|
|
|
|
JOIN users u ON u.id = a.author_id
|
|
|
|
WHERE a.question_id = ?`), questionID).Scan(&a.QuestionID, &a.AuthorID, &a.AuthorName, &a.Body, &a.CreatedAt, &a.UpdatedAt)
|
|
|
|
WHERE a.question_id = $1`, questionID).Scan(&a.QuestionID, &a.AuthorID, &a.AuthorName, &a.Body, &a.CreatedAt, &a.UpdatedAt)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -300,30 +300,30 @@ WHERE a.question_id = ?`), questionID).Scan(&a.QuestionID, &a.AuthorID, &a.Autho
|
|
|
|
func (s *Store) UpsertAnswer(ctx context.Context, questionID, authorID, body string) error {
|
|
|
|
func (s *Store) UpsertAnswer(ctx context.Context, questionID, authorID, body string) error {
|
|
|
|
body = strings.TrimSpace(body)
|
|
|
|
body = strings.TrimSpace(body)
|
|
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`
|
|
|
|
_, err := s.db.ExecContext(ctx, `
|
|
|
|
INSERT INTO answers (question_id, author_id, body, created_at, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
|
|
INSERT INTO answers (question_id, author_id, body, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)
|
|
|
|
ON CONFLICT (question_id) DO UPDATE SET body = excluded.body, author_id = excluded.author_id, updated_at = excluded.updated_at`),
|
|
|
|
ON CONFLICT (question_id) DO UPDATE SET body = excluded.body, author_id = excluded.author_id, updated_at = excluded.updated_at`,
|
|
|
|
questionID, authorID, body, now, now)
|
|
|
|
questionID, authorID, body, now, now)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) HideQuestion(ctx context.Context, id string) error {
|
|
|
|
func (s *Store) HideQuestion(ctx context.Context, id string) error {
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`UPDATE questions SET hidden = 1 WHERE id = ?`), id)
|
|
|
|
_, err := s.db.ExecContext(ctx, `UPDATE questions SET hidden = 1 WHERE id = $1`, id)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) UpdateProfile(ctx context.Context, userID, state, avatarURL string) error {
|
|
|
|
func (s *Store) UpdateProfile(ctx context.Context, userID, state, avatarURL string) error {
|
|
|
|
state = strings.TrimSpace(state)
|
|
|
|
state = strings.TrimSpace(state)
|
|
|
|
if avatarURL == "" {
|
|
|
|
if avatarURL == "" {
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`UPDATE users SET state = ? WHERE id = ?`), state, userID)
|
|
|
|
_, err := s.db.ExecContext(ctx, `UPDATE users SET state = $1 WHERE id = $2`, state, userID)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err := s.db.ExecContext(ctx, s.q(`UPDATE users SET state = ?, avatar_url = ? WHERE id = ?`), state, avatarURL, userID)
|
|
|
|
_, err := s.db.ExecContext(ctx, `UPDATE users SET state = $1, avatar_url = $2 WHERE id = $3`, state, avatarURL, userID)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) ListQuestionsByAuthor(ctx context.Context, authorID string) ([]RankedQuestion, error) {
|
|
|
|
func (s *Store) ListQuestionsByAuthor(ctx context.Context, authorID string) ([]RankedQuestion, error) {
|
|
|
|
rows, err := s.db.QueryContext(ctx, s.q(`
|
|
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
|
CASE WHEN a.question_id IS NULL THEN 0 ELSE 1 END AS answered,
|
|
|
@@ -331,8 +331,8 @@ SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden
|
|
|
|
FROM questions q
|
|
|
|
FROM questions q
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
LEFT JOIN answers a ON a.question_id = q.id
|
|
|
|
WHERE q.author_id = ? AND q.hidden = 0
|
|
|
|
WHERE q.author_id = $1 AND q.hidden = 0
|
|
|
|
ORDER BY q.created_at DESC`), authorID)
|
|
|
|
ORDER BY q.created_at DESC`, authorID)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -341,7 +341,7 @@ ORDER BY q.created_at DESC`), authorID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *Store) ListQuestionsAnsweredBy(ctx context.Context, adminID string) ([]RankedQuestion, error) {
|
|
|
|
func (s *Store) ListQuestionsAnsweredBy(ctx context.Context, adminID string) ([]RankedQuestion, error) {
|
|
|
|
rows, err := s.db.QueryContext(ctx, s.q(`
|
|
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden, q.created_at,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
COALESCE((SELECT SUM(value) FROM votes WHERE question_id = q.id), 0) AS score,
|
|
|
|
1 AS answered,
|
|
|
|
1 AS answered,
|
|
|
@@ -349,8 +349,8 @@ SELECT q.id, q.author_id, u.name, q.title, q.body, q.city, q.hunt_date, q.hidden
|
|
|
|
FROM answers ans
|
|
|
|
FROM answers ans
|
|
|
|
JOIN questions q ON q.id = ans.question_id
|
|
|
|
JOIN questions q ON q.id = ans.question_id
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
JOIN users u ON u.id = q.author_id
|
|
|
|
WHERE ans.author_id = ? AND q.hidden = 0
|
|
|
|
WHERE ans.author_id = $1 AND q.hidden = 0
|
|
|
|
ORDER BY ans.updated_at DESC`), adminID)
|
|
|
|
ORDER BY ans.updated_at DESC`, adminID)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|