222 lines
6.8 KiB
Go
222 lines
6.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/fpmb/server/internal/database"
|
|
"github.com/fpmb/server/internal/models"
|
|
"github.com/gofiber/fiber/v2"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
func ListDocs(c *fiber.Ctx) error {
|
|
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid user"})
|
|
}
|
|
|
|
teamID, err := primitive.ObjectIDFromHex(c.Params("teamId"))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid team ID"})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
if _, err := getTeamRole(ctx, teamID, userID); err != nil {
|
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
|
|
}
|
|
|
|
cursor, err := database.GetCollection("docs").Find(ctx,
|
|
bson.M{"team_id": teamID},
|
|
options.Find().SetSort(bson.M{"updated_at": -1}).SetProjection(bson.M{"content": 0}))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to fetch docs"})
|
|
}
|
|
defer cursor.Close(ctx)
|
|
|
|
var docs []models.Doc
|
|
cursor.All(ctx, &docs)
|
|
if docs == nil {
|
|
docs = []models.Doc{}
|
|
}
|
|
return c.JSON(docs)
|
|
}
|
|
|
|
func CreateDoc(c *fiber.Ctx) error {
|
|
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid user"})
|
|
}
|
|
|
|
teamID, err := primitive.ObjectIDFromHex(c.Params("teamId"))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid team ID"})
|
|
}
|
|
|
|
var body struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
}
|
|
if err := c.BodyParser(&body); err != nil || body.Title == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Title is required"})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
roleFlags, err := getTeamRole(ctx, teamID, userID)
|
|
if err != nil || !hasPermission(roleFlags, RoleEditor) {
|
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Insufficient permissions"})
|
|
}
|
|
|
|
now := time.Now()
|
|
doc := &models.Doc{
|
|
ID: primitive.NewObjectID(),
|
|
TeamID: teamID,
|
|
Title: body.Title,
|
|
Content: body.Content,
|
|
CreatedBy: userID,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
database.GetCollection("docs").InsertOne(ctx, doc)
|
|
|
|
docDir := filepath.Join("../data/teams", teamID.Hex(), "docs")
|
|
if err := os.MkdirAll(docDir, 0755); err != nil {
|
|
log.Printf("CreateDoc: mkdir %s: %v", docDir, err)
|
|
} else {
|
|
content := fmt.Sprintf("# %s\n\n%s", doc.Title, doc.Content)
|
|
if err := os.WriteFile(filepath.Join(docDir, doc.ID.Hex()+".md"), []byte(content), 0644); err != nil {
|
|
log.Printf("CreateDoc: write file: %v", err)
|
|
}
|
|
}
|
|
|
|
return c.Status(fiber.StatusCreated).JSON(doc)
|
|
}
|
|
|
|
func GetDoc(c *fiber.Ctx) error {
|
|
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid user"})
|
|
}
|
|
|
|
docID, err := primitive.ObjectIDFromHex(c.Params("docId"))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid doc ID"})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var doc models.Doc
|
|
if err := database.GetCollection("docs").FindOne(ctx, bson.M{"_id": docID}).Decode(&doc); err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Doc not found"})
|
|
}
|
|
|
|
if _, err := getTeamRole(ctx, doc.TeamID, userID); err != nil {
|
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
|
|
}
|
|
|
|
return c.JSON(doc)
|
|
}
|
|
|
|
func UpdateDoc(c *fiber.Ctx) error {
|
|
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid user"})
|
|
}
|
|
|
|
docID, err := primitive.ObjectIDFromHex(c.Params("docId"))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid doc ID"})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var existing models.Doc
|
|
if err := database.GetCollection("docs").FindOne(ctx, bson.M{"_id": docID}).Decode(&existing); err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Doc not found"})
|
|
}
|
|
|
|
roleFlags, err := getTeamRole(ctx, existing.TeamID, userID)
|
|
if err != nil || !hasPermission(roleFlags, RoleEditor) {
|
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Insufficient permissions"})
|
|
}
|
|
|
|
var body struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
}
|
|
c.BodyParser(&body)
|
|
|
|
update := bson.M{"updated_at": time.Now()}
|
|
if body.Title != "" {
|
|
update["title"] = body.Title
|
|
}
|
|
if body.Content != "" {
|
|
update["content"] = body.Content
|
|
}
|
|
|
|
col := database.GetCollection("docs")
|
|
col.UpdateOne(ctx, bson.M{"_id": docID}, bson.M{"$set": update})
|
|
|
|
var doc models.Doc
|
|
col.FindOne(ctx, bson.M{"_id": docID}).Decode(&doc)
|
|
|
|
docDir := filepath.Join("../data/teams", existing.TeamID.Hex(), "docs")
|
|
if err := os.MkdirAll(docDir, 0755); err != nil {
|
|
log.Printf("UpdateDoc: mkdir %s: %v", docDir, err)
|
|
} else {
|
|
content := fmt.Sprintf("# %s\n\n%s", doc.Title, doc.Content)
|
|
if err := os.WriteFile(filepath.Join(docDir, doc.ID.Hex()+".md"), []byte(content), 0644); err != nil {
|
|
log.Printf("UpdateDoc: write file: %v", err)
|
|
}
|
|
}
|
|
|
|
return c.JSON(doc)
|
|
}
|
|
|
|
func DeleteDoc(c *fiber.Ctx) error {
|
|
userID, err := primitive.ObjectIDFromHex(c.Locals("user_id").(string))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid user"})
|
|
}
|
|
|
|
docID, err := primitive.ObjectIDFromHex(c.Params("docId"))
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid doc ID"})
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var doc models.Doc
|
|
if err := database.GetCollection("docs").FindOne(ctx, bson.M{"_id": docID}).Decode(&doc); err != nil {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Doc not found"})
|
|
}
|
|
|
|
roleFlags, err := getTeamRole(ctx, doc.TeamID, userID)
|
|
if err != nil || !hasPermission(roleFlags, RoleAdmin) {
|
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Insufficient permissions"})
|
|
}
|
|
|
|
database.GetCollection("docs").DeleteOne(ctx, bson.M{"_id": docID})
|
|
|
|
mdPath := filepath.Join("../data/teams", doc.TeamID.Hex(), "docs", docID.Hex()+".md")
|
|
if err := os.Remove(mdPath); err != nil && !os.IsNotExist(err) {
|
|
log.Printf("DeleteDoc: remove file %s: %v", mdPath, err)
|
|
}
|
|
|
|
return c.JSON(fiber.Map{"message": "Doc deleted"})
|
|
}
|