Continued work around getting messages and notifications cleaned up since moving to MySQL and changing to Gin, SCS, NoSurf.
This commit is contained in:
177
internal/platform/services/messages/service.go
Normal file
177
internal/platform/services/messages/service.go
Normal file
@@ -0,0 +1,177 @@
|
||||
// ToDo: not currently used and need to carve out sql
|
||||
package messagesvc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
accountMessageHandler "synlotto-website/internal/handlers/account/messages"
|
||||
)
|
||||
|
||||
// Service implements accountMessageHandler.MessageService.
|
||||
type Service struct {
|
||||
DB *sql.DB
|
||||
Dialect string // "postgres", "mysql", "sqlite" (affects INSERT id retrieval)
|
||||
Now func() time.Time
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func New(db *sql.DB, opts ...func(*Service)) *Service {
|
||||
s := &Service{
|
||||
DB: db,
|
||||
Dialect: "mysql", // sane default for LastInsertId (works for mysql/sqlite)
|
||||
Now: time.Now,
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// WithDialect sets SQL dialect hints: "postgres" uses RETURNING id.
|
||||
func WithDialect(d string) func(*Service) { return func(s *Service) { s.Dialect = d } }
|
||||
|
||||
// WithTimeout overrides per-call context timeout.
|
||||
func WithTimeout(d time.Duration) func(*Service) { return func(s *Service) { s.Timeout = d } }
|
||||
|
||||
func (s *Service) ListInbox(userID int64) ([]accountMessageHandler.Message, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||
defer cancel()
|
||||
|
||||
const q = `
|
||||
SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at
|
||||
FROM messages
|
||||
WHERE user_id = ? AND is_archived = FALSE
|
||||
ORDER BY created_at DESC`
|
||||
rows, err := s.DB.QueryContext(s.rebind(ctx), s.bind(q), userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []accountMessageHandler.Message
|
||||
for rows.Next() {
|
||||
var m accountMessageHandler.Message
|
||||
if err := rows.Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Service) ListArchived(userID int64) ([]accountMessageHandler.Message, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||
defer cancel()
|
||||
|
||||
const q = `
|
||||
SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at
|
||||
FROM messages
|
||||
WHERE user_id = ? AND is_archived = TRUE
|
||||
ORDER BY created_at DESC`
|
||||
rows, err := s.DB.QueryContext(s.rebind(ctx), s.bind(q), userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []accountMessageHandler.Message
|
||||
for rows.Next() {
|
||||
var m accountMessageHandler.Message
|
||||
if err := rows.Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Service) GetByID(userID, id int64) (*accountMessageHandler.Message, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||
defer cancel()
|
||||
|
||||
const q = `
|
||||
SELECT id, from_email, to_email, subject, body, is_read, is_archived, created_at
|
||||
FROM messages
|
||||
WHERE user_id = ? AND id = ?`
|
||||
var m accountMessageHandler.Message
|
||||
err := s.DB.QueryRowContext(s.rebind(ctx), s.bind(q), userID, id).
|
||||
Scan(&m.ID, &m.From, &m.To, &m.Subject, &m.Body, &m.IsRead, &m.IsArchived, &m.CreatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (s *Service) Create(userID int64, in accountMessageHandler.CreateMessageInput) (int64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
|
||||
defer cancel()
|
||||
|
||||
switch s.Dialect {
|
||||
case "postgres":
|
||||
const q = `
|
||||
INSERT INTO messages (user_id, from_email, to_email, subject, body, is_read, is_archived, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, FALSE, FALSE, NOW())
|
||||
RETURNING id`
|
||||
var id int64
|
||||
if err := s.DB.QueryRowContext(ctx, q, userID, "", in.To, in.Subject, in.Body).Scan(&id); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return id, nil
|
||||
default: // mysql/sqlite
|
||||
const q = `
|
||||
INSERT INTO messages (user_id, from_email, to_email, subject, body, is_read, is_archived, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, FALSE, FALSE, CURRENT_TIMESTAMP)`
|
||||
res, err := s.DB.ExecContext(s.rebind(ctx), q, userID, "", in.To, in.Subject, in.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.LastInsertId()
|
||||
}
|
||||
}
|
||||
|
||||
// --- small helpers ---
|
||||
|
||||
// bind replaces ? with $1.. for Postgres if needed.
|
||||
// We keep queries written with ? for brevity and adapt here.
|
||||
func (s *Service) bind(q string) string {
|
||||
if s.Dialect != "postgres" {
|
||||
return q
|
||||
}
|
||||
// cheap replacer for positional params:
|
||||
n := 0
|
||||
out := make([]byte, 0, len(q)+10)
|
||||
for i := 0; i < len(q); i++ {
|
||||
if q[i] == '?' {
|
||||
n++
|
||||
out = append(out, '$')
|
||||
out = append(out, []byte(intToStr(n))...)
|
||||
continue
|
||||
}
|
||||
out = append(out, q[i])
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func (s *Service) rebind(ctx context.Context) context.Context { return ctx }
|
||||
|
||||
// intToStr avoids fmt for tiny helper
|
||||
func intToStr(n int) string {
|
||||
if n == 0 {
|
||||
return "0"
|
||||
}
|
||||
var b [12]byte
|
||||
i := len(b)
|
||||
for n > 0 {
|
||||
i--
|
||||
b[i] = byte('0' + n%10)
|
||||
n /= 10
|
||||
}
|
||||
return string(b[i:])
|
||||
}
|
||||
Reference in New Issue
Block a user