Compare commits

...

3 Commits

Author SHA1 Message Date
fec7aa3939 Update jail name 2025-10-28 09:17:59 -07:00
214e02fa6a Log the port 2025-10-28 08:52:09 -07:00
a1be538d14 Simplify character deletion 2025-10-28 08:52:09 -07:00
7 changed files with 37 additions and 45 deletions

View File

@ -28,4 +28,4 @@ steps:
event: push
image: sh
commands:
- ssh controller faceclaimer-inconnu
- ssh controller fc-inconnu

View File

@ -60,7 +60,7 @@ Downloads an image from a URL, converts it to WebP, and stores it.
### Delete Single Image
**DELETE** `/image/{guild}/{user}/{charid}/{imageid}.webp`
**DELETE** `/image/{charid}/{imageid}.webp`
Deletes a specific image file. Automatically cleans up empty parent directories.
@ -81,13 +81,13 @@ curl -X DELETE http://localhost:8080/image/68f5a69c1cd9d39b5e9d7ba1/68f5ce16713c
### Delete Character Images
**DELETE** `/character/{guild}/{user}/{charid}`
**DELETE** `/character/{charid}`
Deletes all images for a specific character. Automatically cleans up empty parent directories.
**Example:**
```bash
curl -X DELETE http://localhost:8080/character/12345/67890/68f5a69c1cd9d39b5e9d7ba1
curl -X DELETE http://localhost:8080/character/68f5a69c1cd9d39b5e9d7ba1
```
**Response:**
@ -106,16 +106,12 @@ Images are organized in a hierarchical directory structure:
```
images/
└── {guild}/
└── {user}/
── {charid}/
── {imageid1}.webp
├── {imageid2}.webp
└── {imageid3}.webp
└── {charid}/
├── {imageid1}.webp
── {imageid2}.webp
── {imageid3}.webp
```
- `guild`: Discord guild (server) ID (integer)
- `user`: Discord user ID (integer)
- `charid`: Character ID (MongoDB ObjectID - 24 hex characters)
- `imageid`: Image ID (MongoDB ObjectID - 24 hex characters)

View File

@ -62,7 +62,7 @@ without an internet connection.`,
return nil
},
Run: func(cmd *cobra.Command, args []string) {
slog.Info("Starting images-processor", "imagesDir", imagesDir, "baseURL", baseURL, "quality", quality)
slog.Info("Starting images-processor", "imagesDir", imagesDir, "baseURL", baseURL, "quality", quality, "port", port)
routes.Run(baseURL, imagesDir, port, quality)
},
}

View File

@ -40,7 +40,7 @@ func setupRouter(cfg *Config) *gin.Engine {
r.DELETE("/image/*imagePath", func(c *gin.Context) {
handleSingleDelete(c, cfg)
})
r.DELETE("/character/:guild/:user/:charID", func(c *gin.Context) {
r.DELETE("/character/:charID", func(c *gin.Context) {
handleCharacterDelete(c, cfg)
})
@ -173,8 +173,6 @@ func handleSingleDelete(c *gin.Context, cfg *Config) {
// handleCharacterDelete deletes all of a character's images.
func handleCharacterDelete(c *gin.Context, cfg *Config) {
guild := c.Param("guild")
user := c.Param("user")
charID := c.Param("charID")
if !checks.IsValidObjectId(charID) {
@ -182,8 +180,7 @@ func handleCharacterDelete(c *gin.Context, cfg *Config) {
return
}
charPath := filepath.Join(guild, user, charID)
charPath, err := checks.AbsPath(cfg.ImagesDir, charPath)
charPath, err := checks.AbsPath(cfg.ImagesDir, charID)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return

View File

@ -451,7 +451,7 @@ func TestHandleCharacterDelete(t *testing.T) {
defer os.RemoveAll(tmpDir)
// Create character directory with multiple images
charPath := filepath.Join(tmpDir, "123", "456", "507f1f77bcf86cd799439011")
charPath := filepath.Join(tmpDir, "507f1f77bcf86cd799439011")
if err := os.MkdirAll(charPath, 0755); err != nil {
t.Fatalf("Failed to create char dir: %v", err)
}
@ -463,7 +463,7 @@ func TestHandleCharacterDelete(t *testing.T) {
router := setupRouter(cfg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/character/123/456/507f1f77bcf86cd799439011", nil)
req, _ := http.NewRequest("DELETE", "/character/507f1f77bcf86cd799439011", nil)
router.ServeHTTP(w, req)
if w.Code != http.StatusOK {
@ -485,7 +485,7 @@ func TestHandleCharacterDelete(t *testing.T) {
router := setupRouter(cfg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/character/123/456/not-a-valid-objectid", nil)
req, _ := http.NewRequest("DELETE", "/character/not-a-valid-objectid", nil)
router.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
@ -507,7 +507,7 @@ func TestHandleCharacterDelete(t *testing.T) {
router := setupRouter(cfg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/character/123/456/507f1f77bcf86cd799439011", nil)
req, _ := http.NewRequest("DELETE", "/character/507f1f77bcf86cd799439011", nil)
router.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
@ -528,10 +528,10 @@ func TestHandleCharacterDelete(t *testing.T) {
cfg := &Config{ImagesDir: tmpDir, BaseURL: "https://example.com", Quality: 90}
router := setupRouter(cfg)
// Try to use path traversal via params (e.g. guild="../..", user="etc", charID=valid ObjectID)
// Try to use path traversal via charID param (e.g. charID="../../../etc/passwd")
// This should be blocked by checks.AbsPath
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/character/../.././usr/507f1f77bcf86cd799439011", nil)
req, _ := http.NewRequest("DELETE", "/character/../../../etc/passwd", nil)
router.ServeHTTP(w, req)
// Should either be 400 (blocked by path validation) or 404 (route not matched)
@ -548,7 +548,7 @@ func TestHandleCharacterDelete(t *testing.T) {
defer os.RemoveAll(tmpDir)
// Create character directory
charPath := filepath.Join(tmpDir, "123", "456", "507f1f77bcf86cd799439011")
charPath := filepath.Join(tmpDir, "507f1f77bcf86cd799439011")
if err := os.MkdirAll(charPath, 0755); err != nil {
t.Fatalf("Failed to create char dir: %v", err)
}
@ -558,17 +558,16 @@ func TestHandleCharacterDelete(t *testing.T) {
router := setupRouter(cfg)
w := httptest.NewRecorder()
req, _ := http.NewRequest("DELETE", "/character/123/456/507f1f77bcf86cd799439011", nil)
req, _ := http.NewRequest("DELETE", "/character/507f1f77bcf86cd799439011", nil)
router.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
// Verify empty parent directories were cleaned up
guildDir := filepath.Join(tmpDir, "123")
if checks.PathExists(guildDir) {
t.Error("Empty parent directories should have been cleaned up")
// Verify character directory was deleted
if checks.PathExists(charPath) {
t.Error("Character directory should have been deleted")
}
})
}
@ -814,13 +813,13 @@ func TestSetupRouter(t *testing.T) {
t.Error("DELETE /image/* route not registered")
}
// Test DELETE /character/:guild/:user/:charID exists
// Test DELETE /character/:charID exists
w = httptest.NewRecorder()
req, _ = http.NewRequest("DELETE", "/character/123/456/507f1f77bcf86cd799439011", nil)
req, _ = http.NewRequest("DELETE", "/character/507f1f77bcf86cd799439011", nil)
router.ServeHTTP(w, req)
// Should not be 404 (might be 400 due to non-existent dir, but route exists)
if w.Code == http.StatusNotFound {
t.Error("DELETE /character/:guild/:user/:charID route not registered")
t.Error("DELETE /character/:charID route not registered")
}
})

View File

@ -1,16 +1,16 @@
#!/bin/sh
# Create test directory structure with multiple images for a character
mkdir -p ../images/12345/67890/68f5a69c1cd9d39b5e9d7ba2
touch ../images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-001.webp
touch ../images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-002.webp
touch ../images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-003.webp
mkdir -p ../images/68f5a69c1cd9d39b5e9d7ba2
touch ../images/68f5a69c1cd9d39b5e9d7ba2/test-image-001.webp
touch ../images/68f5a69c1cd9d39b5e9d7ba2/test-image-002.webp
touch ../images/68f5a69c1cd9d39b5e9d7ba2/test-image-003.webp
echo "Created test character with 3 images:"
echo " images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-001.webp"
echo " images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-002.webp"
echo " images/12345/67890/68f5a69c1cd9d39b5e9d7ba2/test-image-003.webp"
echo " images/68f5a69c1cd9d39b5e9d7ba2/test-image-001.webp"
echo " images/68f5a69c1cd9d39b5e9d7ba2/test-image-002.webp"
echo " images/68f5a69c1cd9d39b5e9d7ba2/test-image-003.webp"
# Delete all images for the character
curl -X DELETE \
http://127.0.0.1:8080/character/12345/67890/68f5a69c1cd9d39b5e9d7ba2
http://127.0.0.1:8080/character/68f5a69c1cd9d39b5e9d7ba2

View File

@ -1,11 +1,11 @@
#!/bin/sh
# Create test directory structure and image
mkdir -p ../images/12345/67890/test-char-abc123
touch ../images/12345/67890/test-char-abc123/test-image-xyz789.webp
mkdir -p ../images/68f5a69c1cd9d39b5e9d7ba1
touch ../images/68f5a69c1cd9d39b5e9d7ba1/test-image-xyz789.webp
echo "Created test image: images/12345/67890/test-char-abc123/test-image-xyz789.webp"
echo "Created test image: images/68f5a69c1cd9d39b5e9d7ba1/test-image-xyz789.webp"
# Delete the single image
curl -X DELETE \
http://127.0.0.1:8080/image/12345/67890/test-char-abc123/test-image-xyz789.webp
http://127.0.0.1:8080/image/68f5a69c1cd9d39b5e9d7ba1/test-image-xyz789.webp