remove edit elements if user is not owner
This commit is contained in:
@@ -30,6 +30,17 @@ func respondWithError(c *gin.Context, status int, message string) {
|
|||||||
c.JSON(status, gin.H{"error": message})
|
c.JSON(status, gin.H{"error": message})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkCharacterOwnership verifies that the logged-in user owns the character
|
||||||
|
func checkCharacterOwnership(c *gin.Context, character *models.Char) bool {
|
||||||
|
userID := c.GetUint("userID")
|
||||||
|
if character.UserID != userID {
|
||||||
|
logger.Warn("Unauthorized access attempt: user %d tried to modify character %d owned by user %d", userID, character.ID, character.UserID)
|
||||||
|
respondWithError(c, http.StatusForbidden, "You are not authorized to modify this character")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func ListCharacters(c *gin.Context) {
|
func ListCharacters(c *gin.Context) {
|
||||||
logger.Debug("ListCharacters aufgerufen")
|
logger.Debug("ListCharacters aufgerufen")
|
||||||
|
|
||||||
@@ -109,6 +120,11 @@ func UpdateCharacter(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Store the original ID to preserve it
|
// Store the original ID to preserve it
|
||||||
originalID := character.ID
|
originalID := character.ID
|
||||||
originalGameSystem := character.GameSystem
|
originalGameSystem := character.GameSystem
|
||||||
@@ -141,6 +157,12 @@ func DeleteCharacter(c *gin.Context) {
|
|||||||
respondWithError(c, http.StatusNotFound, "Character not found")
|
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = character.Delete()
|
err = character.Delete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "Failed to delete character")
|
respondWithError(c, http.StatusInternalServerError, "Failed to delete character")
|
||||||
@@ -275,6 +297,11 @@ func UpdateCharacterExperience(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Parse Request
|
// Parse Request
|
||||||
var req UpdateExperienceRequest
|
var req UpdateExperienceRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -365,6 +392,11 @@ func UpdateCharacterWealth(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Parse Request
|
// Parse Request
|
||||||
var req UpdateWealthRequest
|
var req UpdateWealthRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -575,6 +607,11 @@ func LearnSkill(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Verwende gsmaster.LernCostRequest direkt
|
// Verwende gsmaster.LernCostRequest direkt
|
||||||
var request gsmaster.LernCostRequest
|
var request gsmaster.LernCostRequest
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
@@ -1060,6 +1097,11 @@ func ImproveSkill(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, char) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Skill validieren und Level ermitteln
|
// 2. Skill validieren und Level ermitteln
|
||||||
characterClass, skillInfo, currentLevel, err := validateSkillForImprovement(char, &request)
|
characterClass, skillInfo, currentLevel, err := validateSkillForImprovement(char, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1233,6 +1275,18 @@ func LearnSpell(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
charID := uint(charIDInt)
|
charID := uint(charIDInt)
|
||||||
|
|
||||||
|
// Load character to check ownership
|
||||||
|
var character models.Char
|
||||||
|
if err := character.FirstID(char_ID); err != nil {
|
||||||
|
respondWithError(c, http.StatusNotFound, "Charakter nicht gefunden")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var lernRequest gsmaster.LernCostRequest
|
var lernRequest gsmaster.LernCostRequest
|
||||||
if err := c.ShouldBindJSON(&lernRequest); err != nil {
|
if err := c.ShouldBindJSON(&lernRequest); err != nil {
|
||||||
respondWithError(c, http.StatusBadRequest, "Ungültige Anfrageparameter: "+err.Error())
|
respondWithError(c, http.StatusBadRequest, "Ungültige Anfrageparameter: "+err.Error())
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ func UpdateCharacterImage(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var request ImageUpdateRequest
|
var request ImageUpdateRequest
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
logger.Error("Invalid request data: %s", err.Error())
|
logger.Error("Invalid request data: %s", err.Error())
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ func UpdatePracticePoints(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Request-Parameter abrufen
|
// Request-Parameter abrufen
|
||||||
var practicePoints []PracticePointResponse
|
var practicePoints []PracticePointResponse
|
||||||
if err := c.ShouldBindJSON(&practicePoints); err != nil {
|
if err := c.ShouldBindJSON(&practicePoints); err != nil {
|
||||||
@@ -120,6 +125,11 @@ func AddPracticePoint(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Request-Parameter abrufen
|
// Request-Parameter abrufen
|
||||||
type AddPPRequest struct {
|
type AddPPRequest struct {
|
||||||
SkillName string `json:"skill_name" binding:"required"`
|
SkillName string `json:"skill_name" binding:"required"`
|
||||||
@@ -218,6 +228,11 @@ func UsePracticePoint(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkCharacterOwnership(c, &character) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Request-Parameter abrufen
|
// Request-Parameter abrufen
|
||||||
type UsePPRequest struct {
|
type UsePPRequest struct {
|
||||||
SkillName string `json:"skill_name" binding:"required"`
|
SkillName string `json:"skill_name" binding:"required"`
|
||||||
|
|||||||
@@ -19,6 +19,21 @@ func respondWithError(c *gin.Context, status int, message string) {
|
|||||||
c.JSON(status, gin.H{"error": message})
|
c.JSON(status, gin.H{"error": message})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkEquipmentOwnership verifies that the logged-in user owns the equipment's character
|
||||||
|
func checkEquipmentOwnership(c *gin.Context, characterID uint) bool {
|
||||||
|
userID := c.GetUint("userID")
|
||||||
|
var character models.Char
|
||||||
|
if err := database.DB.Select("id", "user_id").First(&character, characterID).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusNotFound, "Character not found")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if character.UserID != userID {
|
||||||
|
respondWithError(c, http.StatusForbidden, "You are not authorized to modify this character's equipment")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func CreateAusruestung(c *gin.Context) {
|
func CreateAusruestung(c *gin.Context) {
|
||||||
var ausruestung models.EqAusruestung
|
var ausruestung models.EqAusruestung
|
||||||
if err := c.ShouldBindJSON(&ausruestung); err != nil {
|
if err := c.ShouldBindJSON(&ausruestung); err != nil {
|
||||||
@@ -26,6 +41,11 @@ func CreateAusruestung(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, ausruestung.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := database.DB.Create(&ausruestung).Error; err != nil {
|
if err := database.DB.Create(&ausruestung).Error; err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "Failed to create Ausruestung")
|
respondWithError(c, http.StatusInternalServerError, "Failed to create Ausruestung")
|
||||||
return
|
return
|
||||||
@@ -55,6 +75,11 @@ func UpdateAusruestung(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, ausruestung.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&ausruestung); err != nil {
|
if err := c.ShouldBindJSON(&ausruestung); err != nil {
|
||||||
respondWithError(c, http.StatusBadRequest, err.Error())
|
respondWithError(c, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
@@ -70,7 +95,19 @@ func UpdateAusruestung(c *gin.Context) {
|
|||||||
|
|
||||||
func DeleteAusruestung(c *gin.Context) {
|
func DeleteAusruestung(c *gin.Context) {
|
||||||
ausruestungID := c.Param("ausruestung_id")
|
ausruestungID := c.Param("ausruestung_id")
|
||||||
if err := database.DB.Delete(&models.EqAusruestung{}, ausruestungID).Error; err != nil {
|
|
||||||
|
var ausruestung models.EqAusruestung
|
||||||
|
if err := database.DB.First(&ausruestung, ausruestungID).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusNotFound, "Ausruestung not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, ausruestung.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Delete(&ausruestung).Error; err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "Failed to delete Ausruestung")
|
respondWithError(c, http.StatusInternalServerError, "Failed to delete Ausruestung")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -89,6 +126,11 @@ func CreateWaffe(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, waffe.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := database.DB.Create(&waffe).Error; err != nil {
|
if err := database.DB.Create(&waffe).Error; err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "Failed to create Waffe")
|
respondWithError(c, http.StatusInternalServerError, "Failed to create Waffe")
|
||||||
return
|
return
|
||||||
@@ -118,6 +160,11 @@ func UpdateWaffe(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, waffe.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&waffe); err != nil {
|
if err := c.ShouldBindJSON(&waffe); err != nil {
|
||||||
respondWithError(c, http.StatusBadRequest, err.Error())
|
respondWithError(c, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
@@ -133,7 +180,19 @@ func UpdateWaffe(c *gin.Context) {
|
|||||||
|
|
||||||
func DeleteWaffe(c *gin.Context) {
|
func DeleteWaffe(c *gin.Context) {
|
||||||
waffeID := c.Param("waffe_id")
|
waffeID := c.Param("waffe_id")
|
||||||
if err := database.DB.Delete(&models.EqWaffe{}, waffeID).Error; err != nil {
|
|
||||||
|
var waffe models.EqWaffe
|
||||||
|
if err := database.DB.First(&waffe, waffeID).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusNotFound, "Waffe not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if !checkEquipmentOwnership(c, waffe.CharacterID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Delete(&waffe).Error; err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "Failed to delete Waffe")
|
respondWithError(c, http.StatusInternalServerError, "Failed to delete Waffe")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-21
@@ -8,50 +8,52 @@ import (
|
|||||||
|
|
||||||
func RegisterRoutes(r *gin.RouterGroup) {
|
func RegisterRoutes(r *gin.RouterGroup) {
|
||||||
maintGrp := r.Group("/maintenance")
|
maintGrp := r.Group("/maintenance")
|
||||||
|
|
||||||
|
maintGrp.GET("", GetMasterData)
|
||||||
|
maintGrp.GET("/skills", GetMDSkills)
|
||||||
|
maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/skills/:id", GetMDSkill)
|
||||||
|
maintGrp.GET("/skills-enhanced/:id", GetEnhancedMDSkill) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/weaponskills", GetMDWeaponSkills)
|
||||||
|
maintGrp.GET("/weaponskills-enhanced", GetEnhancedMDWeaponSkills) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/weaponskills/:id", GetMDWeaponSkill)
|
||||||
|
maintGrp.GET("/weaponskills-enhanced/:id", GetEnhancedMDWeaponSkill) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/spells", GetMDSpells)
|
||||||
|
maintGrp.GET("/spells-enhanced", GetEnhancedMDSpells) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/spells/:id", GetMDSpell)
|
||||||
|
maintGrp.GET("/spells-enhanced/:id", GetEnhancedMDSpell) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/equipment", GetMDEquipments)
|
||||||
|
maintGrp.GET("/equipment-enhanced", GetEnhancedMDEquipment) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/equipment/:id", GetMDEquipment)
|
||||||
|
maintGrp.GET("/equipment-enhanced/:id", GetEnhancedMDEquipmentItem) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/weapons", GetMDWeapons)
|
||||||
|
maintGrp.GET("/weapons-enhanced", GetEnhancedMDWeapons) // New enhanced endpoint
|
||||||
|
maintGrp.GET("/weapons/:id", GetMDWeapon)
|
||||||
|
maintGrp.GET("/weapons-enhanced/:id", GetEnhancedMDWeapon) // New enhanced endpoint
|
||||||
|
|
||||||
maintGrp.Use(user.RequireMaintainer())
|
maintGrp.Use(user.RequireMaintainer())
|
||||||
{
|
{
|
||||||
maintGrp.GET("", GetMasterData)
|
|
||||||
maintGrp.GET("/skills", GetMDSkills)
|
|
||||||
maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint
|
|
||||||
maintGrp.POST("/skills-enhanced", CreateEnhancedMDSkill) // Create new skill
|
maintGrp.POST("/skills-enhanced", CreateEnhancedMDSkill) // Create new skill
|
||||||
maintGrp.GET("/skills/:id", GetMDSkill)
|
|
||||||
maintGrp.GET("/skills-enhanced/:id", GetEnhancedMDSkill) // New enhanced endpoint
|
|
||||||
maintGrp.PUT("/skills/:id", UpdateMDSkill)
|
maintGrp.PUT("/skills/:id", UpdateMDSkill)
|
||||||
maintGrp.PUT("/skills-enhanced/:id", UpdateEnhancedMDSkill) // New enhanced endpoint
|
maintGrp.PUT("/skills-enhanced/:id", UpdateEnhancedMDSkill) // New enhanced endpoint
|
||||||
maintGrp.POST("/skills", AddSkill)
|
maintGrp.POST("/skills", AddSkill)
|
||||||
maintGrp.DELETE("/skills/:id", DeleteMDSkill)
|
maintGrp.DELETE("/skills/:id", DeleteMDSkill)
|
||||||
|
|
||||||
maintGrp.GET("/weaponskills", GetMDWeaponSkills)
|
|
||||||
maintGrp.GET("/weaponskills-enhanced", GetEnhancedMDWeaponSkills) // New enhanced endpoint
|
|
||||||
maintGrp.GET("/weaponskills/:id", GetMDWeaponSkill)
|
|
||||||
maintGrp.GET("/weaponskills-enhanced/:id", GetEnhancedMDWeaponSkill) // New enhanced endpoint
|
|
||||||
maintGrp.PUT("/weaponskills/:id", UpdateMDWeaponSkill)
|
maintGrp.PUT("/weaponskills/:id", UpdateMDWeaponSkill)
|
||||||
maintGrp.PUT("/weaponskills-enhanced/:id", UpdateEnhancedMDWeaponSkill) // New enhanced endpoint
|
maintGrp.PUT("/weaponskills-enhanced/:id", UpdateEnhancedMDWeaponSkill) // New enhanced endpoint
|
||||||
maintGrp.POST("/weaponskills", AddWeaponSkill)
|
maintGrp.POST("/weaponskills", AddWeaponSkill)
|
||||||
maintGrp.DELETE("/weaponskills/:id", DeleteMDWeaponSkill)
|
maintGrp.DELETE("/weaponskills/:id", DeleteMDWeaponSkill)
|
||||||
|
|
||||||
maintGrp.GET("/spells", GetMDSpells)
|
|
||||||
maintGrp.GET("/spells-enhanced", GetEnhancedMDSpells) // New enhanced endpoint
|
|
||||||
maintGrp.GET("/spells/:id", GetMDSpell)
|
|
||||||
maintGrp.GET("/spells-enhanced/:id", GetEnhancedMDSpell) // New enhanced endpoint
|
|
||||||
maintGrp.PUT("/spells/:id", UpdateMDSpell)
|
maintGrp.PUT("/spells/:id", UpdateMDSpell)
|
||||||
maintGrp.PUT("/spells-enhanced/:id", UpdateEnhancedMDSpell) // New enhanced endpoint
|
maintGrp.PUT("/spells-enhanced/:id", UpdateEnhancedMDSpell) // New enhanced endpoint
|
||||||
maintGrp.POST("/spells", AddSpell)
|
maintGrp.POST("/spells", AddSpell)
|
||||||
maintGrp.DELETE("/spells/:id", DeleteMDSpell)
|
maintGrp.DELETE("/spells/:id", DeleteMDSpell)
|
||||||
|
|
||||||
maintGrp.GET("/equipment", GetMDEquipments)
|
|
||||||
maintGrp.GET("/equipment-enhanced", GetEnhancedMDEquipment) // New enhanced endpoint
|
|
||||||
maintGrp.GET("/equipment/:id", GetMDEquipment)
|
|
||||||
maintGrp.GET("/equipment-enhanced/:id", GetEnhancedMDEquipmentItem) // New enhanced endpoint
|
|
||||||
maintGrp.PUT("/equipment/:id", UpdateMDEquipment)
|
maintGrp.PUT("/equipment/:id", UpdateMDEquipment)
|
||||||
maintGrp.PUT("/equipment-enhanced/:id", UpdateEnhancedMDEquipmentItem) // New enhanced endpoint
|
maintGrp.PUT("/equipment-enhanced/:id", UpdateEnhancedMDEquipmentItem) // New enhanced endpoint
|
||||||
maintGrp.POST("/equipment", AddEquipment)
|
maintGrp.POST("/equipment", AddEquipment)
|
||||||
maintGrp.DELETE("/equipment/:id", DeleteMDEquipment)
|
maintGrp.DELETE("/equipment/:id", DeleteMDEquipment)
|
||||||
|
|
||||||
maintGrp.GET("/weapons", GetMDWeapons)
|
|
||||||
maintGrp.GET("/weapons-enhanced", GetEnhancedMDWeapons) // New enhanced endpoint
|
|
||||||
maintGrp.GET("/weapons/:id", GetMDWeapon)
|
|
||||||
maintGrp.GET("/weapons-enhanced/:id", GetEnhancedMDWeapon) // New enhanced endpoint
|
|
||||||
maintGrp.PUT("/weapons/:id", UpdateMDWeapon)
|
maintGrp.PUT("/weapons/:id", UpdateMDWeapon)
|
||||||
maintGrp.PUT("/weapons-enhanced/:id", UpdateEnhancedMDWeapon) // New enhanced endpoint
|
maintGrp.PUT("/weapons-enhanced/:id", UpdateEnhancedMDWeapon) // New enhanced endpoint
|
||||||
maintGrp.POST("/weapons", AddWeapon)
|
maintGrp.POST("/weapons", AddWeapon)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.title')">
|
<button @click="showExportDialog = true" class="export-button-small" :title="$t('export.title')">
|
||||||
📄
|
📄
|
||||||
</button>
|
</button>
|
||||||
<button @click="showVisibilityDialog = true" class="export-button-small" :title="$t('visibility.title')">
|
<button v-if="isOwner" @click="showVisibilityDialog = true" class="export-button-small" :title="$t('visibility.title')">
|
||||||
{{ character.public ? '🌐' : '🔒' }}
|
{{ character.public ? '🌐' : '🔒' }}
|
||||||
</button>
|
</button>
|
||||||
<h2>{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})</h2>
|
<h2>{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})</h2>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
<!-- Submenu Content -->
|
<!-- Submenu Content -->
|
||||||
<!-- <div class="character-aspect"> -->
|
<!-- <div class="character-aspect"> -->
|
||||||
<component :is="currentView" :character="character" @character-updated="refreshCharacter"/>
|
<component :is="currentView" :character="character" :isOwner="isOwner" @character-updated="refreshCharacter"/>
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
|
|
||||||
<!-- Submenu -->
|
<!-- Submenu -->
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
|
import { useUserStore } from '../stores/userStore'
|
||||||
import ExportDialog from "./ExportDialog.vue";
|
import ExportDialog from "./ExportDialog.vue";
|
||||||
import VisibilityDialog from "./VisibilityDialog.vue";
|
import VisibilityDialog from "./VisibilityDialog.vue";
|
||||||
import DatasheetView from "./DatasheetView.vue"; // Component for character stats
|
import DatasheetView from "./DatasheetView.vue"; // Component for character stats
|
||||||
@@ -111,6 +112,12 @@ export default {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isOwner() {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
return userStore.currentUser && this.character.user_id === userStore.currentUser.id
|
||||||
|
}
|
||||||
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
const response = await API.get(`/api/characters/${this.id}`, {
|
const response = await API.get(`/api/characters/${this.id}`, {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cd-view">
|
<div class="cd-view">
|
||||||
<p>Are you sure you want to delete {{ character.name }}?</p>
|
<div v-if="!isOwner" class="error-message">
|
||||||
<button @click="deleteCharacter" class="btn btn-danger">Yes, Delete</button>
|
<p>{{ $t('deleteChar.notAuthorized') }}</p>
|
||||||
<button @click="$emit('cancel')" class="btn btn-secondary">Cancel</button>
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p>Are you sure you want to delete {{ character.name }}?</p>
|
||||||
|
<button @click="deleteCharacter" class="btn btn-danger">Yes, Delete</button>
|
||||||
|
<button @click="$emit('cancel')" class="btn btn-secondary">Cancel</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -16,6 +21,10 @@ export default {
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="fullwidth-container">
|
<div class="fullwidth-container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<h2>{{ $t('EquipmentView') }}</h2>
|
<h2>{{ $t('EquipmentView') }}</h2>
|
||||||
<button @click="openAddEquipmentDialog" class="btn-add-equipment">
|
<button v-if="isOwner" @click="openAddEquipmentDialog" class="btn-add-equipment">
|
||||||
{{ $t('equipment.add') }}
|
{{ $t('equipment.add') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<th>{{ $t('equipment.amount') }}</th>
|
<th>{{ $t('equipment.amount') }}</th>
|
||||||
<th>{{ $t('equipment.contained_in') }}</th>
|
<th>{{ $t('equipment.contained_in') }}</th>
|
||||||
<th>{{ $t('equipment.bonus') }}</th>
|
<th>{{ $t('equipment.bonus') }}</th>
|
||||||
<th>{{ $t('equipment.actions') }}</th>
|
<th v-if="isOwner">{{ $t('equipment.actions') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<td>{{ equipment.anzahl || '-' }}</td>
|
<td>{{ equipment.anzahl || '-' }}</td>
|
||||||
<td>{{ equipment.beinhaltet_in || '-' }}</td>
|
<td>{{ equipment.beinhaltet_in || '-' }}</td>
|
||||||
<td>{{ equipment.bonus || '-' }}</td>
|
<td>{{ equipment.bonus || '-' }}</td>
|
||||||
<td class="action-cell">
|
<td v-if="isOwner" class="action-cell">
|
||||||
<button @click="deleteEquipment(equipment)" class="btn-delete" title="Löschen">
|
<button @click="deleteEquipment(equipment)" class="btn-delete" title="Löschen">
|
||||||
🗑️
|
🗑️
|
||||||
</button>
|
</button>
|
||||||
@@ -126,6 +126,24 @@
|
|||||||
.cd-table th {
|
.cd-table th {
|
||||||
background-color: #1da766;
|
background-color: #1da766;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix modal footer visibility */
|
||||||
|
.modal-fullscreen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: calc(100vh - 50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-fullscreen .modal-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-fullscreen .modal-header,
|
||||||
|
.modal-fullscreen .modal-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -137,6 +155,10 @@ name: "EquipmentView",
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="resource-amount">{{ character.erfahrungsschatz?.ep || 0 }} EP</div>
|
<div class="resource-amount">{{ character.erfahrungsschatz?.ep || 0 }} EP</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row control-row">
|
<div v-if="isOwner" class="form-row control-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input
|
<input
|
||||||
v-model.number="experienceAmount"
|
v-model.number="experienceAmount"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<div class="resource-amount">{{ character.vermoegen?.goldstücke || 0 }} GS</div>
|
<div class="resource-amount">{{ character.vermoegen?.goldstücke || 0 }} GS</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row control-row">
|
<div v-if="isOwner" class="form-row control-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input
|
<input
|
||||||
v-model.number="goldAmount"
|
v-model.number="goldAmount"
|
||||||
@@ -147,6 +147,10 @@ export default {
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="table-wrapper-left">
|
<div class="table-wrapper-left">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<!-- Lernmodus Toggle Button -->
|
<!-- Lernmodus Toggle Button -->
|
||||||
<div class="learning-mode-controls">
|
<div v-if="isOwner" class="learning-mode-controls">
|
||||||
<!-- Ressourcen-Anzeige (nur sichtbar wenn Lernmodus aktiv) -->
|
<!-- Ressourcen-Anzeige (nur sichtbar wenn Lernmodus aktiv) -->
|
||||||
<div v-if="learningMode" class="resources-display">
|
<div v-if="learningMode" class="resources-display">
|
||||||
<div class="resource-item">
|
<div class="resource-item">
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<td>{{ skill.fertigkeitswert || '-' }}</td>
|
<td>{{ skill.fertigkeitswert || '-' }}</td>
|
||||||
<td>{{ skill.bonus || '0' }}</td>
|
<td>{{ skill.bonus || '0' }}</td>
|
||||||
<td class="pp-cell">
|
<td class="pp-cell">
|
||||||
<div class="pp-container">
|
<div v-if="isOwner" class="pp-container">
|
||||||
<button
|
<button
|
||||||
@click="decreasePP(skill)"
|
@click="decreasePP(skill)"
|
||||||
class="pp-btn pp-btn-minus"
|
class="pp-btn pp-btn-minus"
|
||||||
@@ -86,6 +86,7 @@
|
|||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<span v-else>{{ skill.pp || '0' }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ skill.bemerkung || '-' }}</td>
|
<td>{{ skill.bemerkung || '-' }}</td>
|
||||||
<td v-if="learningMode" class="action-cell">
|
<td v-if="learningMode" class="action-cell">
|
||||||
@@ -109,7 +110,7 @@
|
|||||||
<td>{{ skill.fertigkeitswert || '-' }}</td>
|
<td>{{ skill.fertigkeitswert || '-' }}</td>
|
||||||
<td>{{ skill.bonus || '0' }}</td>
|
<td>{{ skill.bonus || '0' }}</td>
|
||||||
<td class="pp-cell">
|
<td class="pp-cell">
|
||||||
<div class="pp-container">
|
<div v-if="isOwner" class="pp-container">
|
||||||
<button
|
<button
|
||||||
@click="decreaseWeaponPP(skill)"
|
@click="decreaseWeaponPP(skill)"
|
||||||
class="pp-btn pp-btn-minus"
|
class="pp-btn pp-btn-minus"
|
||||||
@@ -127,6 +128,7 @@
|
|||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<span v-else>{{ skill.pp || '0' }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ skill.bemerkung || '-' }}</td>
|
<td>{{ skill.bemerkung || '-' }}</td>
|
||||||
<td v-if="learningMode" class="action-cell">
|
<td v-if="learningMode" class="action-cell">
|
||||||
@@ -290,6 +292,10 @@ export default {
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fullwidth-container">
|
<div class="fullwidth-container">
|
||||||
<!-- Header mit Lernmodus-Kontrollen -->
|
<!-- Header mit Lernmodus-Kontrollen -->
|
||||||
<div class="page-header header-section">
|
<div v-if="isOwner" class="page-header header-section">
|
||||||
<div class="learning-mode-controls">
|
<div class="learning-mode-controls">
|
||||||
<!-- Lernmodus Toggle Button -->
|
<!-- Lernmodus Toggle Button -->
|
||||||
<button
|
<button
|
||||||
@@ -99,6 +99,10 @@ export default {
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="cd-view">
|
<div class="cd-view">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<h2>{{ $t('WeaponView') }}</h2>
|
<h2>{{ $t('WeaponView') }}</h2>
|
||||||
<button @click="openAddWeaponDialog" class="btn-add-weapon">
|
<button v-if="isOwner" @click="openAddWeaponDialog" class="btn-add-weapon">
|
||||||
{{ $t('weapon.add') }}
|
{{ $t('weapon.add') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<th>{{ $t('weapon.amount') }}</th>
|
<th>{{ $t('weapon.amount') }}</th>
|
||||||
<th>{{ $t('weapon.contained_in') }}</th>
|
<th>{{ $t('weapon.contained_in') }}</th>
|
||||||
<th>{{ $t('weapon.bonus') }}</th>
|
<th>{{ $t('weapon.bonus') }}</th>
|
||||||
<th>{{ $t('weapon.actions') }}</th>
|
<th v-if="isOwner">{{ $t('weapon.actions') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<td>{{ weapon.anzahl || '-' }}</td>
|
<td>{{ weapon.anzahl || '-' }}</td>
|
||||||
<td>{{ weapon.beinhaltet_in || '-' }}</td>
|
<td>{{ weapon.beinhaltet_in || '-' }}</td>
|
||||||
<td>{{ weapon.anb || '-' }}/{{ weapon.abwb || '-' }}</td>
|
<td>{{ weapon.anb || '-' }}/{{ weapon.abwb || '-' }}</td>
|
||||||
<td class="action-cell">
|
<td v-if="isOwner" class="action-cell">
|
||||||
<button @click="editWeapon(weapon)" class="btn-edit" title="Bearbeiten">
|
<button @click="editWeapon(weapon)" class="btn-edit" title="Bearbeiten">
|
||||||
✏️
|
✏️
|
||||||
</button>
|
</button>
|
||||||
@@ -378,6 +378,10 @@ name: "WeaponView",
|
|||||||
character: {
|
character: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isOwner: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ export default {
|
|||||||
Characters:'Charaktere',
|
Characters:'Charaktere',
|
||||||
Admin:'Administration',
|
Admin:'Administration',
|
||||||
},
|
},
|
||||||
|
deleteChar: {
|
||||||
|
notAuthorized: 'Sie sind nicht berechtigt, diesen Charakter zu löschen. Nur der Besitzer kann diese Aktion durchführen.'
|
||||||
|
},
|
||||||
landing:{
|
landing:{
|
||||||
title:'BaMoRT - Charakterverwaltung für mein Lieblingsrollenspielsystem',
|
title:'BaMoRT - Charakterverwaltung für mein Lieblingsrollenspielsystem',
|
||||||
description:'BaMoRT ist ein Werkzeug zur Charakterverwaltung für Rollenspiele. Es bietet Funktionen zur Charaktererstellung, -entwicklung und -verwaltung mit Unterstützung für Fertigkeiten, Zauber, Ausrüstung und mehr. Viele Ausrüstungsteile, Fertikeiten und Zauber fehlen noch, da das Projekt noch in der Entwicklung ist.',
|
description:'BaMoRT ist ein Werkzeug zur Charakterverwaltung für Rollenspiele. Es bietet Funktionen zur Charaktererstellung, -entwicklung und -verwaltung mit Unterstützung für Fertigkeiten, Zauber, Ausrüstung und mehr. Viele Ausrüstungsteile, Fertikeiten und Zauber fehlen noch, da das Projekt noch in der Entwicklung ist.',
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ export default {
|
|||||||
Characters:'Characters',
|
Characters:'Characters',
|
||||||
Admin:'Administration',
|
Admin:'Administration',
|
||||||
},
|
},
|
||||||
|
deleteChar: {
|
||||||
|
notAuthorized: 'You are not authorized to delete this character. Only the owner can perform this action.'
|
||||||
|
},
|
||||||
landing:{
|
landing:{
|
||||||
title:'BaMoRT - Character Management for Role-Playing Games',
|
title:'BaMoRT - Character Management for Role-Playing Games',
|
||||||
description:'BaMoRT is a modern character management tool for role-playing games. It provides comprehensive features for character creation, development, and management with support for skills, spells, equipment, and more.',
|
description:'BaMoRT is a modern character management tool for role-playing games. It provides comprehensive features for character creation, development, and management with support for skills, spells, equipment, and more.',
|
||||||
|
|||||||
Reference in New Issue
Block a user