summaryrefslogtreecommitdiff
path: root/shrine
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-13 18:25:44 +0530
committerBobby <[email protected]>2026-03-13 18:25:44 +0530
commit344d02a7feddefb5c08f88dbe5f3a3f7e7da385f (patch)
tree94deed23d82d7f868721cc00b5550f5c27e8b8f7 /shrine
parent9f808807a557fc10a38a44cb52be6bfcdfda68b2 (diff)
downloadpagoda-main.tar.xz
pagoda-main.zip
feat: add letters feature with detail view and listingHEADmain
- Introduced new routes for letters and their details. - Created pages for displaying letter details and listing letters. - Added new types for letters, including participants, messages, and attachments. - Implemented API calls for fetching letters and managing messages (reply, edit, delete). - Enhanced stats to include unread letters and pending districts for staff users. - Updated styles for letters and their components. - Added privacy settings for letters (public and friends). - Modified user model to include letter privacy settings. - Improved error handling and user feedback in the UI.
Diffstat (limited to 'shrine')
-rw-r--r--shrine/controllers/stats.go5
-rw-r--r--shrine/enums/privacy.go8
-rw-r--r--shrine/messages/letter.go1
-rw-r--r--shrine/messages/warning.go3
-rw-r--r--shrine/models/user.go22
-rw-r--r--shrine/repositories/letter.go11
-rw-r--r--shrine/repositories/user.go4
-rw-r--r--shrine/router/council.go1
-rw-r--r--shrine/services/letter.go3
-rw-r--r--shrine/services/stats.go14
-rw-r--r--shrine/services/warning.go2
-rw-r--r--shrine/types/user/user.go10
12 files changed, 66 insertions, 18 deletions
diff --git a/shrine/controllers/stats.go b/shrine/controllers/stats.go
index bf31c2e..cc2bf93 100644
--- a/shrine/controllers/stats.go
+++ b/shrine/controllers/stats.go
@@ -2,11 +2,14 @@ package controllers
import (
"shrine/services"
+ "shrine/utils/auth"
"shrine/utils/shortcuts"
"github.com/gofiber/fiber/v2"
)
func StatsController(context *fiber.Ctx) error {
- return shortcuts.Success(context, services.GetStats())
+ auth.IsAuthenticated(context)
+ citizen := auth.GetUser(context)
+ return shortcuts.Success(context, services.GetStats(citizen))
} \ No newline at end of file
diff --git a/shrine/enums/privacy.go b/shrine/enums/privacy.go
new file mode 100644
index 0000000..99739f3
--- /dev/null
+++ b/shrine/enums/privacy.go
@@ -0,0 +1,8 @@
+package enums
+
+type LetterPrivacy string
+
+const (
+ LetterPrivacyPublic LetterPrivacy = "public"
+ LetterPrivacyFriends LetterPrivacy = "friends"
+) \ No newline at end of file
diff --git a/shrine/messages/letter.go b/shrine/messages/letter.go
index 49d01f9..181f00d 100644
--- a/shrine/messages/letter.go
+++ b/shrine/messages/letter.go
@@ -29,6 +29,7 @@ const (
FailedUploadFile = "Failed to upload file."
LetterRenamed = "Letter renamed."
LeftConversation = "You have left the conversation."
+ RecipientNotAcceptingLetters = "User '%s' is not accepting letters."
RecipientNotFound = "User '%s' not found."
ParticipantRemoved = "%s has been removed."
FileExceedsMaxSize = "File exceeds the maximum size of %d MB."
diff --git a/shrine/messages/warning.go b/shrine/messages/warning.go
index d29a34b..748844f 100644
--- a/shrine/messages/warning.go
+++ b/shrine/messages/warning.go
@@ -8,6 +8,7 @@ const (
WarningMessageRequired = "Warning message is required."
WarningNotFound = "Warning not found."
WarningAlreadyInactive = "Warning is already inactive."
- FailedCreateWarning = "Failed to create warning."
+ FailedCreateWarning = "Failed to create warning."
+ FailedCreateWarningDetailed = "Failed to create warning for %s: %v."
FailedDeactivateWarn = "Failed to deactivate warning."
) \ No newline at end of file
diff --git a/shrine/models/user.go b/shrine/models/user.go
index 503c61a..8846aa4 100644
--- a/shrine/models/user.go
+++ b/shrine/models/user.go
@@ -45,6 +45,7 @@ type User struct {
DisabledBy *uint `gorm:"index"`
DisabledUntil *time.Time
WarningCount uint `gorm:"not null;default:0"`
+ LetterPrivacy enums.LetterPrivacy `gorm:"size:20;not null;default:public"`
LastSeenAt *time.Time
IP string `gorm:"size:45"`
}
@@ -194,20 +195,27 @@ func (self *User) BeforeCreate(tx *gorm.DB) error {
}
func (self *User) BeforeUpdate(tx *gorm.DB) error {
- if !validators.IsValidEmail(self.Email) {
- return errors.New(messages.InvalidEmail)
+ if self.Email == "" && self.DisplayName == "" {
+ return nil
}
- if len(strings.TrimSpace(self.DisplayName)) < 1 || len(strings.TrimSpace(self.DisplayName)) > 50 {
- return errors.New(messages.InvalidDisplayName)
+ if self.Email != "" {
+ if !validators.IsValidEmail(self.Email) {
+ return errors.New(messages.InvalidEmail)
+ }
+ self.Email = strings.ToLower(strings.TrimSpace(self.Email))
+ }
+
+ if self.DisplayName != "" {
+ if len(strings.TrimSpace(self.DisplayName)) < 1 || len(strings.TrimSpace(self.DisplayName)) > 50 {
+ return errors.New(messages.InvalidDisplayName)
+ }
+ self.DisplayName = strings.TrimSpace(self.DisplayName)
}
if self.Jade > validators.MaxJade {
return errors.New(messages.JadeExceedsMax)
}
- self.Email = strings.ToLower(strings.TrimSpace(self.Email))
- self.DisplayName = strings.TrimSpace(self.DisplayName)
-
return nil
} \ No newline at end of file
diff --git a/shrine/repositories/letter.go b/shrine/repositories/letter.go
index b9c81aa..a52f5bc 100644
--- a/shrine/repositories/letter.go
+++ b/shrine/repositories/letter.go
@@ -118,6 +118,17 @@ func GetLetterMessages(letterID uint, p meta.Pagination) ([]models.LetterMessage
return messages, total
}
+func CountUnreadLetters(userID uint) int64 {
+ var count int64
+ database.DB.Model(&models.LetterParticipant{}).
+ Joins("JOIN letters ON letters.id = letter_participants.letter_id").
+ Joins("JOIN letter_messages ON letter_messages.letter_id = letters.id AND letter_messages.deleted_at IS NULL").
+ Where("letter_participants.user_id = ? AND (letter_participants.last_read_at IS NULL OR letter_messages.created_at > letter_participants.last_read_at)", userID).
+ Distinct("letter_participants.letter_id").
+ Count(&count)
+ return count
+}
+
func ListLettersForUser(userID uint, p meta.Pagination) ([]models.Letter, int64) {
var letters []models.Letter
var total int64
diff --git a/shrine/repositories/user.go b/shrine/repositories/user.go
index 73973da..39b75b5 100644
--- a/shrine/repositories/user.go
+++ b/shrine/repositories/user.go
@@ -49,13 +49,13 @@ func UpdateLastSeen(user *models.User) {
func CountCitizens() int64 {
var count int64
- database.DB.Model(&models.User{}).Where("email_verified = ?", true).Count(&count)
+ database.DB.Model(&models.User{}).Where("email_verified = ? AND account_banned = ? AND account_disabled = ?", true, false, false).Count(&count)
return count
}
func CountOnline() int64 {
var count int64
- database.DB.Model(&models.User{}).Where("last_seen_at > ?", time.Now().Add(-5*time.Minute)).Count(&count)
+ database.DB.Model(&models.User{}).Where("last_seen_at > ? AND account_banned = ? AND account_disabled = ?", time.Now().Add(-5*time.Minute), false, false).Count(&count)
return count
}
diff --git a/shrine/router/council.go b/shrine/router/council.go
index 9603b53..205cd70 100644
--- a/shrine/router/council.go
+++ b/shrine/router/council.go
@@ -44,5 +44,4 @@ func init() {
urls.Path(enums.POST, "/districts/sites/:ref/review", auth.RequireStaff(controllers.ReviewSiteController), "districtreview")
urls.Path(enums.GET, "/districts/sites", auth.RequireStaff(controllers.ListAdminSitesController), "districtsites")
urls.Path(enums.PATCH, "/districts/sites/:ref", auth.RequireStaff(controllers.EditSiteController), "districtedit")
- urls.Path(enums.GET, "/districts/pending", auth.RequireStaff(controllers.CountPendingSitesController), "districtpending")
} \ No newline at end of file
diff --git a/shrine/services/letter.go b/shrine/services/letter.go
index 20a4f93..c7ea741 100644
--- a/shrine/services/letter.go
+++ b/shrine/services/letter.go
@@ -75,6 +75,9 @@ func CreateLetter(userID uint, request letter.CreateRequest) (*common.MessageRes
if recipient.ID == userID {
continue
}
+ if recipient.LetterPrivacy == enums.LetterPrivacyFriends {
+ return nil, fail(enums.BadRequest, fmt.Sprintf(messages.RecipientNotAcceptingLetters, username))
+ }
recipientIDs = append(recipientIDs, recipient.ID)
}
diff --git a/shrine/services/stats.go b/shrine/services/stats.go
index 2e31c02..aa2eaab 100644
--- a/shrine/services/stats.go
+++ b/shrine/services/stats.go
@@ -1,15 +1,25 @@
package services
import (
+ "shrine/models"
"shrine/repositories"
"shrine/types/user"
)
-func GetStats() user.StatsResponse {
- return user.StatsResponse{
+func GetStats(citizen *models.User) user.StatsResponse {
+ response := user.StatsResponse{
Citizens: repositories.CountCitizens(),
Online: repositories.CountOnline(),
NewestCitizens: buildCitizenSummaries(repositories.NewestCitizens(5)),
OnlineCitizens: buildCitizenSummaries(repositories.OnlineCitizens(10)),
}
+
+ if citizen != nil {
+ response.UnreadLetters = repositories.CountUnreadLetters(citizen.ID)
+ if citizen.IsStaff() {
+ response.PendingDistricts = repositories.CountPendingDistrictSites()
+ }
+ }
+
+ return response
} \ No newline at end of file
diff --git a/shrine/services/warning.go b/shrine/services/warning.go
index 0b27591..553c7fb 100644
--- a/shrine/services/warning.go
+++ b/shrine/services/warning.go
@@ -10,6 +10,7 @@ import (
"shrine/types/hypertext"
"shrine/types/warning"
"shrine/utils/auth"
+ "shrine/utils/logger"
"shrine/utils/meta"
"shrine/utils/sanitize"
"strings"
@@ -32,6 +33,7 @@ func WarnUser(admin *models.User, target *models.User, request warning.WarnReque
record, err := repositories.CreateWarning(admin.ID, target.ID, title, sanitizedMessage)
if err != nil {
+ logger.Errorf("Warnings", messages.FailedCreateWarningDetailed, target.Username, err)
return nil, fail(enums.Internal, messages.FailedCreateWarning)
}
diff --git a/shrine/types/user/user.go b/shrine/types/user/user.go
index f28f2f3..7e33d8c 100644
--- a/shrine/types/user/user.go
+++ b/shrine/types/user/user.go
@@ -41,8 +41,10 @@ type AdminUserResponse struct {
}
type StatsResponse struct {
- Citizens int64 `json:"citizens"`
- Online int64 `json:"online"`
- NewestCitizens []CitizenSummaryResponse `json:"newest_citizens"`
- OnlineCitizens []CitizenSummaryResponse `json:"online_citizens"`
+ Citizens int64 `json:"citizens"`
+ Online int64 `json:"online"`
+ UnreadLetters int64 `json:"unread_letters"`
+ PendingDistricts int64 `json:"pending_districts"`
+ NewestCitizens []CitizenSummaryResponse `json:"newest_citizens"`
+ OnlineCitizens []CitizenSummaryResponse `json:"online_citizens"`
} \ No newline at end of file