Code Warning Fixes

This commit is contained in:
2026-02-28 06:11:07 +00:00
parent 2e94a84054
commit 3e33a1317a
28 changed files with 1317 additions and 452 deletions

View File

@@ -124,6 +124,10 @@ func main() {
auth.Post("/refresh", handlers.RefreshToken)
auth.Post("/logout", middleware.Protected(), handlers.Logout)
// Public avatar/media routes (no auth needed for <img> tags)
api.Get("/avatar/:userId", handlers.ServePublicAvatar)
api.Get("/team-media/:teamId/:imageType", handlers.ServePublicTeamImage)
users := api.Group("/users", middleware.Protected())
users.Get("/me", handlers.GetMe)
users.Put("/me", handlers.UpdateMe)

BIN
server/fpmb_server Executable file

Binary file not shown.

View File

@@ -184,28 +184,30 @@ func TeamChatWS(c *websocket.Conn) {
}
var incoming struct {
Type string `json:"type"`
Content string `json:"content"`
Type string `json:"type"`
Content string `json:"content"`
MessageID string `json:"message_id,omitempty"`
ReplyTo string `json:"reply_to,omitempty"`
}
if json.Unmarshal(msg, &incoming) != nil {
continue
}
teamOID, err := primitive.ObjectIDFromHex(teamID)
if err != nil {
continue
}
userOID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
continue
}
if incoming.Type == "message" {
content := strings.TrimSpace(incoming.Content)
if content == "" || len(content) > 5000 {
continue
}
teamOID, err := primitive.ObjectIDFromHex(teamID)
if err != nil {
continue
}
userOID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
continue
}
chatMsg := models.ChatMessage{
ID: primitive.NewObjectID(),
TeamID: teamOID,
@@ -215,6 +217,12 @@ func TeamChatWS(c *websocket.Conn) {
CreatedAt: time.Now(),
}
if incoming.ReplyTo != "" {
if replyID, err := primitive.ObjectIDFromHex(incoming.ReplyTo); err == nil {
chatMsg.ReplyTo = &replyID
}
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
_, _ = database.GetCollection("chat_messages").InsertOne(ctx, chatMsg)
cancel()
@@ -224,6 +232,56 @@ func TeamChatWS(c *websocket.Conn) {
"message": chatMsg,
})
room.broadcastAll(outMsg)
} else if incoming.Type == "edit" {
content := strings.TrimSpace(incoming.Content)
if content == "" || len(content) > 5000 || incoming.MessageID == "" {
continue
}
msgID, err := primitive.ObjectIDFromHex(incoming.MessageID)
if err != nil {
continue
}
now := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
res, err := database.GetCollection("chat_messages").UpdateOne(ctx,
bson.M{"_id": msgID, "user_id": userOID, "team_id": teamOID, "deleted": bson.M{"$ne": true}},
bson.M{"$set": bson.M{"content": content, "edited_at": now}},
)
cancel()
if err == nil && res.ModifiedCount > 0 {
outMsg, _ := json.Marshal(map[string]interface{}{
"type": "edit",
"message_id": msgID.Hex(),
"content": content,
"edited_at": now,
})
room.broadcastAll(outMsg)
}
} else if incoming.Type == "delete" {
if incoming.MessageID == "" {
continue
}
msgID, err := primitive.ObjectIDFromHex(incoming.MessageID)
if err != nil {
continue
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
res, err := database.GetCollection("chat_messages").UpdateOne(ctx,
bson.M{"_id": msgID, "user_id": userOID, "team_id": teamOID},
bson.M{"$set": bson.M{"deleted": true, "content": ""}},
)
cancel()
if err == nil && res.ModifiedCount > 0 {
outMsg, _ := json.Marshal(map[string]interface{}{
"type": "delete",
"message_id": msgID.Hex(),
})
room.broadcastAll(outMsg)
}
}
if incoming.Type == "typing" {

View File

@@ -526,12 +526,12 @@ func uploadTeamImage(c *fiber.Ctx, imageType string) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to save image"})
}
imageURL := fmt.Sprintf("/api/teams/%s/%s", teamID.Hex(), imageType)
imageURL := fmt.Sprintf("/api/team-media/%s/%s", teamID.Hex(), imageType)
field := imageType + "_url"
col := database.GetCollection("teams")
if _, err := col.UpdateOne(ctx, bson.M{"_id": teamID}, bson.M{"$set": bson.M{
field: imageURL,
field: imageURL,
"updated_at": time.Now(),
}}); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to update team"})
@@ -600,3 +600,25 @@ func ServeTeamAvatar(c *fiber.Ctx) error {
func ServeTeamBanner(c *fiber.Ctx) error {
return serveTeamImage(c, "banner")
}
func ServePublicTeamImage(c *fiber.Ctx) error {
teamID := c.Params("teamId")
imageType := c.Params("imageType")
if _, err := primitive.ObjectIDFromHex(teamID); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid team ID"})
}
if imageType != "avatar" && imageType != "banner" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid image type"})
}
dir := filepath.Join("../data/teams", teamID)
for ext := range allowedImageExts {
p := filepath.Join(dir, imageType+ext)
if _, err := os.Stat(p); err == nil {
c.Set("Cache-Control", "public, max-age=3600")
return c.SendFile(p)
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Image not found"})
}

View File

@@ -153,7 +153,7 @@ func UploadUserAvatar(c *fiber.Ctx) error {
col := database.GetCollection("users")
if _, err := col.UpdateOne(ctx, bson.M{"_id": userID}, bson.M{"$set": bson.M{
"avatar_url": "/api/users/me/avatar",
"avatar_url": "/api/avatar/" + userID.Hex(),
"updated_at": time.Now(),
}}); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to update user"})
@@ -184,6 +184,24 @@ func ServeUserAvatar(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Avatar not found"})
}
func ServePublicAvatar(c *fiber.Ctx) error {
userID := c.Params("userId")
if _, err := primitive.ObjectIDFromHex(userID); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid user ID"})
}
dir := filepath.Join("../data/users", userID)
for ext := range allowedImageExts {
p := filepath.Join(dir, "avatar"+ext)
if _, err := os.Stat(p); err == nil {
c.Set("Cache-Control", "public, max-age=3600")
return c.SendFile(p)
}
}
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Avatar not found"})
}
func ChangePassword(c *fiber.Ctx) error {
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
if err != nil {

View File

@@ -73,20 +73,22 @@ type Subtask struct {
}
type Card struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
ColumnID primitive.ObjectID `bson:"column_id" json:"column_id"`
ProjectID primitive.ObjectID `bson:"project_id" json:"project_id"`
Title string `bson:"title" json:"title"`
Description string `bson:"description" json:"description"`
Priority string `bson:"priority" json:"priority"`
Color string `bson:"color" json:"color"`
DueDate *time.Time `bson:"due_date,omitempty" json:"due_date,omitempty"`
Assignees []string `bson:"assignees" json:"assignees"`
Subtasks []Subtask `bson:"subtasks" json:"subtasks"`
Position int `bson:"position" json:"position"`
CreatedBy primitive.ObjectID `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
ColumnID primitive.ObjectID `bson:"column_id" json:"column_id"`
ProjectID primitive.ObjectID `bson:"project_id" json:"project_id"`
Title string `bson:"title" json:"title"`
Description string `bson:"description" json:"description"`
Priority string `bson:"priority" json:"priority"`
Color string `bson:"color" json:"color"`
DueDate *time.Time `bson:"due_date,omitempty" json:"due_date,omitempty"`
Assignees []string `bson:"assignees" json:"assignees"`
EstimatedMinutes *int `bson:"estimated_minutes,omitempty" json:"estimated_minutes,omitempty"`
ActualMinutes *int `bson:"actual_minutes,omitempty" json:"actual_minutes,omitempty"`
Subtasks []Subtask `bson:"subtasks" json:"subtasks"`
Position int `bson:"position" json:"position"`
CreatedBy primitive.ObjectID `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}
type Event struct {
@@ -175,10 +177,13 @@ type APIKey struct {
}
type ChatMessage struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
TeamID primitive.ObjectID `bson:"team_id" json:"team_id"`
UserID primitive.ObjectID `bson:"user_id" json:"user_id"`
UserName string `bson:"user_name" json:"user_name"`
Content string `bson:"content" json:"content"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
TeamID primitive.ObjectID `bson:"team_id" json:"team_id"`
UserID primitive.ObjectID `bson:"user_id" json:"user_id"`
UserName string `bson:"user_name" json:"user_name"`
Content string `bson:"content" json:"content"`
ReplyTo *primitive.ObjectID `bson:"reply_to,omitempty" json:"reply_to,omitempty"`
EditedAt *time.Time `bson:"edited_at,omitempty" json:"edited_at,omitempty"`
Deleted bool `bson:"deleted,omitempty" json:"deleted,omitempty"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
}