{
  "openapi": "3.0.0",
  "info": {
    "title": "FileRise API",
    "version": "1.5.2"
  },
  "servers": [
    {
      "url": "/",
      "description": "Same-origin server"
    }
  ],
  "paths": {
    "/api/addUser.php": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Add a new user",
        "description": "Adds a new user to the system. In setup mode, the new user is automatically made admin.",
        "operationId": "addUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "username",
                  "password"
                ],
                "properties": {
                  "username": {
                    "type": "string",
                    "example": "johndoe"
                  },
                  "password": {
                    "type": "string",
                    "example": "securepassword"
                  },
                  "isAdmin": {
                    "type": "boolean",
                    "example": true
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User added successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "User added successfully"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/admin/acl/getGrants.php": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get per-folder grants for a user (admin-only)",
        "description": "Returns the explicit ACL grants for the specified user across all known folders. Booleans mean: `view` = full view, `viewOwn` = view own uploads only, `upload` = write (upload/edit/delete), `manage` = owner, `share` = can create share links.",
        "operationId": "adminGetUserFolderGrants",
        "parameters": [
          {
            "name": "user",
            "in": "query",
            "description": "Username to inspect.",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Map of folder → grants for the requested user.",
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "grants"
                  ],
                  "properties": {
                    "grants": {
                      "type": "object",
                      "example": {
                        "root": {
                          "view": true,
                          "viewOwn": false,
                          "upload": true,
                          "manage": false,
                          "share": true
                        },
                        "projects/acme": {
                          "view": true,
                          "viewOwn": false,
                          "upload": true,
                          "manage": true,
                          "share": true
                        },
                        "teams/ops": {
                          "view": false,
                          "viewOwn": true,
                          "upload": true,
                          "manage": false,
                          "share": false
                        }
                      },
                      "additionalProperties": {
                        "required": [
                          "view",
                          "viewOwn",
                          "upload",
                          "manage",
                          "share"
                        ],
                        "properties": {
                          "view": {
                            "description": "Full view of all files in the folder.",
                            "type": "boolean",
                            "example": true
                          },
                          "viewOwn": {
                            "description": "View only files uploaded by the user.",
                            "type": "boolean",
                            "example": false
                          },
                          "upload": {
                            "description": "Write (upload/edit/rename/move/delete).",
                            "type": "boolean",
                            "example": true
                          },
                          "manage": {
                            "description": "Is folder owner (implicit full rights).",
                            "type": "boolean",
                            "example": false
                          },
                          "share": {
                            "description": "Can create share links.",
                            "type": "boolean",
                            "example": true
                          }
                        },
                        "type": "object"
                      }
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid user (missing or fails validation)."
          },
          "401": {
            "description": "Unauthorized (not logged in or not admin)."
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/admin/acl/saveGrants.php": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Save per-folder grants for one or many users (admin-only)",
        "description": "Accepts either a single-user payload `{user, grants}` or a batch payload `{changes:[{user, grants},...]}`. Booleans map to: `view`=full view, `viewOwn`=view only own uploads, `upload`=write (upload/edit/rename/move/delete), `manage`=owner, `share`=can create share links.",
        "operationId": "adminSaveUserFolderGrants",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "description": "CSRF token bound to the current session.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "required": [
                      "user",
                      "grants"
                    ],
                    "properties": {
                      "user": {
                        "type": "string",
                        "example": "alice"
                      },
                      "grants": {
                        "description": "Map of folder → flags.",
                        "type": "object",
                        "example": {
                          "root": {
                            "view": true,
                            "viewOwn": false,
                            "upload": true,
                            "manage": false,
                            "share": true
                          },
                          "projects/acme": {
                            "view": true,
                            "viewOwn": false,
                            "upload": true,
                            "manage": true,
                            "share": true
                          }
                        },
                        "additionalProperties": {
                          "required": [
                            "view",
                            "viewOwn",
                            "upload",
                            "manage",
                            "share"
                          ],
                          "properties": {
                            "view": {
                              "type": "boolean"
                            },
                            "viewOwn": {
                              "type": "boolean"
                            },
                            "upload": {
                              "type": "boolean"
                            },
                            "manage": {
                              "type": "boolean"
                            },
                            "share": {
                              "type": "boolean"
                            }
                          },
                          "type": "object"
                        }
                      }
                    },
                    "type": "object"
                  },
                  {
                    "required": [
                      "changes"
                    ],
                    "properties": {
                      "changes": {
                        "type": "array",
                        "items": {
                          "required": [
                            "user",
                            "grants"
                          ],
                          "properties": {
                            "user": {
                              "type": "string",
                              "example": "bob"
                            },
                            "grants": {
                              "type": "object",
                              "example": {
                                "teams/ops": {
                                  "view": false,
                                  "viewOwn": true,
                                  "upload": true,
                                  "manage": false,
                                  "share": false
                                }
                              },
                              "additionalProperties": {
                                "required": [
                                  "view",
                                  "viewOwn",
                                  "upload",
                                  "manage",
                                  "share"
                                ],
                                "properties": {
                                  "view": {
                                    "type": "boolean"
                                  },
                                  "viewOwn": {
                                    "type": "boolean"
                                  },
                                  "upload": {
                                    "type": "boolean"
                                  },
                                  "manage": {
                                    "type": "boolean"
                                  },
                                  "share": {
                                    "type": "boolean"
                                  }
                                },
                                "type": "object"
                              }
                            }
                          },
                          "type": "object"
                        }
                      }
                    },
                    "type": "object",
                    "example": {
                      "changes": [
                        {
                          "user": "alice",
                          "grants": {
                            "root": {
                              "view": true,
                              "viewOwn": false,
                              "upload": true,
                              "manage": false,
                              "share": true
                            }
                          }
                        },
                        {
                          "user": "bob",
                          "grants": {
                            "teams/ops": {
                              "view": false,
                              "viewOwn": true,
                              "upload": true,
                              "manage": false,
                              "share": false
                            }
                          }
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Grants saved.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "Single-user result (whatever ACL::applyUserGrantsAtomic returns).",
                      "type": "object",
                      "example": {
                        "updated": {
                          "root": {
                            "view": true,
                            "viewOwn": false,
                            "upload": true,
                            "manage": false,
                            "share": true
                          }
                        }
                      },
                      "additionalProperties": true
                    },
                    {
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "updated": {
                          "description": "Per-user results from ACL::applyUserGrantsAtomic.",
                          "type": "object",
                          "additionalProperties": {
                            "type": "object"
                          }
                        }
                      },
                      "type": "object",
                      "example": {
                        "ok": true,
                        "updated": {
                          "alice": {
                            "root": {
                              "view": true,
                              "viewOwn": false,
                              "upload": true,
                              "manage": false,
                              "share": true
                            }
                          },
                          "bob": {
                            "teams/ops": {
                              "view": false,
                              "viewOwn": true,
                              "upload": true,
                              "manage": false,
                              "share": false
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON / invalid user / invalid payload shape."
          },
          "401": {
            "description": "Unauthorized (not logged in or not admin)."
          },
          "403": {
            "description": "Invalid CSRF token."
          },
          "500": {
            "description": "Failed to save grants."
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/admin/getConfig.php": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get UI configuration",
        "description": "Returns a public subset for everyone; authenticated admins receive additional loginOptions fields.",
        "operationId": "getAdminConfig",
        "responses": {
          "200": {
            "description": "Configuration loaded",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AdminGetConfigPublic"
                    },
                    {
                      "$ref": "#/components/schemas/AdminGetConfigAdmin"
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/admin/readMetadata.php": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Read share metadata (admin-only)",
        "description": "Returns the requested metadata JSON (either `share_links.json` or `share_folder_links.json`). Expired entries are pruned before returning. If the file does not exist, this returns `{}` with 200.",
        "operationId": "adminReadMetadata",
        "parameters": [
          {
            "name": "file",
            "in": "query",
            "description": "Which metadata file to read.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "share_links.json",
                "share_folder_links.json"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cleaned metadata map keyed by token.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "description": "File share links (token → record).",
                      "type": "object",
                      "additionalProperties": {
                        "required": [
                          "folder",
                          "file",
                          "expires"
                        ],
                        "properties": {
                          "folder": {
                            "type": "string",
                            "example": "team"
                          },
                          "file": {
                            "type": "string",
                            "example": "report.pdf"
                          },
                          "expires": {
                            "description": "Unix timestamp (seconds).",
                            "type": "integer",
                            "format": "int64",
                            "example": 1704067200
                          },
                          "password": {
                            "description": "Hashed password if set.",
                            "type": "string",
                            "example": "$2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                            "nullable": true
                          },
                          "createdBy": {
                            "type": "string",
                            "example": "alice",
                            "nullable": true
                          }
                        },
                        "type": "object"
                      }
                    },
                    {
                      "description": "Folder share links (token → record).",
                      "type": "object",
                      "additionalProperties": {
                        "required": [
                          "folder",
                          "expires"
                        ],
                        "properties": {
                          "folder": {
                            "type": "string",
                            "example": "team/sub"
                          },
                          "expires": {
                            "description": "Unix timestamp (seconds).",
                            "type": "integer",
                            "format": "int64",
                            "example": 1704067200
                          },
                          "password": {
                            "description": "Hashed password if set.",
                            "type": "string",
                            "example": "$2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                            "nullable": true
                          },
                          "allowUpload": {
                            "description": "Whether uploads are allowed on the shared folder.",
                            "type": "integer",
                            "enum": [
                              0,
                              1
                            ],
                            "example": 1
                          },
                          "createdBy": {
                            "type": "string",
                            "example": "alice",
                            "nullable": true
                          }
                        },
                        "type": "object"
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing `file` parameter."
          },
          "403": {
            "description": "Forbidden (not admin or invalid filename)."
          },
          "500": {
            "description": "Corrupted JSON on disk."
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/auth/auth.php": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Authenticate user",
        "description": "Handles user authentication via OIDC or form-based credentials. For OIDC flows, processes callbacks; otherwise, performs standard authentication with optional TOTP verification.",
        "operationId": "authUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "username",
                  "password"
                ],
                "properties": {
                  "username": {
                    "type": "string",
                    "example": "johndoe"
                  },
                  "password": {
                    "type": "string",
                    "example": "secretpassword"
                  },
                  "remember_me": {
                    "type": "boolean",
                    "example": true
                  },
                  "totp_code": {
                    "type": "string",
                    "example": "123456"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login successful; returns user info and status",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "success": {
                      "type": "string",
                      "example": "Login successful"
                    },
                    "username": {
                      "type": "string",
                      "example": "johndoe"
                    },
                    "isAdmin": {
                      "type": "boolean",
                      "example": true
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request (e.g., missing credentials)"
          },
          "401": {
            "description": "Unauthorized (e.g., invalid credentials, too many attempts)"
          },
          "429": {
            "description": "Too many failed login attempts"
          }
        }
      }
    },
    "/api/auth/checkAuth.php": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Check authentication status",
        "description": "Checks if the current session is authenticated. If the users file is missing or empty, returns a setup flag. Also returns information about admin privileges, TOTP status, and folder-only access.",
        "operationId": "checkAuth",
        "responses": {
          "200": {
            "description": "Returns authentication status and user details",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "authenticated": {
                      "type": "boolean",
                      "example": true
                    },
                    "isAdmin": {
                      "type": "boolean",
                      "example": true
                    },
                    "totp_enabled": {
                      "type": "boolean",
                      "example": false
                    },
                    "username": {
                      "type": "string",
                      "example": "johndoe"
                    },
                    "folderOnly": {
                      "type": "boolean",
                      "example": false
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/login_basic.php": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Authenticate using HTTP Basic Authentication",
        "description": "Performs HTTP Basic authentication. If credentials are missing, sends a 401 response prompting for Basic auth. On valid credentials, optionally handles TOTP verification and finalizes session login.",
        "operationId": "loginBasic",
        "responses": {
          "200": {
            "description": "Login successful; redirects to index.html",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "Login successful"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized due to missing credentials or invalid credentials."
          }
        }
      }
    },
    "/api/auth/logout.php": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Logout user",
        "description": "Clears the session, removes persistent login tokens, and redirects the user to the login page.",
        "operationId": "logoutUser",
        "responses": {
          "302": {
            "description": "Redirects to the login page with a logout flag."
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/auth/token.php": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Retrieve CSRF token and share URL",
        "description": "Returns the current CSRF token along with the configured share URL.",
        "operationId": "getToken",
        "responses": {
          "200": {
            "description": "CSRF token and share URL",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "csrf_token": {
                      "type": "string",
                      "example": "0123456789abcdef..."
                    },
                    "share_url": {
                      "type": "string",
                      "example": "https://yourdomain.com/share.php"
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/changePassword.php": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Change user password",
        "description": "Allows an authenticated user to change their password by verifying the old password and updating to a new one.",
        "operationId": "changePassword",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "oldPassword",
                  "newPassword",
                  "confirmPassword"
                ],
                "properties": {
                  "oldPassword": {
                    "type": "string",
                    "example": "oldpass123"
                  },
                  "newPassword": {
                    "type": "string",
                    "example": "newpass456"
                  },
                  "confirmPassword": {
                    "type": "string",
                    "example": "newpass456"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "Password updated successfully."
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Invalid CSRF token"
          }
        }
      }
    },
    "/api/file/copyFiles.php": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Copy files between folders",
        "description": "Requires read access on source and write access on destination. Enforces folder scope and ownership.",
        "operationId": "copyFiles",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "description": "CSRF token from the current session",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "source",
                  "destination",
                  "files"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "example": "root"
                  },
                  "destination": {
                    "type": "string",
                    "example": "userA/projects"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "report.pdf",
                      "notes.txt"
                    ]
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Copy result (model-defined)"
          },
          "400": {
            "description": "Invalid request or folder name"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/createFile.php": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Create an empty file",
        "description": "Requires write access on the target folder. Enforces folder-only scope.",
        "operationId": "createFile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "name"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "name": {
                    "type": "string",
                    "example": "new.txt"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Creation result (model-defined)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/createShareLink.php": {
      "post": {
        "tags": [
          "Shares"
        ],
        "summary": "Create a share link for a file",
        "description": "Requires share permission on the folder. Non-admins must own the file unless bypassOwnership.",
        "operationId": "createShareLink",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "file"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "file": {
                    "type": "string",
                    "example": "invoice.pdf"
                  },
                  "expirationValue": {
                    "type": "integer",
                    "example": 60
                  },
                  "expirationUnit": {
                    "type": "string",
                    "enum": [
                      "seconds",
                      "minutes",
                      "hours",
                      "days"
                    ],
                    "example": "minutes"
                  },
                  "password": {
                    "type": "string",
                    "example": ""
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Share link created",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "token": {
                      "type": "string",
                      "example": "abc123"
                    },
                    "url": {
                      "type": "string",
                      "example": "/api/file/share.php?token=abc123"
                    },
                    "expires": {
                      "type": "integer",
                      "example": 1700000000
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/deleteFiles.php": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Delete files to Trash",
        "description": "Requires write access on the folder and (for non-admins) ownership of the files.",
        "operationId": "deleteFiles",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "files"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "old.docx",
                      "draft.md"
                    ]
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Delete result (model-defined)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/deleteShareLink.php": {
      "post": {
        "tags": [
          "Shares"
        ],
        "summary": "Delete a share link by token",
        "description": "Deletes a share token. NOTE: Current implementation does not require authentication.",
        "operationId": "deleteShareLink",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "example": "abc123"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deletion result (success or not found)"
          }
        }
      }
    },
    "/api/file/deleteTrashFiles.php": {
      "post": {
        "tags": [
          "Trash"
        ],
        "summary": "Permanently delete Trash items (admin only)",
        "operationId": "deleteTrashFiles",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "required": [
                      "deleteAll"
                    ],
                    "properties": {
                      "deleteAll": {
                        "type": "boolean",
                        "example": true
                      }
                    },
                    "type": "object"
                  },
                  {
                    "required": [
                      "files"
                    ],
                    "properties": {
                      "files": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "example": [
                          "trash/abc",
                          "trash/def"
                        ]
                      }
                    },
                    "type": "object"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deletion result (model-defined)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Admin only"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/download.php": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a file",
        "description": "Requires view access (or own-only with ownership). Streams the file with appropriate Content-Type.",
        "operationId": "downloadFile",
        "parameters": [
          {
            "name": "folder",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "root"
          },
          {
            "name": "file",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "photo.jpg"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid folder/file"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/downloadZip.php": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Download multiple files as a ZIP",
        "description": "Requires view access (or own-only with ownership). May be gated by account flag.",
        "operationId": "downloadZip",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "files"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "a.jpg",
                      "b.png"
                    ]
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "ZIP archive",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/extractZip.php": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Extract ZIP file(s) into a folder",
        "description": "Requires write access on the target folder.",
        "operationId": "extractZip",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "files"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "archive.zip"
                    ]
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extraction result (model-defined)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/getFileList.php": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "List files in a folder",
        "description": "Requires view access (full) or read_own (own-only results).",
        "operationId": "getFileList",
        "parameters": [
          {
            "name": "folder",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "root"
          }
        ],
        "responses": {
          "200": {
            "description": "Listing result (model-defined JSON)"
          },
          "400": {
            "description": "Invalid folder"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/getFileTags.php": {
      "get": {
        "tags": [
          "Tags"
        ],
        "summary": "Get global file tags",
        "description": "Returns tag metadata (no auth in current implementation).",
        "operationId": "getFileTags",
        "responses": {
          "200": {
            "description": "Tags map (model-defined JSON)"
          }
        }
      }
    },
    "/api/file/getShareLinks.php": {
      "get": {
        "tags": [
          "Shares"
        ],
        "summary": "Get (raw) share links file",
        "description": "Returns the full share links JSON (no auth in current implementation).",
        "operationId": "getShareLinks",
        "responses": {
          "200": {
            "description": "Share links (model-defined JSON)"
          }
        }
      }
    },
    "/api/file/getTrashItems.php": {
      "get": {
        "tags": [
          "Trash"
        ],
        "summary": "List items in Trash (admin only)",
        "operationId": "getTrashItems",
        "responses": {
          "200": {
            "description": "Trash contents (model-defined JSON)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Admin only"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/renameFile.php": {
      "put": {
        "tags": [
          "Files"
        ],
        "summary": "Rename a file",
        "description": "Requires write access; non-admins must own the file.",
        "operationId": "renameFile",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "oldName",
                  "newName"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "oldName": {
                    "type": "string",
                    "example": "old.pdf"
                  },
                  "newName": {
                    "type": "string",
                    "example": "new.pdf"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rename result (model-defined)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/restoreFiles.php": {
      "post": {
        "tags": [
          "Trash"
        ],
        "summary": "Restore files from Trash (admin only)",
        "operationId": "restoreFiles",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "files"
                ],
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "trash/12345.json"
                    ]
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Restore result (model-defined)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Admin only"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/saveFile.php": {
      "put": {
        "tags": [
          "Files"
        ],
        "summary": "Create or overwrite a file’s content",
        "description": "Requires write access. Overwrite enforces ownership for non-admins. Certain executable extensions are denied.",
        "operationId": "saveFile",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "fileName",
                  "content"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "fileName": {
                    "type": "string",
                    "example": "readme.txt"
                  },
                  "content": {
                    "type": "string",
                    "example": "Hello world"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Save result (model-defined)"
          },
          "400": {
            "description": "Invalid input or disallowed extension"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/saveFileTag.php": {
      "post": {
        "tags": [
          "Tags"
        ],
        "summary": "Save tags for a file (or delete one)",
        "description": "Requires write access and (for non-admins) ownership when modifying.",
        "operationId": "saveFileTag",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder",
                  "file"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "root"
                  },
                  "file": {
                    "type": "string",
                    "example": "doc.md"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "work",
                      "urgent"
                    ]
                  },
                  "deleteGlobal": {
                    "type": "boolean",
                    "example": false
                  },
                  "tagToDelete": {
                    "type": "string",
                    "example": null,
                    "nullable": true
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Save result (model-defined)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Internal error"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/file/share.php": {
      "get": {
        "tags": [
          "Shares"
        ],
        "summary": "Open a shared file by token",
        "description": "If the link is password-protected and no password is supplied, an HTML password form is returned. Otherwise the file is streamed.",
        "operationId": "shareFile",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pass",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Binary file (or HTML password form when missing password)",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "text/html": {}
            }
          },
          "400": {
            "description": "Missing token / invalid input"
          },
          "403": {
            "description": "Expired or invalid password"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/folder/capabilities.php": {
      "get": {
        "tags": [
          "Folders"
        ],
        "summary": "Get effective capabilities for the current user in a folder",
        "description": "Computes the caller's capabilities for a given folder by combining account flags (readOnly/disableUpload), ACL grants (read/write/share), and the user-folder-only scope. Returns booleans indicating what the user can do.",
        "operationId": "getFolderCapabilities",
        "parameters": [
          {
            "name": "folder",
            "in": "query",
            "description": "Target folder path. Defaults to 'root'. Supports nested paths like 'team/reports'.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "projects/acme"
          }
        ],
        "responses": {
          "200": {
            "description": "Capabilities computed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "user",
                    "folder",
                    "isAdmin",
                    "flags",
                    "canView",
                    "canUpload",
                    "canCreate",
                    "canRename",
                    "canDelete",
                    "canMoveIn",
                    "canShare"
                  ],
                  "properties": {
                    "user": {
                      "type": "string",
                      "example": "alice"
                    },
                    "folder": {
                      "type": "string",
                      "example": "projects/acme"
                    },
                    "isAdmin": {
                      "type": "boolean",
                      "example": false
                    },
                    "flags": {
                      "required": [
                        "folderOnly",
                        "readOnly",
                        "disableUpload"
                      ],
                      "properties": {
                        "folderOnly": {
                          "type": "boolean",
                          "example": false
                        },
                        "readOnly": {
                          "type": "boolean",
                          "example": false
                        },
                        "disableUpload": {
                          "type": "boolean",
                          "example": false
                        }
                      },
                      "type": "object"
                    },
                    "owner": {
                      "type": "string",
                      "example": "alice",
                      "nullable": true
                    },
                    "canView": {
                      "description": "User can view items in this folder.",
                      "type": "boolean",
                      "example": true
                    },
                    "canUpload": {
                      "description": "User can upload/edit/rename/move/delete items (i.e., WRITE).",
                      "type": "boolean",
                      "example": true
                    },
                    "canCreate": {
                      "description": "User can create subfolders here.",
                      "type": "boolean",
                      "example": true
                    },
                    "canRename": {
                      "description": "User can rename items here.",
                      "type": "boolean",
                      "example": true
                    },
                    "canDelete": {
                      "description": "User can delete items here.",
                      "type": "boolean",
                      "example": true
                    },
                    "canMoveIn": {
                      "description": "User can move items into this folder.",
                      "type": "boolean",
                      "example": true
                    },
                    "canShare": {
                      "description": "User can create share links for this folder.",
                      "type": "boolean",
                      "example": false
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid folder name."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/createFolder.php": {
      "post": {
        "tags": [
          "Folders"
        ],
        "summary": "Create a new folder",
        "description": "Requires authentication, CSRF token, and write access to the parent folder. Seeds ACL owner.",
        "operationId": "createFolder",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "description": "CSRF token from the current session",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folderName"
                ],
                "properties": {
                  "folderName": {
                    "type": "string",
                    "example": "reports"
                  },
                  "parent": {
                    "description": "Parent folder (default root)",
                    "type": "string",
                    "example": "root",
                    "nullable": true
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Creation result (model-defined JSON)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/createShareFolderLink.php": {
      "post": {
        "tags": [
          "Shared Folders"
        ],
        "summary": "Create a share link for a folder",
        "description": "Requires authentication, CSRF token, and share permission. Non-admins must own the folder (unless bypass) and cannot share root.",
        "operationId": "createShareFolderLink",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "team/reports"
                  },
                  "expirationValue": {
                    "type": "integer",
                    "example": 60
                  },
                  "expirationUnit": {
                    "type": "string",
                    "enum": [
                      "seconds",
                      "minutes",
                      "hours",
                      "days"
                    ],
                    "example": "minutes"
                  },
                  "password": {
                    "type": "string",
                    "example": ""
                  },
                  "allowUpload": {
                    "type": "integer",
                    "enum": [
                      0,
                      1
                    ],
                    "example": 0
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Share folder link created",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "token": {
                      "type": "string",
                      "example": "sf_abc123"
                    },
                    "url": {
                      "type": "string",
                      "example": "/api/folder/shareFolder.php?token=sf_abc123"
                    },
                    "expires": {
                      "type": "integer",
                      "example": 1700000000
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/deleteFolder.php": {
      "post": {
        "tags": [
          "Folders"
        ],
        "summary": "Delete a folder",
        "description": "Requires authentication, CSRF token, write scope, and (for non-admins) folder ownership.",
        "operationId": "deleteFolder",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "userA/reports"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deletion result (model-defined JSON)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/deleteShareFolderLink.php": {
      "post": {
        "tags": [
          "Shared Folders",
          "Admin"
        ],
        "summary": "Delete a shared-folder link by token (admin only)",
        "description": "Requires authentication, CSRF token, and admin privileges.",
        "operationId": "deleteShareFolderLink",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "example": "sf_abc123"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "400": {
            "description": "No token provided"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Admin only"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/downloadSharedFile.php": {
      "get": {
        "tags": [
          "Shared Folders"
        ],
        "summary": "Download a file from a shared folder (by token)",
        "description": "Public endpoint; validates token and file name, then streams the file.",
        "operationId": "downloadSharedFile",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "file",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "report.pdf"
          }
        ],
        "responses": {
          "200": {
            "description": "Binary file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/folder/getFolderList.php": {
      "get": {
        "tags": [
          "Folders"
        ],
        "summary": "List folders (optionally under a parent)",
        "description": "Requires authentication. Non-admins see folders for which they have full view or own-only access.",
        "operationId": "getFolderList",
        "parameters": [
          {
            "name": "folder",
            "in": "query",
            "description": "Parent folder to include and descend (default all); use 'root' for top-level",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "root"
          }
        ],
        "responses": {
          "200": {
            "description": "List of folders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "properties": {
                      "folder": {
                        "type": "string",
                        "example": "team/reports"
                      },
                      "fileCount": {
                        "type": "integer",
                        "example": 12
                      },
                      "metadataFile": {
                        "type": "string",
                        "example": "/path/to/meta.json"
                      }
                    },
                    "type": "object"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid folder"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/getShareFolderLinks.php": {
      "get": {
        "tags": [
          "Shared Folders",
          "Admin"
        ],
        "summary": "List active shared-folder links (admin only)",
        "description": "Returns all non-expired shared-folder links. Admin-only.",
        "operationId": "getShareFolderLinks",
        "responses": {
          "200": {
            "description": "Active share-folder links (model-defined JSON)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Admin only"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/renameFolder.php": {
      "post": {
        "tags": [
          "Folders"
        ],
        "summary": "Rename or move a folder",
        "description": "Requires authentication, CSRF token, scope checks on old and new paths, and (for non-admins) ownership of the source folder.",
        "operationId": "renameFolder",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "oldFolder",
                  "newFolder"
                ],
                "properties": {
                  "oldFolder": {
                    "type": "string",
                    "example": "team/q1"
                  },
                  "newFolder": {
                    "type": "string",
                    "example": "team/quarter-1"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rename result (model-defined JSON)"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "405": {
            "description": "Method not allowed"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/folder/shareFolder.php": {
      "get": {
        "tags": [
          "Shared Folders"
        ],
        "summary": "Open a shared folder by token (HTML UI)",
        "description": "If the share is password-protected and no password is supplied, an HTML password form is returned. Otherwise renders an HTML listing with optional upload form.",
        "operationId": "shareFolder",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pass",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page (password form or folder listing)",
            "content": {
              "text/html": {}
            }
          },
          "400": {
            "description": "Missing/invalid token"
          },
          "403": {
            "description": "Forbidden or wrong password"
          }
        }
      }
    },
    "/api/folder/uploadToSharedFolder.php": {
      "post": {
        "tags": [
          "Shared Folders"
        ],
        "summary": "Upload a file into a shared folder (by token)",
        "description": "Public form-upload endpoint. Only allowed when the share link has uploads enabled. On success responds with a redirect to the share page.",
        "operationId": "uploadToSharedFolder",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "token",
                  "fileToUpload"
                ],
                "properties": {
                  "token": {
                    "description": "Share token",
                    "type": "string"
                  },
                  "fileToUpload": {
                    "description": "File to upload",
                    "type": "string",
                    "format": "binary"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "302": {
            "description": "Redirect to /api/folder/shareFolder.php?token=..."
          },
          "400": {
            "description": "Upload error or invalid input"
          },
          "405": {
            "description": "Method not allowed"
          }
        }
      }
    },
    "/api/getUserPermissions.php": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Retrieve user permissions",
        "description": "Returns the permissions for the current user, or all permissions if the user is an admin.",
        "operationId": "getUserPermissions",
        "responses": {
          "200": {
            "description": "Successful response with user permissions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/getUsers.php": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Retrieve a list of users",
        "description": "Returns a JSON array of users. Only available to authenticated admin users.",
        "operationId": "getUsers",
        "responses": {
          "200": {
            "description": "Successful response with an array of users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "properties": {
                      "username": {
                        "type": "string",
                        "example": "johndoe"
                      },
                      "role": {
                        "type": "string",
                        "example": "admin"
                      }
                    },
                    "type": "object"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: the user is not authenticated or is not an admin"
          }
        }
      }
    },
    "/api/profile/getCurrentUser.php": {
      "get": {
        "tags": [
          "Users"
        ],
        "summary": "Get the currently authenticated user's profile",
        "description": "Returns basic info for the logged-in user. Requires a valid session cookie.",
        "operationId": "getCurrentUser",
        "responses": {
          "200": {
            "description": "User profile returned. If the user record is missing, an empty result may be returned.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "required": [
                        "username",
                        "isAdmin",
                        "totp_enabled",
                        "profile_picture"
                      ],
                      "properties": {
                        "username": {
                          "type": "string",
                          "example": "alice"
                        },
                        "isAdmin": {
                          "type": "boolean",
                          "example": false
                        },
                        "totp_enabled": {
                          "type": "boolean",
                          "example": true
                        },
                        "profile_picture": {
                          "description": "Public URL path to the profile picture (leading slash).",
                          "type": "string",
                          "example": "/uploads/profile_pics/alice_9f3c2e1a8bcd.png"
                        }
                      },
                      "type": "object"
                    },
                    {
                      "type": "array",
                      "maxItems": 0,
                      "example": []
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/profile/uploadPicture.php": {
      "post": {
        "tags": [
          "Users"
        ],
        "summary": "Upload or replace the current user's profile picture",
        "description": "Accepts a single image file (JPEG, PNG, or GIF) up to 2&nbsp;MB. Requires a valid session cookie and CSRF token.",
        "operationId": "uploadProfilePicture",
        "parameters": [
          {
            "name": "X-CSRF-Token",
            "in": "header",
            "description": "Anti-CSRF token associated with the current session.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "profile_picture"
                ],
                "properties": {
                  "profile_picture": {
                    "description": "JPEG, PNG, or GIF image. Max size: 2 MB.",
                    "type": "string",
                    "format": "binary"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Profile picture updated.",
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "url"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "url": {
                      "type": "string",
                      "example": "/uploads/profile_pics/alice_9f3c2e1a8bcd.png"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "No file uploaded, invalid file type, or file too large."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "description": "Server error while saving the picture."
          }
        },
        "security": [
          {
            "cookieAuth": []
          }
        ]
      }
    },
    "/api/removeUser.php": {
      "delete": {
        "tags": [
          "Users"
        ],
        "summary": "Remove a user",
        "description": "Removes the specified user from the system. Cannot remove the currently logged-in user.",
        "operationId": "removeUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "username"
                ],
                "properties": {
                  "username": {
                    "type": "string",
                    "example": "johndoe"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User removed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "User removed successfully"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Invalid CSRF token"
          }
        }
      }
    },
    "/api/totp_disable.php": {
      "put": {
        "tags": [
          "TOTP"
        ],
        "summary": "Disable TOTP for the authenticated user",
        "description": "Clears the TOTP secret from the users file for the current user.",
        "operationId": "disableTOTP",
        "responses": {
          "200": {
            "description": "TOTP disabled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "TOTP disabled successfully."
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "403": {
            "description": "Not authenticated or invalid CSRF token"
          },
          "500": {
            "description": "Failed to disable TOTP"
          }
        }
      }
    },
    "/api/totp_recover.php": {
      "post": {
        "tags": [
          "TOTP"
        ],
        "summary": "Recover TOTP",
        "description": "Verifies a recovery code to disable TOTP and finalize login.",
        "operationId": "recoverTOTP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "recovery_code"
                ],
                "properties": {
                  "recovery_code": {
                    "type": "string",
                    "example": "ABC123DEF456"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recovery successful",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input or recovery code"
          },
          "403": {
            "description": "Invalid CSRF token"
          },
          "405": {
            "description": "Method not allowed"
          },
          "429": {
            "description": "Too many attempts"
          }
        }
      }
    },
    "/api/totp_saveCode.php": {
      "post": {
        "tags": [
          "TOTP"
        ],
        "summary": "Generate and save a new TOTP recovery code",
        "description": "Generates a new TOTP recovery code for the authenticated user, stores its hash, and returns the plain text recovery code.",
        "operationId": "totpSaveCode",
        "responses": {
          "200": {
            "description": "Recovery code generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "recoveryCode": {
                      "type": "string",
                      "example": "ABC123DEF456"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "403": {
            "description": "Invalid CSRF token or unauthorized"
          },
          "405": {
            "description": "Method not allowed"
          }
        }
      }
    },
    "/api/totp_setup.php": {
      "get": {
        "tags": [
          "TOTP"
        ],
        "summary": "Set up TOTP and generate a QR code",
        "description": "Generates (or retrieves) the TOTP secret for the user and builds a QR code image for scanning.",
        "operationId": "setupTOTP",
        "responses": {
          "200": {
            "description": "QR code image for TOTP setup",
            "content": {
              "image/png": {}
            }
          },
          "403": {
            "description": "Unauthorized or invalid CSRF token"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/totp_verify.php": {
      "post": {
        "tags": [
          "TOTP"
        ],
        "summary": "Verify TOTP code",
        "description": "Verifies a TOTP code and completes login for pending users or validates TOTP for setup verification.",
        "operationId": "verifyTOTP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "totp_code"
                ],
                "properties": {
                  "totp_code": {
                    "type": "string",
                    "example": "123456"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "TOTP successfully verified",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "message": {
                      "type": "string",
                      "example": "Login successful"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request (e.g., invalid input)"
          },
          "403": {
            "description": "Not authenticated or invalid CSRF token"
          },
          "429": {
            "description": "Too many attempts. Try again later."
          }
        }
      }
    },
    "/api/updateUserPanel.php": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Update user panel settings",
        "description": "Updates user panel settings by disabling TOTP when not enabled. Accessible to authenticated users.",
        "operationId": "updateUserPanel",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "totp_enabled"
                ],
                "properties": {
                  "totp_enabled": {
                    "type": "boolean",
                    "example": false
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User panel updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "User panel updated: TOTP disabled"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Invalid CSRF token"
          }
        }
      }
    },
    "/api/updateUserPermissions.php": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Update user permissions",
        "description": "Updates permissions for users. Only available to authenticated admin users.",
        "operationId": "updateUserPermissions",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "permissions"
                ],
                "properties": {
                  "permissions": {
                    "type": "array",
                    "items": {
                      "properties": {
                        "username": {
                          "type": "string",
                          "example": "johndoe"
                        },
                        "folderOnly": {
                          "type": "boolean",
                          "example": true
                        },
                        "readOnly": {
                          "type": "boolean",
                          "example": false
                        },
                        "disableUpload": {
                          "type": "boolean",
                          "example": false
                        }
                      },
                      "type": "object"
                    }
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User permissions updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "string",
                      "example": "User permissions updated successfully."
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Invalid CSRF token"
          }
        }
      }
    },
    "/api/upload/removeChunks.php": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "summary": "Remove temporary chunk directory",
        "description": "Deletes the temporary directory used for a chunked upload. Requires a valid CSRF token in the form field.",
        "operationId": "removeChunks",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "folder"
                ],
                "properties": {
                  "folder": {
                    "type": "string",
                    "example": "resumable_myupload123"
                  },
                  "csrf_token": {
                    "description": "CSRF token for this session",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removal result",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "Temporary folder removed."
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Invalid CSRF token"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SimpleSuccess": {
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          }
        },
        "type": "object"
      },
      "SimpleError": {
        "properties": {
          "error": {
            "type": "string",
            "example": "Something went wrong"
          }
        },
        "type": "object"
      },
      "LoginOptionsPublic": {
        "properties": {
          "disableFormLogin": {
            "type": "boolean"
          },
          "disableBasicAuth": {
            "type": "boolean"
          },
          "disableOIDCLogin": {
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "LoginOptionsAdminExtra": {
        "properties": {
          "authBypass": {
            "type": "boolean",
            "nullable": true
          },
          "authHeaderName": {
            "type": "string",
            "example": "X-Remote-User",
            "nullable": true
          }
        },
        "type": "object"
      },
      "OIDCConfigPublic": {
        "properties": {
          "providerUrl": {
            "type": "string",
            "example": "https://accounts.example.com"
          },
          "redirectUri": {
            "type": "string",
            "example": "https://your.filerise.app/callback"
          }
        },
        "type": "object"
      },
      "AdminGetConfigPublic": {
        "required": [
          "header_title",
          "loginOptions",
          "globalOtpauthUrl",
          "enableWebDAV",
          "sharedMaxUploadSize",
          "oidc"
        ],
        "properties": {
          "header_title": {
            "type": "string",
            "example": "FileRise"
          },
          "loginOptions": {
            "$ref": "#/components/schemas/LoginOptionsPublic"
          },
          "globalOtpauthUrl": {
            "type": "string"
          },
          "enableWebDAV": {
            "type": "boolean"
          },
          "sharedMaxUploadSize": {
            "type": "integer",
            "format": "int64"
          },
          "oidc": {
            "$ref": "#/components/schemas/OIDCConfigPublic"
          }
        },
        "type": "object"
      },
      "AdminGetConfigAdmin": {
        "allOf": [
          {
            "properties": {
              "loginOptions": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/LoginOptionsPublic"
                  },
                  {
                    "$ref": "#/components/schemas/LoginOptionsAdminExtra"
                  }
                ]
              }
            },
            "type": "object"
          },
          {
            "$ref": "#/components/schemas/AdminGetConfigPublic"
          }
        ]
      },
      "AdminUpdateConfigRequest": {
        "properties": {
          "header_title": {
            "type": "string",
            "maxLength": 100,
            "example": "FileRise"
          },
          "loginOptions": {
            "properties": {
              "disableFormLogin": {
                "type": "boolean",
                "example": false
              },
              "disableBasicAuth": {
                "type": "boolean",
                "example": false
              },
              "disableOIDCLogin": {
                "description": "false = OIDC enabled",
                "type": "boolean",
                "example": true
              },
              "authBypass": {
                "type": "boolean",
                "example": false
              },
              "authHeaderName": {
                "description": "Letters/numbers/dashes only",
                "type": "string",
                "pattern": "^[A-Za-z0-9\\\\-]+$",
                "example": "X-Remote-User"
              }
            },
            "type": "object",
            "additionalProperties": false
          },
          "globalOtpauthUrl": {
            "type": "string",
            "example": "otpauth://totp/{label}?secret={secret}&issuer=FileRise"
          },
          "enableWebDAV": {
            "type": "boolean",
            "example": false
          },
          "sharedMaxUploadSize": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "example": 52428800
          },
          "oidc": {
            "description": "When disableOIDCLogin=false (OIDC enabled), providerUrl, redirectUri, and clientId are required.",
            "properties": {
              "providerUrl": {
                "type": "string",
                "format": "uri",
                "example": "https://issuer.example.com"
              },
              "clientId": {
                "type": "string",
                "example": "my-client-id"
              },
              "clientSecret": {
                "type": "string",
                "writeOnly": true,
                "example": "***"
              },
              "redirectUri": {
                "type": "string",
                "format": "uri",
                "example": "https://app.example.com/auth/callback"
              }
            },
            "type": "object",
            "additionalProperties": false
          }
        },
        "type": "object",
        "additionalProperties": false
      }
    },
    "responses": {
      "200": {
        "description": "Move result (model-defined)"
      },
      "400": {
        "description": "Invalid input"
      },
      "401": {
        "description": "Unauthorized"
      },
      "403": {
        "description": "Forbidden"
      },
      "500": {
        "description": "Internal error"
      },
      "Unauthorized": {
        "description": "Unauthorized (no session)",
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "error": {
                  "type": "string",
                  "example": "Unauthorized"
                }
              },
              "type": "object"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden (not enough privileges)",
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "error": {
                  "type": "string",
                  "example": "Invalid CSRF token."
                }
              },
              "type": "object"
            }
          }
        }
      }
    },
    "parameters": {
      "X-CSRF-Token": {
        "name": "X-CSRF-Token",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "requestBodies": {},
    "securitySchemes": {
      "cookieAuth": {
        "type": "apiKey",
        "description": "Session cookie used for authenticated endpoints",
        "name": "PHPSESSID",
        "in": "cookie"
      },
      "CsrfHeader": {
        "type": "apiKey",
        "description": "CSRF token header required for state-changing requests",
        "name": "X-CSRF-Token",
        "in": "header"
      }
    }
  },
  "tags": [
    {
      "name": "Admin",
      "description": "Admin endpoints"
    }
  ]
}