mirror of
https://github.com/ghostersk/gowebmail.git
synced 2026-06-17 07:59:38 +01:00
update name, project refference and synchronization
This commit is contained in:
+105
-7
@@ -7,8 +7,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yourusername/gomail/internal/crypto"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
"github.com/ghostersk/gowebmail/internal/crypto"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
@@ -526,7 +526,6 @@ func (d *DB) ListAuditLogs(page, pageSize int, eventFilter string) (*models.Audi
|
||||
}, rows.Err()
|
||||
}
|
||||
|
||||
|
||||
// ---- Email Accounts ----
|
||||
|
||||
func (d *DB) CreateAccount(a *models.EmailAccount) error {
|
||||
@@ -797,7 +796,8 @@ func (d *DB) GetFolderByPath(accountID int64, fullPath string) (*models.Folder,
|
||||
COALESCE(is_hidden,0), COALESCE(sync_enabled,1)
|
||||
FROM folders WHERE account_id=? AND full_path=?`, accountID, fullPath,
|
||||
).Scan(&f.ID, &f.AccountID, &f.Name, &f.FullPath, &f.FolderType, &f.UnreadCount, &f.TotalCount, &isHidden, &syncEnabled)
|
||||
f.IsHidden = isHidden == 1; f.SyncEnabled = syncEnabled == 1
|
||||
f.IsHidden = isHidden == 1
|
||||
f.SyncEnabled = syncEnabled == 1
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1161,8 +1161,12 @@ func (d *DB) IsRemoteContentAllowed(userID int64, sender string) (bool, error) {
|
||||
// SetFolderVisibility sets is_hidden and sync_enabled for a folder owned by the user.
|
||||
func (d *DB) SetFolderVisibility(folderID, userID int64, isHidden, syncEnabled bool) error {
|
||||
ih, se := 0, 0
|
||||
if isHidden { ih = 1 }
|
||||
if syncEnabled { se = 1 }
|
||||
if isHidden {
|
||||
ih = 1
|
||||
}
|
||||
if syncEnabled {
|
||||
se = 1
|
||||
}
|
||||
_, err := d.sql.Exec(`
|
||||
UPDATE folders SET is_hidden=?, sync_enabled=?
|
||||
WHERE id=? AND account_id IN (SELECT id FROM email_accounts WHERE user_id=?)`,
|
||||
@@ -1212,7 +1216,8 @@ func (d *DB) GetFolderByID(folderID int64) (*models.Folder, error) {
|
||||
COALESCE(is_hidden,0), COALESCE(sync_enabled,1)
|
||||
FROM folders WHERE id=?`, folderID,
|
||||
).Scan(&f.ID, &f.AccountID, &f.Name, &f.FullPath, &f.FolderType, &f.UnreadCount, &f.TotalCount, &isHidden, &syncEnabled)
|
||||
f.IsHidden = isHidden == 1; f.SyncEnabled = syncEnabled == 1
|
||||
f.IsHidden = isHidden == 1
|
||||
f.SyncEnabled = syncEnabled == 1
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1465,3 +1470,96 @@ func boolToInt(b bool) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// EmptyFolder deletes all messages in a folder (Trash/Spam).
|
||||
// Returns count deleted.
|
||||
func (d *DB) EmptyFolder(folderID, userID int64) (int, error) {
|
||||
res, err := d.sql.Exec(`
|
||||
DELETE FROM messages WHERE folder_id=?
|
||||
AND folder_id IN (SELECT id FROM folders WHERE account_id IN
|
||||
(SELECT id FROM email_accounts WHERE user_id=?))`,
|
||||
folderID, userID,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// EnableAllFolderSync enables sync for all currently-disabled folders belonging
|
||||
// to accounts owned by userID. Returns count updated.
|
||||
func (d *DB) EnableAllFolderSync(accountID, userID int64) (int, error) {
|
||||
res, err := d.sql.Exec(`
|
||||
UPDATE folders SET sync_enabled=1
|
||||
WHERE account_id=? AND sync_enabled=0
|
||||
AND account_id IN (SELECT id FROM email_accounts WHERE user_id=?)`,
|
||||
accountID, userID,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// PollUnread returns inbox unread count + total unread, and whether there are
|
||||
// new messages since `sinceID`. Used by the client-side poller.
|
||||
func (d *DB) PollUnread(userID int64, sinceID int64) (inboxUnread int, totalUnread int, newestID int64, err error) {
|
||||
// Inbox unread count
|
||||
d.sql.QueryRow(`
|
||||
SELECT COALESCE(SUM(f.unread_count),0) FROM folders f
|
||||
JOIN email_accounts a ON a.id=f.account_id
|
||||
WHERE a.user_id=? AND f.folder_type='inbox'`, userID,
|
||||
).Scan(&inboxUnread)
|
||||
|
||||
// Total unread (all folders except trash/spam)
|
||||
d.sql.QueryRow(`
|
||||
SELECT COALESCE(SUM(f.unread_count),0) FROM folders f
|
||||
JOIN email_accounts a ON a.id=f.account_id
|
||||
WHERE a.user_id=? AND f.folder_type NOT IN ('trash','spam')`, userID,
|
||||
).Scan(&totalUnread)
|
||||
|
||||
// Newest message ID in inbox
|
||||
d.sql.QueryRow(`
|
||||
SELECT COALESCE(MAX(m.id),0) FROM messages m
|
||||
JOIN folders f ON f.id=m.folder_id
|
||||
JOIN email_accounts a ON a.id=f.account_id
|
||||
WHERE a.user_id=? AND f.folder_type='inbox'`, userID,
|
||||
).Scan(&newestID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetNewMessagesSince returns inbox message summaries with id > sinceID for notifications.
|
||||
func (d *DB) GetNewMessagesSince(userID int64, sinceID int64) ([]map[string]interface{}, error) {
|
||||
rows, err := d.sql.Query(`
|
||||
SELECT m.id, m.subject, m.from_name, m.from_email
|
||||
FROM messages m
|
||||
JOIN folders f ON f.id=m.folder_id
|
||||
JOIN email_accounts a ON a.id=f.account_id
|
||||
WHERE a.user_id=? AND f.folder_type='inbox' AND m.id>?
|
||||
ORDER BY m.id DESC LIMIT 5`, userID, sinceID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var result []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var subject, fromName, fromEmail string
|
||||
rows.Scan(&id, &subject, &fromName, &fromEmail)
|
||||
// Decrypt
|
||||
subject, _ = d.enc.Decrypt(subject)
|
||||
fromName, _ = d.enc.Decrypt(fromName)
|
||||
fromEmail, _ = d.enc.Decrypt(fromEmail)
|
||||
result = append(result, map[string]interface{}{
|
||||
"id": id, "subject": subject, "from_name": fromName, "from_email": fromEmail,
|
||||
})
|
||||
}
|
||||
if result == nil {
|
||||
result = []map[string]interface{}{}
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/client"
|
||||
|
||||
gomailModels "github.com/yourusername/gomail/internal/models"
|
||||
gomailModels "github.com/ghostersk/gowebmail/internal/models"
|
||||
)
|
||||
|
||||
func imapHostFor(provider gomailModels.AccountProvider) (string, int) {
|
||||
|
||||
+15
-15
@@ -6,11 +6,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/middleware"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/yourusername/gomail/config"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/middleware"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
)
|
||||
|
||||
// AdminHandler handles /admin/* routes (all require admin role).
|
||||
@@ -46,14 +46,14 @@ func (h *AdminHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// Sanitize: strip password hash
|
||||
type safeUser struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
Role models.UserRole `json:"role"`
|
||||
IsActive bool `json:"is_active"`
|
||||
MFAEnabled bool `json:"mfa_enabled"`
|
||||
LastLoginAt interface{} `json:"last_login_at"`
|
||||
CreatedAt interface{} `json:"created_at"`
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
Role models.UserRole `json:"role"`
|
||||
IsActive bool `json:"is_active"`
|
||||
MFAEnabled bool `json:"mfa_enabled"`
|
||||
LastLoginAt interface{} `json:"last_login_at"`
|
||||
CreatedAt interface{} `json:"created_at"`
|
||||
}
|
||||
result := make([]safeUser, 0, len(users))
|
||||
for _, u := range users {
|
||||
@@ -108,9 +108,9 @@ func (h *AdminHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
targetID, _ := strconv.ParseInt(vars["id"], 10, 64)
|
||||
|
||||
var req struct {
|
||||
IsActive *bool `json:"is_active"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
Password string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, "invalid request")
|
||||
|
||||
+123
-28
@@ -11,20 +11,20 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/email"
|
||||
"github.com/ghostersk/gowebmail/internal/middleware"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
"github.com/ghostersk/gowebmail/internal/syncer"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/yourusername/gomail/config"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/email"
|
||||
"github.com/yourusername/gomail/internal/middleware"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
"github.com/yourusername/gomail/internal/syncer"
|
||||
)
|
||||
|
||||
// APIHandler handles all /api/* JSON endpoints.
|
||||
type APIHandler struct {
|
||||
db *db.DB
|
||||
cfg *config.Config
|
||||
syncer *syncer.Scheduler
|
||||
db *db.DB
|
||||
cfg *config.Config
|
||||
syncer *syncer.Scheduler
|
||||
}
|
||||
|
||||
func (h *APIHandler) writeJSON(w http.ResponseWriter, v interface{}) {
|
||||
@@ -93,13 +93,13 @@ func (h *APIHandler) ListAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *APIHandler) AddAccount(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Password string `json:"password"`
|
||||
IMAPHost string `json:"imap_host"`
|
||||
IMAPPort int `json:"imap_port"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Password string `json:"password"`
|
||||
IMAPHost string `json:"imap_host"`
|
||||
IMAPPort int `json:"imap_port"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
@@ -128,8 +128,8 @@ func (h *APIHandler) AddAccount(w http.ResponseWriter, r *http.Request) {
|
||||
account := &models.EmailAccount{
|
||||
UserID: userID, Provider: models.ProviderIMAPSMTP,
|
||||
EmailAddress: req.Email, DisplayName: req.DisplayName,
|
||||
AccessToken: req.Password,
|
||||
IMAPHost: req.IMAPHost, IMAPPort: req.IMAPPort,
|
||||
AccessToken: req.Password,
|
||||
IMAPHost: req.IMAPHost, IMAPPort: req.IMAPPort,
|
||||
SMTPHost: req.SMTPHost, SMTPPort: req.SMTPPort,
|
||||
Color: color, IsActive: true,
|
||||
}
|
||||
@@ -170,12 +170,12 @@ func (h *APIHandler) UpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var req struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Password string `json:"password"`
|
||||
IMAPHost string `json:"imap_host"`
|
||||
IMAPPort int `json:"imap_port"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Password string `json:"password"`
|
||||
IMAPHost string `json:"imap_host"`
|
||||
IMAPPort int `json:"imap_port"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
h.writeError(w, http.StatusBadRequest, "invalid request")
|
||||
@@ -538,7 +538,9 @@ func (h *APIHandler) GetMessage(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *APIHandler) MarkRead(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
messageID := pathInt64(r, "id")
|
||||
var req struct{ Read bool `json:"read"` }
|
||||
var req struct {
|
||||
Read bool `json:"read"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
// Update local DB first
|
||||
@@ -548,7 +550,9 @@ func (h *APIHandler) MarkRead(w http.ResponseWriter, r *http.Request) {
|
||||
uid, folderPath, account, err := h.db.GetMessageIMAPInfo(messageID, userID)
|
||||
if err == nil && uid != 0 && account != nil {
|
||||
val := "0"
|
||||
if req.Read { val = "1" }
|
||||
if req.Read {
|
||||
val = "1"
|
||||
}
|
||||
h.db.EnqueueIMAPOp(&db.PendingIMAPOp{
|
||||
AccountID: account.ID, OpType: "flag_read",
|
||||
RemoteUID: uid, FolderPath: folderPath, Extra: val,
|
||||
@@ -569,7 +573,9 @@ func (h *APIHandler) ToggleStar(w http.ResponseWriter, r *http.Request) {
|
||||
uid, folderPath, account, ierr := h.db.GetMessageIMAPInfo(messageID, userID)
|
||||
if ierr == nil && uid != 0 && account != nil {
|
||||
val := "0"
|
||||
if starred { val = "1" }
|
||||
if starred {
|
||||
val = "1"
|
||||
}
|
||||
h.db.EnqueueIMAPOp(&db.PendingIMAPOp{
|
||||
AccountID: account.ID, OpType: "flag_star",
|
||||
RemoteUID: uid, FolderPath: folderPath, Extra: val,
|
||||
@@ -582,7 +588,9 @@ func (h *APIHandler) ToggleStar(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *APIHandler) MoveMessage(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
messageID := pathInt64(r, "id")
|
||||
var req struct{ FolderID int64 `json:"folder_id"` }
|
||||
var req struct {
|
||||
FolderID int64 `json:"folder_id"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.FolderID == 0 {
|
||||
h.writeError(w, http.StatusBadRequest, "folder_id required")
|
||||
return
|
||||
@@ -967,3 +975,90 @@ func (h *APIHandler) AddRemoteContentWhitelist(w http.ResponseWriter, r *http.Re
|
||||
}
|
||||
h.writeJSON(w, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
// ---- Empty folder (Trash/Spam) ----
|
||||
|
||||
func (h *APIHandler) EmptyFolder(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
folderID := pathInt64(r, "id")
|
||||
|
||||
// Verify folder is trash or spam before allowing bulk delete
|
||||
folder, err := h.db.GetFolderByID(folderID)
|
||||
if err != nil || folder == nil {
|
||||
h.writeError(w, http.StatusNotFound, "folder not found")
|
||||
return
|
||||
}
|
||||
if folder.FolderType != "trash" && folder.FolderType != "spam" {
|
||||
h.writeError(w, http.StatusBadRequest, "can only empty trash or spam folders")
|
||||
return
|
||||
}
|
||||
|
||||
n, err := h.db.EmptyFolder(folderID, userID)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusInternalServerError, "failed to empty folder")
|
||||
return
|
||||
}
|
||||
h.db.UpdateFolderCounts(folderID)
|
||||
h.writeJSON(w, map[string]interface{}{"ok": true, "deleted": n})
|
||||
}
|
||||
|
||||
// ---- Enable sync for all folders of an account ----
|
||||
|
||||
func (h *APIHandler) EnableAllFolderSync(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
vars := mux.Vars(r)
|
||||
accountIDStr := vars["account_id"]
|
||||
var accountID int64
|
||||
fmt.Sscanf(accountIDStr, "%d", &accountID)
|
||||
if accountID == 0 {
|
||||
h.writeError(w, http.StatusBadRequest, "account_id required")
|
||||
return
|
||||
}
|
||||
n, err := h.db.EnableAllFolderSync(accountID, userID)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusInternalServerError, "failed to enable sync")
|
||||
return
|
||||
}
|
||||
h.writeJSON(w, map[string]interface{}{"ok": true, "enabled": n})
|
||||
}
|
||||
|
||||
// ---- Long-poll for unread counts + new message detection ----
|
||||
// GET /api/poll?since=<lastKnownMessageID>
|
||||
// Returns immediately with current counts; client polls every ~20s.
|
||||
|
||||
func (h *APIHandler) PollUnread(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
var sinceID int64
|
||||
fmt.Sscanf(r.URL.Query().Get("since"), "%d", &sinceID)
|
||||
|
||||
inboxUnread, totalUnread, newestID, err := h.db.PollUnread(userID, sinceID)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusInternalServerError, "poll failed")
|
||||
return
|
||||
}
|
||||
|
||||
h.writeJSON(w, map[string]interface{}{
|
||||
"inbox_unread": inboxUnread,
|
||||
"total_unread": totalUnread,
|
||||
"newest_id": newestID,
|
||||
"has_new": newestID > sinceID && sinceID > 0,
|
||||
})
|
||||
}
|
||||
|
||||
// ---- Get new messages since ID (for notification content) ----
|
||||
|
||||
func (h *APIHandler) NewMessagesSince(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
var sinceID int64
|
||||
fmt.Sscanf(r.URL.Query().Get("since"), "%d", &sinceID)
|
||||
if sinceID == 0 {
|
||||
h.writeJSON(w, map[string]interface{}{"messages": []interface{}{}})
|
||||
return
|
||||
}
|
||||
msgs, err := h.db.GetNewMessagesSince(userID, sinceID)
|
||||
if err != nil {
|
||||
h.writeError(w, http.StatusInternalServerError, "query failed")
|
||||
return
|
||||
}
|
||||
h.writeJSON(w, map[string]interface{}{"messages": msgs})
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/yourusername/gomail/config"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
)
|
||||
|
||||
// AppHandler serves the main app pages using the shared Renderer.
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/yourusername/gomail/config"
|
||||
goauth "github.com/yourusername/gomail/internal/auth"
|
||||
"github.com/yourusername/gomail/internal/crypto"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/mfa"
|
||||
"github.com/yourusername/gomail/internal/middleware"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
goauth "github.com/ghostersk/gowebmail/internal/auth"
|
||||
"github.com/ghostersk/gowebmail/internal/crypto"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/mfa"
|
||||
"github.com/ghostersk/gowebmail/internal/middleware"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
@@ -155,7 +155,9 @@ func (h *AuthHandler) MFASetupBegin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *AuthHandler) MFASetupConfirm(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
var req struct{ Code string `json:"code"` }
|
||||
var req struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
user, _ := h.db.GetUserByID(userID)
|
||||
@@ -181,7 +183,9 @@ func (h *AuthHandler) MFASetupConfirm(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *AuthHandler) MFADisable(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.GetUserID(r)
|
||||
var req struct{ Code string `json:"code"` }
|
||||
var req struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
user, _ := h.db.GetUserByID(userID)
|
||||
|
||||
@@ -3,9 +3,9 @@ package handlers
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/yourusername/gomail/config"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/syncer"
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/syncer"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yourusername/gomail/config"
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
"github.com/ghostersk/gowebmail/config"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/yourusername/gomail/internal/db"
|
||||
"github.com/yourusername/gomail/internal/email"
|
||||
"github.com/yourusername/gomail/internal/models"
|
||||
"github.com/ghostersk/gowebmail/internal/db"
|
||||
"github.com/ghostersk/gowebmail/internal/email"
|
||||
"github.com/ghostersk/gowebmail/internal/models"
|
||||
)
|
||||
|
||||
// Scheduler coordinates all background sync activity.
|
||||
@@ -24,8 +24,8 @@ type Scheduler struct {
|
||||
wg sync.WaitGroup
|
||||
|
||||
// push channels: accountID -> channel to signal "something changed on server"
|
||||
pushMu sync.Mutex
|
||||
pushCh map[int64]chan struct{}
|
||||
pushMu sync.Mutex
|
||||
pushCh map[int64]chan struct{}
|
||||
}
|
||||
|
||||
// New creates a new Scheduler.
|
||||
@@ -570,5 +570,3 @@ func (s *Scheduler) SyncFolderNow(accountID, folderID int64) (int, error) {
|
||||
|
||||
return s.syncFolder(c, account, folder)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user