Compare commits
No commits in common. "227df20bf21e6b5d48ef44d820dda0d51b7bde75" and "b2c7dcae830a0c4b535df21f9296a3683ed55298" have entirely different histories.
227df20bf2
...
b2c7dcae83
@ -8,7 +8,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@ -55,52 +54,11 @@ var logoutCmd = &cobra.Command{
|
|||||||
Run: runLogout,
|
Run: runLogout,
|
||||||
}
|
}
|
||||||
|
|
||||||
var inboxCmd = &cobra.Command{
|
|
||||||
Use: "inbox",
|
|
||||||
Short: "View your termail inbox",
|
|
||||||
Long: "View, search, and manage your termail inbox.",
|
|
||||||
Run: runInbox,
|
|
||||||
}
|
|
||||||
|
|
||||||
var inboxReadCmd = &cobra.Command{
|
|
||||||
Use: "read [termail_id]",
|
|
||||||
Short: "Mark termail as read",
|
|
||||||
Long: "Mark a specific termail as read by ID.",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: runInboxRead,
|
|
||||||
}
|
|
||||||
|
|
||||||
var inboxDeleteCmd = &cobra.Command{
|
|
||||||
Use: "delete [termail_id]",
|
|
||||||
Short: "Delete termail",
|
|
||||||
Long: "Delete a specific termail by ID.",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: runInboxDelete,
|
|
||||||
}
|
|
||||||
|
|
||||||
var sendCmd = &cobra.Command{
|
|
||||||
Use: "send [username]",
|
|
||||||
Short: "Send termail to a user",
|
|
||||||
Long: "Send termail to a specific user by username.",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
Run: runSend,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inboxCmd.Flags().StringP("search", "s", "", "Search termails by content, subject, or sender")
|
|
||||||
inboxCmd.Flags().IntP("limit", "l", 10, "Number of termails to show")
|
|
||||||
inboxCmd.Flags().IntP("offset", "o", 0, "Number of termails to skip")
|
|
||||||
inboxCmd.Flags().Bool("unread", false, "Show only unread termails")
|
|
||||||
|
|
||||||
inboxCmd.AddCommand(inboxReadCmd)
|
|
||||||
inboxCmd.AddCommand(inboxDeleteCmd)
|
|
||||||
|
|
||||||
authCmd.AddCommand(registerCmd)
|
authCmd.AddCommand(registerCmd)
|
||||||
authCmd.AddCommand(loginCmd)
|
authCmd.AddCommand(loginCmd)
|
||||||
authCmd.AddCommand(logoutCmd)
|
authCmd.AddCommand(logoutCmd)
|
||||||
rootCmd.AddCommand(authCmd)
|
rootCmd.AddCommand(authCmd)
|
||||||
rootCmd.AddCommand(inboxCmd)
|
|
||||||
rootCmd.AddCommand(sendCmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() error {
|
func Execute() error {
|
||||||
|
|||||||
@ -72,19 +72,13 @@ func CreateTables() error {
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS termails (
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
sender_id INT REFERENCES users(id),
|
sender_id INT REFERENCES users(id),
|
||||||
receiver_id INT REFERENCES users(id),
|
receiver_id INT REFERENCES users(id),
|
||||||
subject VARCHAR(255) NOT NULL,
|
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
is_read BOOLEAN DEFAULT FALSE,
|
|
||||||
sent_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
sent_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_termails_receiver_id ON termails(receiver_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_termails_sender_id ON termails(sender_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_termails_sent_at ON termails(sent_at);
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -16,16 +16,10 @@ CREATE TABLE verification_codes (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE termails (
|
CREATE TABLE messages (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
sender_id INT REFERENCES users(id),
|
sender_id INT REFERENCES users(id),
|
||||||
receiver_id INT REFERENCES users(id),
|
receiver_id INT REFERENCES users(id),
|
||||||
subject VARCHAR(255) NOT NULL,
|
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
is_read BOOLEAN DEFAULT FALSE,
|
|
||||||
sent_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
sent_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_termails_receiver_id ON termails(receiver_id);
|
|
||||||
CREATE INDEX idx_termails_sender_id ON termails(sender_id);
|
|
||||||
CREATE INDEX idx_termails_sent_at ON termails(sent_at);
|
|
||||||
|
|||||||
@ -173,127 +173,6 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) {
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Termail struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
SenderID int `json:"sender_id"`
|
|
||||||
ReceiverID int `json:"receiver_id"`
|
|
||||||
Subject string `json:"subject"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
IsRead bool `json:"is_read"`
|
|
||||||
SentAt time.Time `json:"sent_at"`
|
|
||||||
Sender string `json:"sender,omitempty"`
|
|
||||||
Receiver string `json:"receiver,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SendTermailRequest struct {
|
|
||||||
ReceiverUsername string `json:"receiver_username"`
|
|
||||||
Subject string `json:"subject"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendTermail(ctx context.Context, senderID int, req SendTermailRequest) (*Termail, error) {
|
|
||||||
receiver, err := GetUserByUsername(ctx, req.ReceiverUsername)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("user not found: %s", req.ReceiverUsername)
|
|
||||||
}
|
|
||||||
|
|
||||||
var termail Termail
|
|
||||||
err = Pool.QueryRow(ctx,
|
|
||||||
"INSERT INTO termails (sender_id, receiver_id, subject, content) VALUES ($1, $2, $3, $4) RETURNING id, sender_id, receiver_id, subject, content, is_read, sent_at",
|
|
||||||
senderID, receiver.ID, req.Subject, req.Content,
|
|
||||||
).Scan(&termail.ID, &termail.SenderID, &termail.ReceiverID, &termail.Subject, &termail.Content, &termail.IsRead, &termail.SentAt)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &termail, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetInbox(ctx context.Context, userID int, limit, offset int) ([]Termail, error) {
|
|
||||||
query := `
|
|
||||||
SELECT t.id, t.sender_id, t.receiver_id, t.subject, t.content, t.is_read, t.sent_at, u.username as sender
|
|
||||||
FROM termails t
|
|
||||||
JOIN users u ON t.sender_id = u.id
|
|
||||||
WHERE t.receiver_id = $1
|
|
||||||
ORDER BY t.sent_at DESC
|
|
||||||
LIMIT $2 OFFSET $3
|
|
||||||
`
|
|
||||||
|
|
||||||
rows, err := Pool.Query(ctx, query, userID, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var termails []Termail
|
|
||||||
for rows.Next() {
|
|
||||||
var t Termail
|
|
||||||
err := rows.Scan(&t.ID, &t.SenderID, &t.ReceiverID, &t.Subject, &t.Content, &t.IsRead, &t.SentAt, &t.Sender)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
termails = append(termails, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return termails, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MarkTermailAsRead(ctx context.Context, termailID, userID int) error {
|
|
||||||
_, err := Pool.Exec(ctx,
|
|
||||||
"UPDATE termails SET is_read = true WHERE id = $1 AND receiver_id = $2",
|
|
||||||
termailID, userID,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteTermail(ctx context.Context, termailID, userID int) error {
|
|
||||||
result, err := Pool.Exec(ctx,
|
|
||||||
"DELETE FROM termails WHERE id = $1 AND receiver_id = $2",
|
|
||||||
termailID, userID,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rowsAffected := result.RowsAffected()
|
|
||||||
if rowsAffected == 0 {
|
|
||||||
return fmt.Errorf("termail not found or access denied")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SearchTermails(ctx context.Context, userID int, query string, limit, offset int) ([]Termail, error) {
|
|
||||||
sqlQuery := `
|
|
||||||
SELECT t.id, t.sender_id, t.receiver_id, t.subject, t.content, t.is_read, t.sent_at, u.username as sender
|
|
||||||
FROM termails t
|
|
||||||
JOIN users u ON t.sender_id = u.id
|
|
||||||
WHERE t.receiver_id = $1 AND (t.subject ILIKE $2 OR t.content ILIKE $2 OR u.username ILIKE $2)
|
|
||||||
ORDER BY t.sent_at DESC
|
|
||||||
LIMIT $3 OFFSET $4
|
|
||||||
`
|
|
||||||
|
|
||||||
searchPattern := "%" + query + "%"
|
|
||||||
rows, err := Pool.Query(ctx, sqlQuery, userID, searchPattern, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var termails []Termail
|
|
||||||
for rows.Next() {
|
|
||||||
var t Termail
|
|
||||||
err := rows.Scan(&t.ID, &t.SenderID, &t.ReceiverID, &t.Subject, &t.Content, &t.IsRead, &t.SentAt, &t.Sender)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
termails = append(termails, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return termails, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CleanupUnverifiedUsers(ctx context.Context) error {
|
func CleanupUnverifiedUsers(ctx context.Context) error {
|
||||||
_, err := Pool.Exec(ctx, `
|
_, err := Pool.Exec(ctx, `
|
||||||
DELETE FROM users
|
DELETE FROM users
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user