fix(admin): modal bugs; chore(api): update ReDoc SRI; docs(openapi): add annotations + spec
This commit is contained in:
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## Changes 10/19/2025 (v1.5.2)
|
||||
|
||||
fix(admin): modal bugs; chore(api): update ReDoc SRI; docs(openapi): add annotations + spec
|
||||
|
||||
- adminPanel.js
|
||||
- Fix modal open/close reliability and stacking order
|
||||
- Prevent background scroll while modal is open
|
||||
- Tidy focus/keyboard handling for better UX
|
||||
|
||||
- style.css
|
||||
- Polish styles for Folder Access + Users views (spacing, tables, badges)
|
||||
- Improve responsiveness and visual consistency
|
||||
|
||||
- api.php
|
||||
- Update Redoc SRI hash and pin to the current bundle URL
|
||||
|
||||
- OpenAPI
|
||||
- Add/refresh inline @OA annotations across endpoints
|
||||
- Introduce src/openapi/Components.php with base Info/Server,
|
||||
common responses, and shared components
|
||||
- Regenerate and commit openapi.json.dist
|
||||
|
||||
- public/js/adminPanel.js
|
||||
- public/css/style.css
|
||||
- public/api.php
|
||||
- src/openapi/Components.php
|
||||
- openapi.json.dist
|
||||
- public/api/** (annotated endpoints)
|
||||
|
||||
---
|
||||
|
||||
## Changes 10/19/2025 (v1.5.1)
|
||||
|
||||
fix(config/ui): serve safe public config to non-admins; init early; gate trash UI to admins; dynamic title; demo toast (closes #56)
|
||||
|
||||
@@ -63,7 +63,7 @@ Upload, organize, and share files or folders through a sleek web interface. **Fi
|
||||
[](https://demo.filerise.net)
|
||||
**Demo credentials:** `demo` / `demo`
|
||||
|
||||
Curious about the UI? **Check out the live demo:** <https://demo.filerise.net> (login with username “demo” and password “demo”). *The demo is read-only for security*. Explore the interface, switch themes, preview files, and see FileRise in action!
|
||||
Curious about the UI? **Check out the live demo:** <https://demo.filerise.net> (login with username “demo” and password “demo”). **The demo is read-only for security.** Explore the interface, switch themes, preview files, and see FileRise in action!
|
||||
|
||||
---
|
||||
|
||||
|
||||
5090
openapi.json.dist
5090
openapi.json.dist
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,7 @@ if (isset($_GET['spec'])) {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>FileRise API Docs</title>
|
||||
<script defer src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"
|
||||
integrity="sha384-4vOjrBu7SuDWXcAw1qFznVLA/sKL+0l4nn+J1HY8w7cpa6twQEYuh4b0Cwuo7CyX"
|
||||
integrity="sha384-70P5pmIdaQdVbxvjhrcTDv1uKcKqalZ3OHi7S2J+uzDl0PW8dO6L+pHOpm9EEjGJ"
|
||||
crossorigin="anonymous"></script>
|
||||
<script defer src="/js/redoc-init.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
<?php
|
||||
// public/api/addUser.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/addUser.php",
|
||||
* 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",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username", "password"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="password", type="string", example="securepassword"),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User added successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User added successfully")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
<?php
|
||||
// public/api/admin/acl/getGrants.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/admin/acl/getGrants.php",
|
||||
* summary="Get ACL grants for a user",
|
||||
* tags={"Admin","ACL"},
|
||||
* security={{"cookieAuth":{}}},
|
||||
* @OA\Parameter(name="user", in="query", required=true, @OA\Schema(type="string")),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Map of folder → grant flags",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* required={"grants"},
|
||||
* @OA\Property(property="grants", ref="#/components/schemas/GrantsMap")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid user"),
|
||||
* @OA\Response(response=401, description="Unauthorized")
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
<?php
|
||||
// public/api/admin/acl/saveGrants.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/admin/acl/saveGrants.php",
|
||||
* summary="Save ACL grants (single-user or batch)",
|
||||
* tags={"Admin","ACL"},
|
||||
* security={{"cookieAuth":{}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* description="Either {user,grants} or {changes:[{user,grants}]}",
|
||||
* @OA\JsonContent(oneOf={
|
||||
* @OA\Schema(ref="#/components/schemas/SaveGrantsSingle"),
|
||||
* @OA\Schema(ref="#/components/schemas/SaveGrantsBatch")
|
||||
* })
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Saved"),
|
||||
* @OA\Response(response=400, description="Invalid payload"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Invalid CSRF")
|
||||
* )
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../../../../config/config.php';
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
<?php
|
||||
// public/api/admin/getConfig.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/admin/getConfig.php",
|
||||
* tags={"Admin"},
|
||||
* summary="Get UI configuration",
|
||||
* description="Returns a public subset for everyone; authenticated admins receive additional loginOptions fields.",
|
||||
* operationId="getAdminConfig",
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Configuration loaded",
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema(ref="#/components/schemas/AdminGetConfigPublic"),
|
||||
* @OA\Schema(ref="#/components/schemas/AdminGetConfigAdmin")
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=500, description="Server error")
|
||||
* )
|
||||
*
|
||||
* Retrieves the admin configuration settings and outputs JSON.
|
||||
* @return void
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,35 @@
|
||||
<?php
|
||||
// public/api/admin/readMetadata.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/admin/readMetadata.php",
|
||||
* summary="Read share metadata JSON",
|
||||
* description="Admin-only: returns the cleaned metadata for file or folder share links.",
|
||||
* tags={"Admin"},
|
||||
* operationId="readMetadata",
|
||||
* security={{"cookieAuth":{}}},
|
||||
* @OA\Parameter(
|
||||
* name="file",
|
||||
* in="query",
|
||||
* required=true,
|
||||
* description="Which metadata file to read",
|
||||
* @OA\Schema(type="string", enum={"share_links.json","share_folder_links.json"})
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="OK",
|
||||
* @OA\JsonContent(oneOf={
|
||||
* @OA\Schema(ref="#/components/schemas/ShareLinksMap"),
|
||||
* @OA\Schema(ref="#/components/schemas/ShareFolderLinksMap")
|
||||
* })
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Missing or invalid file param"),
|
||||
* @OA\Response(response=403, description="Forbidden (admin only)"),
|
||||
* @OA\Response(response=500, description="Corrupted JSON")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
|
||||
// Only admins may read these
|
||||
|
||||
@@ -1,6 +1,45 @@
|
||||
<?php
|
||||
// public/api/admin/updateConfig.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/admin/updateConfig.php",
|
||||
* summary="Update admin configuration",
|
||||
* description="Merges the provided settings into the on-disk configuration and persists them. Requires an authenticated admin session and a valid CSRF token. When OIDC is enabled (disableOIDCLogin=false), `providerUrl`, `redirectUri`, and `clientId` are required and must be HTTPS (HTTP allowed only for localhost).",
|
||||
* operationId="updateAdminConfig",
|
||||
* tags={"Admin"},
|
||||
* security={ {{"cookieAuth": {}, "CsrfHeader": {}}} },
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(ref="#/components/schemas/AdminUpdateConfigRequest")
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Configuration updated",
|
||||
* @OA\JsonContent(ref="#/components/schemas/SimpleSuccess")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Validation error (e.g., bad authHeaderName, missing OIDC fields when enabled, or negative upload limit)",
|
||||
* @OA\JsonContent(ref="#/components/schemas/SimpleError")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Unauthorized access or invalid CSRF token",
|
||||
* @OA\JsonContent(ref="#/components/schemas/SimpleError")
|
||||
* // or: ref to the reusable response
|
||||
* // ref="#/components/responses/Forbidden"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Server error while loading or saving configuration",
|
||||
* @OA\JsonContent(ref="#/components/schemas/SimpleError")
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AdminController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,52 @@
|
||||
<?php
|
||||
// public/api/auth/auth.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/auth/auth.php",
|
||||
* 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",
|
||||
* tags={"Auth"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username", "password"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="password", type="string", example="secretpassword"),
|
||||
* @OA\Property(property="remember_me", type="boolean", example=true),
|
||||
* @OA\Property(property="totp_code", type="string", example="123456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Login successful; returns user info and status",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="success", type="string", example="Login successful"),
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., missing credentials)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized (e.g., invalid credentials, too many attempts)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many failed login attempts"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Handles user authentication via OIDC or form-based login.
|
||||
*
|
||||
* @return void Redirects on success or outputs JSON error.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||
|
||||
@@ -1,6 +1,35 @@
|
||||
<?php
|
||||
// public/api/auth/checkAuth.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/checkAuth.php",
|
||||
* summary="Check authentication status",
|
||||
* operationId="checkAuth",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Authenticated status or setup flag",
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(property="authenticated", type="boolean", example=true),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true),
|
||||
* @OA\Property(property="totp_enabled", type="boolean", example=false),
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="folderOnly", type="boolean", example=false)
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(property="setup", type="boolean", example=true)
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/auth/login_basic.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/login_basic.php",
|
||||
* 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",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Login successful; redirects to index.html",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="Login successful")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized due to missing credentials or invalid credentials."
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Handles HTTP Basic authentication (with optional TOTP) and logs the user in.
|
||||
*
|
||||
* @return void Redirects on success or sends a 401 header.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
<?php
|
||||
// public/api/auth/logout.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/auth/logout.php",
|
||||
* summary="Logout user",
|
||||
* description="Clears the session, removes persistent login tokens, and redirects the user to the login page.",
|
||||
* operationId="logoutUser",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=302,
|
||||
* description="Redirects to the login page with a logout flag."
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Logs the user out by clearing session data, removing persistent tokens, and destroying the session.
|
||||
*
|
||||
* @return void Redirects to index.html with a logout flag.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
<?php
|
||||
// public/api/auth/token.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/token.php",
|
||||
* summary="Retrieve CSRF token and share URL",
|
||||
* description="Returns the current CSRF token along with the configured share URL.",
|
||||
* operationId="getToken",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="CSRF token and share URL",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="csrf_token", type="string", example="0123456789abcdef..."),
|
||||
* @OA\Property(property="share_url", type="string", example="https://yourdomain.com/share.php")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Returns the CSRF token and share URL.
|
||||
*
|
||||
* @return void Outputs the JSON response.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/AuthController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,44 @@
|
||||
<?php
|
||||
// public/api/changePassword.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/changePassword.php",
|
||||
* 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",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"oldPassword", "newPassword", "confirmPassword"},
|
||||
* @OA\Property(property="oldPassword", type="string", example="oldpass123"),
|
||||
* @OA\Property(property="newPassword", type="string", example="newpass456"),
|
||||
* @OA\Property(property="confirmPassword", type="string", example="newpass456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Password updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="Password updated successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
<?php
|
||||
// public/api/file/copyFiles.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/copyFiles.php",
|
||||
* summary="Copy files between folders",
|
||||
* description="Requires read access on source and write access on destination. Enforces folder scope and ownership.",
|
||||
* operationId="copyFiles",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(
|
||||
* name="X-CSRF-Token", in="header", required=true,
|
||||
* description="CSRF token from the current session",
|
||||
* @OA\Schema(type="string")
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"source","destination","files"},
|
||||
* @OA\Property(property="source", type="string", example="root"),
|
||||
* @OA\Property(property="destination", type="string", example="userA/projects"),
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"report.pdf","notes.txt"})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Copy result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid request or folder name"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
<?php
|
||||
// public/api/file/createFile.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/createFile.php",
|
||||
* summary="Create an empty file",
|
||||
* description="Requires write access on the target folder. Enforces folder-only scope.",
|
||||
* operationId="createFile",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","name"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="name", type="string", example="new.txt")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Creation result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
<?php
|
||||
// public/api/file/createShareLink.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/createShareLink.php",
|
||||
* 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",
|
||||
* tags={"Shares"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","file"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="file", type="string", example="invoice.pdf"),
|
||||
* @OA\Property(property="expirationValue", type="integer", example=60),
|
||||
* @OA\Property(property="expirationUnit", type="string", enum={"seconds","minutes","hours","days"}, example="minutes"),
|
||||
* @OA\Property(property="password", type="string", example="")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Share link created",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="token", type="string", example="abc123"),
|
||||
* @OA\Property(property="url", type="string", example="/api/file/share.php?token=abc123"),
|
||||
* @OA\Property(property="expires", type="integer", example=1700000000)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
<?php
|
||||
// public/api/file/deleteFiles.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/deleteFiles.php",
|
||||
* summary="Delete files to Trash",
|
||||
* description="Requires write access on the folder and (for non-admins) ownership of the files.",
|
||||
* operationId="deleteFiles",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(
|
||||
* name="X-CSRF-Token", in="header", required=true,
|
||||
* @OA\Schema(type="string")
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","files"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"old.docx","draft.md"})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Delete result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/deleteShareLink.php",
|
||||
* summary="Delete a share link by token",
|
||||
* description="Deletes a share token. NOTE: Current implementation does not require authentication.",
|
||||
* operationId="deleteShareLink",
|
||||
* tags={"Shares"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"token"},
|
||||
* @OA\Property(property="token", type="string", example="abc123")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Deletion result (success or not found)")
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
<?php
|
||||
// public/api/file/deleteTrashFiles.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/deleteTrashFiles.php",
|
||||
* summary="Permanently delete Trash items (admin only)",
|
||||
* operationId="deleteTrashFiles",
|
||||
* tags={"Trash"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema(
|
||||
* required={"deleteAll"},
|
||||
* @OA\Property(property="deleteAll", type="boolean", example=true)
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* required={"files"},
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"trash/abc","trash/def"})
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Deletion result (model-defined)"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Admin only"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
<?php
|
||||
// public/api/file/download.php
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/download.php",
|
||||
* summary="Download a file",
|
||||
* description="Requires view access (or own-only with ownership). Streams the file with appropriate Content-Type.",
|
||||
* operationId="downloadFile",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="folder", in="query", required=true, @OA\Schema(type="string"), example="root"),
|
||||
* @OA\Parameter(name="file", in="query", required=true, @OA\Schema(type="string"), example="photo.jpg"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Binary file",
|
||||
* content={
|
||||
* "application/octet-stream": @OA\MediaType(
|
||||
* mediaType="application/octet-stream",
|
||||
* @OA\Schema(type="string", format="binary")
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid folder/file"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=404, description="Not found")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,41 @@
|
||||
<?php
|
||||
// public/api/file/downloadZip.php
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/downloadZip.php",
|
||||
* summary="Download multiple files as a ZIP",
|
||||
* description="Requires view access (or own-only with ownership). May be gated by account flag.",
|
||||
* operationId="downloadZip",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","files"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"a.jpg","b.png"})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="ZIP archive",
|
||||
* content={
|
||||
* "application/zip": @OA\MediaType(
|
||||
* mediaType="application/zip",
|
||||
* @OA\Schema(type="string", format="binary")
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
<?php
|
||||
// public/api/file/extractZip.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/extractZip.php",
|
||||
* summary="Extract ZIP file(s) into a folder",
|
||||
* description="Requires write access on the target folder.",
|
||||
* operationId="extractZip",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","files"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"archive.zip"})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Extraction result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
<?php
|
||||
// public/api/file/getFileList.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/getFileList.php",
|
||||
* summary="List files in a folder",
|
||||
* description="Requires view access (full) or read_own (own-only results).",
|
||||
* operationId="getFileList",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="folder", in="query", required=true, @OA\Schema(type="string"), example="root"),
|
||||
* @OA\Response(response=200, description="Listing result (model-defined JSON)"),
|
||||
* @OA\Response(response=400, description="Invalid folder"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
<?php
|
||||
// public/api/file/getFileTag.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/getFileTags.php",
|
||||
* summary="Get global file tags",
|
||||
* description="Returns tag metadata (no auth in current implementation).",
|
||||
* operationId="getFileTags",
|
||||
* tags={"Tags"},
|
||||
* @OA\Response(response=200, description="Tags map (model-defined JSON)")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/getShareLinks.php",
|
||||
* summary="Get (raw) share links file",
|
||||
* description="Returns the full share links JSON (no auth in current implementation).",
|
||||
* operationId="getShareLinks",
|
||||
* tags={"Shares"},
|
||||
* @OA\Response(response=200, description="Share links (model-defined JSON)")
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
<?php
|
||||
// public/api/file/getTrashItems.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/getTrashItems.php",
|
||||
* summary="List items in Trash (admin only)",
|
||||
* operationId="getTrashItems",
|
||||
* tags={"Trash"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Response(response=200, description="Trash contents (model-defined JSON)"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Admin only"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
<?php
|
||||
// public/api/file/moveFiles.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/moveFiles.php",
|
||||
* operationId="moveFiles",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth":{}}},
|
||||
* @OA\RequestBody(ref="#/components/requestBodies/MoveFilesRequest"),
|
||||
* @OA\Response(response=200, description="Moved"),
|
||||
* @OA\Response(response=400, description="Bad Request"),
|
||||
* @OA\Response(response=401, ref="#/components/responses/Unauthorized"),
|
||||
* @OA\Response(response=403, ref="#/components/responses/Forbidden")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/file/renameFile.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/file/renameFile.php",
|
||||
* summary="Rename a file",
|
||||
* description="Requires write access; non-admins must own the file.",
|
||||
* operationId="renameFile",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","oldName","newName"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="oldName", type="string", example="old.pdf"),
|
||||
* @OA\Property(property="newName", type="string", example="new.pdf")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Rename result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
<?php
|
||||
// public/api/file/restoreFiles.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/restoreFiles.php",
|
||||
* summary="Restore files from Trash (admin only)",
|
||||
* operationId="restoreFiles",
|
||||
* tags={"Trash"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"files"},
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"), example={"trash/12345.json"})
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Restore result (model-defined)"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Admin only"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/file/saveFile.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/file/saveFile.php",
|
||||
* 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",
|
||||
* tags={"Files"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","fileName","content"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="fileName", type="string", example="readme.txt"),
|
||||
* @OA\Property(property="content", type="string", example="Hello world")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Save result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input or disallowed extension"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
<?php
|
||||
// public/api/file/saveFileTag.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/file/saveFileTag.php",
|
||||
* summary="Save tags for a file (or delete one)",
|
||||
* description="Requires write access and (for non-admins) ownership when modifying.",
|
||||
* operationId="saveFileTag",
|
||||
* tags={"Tags"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder","file"},
|
||||
* @OA\Property(property="folder", type="string", example="root"),
|
||||
* @OA\Property(property="file", type="string", example="doc.md"),
|
||||
* @OA\Property(property="tags", type="array", @OA\Items(type="string"), example={"work","urgent"}),
|
||||
* @OA\Property(property="deleteGlobal", type="boolean", example=false),
|
||||
* @OA\Property(property="tagToDelete", type="string", nullable=true, example=null)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Save result (model-defined)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=500, description="Internal error")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/file/share.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/file/share.php",
|
||||
* 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",
|
||||
* tags={"Shares"},
|
||||
* @OA\Parameter(name="token", in="query", required=true, @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="pass", in="query", required=false, @OA\Schema(type="string")),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Binary file (or HTML password form when missing password)",
|
||||
* content={
|
||||
* "application/octet-stream": @OA\MediaType(
|
||||
* mediaType="application/octet-stream",
|
||||
* @OA\Schema(type="string", format="binary")
|
||||
* ),
|
||||
* "text/html": @OA\MediaType(mediaType="text/html")
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Missing token / invalid input"),
|
||||
* @OA\Response(response=403, description="Expired or invalid password"),
|
||||
* @OA\Response(response=404, description="Not found")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FileController.php';
|
||||
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
<?php
|
||||
// public/api/folder/capabilities.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/folder/capabilities.php",
|
||||
* 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",
|
||||
* tags={"Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="folder",
|
||||
* in="query",
|
||||
* required=false,
|
||||
* description="Target folder path. Defaults to 'root'. Supports nested paths like 'team/reports'.",
|
||||
* @OA\Schema(type="string"),
|
||||
* example="projects/acme"
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Capabilities computed successfully.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* required={"user","folder","isAdmin","flags","canView","canUpload","canCreate","canRename","canDelete","canMoveIn","canShare"},
|
||||
* @OA\Property(property="user", type="string", example="alice"),
|
||||
* @OA\Property(property="folder", type="string", example="projects/acme"),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=false),
|
||||
* @OA\Property(
|
||||
* property="flags",
|
||||
* type="object",
|
||||
* required={"folderOnly","readOnly","disableUpload"},
|
||||
* @OA\Property(property="folderOnly", type="boolean", example=false),
|
||||
* @OA\Property(property="readOnly", type="boolean", example=false),
|
||||
* @OA\Property(property="disableUpload", type="boolean", example=false)
|
||||
* ),
|
||||
* @OA\Property(property="owner", type="string", nullable=true, example="alice"),
|
||||
* @OA\Property(property="canView", type="boolean", example=true, description="User can view items in this folder."),
|
||||
* @OA\Property(property="canUpload", type="boolean", example=true, description="User can upload/edit/rename/move/delete items (i.e., WRITE)."),
|
||||
* @OA\Property(property="canCreate", type="boolean", example=true, description="User can create subfolders here."),
|
||||
* @OA\Property(property="canRename", type="boolean", example=true, description="User can rename items here."),
|
||||
* @OA\Property(property="canDelete", type="boolean", example=true, description="User can delete items here."),
|
||||
* @OA\Property(property="canMoveIn", type="boolean", example=true, description="User can move items into this folder."),
|
||||
* @OA\Property(property="canShare", type="boolean", example=false, description="User can create share links for this folder.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid folder name."),
|
||||
* @OA\Response(response=401, ref="#/components/responses/Unauthorized")
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
<?php
|
||||
// public/api/folder/createFolder.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/createFolder.php",
|
||||
* summary="Create a new folder",
|
||||
* description="Requires authentication, CSRF token, and write access to the parent folder. Seeds ACL owner.",
|
||||
* operationId="createFolder",
|
||||
* tags={"Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(
|
||||
* name="X-CSRF-Token", in="header", required=true,
|
||||
* description="CSRF token from the current session",
|
||||
* @OA\Schema(type="string")
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folderName"},
|
||||
* @OA\Property(property="folderName", type="string", example="reports"),
|
||||
* @OA\Property(property="parent", type="string", nullable=true, example="root",
|
||||
* description="Parent folder (default root)")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Creation result (model-defined JSON)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=405, description="Method not allowed")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
<?php
|
||||
// public/api/folder/createShareFolderLink.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/createShareFolderLink.php",
|
||||
* 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",
|
||||
* tags={"Shared Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder"},
|
||||
* @OA\Property(property="folder", type="string", example="team/reports"),
|
||||
* @OA\Property(property="expirationValue", type="integer", example=60),
|
||||
* @OA\Property(property="expirationUnit", type="string", enum={"seconds","minutes","hours","days"}, example="minutes"),
|
||||
* @OA\Property(property="password", type="string", example=""),
|
||||
* @OA\Property(property="allowUpload", type="integer", enum={0,1}, example=0)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Share folder link created",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="token", type="string", example="sf_abc123"),
|
||||
* @OA\Property(property="url", type="string", example="/api/folder/shareFolder.php?token=sf_abc123"),
|
||||
* @OA\Property(property="expires", type="integer", example=1700000000)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
<?php
|
||||
// public/api/folder/deleteFolder.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/deleteFolder.php",
|
||||
* summary="Delete a folder",
|
||||
* description="Requires authentication, CSRF token, write scope, and (for non-admins) folder ownership.",
|
||||
* operationId="deleteFolder",
|
||||
* tags={"Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder"},
|
||||
* @OA\Property(property="folder", type="string", example="userA/reports")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Deletion result (model-defined JSON)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=405, description="Method not allowed")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/deleteShareFolderLink.php",
|
||||
* summary="Delete a shared-folder link by token (admin only)",
|
||||
* description="Requires authentication, CSRF token, and admin privileges.",
|
||||
* operationId="deleteShareFolderLink",
|
||||
* tags={"Shared Folders","Admin"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"token"},
|
||||
* @OA\Property(property="token", type="string", example="sf_abc123")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Deleted"),
|
||||
* @OA\Response(response=400, description="No token provided"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Admin only"),
|
||||
* @OA\Response(response=404, description="Not found")
|
||||
* )
|
||||
*/
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
<?php
|
||||
// public/api/folder/downloadSharedFile.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/folder/downloadSharedFile.php",
|
||||
* summary="Download a file from a shared folder (by token)",
|
||||
* description="Public endpoint; validates token and file name, then streams the file.",
|
||||
* operationId="downloadSharedFile",
|
||||
* tags={"Shared Folders"},
|
||||
* @OA\Parameter(name="token", in="query", required=true, @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="file", in="query", required=true, @OA\Schema(type="string"), example="report.pdf"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Binary file",
|
||||
* content={
|
||||
* "application/octet-stream": @OA\MediaType(
|
||||
* mediaType="application/octet-stream",
|
||||
* @OA\Schema(type="string", format="binary")
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=404, description="Not found")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,38 @@
|
||||
<?php
|
||||
// public/api/folder/getFolderList.php
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/folder/getFolderList.php",
|
||||
* 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",
|
||||
* tags={"Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(
|
||||
* name="folder", in="query", required=false,
|
||||
* description="Parent folder to include and descend (default all); use 'root' for top-level",
|
||||
* @OA\Schema(type="string"), example="root"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="List of folders",
|
||||
* @OA\JsonContent(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
* @OA\Property(property="folder", type="string", example="team/reports"),
|
||||
* @OA\Property(property="fileCount", type="integer", example=12),
|
||||
* @OA\Property(property="metadataFile", type="string", example="/path/to/meta.json")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid folder"),
|
||||
* @OA\Response(response=401, description="Unauthorized")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/folder/getShareFolderLinks.php",
|
||||
* summary="List active shared-folder links (admin only)",
|
||||
* description="Returns all non-expired shared-folder links. Admin-only.",
|
||||
* operationId="getShareFolderLinks",
|
||||
* tags={"Shared Folders","Admin"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Response(response=200, description="Active share-folder links (model-defined JSON)"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Admin only")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
<?php
|
||||
// public/api/folder/renameFolder.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/renameFolder.php",
|
||||
* 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",
|
||||
* tags={"Folders"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(name="X-CSRF-Token", in="header", required=true, @OA\Schema(type="string")),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"oldFolder","newFolder"},
|
||||
* @OA\Property(property="oldFolder", type="string", example="team/q1"),
|
||||
* @OA\Property(property="newFolder", type="string", example="team/quarter-1")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="Rename result (model-defined JSON)"),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=401, description="Unauthorized"),
|
||||
* @OA\Response(response=403, description="Forbidden"),
|
||||
* @OA\Response(response=405, description="Method not allowed")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
<?php
|
||||
// public/api/folder/shareFolder.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/folder/shareFolder.php",
|
||||
* 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",
|
||||
* tags={"Shared Folders"},
|
||||
* @OA\Parameter(name="token", in="query", required=true, @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="pass", in="query", required=false, @OA\Schema(type="string")),
|
||||
* @OA\Parameter(name="page", in="query", required=false, @OA\Schema(type="integer", minimum=1), example=1),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="HTML page (password form or folder listing)",
|
||||
* content={"text/html": @OA\MediaType(mediaType="text/html")}
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Missing/invalid token"),
|
||||
* @OA\Response(response=403, description="Forbidden or wrong password")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
<?php
|
||||
// public/api/folder/uploadToSharedFolder.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/folder/uploadToSharedFolder.php",
|
||||
* 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",
|
||||
* tags={"Shared Folders"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* content={
|
||||
* "multipart/form-data": @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* required={"token","fileToUpload"},
|
||||
* @OA\Property(property="token", type="string", description="Share token"),
|
||||
* @OA\Property(property="fileToUpload", type="string", format="binary", description="File to upload")
|
||||
* )
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(response=302, description="Redirect to /api/folder/shareFolder.php?token=..."),
|
||||
* @OA\Response(response=400, description="Upload error or invalid input"),
|
||||
* @OA\Response(response=405, description="Method not allowed")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/FolderController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
<?php
|
||||
// public/api/getUserPermissions.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/getUserPermissions.php",
|
||||
* summary="Retrieve user permissions",
|
||||
* description="Returns the permissions for the current user, or all permissions if the user is an admin.",
|
||||
* operationId="getUserPermissions",
|
||||
* tags={"Users"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Successful response with user permissions",
|
||||
* @OA\JsonContent(type="object")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/getUsers.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/getUsers.php",
|
||||
* summary="Retrieve a list of users",
|
||||
* description="Returns a JSON array of users. Only available to authenticated admin users.",
|
||||
* operationId="getUsers",
|
||||
* tags={"Users"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Successful response with an array of users",
|
||||
* @OA\JsonContent(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="role", type="string", example="admin")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized: the user is not authenticated or is not an admin"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,4 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/profile/getCurrentUser.php",
|
||||
* operationId="getCurrentUser",
|
||||
* tags={"Users"},
|
||||
* security={{"cookieAuth":{}}},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Current user",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* required={"username","isAdmin","totp_enabled","profile_picture"},
|
||||
* @OA\Property(property="username", type="string", example="ryan"),
|
||||
* @OA\Property(property="isAdmin", type="boolean"),
|
||||
* @OA\Property(property="totp_enabled", type="boolean"),
|
||||
* @OA\Property(property="profile_picture", type="string", example="/uploads/profile_pics/ryan.png")
|
||||
* // If you had an array: @OA\Property(property="roles", type="array", @OA\Items(type="string"))
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=401, ref="#/components/responses/Unauthorized")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/models/UserModel.php';
|
||||
|
||||
|
||||
@@ -2,6 +2,57 @@
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/profile/uploadPicture.php",
|
||||
* summary="Upload or replace the current user's profile picture",
|
||||
* description="Accepts a single image file (JPEG, PNG, or GIF) up to 2 MB. Requires a valid session cookie and CSRF token.",
|
||||
* operationId="uploadProfilePicture",
|
||||
* tags={"Users"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="X-CSRF-Token",
|
||||
* in="header",
|
||||
* required=true,
|
||||
* description="Anti-CSRF token associated with the current session.",
|
||||
* @OA\Schema(type="string")
|
||||
* ),
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
* @OA\Schema(
|
||||
* required={"profile_picture"},
|
||||
* @OA\Property(
|
||||
* property="profile_picture",
|
||||
* type="string",
|
||||
* format="binary",
|
||||
* description="JPEG, PNG, or GIF image. Max size: 2 MB."
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Profile picture updated.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* required={"success","url"},
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="url", type="string", example="/uploads/profile_pics/alice_9f3c2e1a8bcd.png")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="No file uploaded, invalid file type, or file too large."),
|
||||
* @OA\Response(response=401, ref="#/components/responses/Unauthorized"),
|
||||
* @OA\Response(response=403, ref="#/components/responses/Forbidden"),
|
||||
* @OA\Response(response=500, description="Server error while saving the picture.")
|
||||
* )
|
||||
*/
|
||||
|
||||
// Always JSON, even on PHP notices
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
<?php
|
||||
// public/api/removeUser.php
|
||||
|
||||
/**
|
||||
* @OA\Delete(
|
||||
* path="/api/removeUser.php",
|
||||
* summary="Remove a user",
|
||||
* description="Removes the specified user from the system. Cannot remove the currently logged-in user.",
|
||||
* operationId="removeUser",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User removed successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User removed successfully")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
// public/api/totp_disable.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/totp_disable.php",
|
||||
* summary="Disable TOTP for the authenticated user",
|
||||
* description="Clears the TOTP secret from the users file for the current user.",
|
||||
* operationId="disableTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="TOTP disabled successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="TOTP disabled successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Not authenticated or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Failed to disable TOTP"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
<?php
|
||||
// public/api/totp_recover.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_recover.php",
|
||||
* summary="Recover TOTP",
|
||||
* description="Verifies a recovery code to disable TOTP and finalize login.",
|
||||
* operationId="recoverTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"recovery_code"},
|
||||
* @OA\Property(property="recovery_code", type="string", example="ABC123DEF456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Recovery successful",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Invalid input or recovery code"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=405,
|
||||
* description="Method not allowed"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many attempts"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
<?php
|
||||
// public/api/totp_saveCode.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_saveCode.php",
|
||||
* 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",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Recovery code generated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="recoveryCode", type="string", example="ABC123DEF456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token or unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=405,
|
||||
* description="Method not allowed"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
<?php
|
||||
// public/api/totp_setup.php
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/totp_setup.php",
|
||||
* 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",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="QR code image for TOTP setup",
|
||||
* @OA\MediaType(
|
||||
* mediaType="image/png"
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Unauthorized or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Server error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
<?php
|
||||
// public/api/totp_verify.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_verify.php",
|
||||
* summary="Verify TOTP code",
|
||||
* description="Verifies a TOTP code and completes login for pending users or validates TOTP for setup verification.",
|
||||
* operationId="verifyTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"totp_code"},
|
||||
* @OA\Property(property="totp_code", type="string", example="123456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="TOTP successfully verified",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="message", type="string", example="Login successful")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., invalid input)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Not authenticated or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many attempts. Try again later."
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
<?php
|
||||
// public/api/updateUserPanel.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/updateUserPanel.php",
|
||||
* summary="Update user panel settings",
|
||||
* description="Updates user panel settings by disabling TOTP when not enabled. Accessible to authenticated users.",
|
||||
* operationId="updateUserPanel",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"totp_enabled"},
|
||||
* @OA\Property(property="totp_enabled", type="boolean", example=false)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User panel updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User panel updated: TOTP disabled")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,52 @@
|
||||
<?php
|
||||
// public/api/updateUserPermissions.php
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/updateUserPermissions.php",
|
||||
* summary="Update user permissions",
|
||||
* description="Updates permissions for users. Only available to authenticated admin users.",
|
||||
* operationId="updateUserPermissions",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"permissions"},
|
||||
* @OA\Property(
|
||||
* property="permissions",
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="folderOnly", type="boolean", example=true),
|
||||
* @OA\Property(property="readOnly", type="boolean", example=false),
|
||||
* @OA\Property(property="disableUpload", type="boolean", example=false)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User permissions updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User permissions updated successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UserController.php';
|
||||
|
||||
|
||||
@@ -1,6 +1,35 @@
|
||||
<?php
|
||||
// public/api/upload/removeChunks.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/upload/removeChunks.php",
|
||||
* 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",
|
||||
* tags={"Uploads"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder"},
|
||||
* @OA\Property(property="folder", type="string", example="resumable_myupload123"),
|
||||
* @OA\Property(property="csrf_token", type="string", description="CSRF token for this session")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Removal result",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="Temporary folder removed.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Invalid input"),
|
||||
* @OA\Response(response=403, description="Invalid CSRF token")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UploadController.php';
|
||||
|
||||
|
||||
@@ -1,5 +1,84 @@
|
||||
<?php
|
||||
// public/api/upload/upload.php
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/upload/upload.php",
|
||||
* summary="Upload a file (supports chunked + full uploads)",
|
||||
* description="Requires a session (cookie) and a CSRF token (header preferred; falls back to form field). Checks user/account flags and folder-level WRITE ACL, then delegates to the model. Returns JSON for chunked uploads; full uploads may redirect after success.",
|
||||
* operationId="handleUpload",
|
||||
* tags={"Uploads"},
|
||||
* security={{"cookieAuth": {}}},
|
||||
* @OA\Parameter(
|
||||
* name="X-CSRF-Token", in="header", required=false,
|
||||
* description="CSRF token for this session (preferred). If omitted, send as form field `csrf_token`.",
|
||||
* @OA\Schema(type="string")
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* content={
|
||||
* "multipart/form-data": @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* required={"fileToUpload"},
|
||||
* @OA\Property(
|
||||
* property="fileToUpload", type="string", format="binary",
|
||||
* description="File or chunk payload."
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="folder", type="string", example="root",
|
||||
* description="Target folder (defaults to 'root' if omitted)."
|
||||
* ),
|
||||
* @OA\Property(property="csrf_token", type="string", description="CSRF token (form fallback)."),
|
||||
* @OA\Property(property="upload_token", type="string", description="Legacy alias for CSRF token (accepted by server)."),
|
||||
* @OA\Property(property="resumableChunkNumber", type="integer"),
|
||||
* @OA\Property(property="resumableTotalChunks", type="integer"),
|
||||
* @OA\Property(property="resumableChunkSize", type="integer"),
|
||||
* @OA\Property(property="resumableCurrentChunkSize", type="integer"),
|
||||
* @OA\Property(property="resumableTotalSize", type="integer"),
|
||||
* @OA\Property(property="resumableType", type="string"),
|
||||
* @OA\Property(property="resumableIdentifier", type="string"),
|
||||
* @OA\Property(property="resumableFilename", type="string"),
|
||||
* @OA\Property(property="resumableRelativePath", type="string")
|
||||
* )
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="JSON result (success, chunk status, or CSRF refresh).",
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema( ; Success (full or model-returned)
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="File uploaded successfully"),
|
||||
* @OA\Property(property="newFilename", type="string", example="5f2d7c123a_example.png")
|
||||
* ),
|
||||
* @OA\Schema( ; Chunk flow
|
||||
* type="object",
|
||||
* @OA\Property(property="status", type="string", example="chunk uploaded")
|
||||
* ),
|
||||
* @OA\Schema( ; CSRF soft-refresh path
|
||||
* type="object",
|
||||
* @OA\Property(property="csrf_expired", type="boolean", example=true),
|
||||
* @OA\Property(property="csrf_token", type="string", example="b1c2...f9")
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=302,
|
||||
* description="Redirect after a successful full upload.",
|
||||
* @OA\Header(header="Location", description="Where the client is redirected", @OA\Schema(type="string"))
|
||||
* ),
|
||||
* @OA\Response(response=400, description="Bad request (missing/invalid fields, model error)"),
|
||||
* @OA\Response(response=401, description="Unauthorized (no session)"),
|
||||
* @OA\Response(response=403, description="Forbidden (upload disabled or no WRITE to folder)"),
|
||||
* @OA\Response(response=500, description="Server error while processing upload")
|
||||
* )
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/controllers/UploadController.php';
|
||||
|
||||
|
||||
@@ -2306,3 +2306,6 @@ body.dark-mode .user-dropdown .user-menu .item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
:root { --perm-caret: #444; } /* light */
|
||||
body.dark-mode { --perm-caret: #ccc; } /* dark */
|
||||
@@ -4,7 +4,7 @@ import { loadAdminConfigFunc } from './auth.js';
|
||||
import { showToast, toggleVisibility, attachEnterKeyListener } from './domUtils.js';
|
||||
import { sendRequest } from './networkUtils.js';
|
||||
|
||||
const version = "v1.5.1";
|
||||
const version = "v1.5.2";
|
||||
const adminTitle = `${t("admin_panel")} <small style="font-size:12px;color:gray;">${version}</small>`;
|
||||
|
||||
// Translate with fallback: if t(key) just echos the key, use a readable string.
|
||||
@@ -615,13 +615,24 @@ function renderFolderGrantsUI(username, container, folders, grants) {
|
||||
// toolbar
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.className = 'folder-access-toolbar';
|
||||
// Toolbar (bulk toggles with descriptions)
|
||||
toolbar.innerHTML = `
|
||||
<input type="text" class="form-control" style="max-width:220px;" placeholder="${tf('search_folders', 'Search folders')}" />
|
||||
<label class="muted"><input type="checkbox" data-bulk="view" /> ${tf('view_all','View (all)')}</label>
|
||||
<label class="muted"><input type="checkbox" data-bulk="viewOwn" /> ${tf('view_own','View (own)')}</label>
|
||||
<label class="muted"><input type="checkbox" data-bulk="upload" /> ${tf('upload','Upload')}</label>
|
||||
<label class="muted"><input type="checkbox" data-bulk="manage" /> ${tf('manage','Manage')}</label>
|
||||
<label class="muted"><input type="checkbox" data-bulk="share" /> ${tf('share','Share')}</label>
|
||||
<label class="muted" title="${tf('view_all_help', 'See all files in this folder (everyone’s files)')}">
|
||||
<input type="checkbox" data-bulk="view" /> ${tf('view_all', 'View (all)')}
|
||||
</label>
|
||||
<label class="muted" title="${tf('view_own_help', 'See only files you uploaded in this folder')}">
|
||||
<input type="checkbox" data-bulk="viewOwn" /> ${tf('view_own', 'View (own)')}
|
||||
</label>
|
||||
<label class="muted" title="${tf('write_help', 'Create/upload files and edit/rename/move/delete items in this folder')}">
|
||||
<input type="checkbox" data-bulk="upload" /> ${tf('write_full', 'Write (upload/edit/delete)')}
|
||||
</label>
|
||||
<label class="muted" title="${tf('manage_help', 'Owner-level: can grant access; implies View (all) + Write + Share')}">
|
||||
<input type="checkbox" data-bulk="manage" /> ${tf('manage', 'Manage')}
|
||||
</label>
|
||||
<label class="muted" title="${tf('share_help', 'Create/manage share links; implies View (all)')}">
|
||||
<input type="checkbox" data-bulk="share" /> ${tf('share', 'Share')}
|
||||
</label>
|
||||
<span class="muted">(${tf('applies_to_filtered', 'applies to filtered list')})</span>
|
||||
`;
|
||||
container.appendChild(toolbar);
|
||||
@@ -631,14 +642,25 @@ function renderFolderGrantsUI(username, container, folders, grants) {
|
||||
list.className = 'folder-access-list';
|
||||
container.appendChild(list);
|
||||
|
||||
// Header (compact labels, descriptive tooltips so the column width stays the same)
|
||||
const headerHtml = `
|
||||
<div class="folder-access-header">
|
||||
<div>${tf('folder', 'Folder')}</div>
|
||||
<div class="perm-col">${tf('view_all','View (all)')}</div>
|
||||
<div class="perm-col">${tf('view_own','View (own)')}</div>
|
||||
<div class="perm-col">${tf('upload','Upload')}</div>
|
||||
<div class="perm-col">${tf('manage','Manage')}</div>
|
||||
<div class="perm-col">${tf('share','Share')}</div>
|
||||
<div title="${tf('folder_help', 'Folder path within FileRise')}">${tf('folder', 'Folder')}</div>
|
||||
<div class="perm-col" title="${tf('view_all_help', 'See all files in this folder (everyones files)')}">
|
||||
${tf('view_all', 'View (all)')}
|
||||
</div>
|
||||
<div class="perm-col" title="${tf('view_own_help', 'See only files you uploaded in this folder')}">
|
||||
${tf('view_own', 'View (own)')}
|
||||
</div>
|
||||
<div class="perm-col" title="${tf('write_help', 'Create/upload files and edit/rename/move/delete items in this folder')}">
|
||||
${tf('write', 'Write')}
|
||||
</div>
|
||||
<div class="perm-col" title="${tf('manage_help', 'Owner-level: can grant access; implies View (all) + Write + Share')}">
|
||||
${tf('manage', 'Manage')}
|
||||
</div>
|
||||
<div class="perm-col" title="${tf('share_help', 'Create/manage share links; implies View (all)')}">
|
||||
${tf('share', 'Share')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -804,10 +826,13 @@ export function openUserPermissionsModal() {
|
||||
background: ${isDarkMode ? "#2c2c2c" : "#fff"};
|
||||
color: ${isDarkMode ? "#e0e0e0" : "#000"};
|
||||
padding: 20px;
|
||||
max-width: 780px;
|
||||
width: 95%;
|
||||
/* Wider, responsive */
|
||||
width: clamp(980px, 92vw, 1280px);
|
||||
max-width: none;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
if (!userPermissionsModal) {
|
||||
@@ -825,9 +850,9 @@ export function openUserPermissionsModal() {
|
||||
<span id="closeUserPermissionsModal" class="editor-close-btn">×</span>
|
||||
<h3>${tf("folder_access", "Folder Access")}</h3>
|
||||
<div class="muted" style="margin:-4px 0 10px;">
|
||||
${tf("grant_folders_help", "Grant per-folder capabilities to each user. 'Upload/Manage/Share' imply 'View'.")}
|
||||
${tf("grant_folders_help", "Grant per-folder capabilities to each user. 'Write/Manage/Share' imply 'View'.")}
|
||||
</div>
|
||||
<div id="userPermissionsList" style="max-height: 60vh; overflow-y: auto; margin-bottom: 15px;">
|
||||
<div id="userPermissionsList" style="max-height: 70vh; overflow-y: auto; margin-bottom: 15px;">
|
||||
<!-- User rows will load here -->
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 10px;">
|
||||
@@ -921,24 +946,40 @@ function flagRow(u, flags) {
|
||||
}
|
||||
|
||||
export async function openUserFlagsModal() {
|
||||
const isDark = document.body.classList.contains("dark-mode");
|
||||
const overlayBg = isDark ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
|
||||
const contentBg = isDark ? "#2c2c2c" : "#fff";
|
||||
const contentFg = isDark ? "#e0e0e0" : "#000";
|
||||
const borderCol = isDark ? "#555" : "#ccc";
|
||||
|
||||
let modal = document.getElementById("userFlagsModal");
|
||||
if (!modal) {
|
||||
modal = document.createElement("div");
|
||||
modal.id = "userFlagsModal";
|
||||
modal.style.cssText = `
|
||||
position:fixed; inset:0; background:rgba(0,0,0,.5);
|
||||
position:fixed; inset:0; background:${overlayBg};
|
||||
display:flex; align-items:center; justify-content:center; z-index:3600;
|
||||
`;
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content" style="background:#fff; color:#000; padding:16px; max-width:900px; width:95%; border-radius:8px; position:relative;">
|
||||
<span id="closeUserFlagsModal" class="editor-close-btn" style="right:8px; top:8px;">×</span>
|
||||
<div class="modal-content"
|
||||
style="background:${contentBg}; color:${contentFg};
|
||||
padding:16px; max-width:900px; width:95%;
|
||||
border-radius:8px; position:relative;
|
||||
border:1px solid ${borderCol};">
|
||||
<span id="closeUserFlagsModal"
|
||||
class="editor-close-btn"
|
||||
style="right:8px; top:8px;">×</span>
|
||||
|
||||
<h3>${tf("user_permissions", "User Permissions")}</h3>
|
||||
<p class="muted" style="margin-top:-6px;">
|
||||
${tf("user_flags_help", "Account-level switches. These are NOT per-folder grants.")}
|
||||
</p>
|
||||
<div id="userFlagsBody" style="max-height:60vh; overflow:auto; margin:8px 0;">
|
||||
|
||||
<div id="userFlagsBody"
|
||||
style="max-height:60vh; overflow:auto; margin:8px 0;">
|
||||
${t("loading")}…
|
||||
</div>
|
||||
|
||||
<div style="display:flex; justify-content:flex-end; gap:8px;">
|
||||
<button type="button" id="cancelUserFlags" class="btn btn-secondary">${t("cancel")}</button>
|
||||
<button type="button" id="saveUserFlags" class="btn btn-primary">${t("save_permissions")}</button>
|
||||
@@ -946,10 +987,21 @@ export async function openUserFlagsModal() {
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
document.getElementById("closeUserFlagsModal").onclick = () => modal.style.display = "none";
|
||||
document.getElementById("cancelUserFlags").onclick = () => modal.style.display = "none";
|
||||
|
||||
document.getElementById("closeUserFlagsModal").onclick = () => (modal.style.display = "none");
|
||||
document.getElementById("cancelUserFlags").onclick = () => (modal.style.display = "none");
|
||||
document.getElementById("saveUserFlags").onclick = saveUserFlags;
|
||||
} else {
|
||||
// Re-apply theme if user toggled dark mode since last open
|
||||
modal.style.background = overlayBg;
|
||||
const content = modal.querySelector(".modal-content");
|
||||
if (content) {
|
||||
content.style.background = contentBg;
|
||||
content.style.color = contentFg;
|
||||
content.style.border = `1px solid ${borderCol}`;
|
||||
}
|
||||
}
|
||||
|
||||
modal.style.display = "flex";
|
||||
loadUserFlagsList();
|
||||
}
|
||||
@@ -1051,7 +1103,10 @@ async function loadUserPermissionsList() {
|
||||
padding:8px 6px;border-radius:6px;cursor:pointer;
|
||||
background:var(--perm-header-bg, rgba(0,0,0,0.04));">
|
||||
<span style="font-weight:600;">${user.username}</span>
|
||||
<i class="material-icons perm-caret" style="transition:transform .2s; transform:rotate(-90deg);">expand_more</i>
|
||||
<i class="material-icons perm-caret"
|
||||
style="transition:transform .2s; transform:rotate(-90deg); color: var(--perm-caret, #444);">
|
||||
expand_more
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<div class="user-perm-details" style="display:none;margin:8px 4px 2px 10px;">
|
||||
|
||||
@@ -6,50 +6,6 @@ require_once PROJECT_ROOT . '/src/models/AdminModel.php';
|
||||
|
||||
class AdminController
|
||||
{
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/admin/getConfig.php",
|
||||
* summary="Retrieve admin configuration",
|
||||
* description="Returns the admin configuration settings, decrypting the configuration file and providing default values if not set.",
|
||||
* operationId="getAdminConfig",
|
||||
* tags={"Admin"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Configuration retrieved successfully",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="header_title", type="string", example="FileRise"),
|
||||
* @OA\Property(
|
||||
* property="oidc",
|
||||
* type="object",
|
||||
* @OA\Property(property="providerUrl", type="string", example="https://your-oidc-provider.com"),
|
||||
* @OA\Property(property="clientId", type="string", example="YOUR_CLIENT_ID"),
|
||||
* @OA\Property(property="clientSecret", type="string", example="YOUR_CLIENT_SECRET"),
|
||||
* @OA\Property(property="redirectUri", type="string", example="https://yourdomain.com/auth.php?oidc=callback")
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="loginOptions",
|
||||
* type="object",
|
||||
* @OA\Property(property="disableFormLogin", type="boolean", example=false),
|
||||
* @OA\Property(property="disableBasicAuth", type="boolean", example=false),
|
||||
* @OA\Property(property="disableOIDCLogin", type="boolean", example=false)
|
||||
* ),
|
||||
* @OA\Property(property="globalOtpauthUrl", type="string", example=""),
|
||||
* @OA\Property(property="enableWebDAV", type="boolean", example=false),
|
||||
* @OA\Property(property="sharedMaxUploadSize", type="integer", example=52428800)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Failed to decrypt configuration or server error"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Retrieves the admin configuration settings.
|
||||
*
|
||||
* @return void Outputs a JSON response with configuration data.
|
||||
*/
|
||||
public function getConfig(): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
@@ -100,64 +56,6 @@ class AdminController
|
||||
echo json_encode($public);
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/admin/updateConfig.php",
|
||||
* summary="Update admin configuration",
|
||||
* description="Updates the admin configuration settings. Requires admin privileges and a valid CSRF token.",
|
||||
* operationId="updateAdminConfig",
|
||||
* tags={"Admin"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"header_title", "oidc", "loginOptions"},
|
||||
* @OA\Property(property="header_title", type="string", example="FileRise"),
|
||||
* @OA\Property(
|
||||
* property="oidc",
|
||||
* type="object",
|
||||
* @OA\Property(property="providerUrl", type="string", example="https://your-oidc-provider.com"),
|
||||
* @OA\Property(property="clientId", type="string", example="YOUR_CLIENT_ID"),
|
||||
* @OA\Property(property="clientSecret", type="string", example="YOUR_CLIENT_SECRET"),
|
||||
* @OA\Property(property="redirectUri", type="string", example="https://yourdomain.com/api/auth/auth.php?oidc=callback")
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="loginOptions",
|
||||
* type="object",
|
||||
* @OA\Property(property="disableFormLogin", type="boolean", example=false),
|
||||
* @OA\Property(property="disableBasicAuth", type="boolean", example=false),
|
||||
* @OA\Property(property="disableOIDCLogin", type="boolean", example=false)
|
||||
* ),
|
||||
* @OA\Property(property="globalOtpauthUrl", type="string", example=""),
|
||||
* @OA\Property(property="enableWebDAV", type="boolean", example=false),
|
||||
* @OA\Property(property="sharedMaxUploadSize", type="integer", example=52428800)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Configuration updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="Configuration updated successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., invalid input, incomplete OIDC configuration)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Unauthorized (user not admin or invalid CSRF token)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Server error (failed to write configuration file)"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Updates the admin configuration settings.
|
||||
*
|
||||
* @return void Outputs a JSON response indicating success or failure.
|
||||
*/
|
||||
public function updateConfig(): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
|
||||
@@ -13,53 +13,6 @@ use Jumbojett\OpenIDConnectClient;
|
||||
class AuthController
|
||||
{
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/auth/auth.php",
|
||||
* 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",
|
||||
* tags={"Auth"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username", "password"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="password", type="string", example="secretpassword"),
|
||||
* @OA\Property(property="remember_me", type="boolean", example=true),
|
||||
* @OA\Property(property="totp_code", type="string", example="123456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Login successful; returns user info and status",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="success", type="string", example="Login successful"),
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., missing credentials)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized (e.g., invalid credentials, too many attempts)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many failed login attempts"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Handles user authentication via OIDC or form-based login.
|
||||
*
|
||||
* @return void Redirects on success or outputs JSON error.
|
||||
*/
|
||||
// in src/controllers/AuthController.php
|
||||
|
||||
public function auth(): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
@@ -307,40 +260,6 @@ class AuthController
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/checkAuth.php",
|
||||
* 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",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns authentication status and user details",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="authenticated", type="boolean", example=true),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true),
|
||||
* @OA\Property(property="totp_enabled", type="boolean", example=false),
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="folderOnly", type="boolean", example=false)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Setup mode (if the users file is missing or empty)",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="setup", type="boolean", example=true)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Checks whether the user is authenticated or if the system is in setup mode.
|
||||
*
|
||||
* @return void Outputs a JSON response with authentication details.
|
||||
*/
|
||||
|
||||
public function checkAuth(): void
|
||||
{
|
||||
|
||||
@@ -427,28 +346,6 @@ class AuthController
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/token.php",
|
||||
* summary="Retrieve CSRF token and share URL",
|
||||
* description="Returns the current CSRF token along with the configured share URL.",
|
||||
* operationId="getToken",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="CSRF token and share URL",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="csrf_token", type="string", example="0123456789abcdef..."),
|
||||
* @OA\Property(property="share_url", type="string", example="https://yourdomain.com/share.php")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Returns the CSRF token and share URL.
|
||||
*
|
||||
* @return void Outputs the JSON response.
|
||||
*/
|
||||
public function getToken(): void
|
||||
{
|
||||
// 1) Ensure session and CSRF token exist
|
||||
@@ -468,31 +365,6 @@ class AuthController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/login_basic.php",
|
||||
* 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",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Login successful; redirects to index.html",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="Login successful")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized due to missing credentials or invalid credentials."
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Handles HTTP Basic authentication (with optional TOTP) and logs the user in.
|
||||
*
|
||||
* @return void Redirects on success or sends a 401 header.
|
||||
*/
|
||||
public function loginBasic(): void
|
||||
{
|
||||
// Set header for plain-text or JSON as needed.
|
||||
@@ -550,27 +422,6 @@ class AuthController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/auth/logout.php",
|
||||
* summary="Logout user",
|
||||
* description="Clears the session, removes persistent login tokens, and redirects the user to the login page.",
|
||||
* operationId="logoutUser",
|
||||
* tags={"Auth"},
|
||||
* @OA\Response(
|
||||
* response=302,
|
||||
* description="Redirects to the login page with a logout flag."
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Logs the user out by clearing session data, removing persistent tokens, and destroying the session.
|
||||
*
|
||||
* @return void Redirects to index.html with a logout flag.
|
||||
*/
|
||||
public function logout(): void
|
||||
{
|
||||
// Retrieve headers and check CSRF token.
|
||||
|
||||
@@ -7,70 +7,6 @@ require_once PROJECT_ROOT . '/src/models/UploadModel.php';
|
||||
|
||||
class UploadController {
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/upload/upload.php",
|
||||
* summary="Handle file upload",
|
||||
* description="Handles file uploads for both chunked and non-chunked (full) uploads. Validates CSRF, user authentication, and permissions, and processes file uploads accordingly. On success, returns a JSON status for chunked uploads or redirects for full uploads.",
|
||||
* operationId="handleUpload",
|
||||
* tags={"Uploads"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* description="Multipart form data for file upload. For chunked uploads, include fields like 'resumableChunkNumber', 'resumableTotalChunks', 'resumableIdentifier', 'resumableFilename', etc.",
|
||||
* @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
* @OA\Schema(
|
||||
* required={"token", "fileToUpload"},
|
||||
* @OA\Property(property="token", type="string", description="Share token or upload token."),
|
||||
* @OA\Property(
|
||||
* property="fileToUpload",
|
||||
* type="string",
|
||||
* format="binary",
|
||||
* description="The file to upload."
|
||||
* ),
|
||||
* @OA\Property(property="resumableChunkNumber", type="integer", description="Chunk number for chunked uploads."),
|
||||
* @OA\Property(property="resumableTotalChunks", type="integer", description="Total number of chunks."),
|
||||
* @OA\Property(property="resumableFilename", type="string", description="Original filename."),
|
||||
* @OA\Property(property="folder", type="string", description="Target folder (default 'root').")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="File uploaded successfully (or chunk uploaded status).",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="File uploaded successfully"),
|
||||
* @OA\Property(property="newFilename", type="string", example="5f2d7c123a_example.png"),
|
||||
* @OA\Property(property="status", type="string", example="chunk uploaded")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=302,
|
||||
* description="Redirection on full upload success."
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., missing file, invalid parameters)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Forbidden (e.g., invalid CSRF token, upload disabled)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Server error during file processing"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Handles file uploads, both chunked and full, and redirects upon success.
|
||||
*
|
||||
* @return void Outputs JSON response (for chunked uploads) or redirects on successful full upload.
|
||||
*/
|
||||
public function handleUpload(): void {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
@@ -149,42 +85,6 @@ class UploadController {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/upload/removeChunks.php",
|
||||
* summary="Remove chunked upload temporary directory",
|
||||
* description="Removes the temporary directory used for chunked uploads, given a folder name matching the expected resumable pattern.",
|
||||
* operationId="removeChunks",
|
||||
* tags={"Uploads"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"folder"},
|
||||
* @OA\Property(property="folder", type="string", example="resumable_myupload123")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Temporary folder removed successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="Temporary folder removed.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Invalid input (e.g., missing folder or invalid folder name)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* Removes the temporary upload folder for chunked uploads.
|
||||
*
|
||||
* @return void Outputs a JSON response.
|
||||
*/
|
||||
public function removeChunks(): void {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
||||
@@ -122,31 +122,6 @@ class UserController
|
||||
|
||||
/* ------------------------- End helpers -------------------------- */
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/getUsers.php",
|
||||
* summary="Retrieve a list of users",
|
||||
* description="Returns a JSON array of users. Only available to authenticated admin users.",
|
||||
* operationId="getUsers",
|
||||
* tags={"Users"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Successful response with an array of users",
|
||||
* @OA\JsonContent(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="role", type="string", example="admin")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized: the user is not authenticated or is not an admin"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function getUsers()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -158,39 +133,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/addUser.php",
|
||||
* 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",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username", "password"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="password", type="string", example="securepassword"),
|
||||
* @OA\Property(property="isAdmin", type="boolean", example=true)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User added successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User added successfully")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function addUser()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -258,41 +200,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Delete(
|
||||
* path="/api/removeUser.php",
|
||||
* summary="Remove a user",
|
||||
* description="Removes the specified user from the system. Cannot remove the currently logged-in user.",
|
||||
* operationId="removeUser",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"username"},
|
||||
* @OA\Property(property="username", type="string", example="johndoe")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User removed successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User removed successfully")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function removeUser()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -322,24 +229,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/getUserPermissions.php",
|
||||
* summary="Retrieve user permissions",
|
||||
* description="Returns the permissions for the current user, or all permissions if the user is an admin.",
|
||||
* operationId="getUserPermissions",
|
||||
* tags={"Users"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Successful response with user permissions",
|
||||
* @OA\JsonContent(type="object")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function getUserPermissions()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -350,51 +239,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/updateUserPermissions.php",
|
||||
* summary="Update user permissions",
|
||||
* description="Updates permissions for users. Only available to authenticated admin users.",
|
||||
* operationId="updateUserPermissions",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"permissions"},
|
||||
* @OA\Property(
|
||||
* property="permissions",
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
* @OA\Property(property="username", type="string", example="johndoe"),
|
||||
* @OA\Property(property="folderOnly", type="boolean", example=true),
|
||||
* @OA\Property(property="readOnly", type="boolean", example=false),
|
||||
* @OA\Property(property="disableUpload", type="boolean", example=false)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User permissions updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User permissions updated successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function updateUserPermissions()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -415,43 +259,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/changePassword.php",
|
||||
* 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",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"oldPassword", "newPassword", "confirmPassword"},
|
||||
* @OA\Property(property="oldPassword", type="string", example="oldpass123"),
|
||||
* @OA\Property(property="newPassword", type="string", example="newpass456"),
|
||||
* @OA\Property(property="confirmPassword", type="string", example="newpass456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Password updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="Password updated successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function changePassword()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -488,41 +295,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/updateUserPanel.php",
|
||||
* summary="Update user panel settings",
|
||||
* description="Updates user panel settings by disabling TOTP when not enabled. Accessible to authenticated users.",
|
||||
* operationId="updateUserPanel",
|
||||
* tags={"Users"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"totp_enabled"},
|
||||
* @OA\Property(property="totp_enabled", type="boolean", example=false)
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="User panel updated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="string", example="User panel updated: TOTP disabled")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function updateUserPanel()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -551,31 +323,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Put(
|
||||
* path="/api/totp_disable.php",
|
||||
* summary="Disable TOTP for the authenticated user",
|
||||
* description="Clears the TOTP secret from the users file for the current user.",
|
||||
* operationId="disableTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="TOTP disabled successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="TOTP disabled successfully.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Not authenticated or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Failed to disable TOTP"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function disableTOTP()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -601,45 +348,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_recover.php",
|
||||
* summary="Recover TOTP",
|
||||
* description="Verifies a recovery code to disable TOTP and finalize login.",
|
||||
* operationId="recoverTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"recovery_code"},
|
||||
* @OA\Property(property="recovery_code", type="string", example="ABC123DEF456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Recovery successful",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Invalid input or recovery code"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=405,
|
||||
* description="Method not allowed"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many attempts"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function recoverTOTP()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -681,35 +389,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_saveCode.php",
|
||||
* 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",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Recovery code generated successfully",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="recoveryCode", type="string", example="ABC123DEF456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Invalid CSRF token or unauthorized"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=405,
|
||||
* description="Method not allowed"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function saveTOTPRecoveryCode()
|
||||
{
|
||||
self::jsonHeaders();
|
||||
@@ -739,30 +418,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/totp_setup.php",
|
||||
* 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",
|
||||
* tags={"TOTP"},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="QR code image for TOTP setup",
|
||||
* @OA\MediaType(
|
||||
* mediaType="image/png"
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Unauthorized or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Server error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function setupTOTP()
|
||||
{
|
||||
// Allow access if authenticated OR pending TOTP
|
||||
@@ -799,42 +454,6 @@ class UserController
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/totp_verify.php",
|
||||
* summary="Verify TOTP code",
|
||||
* description="Verifies a TOTP code and completes login for pending users or validates TOTP for setup verification.",
|
||||
* operationId="verifyTOTP",
|
||||
* tags={"TOTP"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"totp_code"},
|
||||
* @OA\Property(property="totp_code", type="string", example="123456")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="TOTP successfully verified",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="status", type="string", example="ok"),
|
||||
* @OA\Property(property="message", type="string", example="Login successful")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Bad Request (e.g., invalid input)"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Not authenticated or invalid CSRF token"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=429,
|
||||
* description="Too many attempts. Try again later."
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function verifyTOTP()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
|
||||
168
src/openapi/Components.php
Normal file
168
src/openapi/Components.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
// src/openapi/Components.php
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* title="FileRise API",
|
||||
* version="1.5.2"
|
||||
* )
|
||||
*
|
||||
* @OA\Server(
|
||||
* url="/",
|
||||
* description="Same-origin server"
|
||||
* )
|
||||
*
|
||||
* @OA\Tag(
|
||||
* name="Admin",
|
||||
* description="Admin endpoints"
|
||||
* )
|
||||
*
|
||||
* @OA\Components(
|
||||
* @OA\SecurityScheme(
|
||||
* securityScheme="cookieAuth",
|
||||
* type="apiKey",
|
||||
* in="cookie",
|
||||
* name="PHPSESSID",
|
||||
* description="Session cookie used for authenticated endpoints"
|
||||
* ),
|
||||
* @OA\SecurityScheme(
|
||||
* securityScheme="CsrfHeader",
|
||||
* type="apiKey",
|
||||
* in="header",
|
||||
* name="X-CSRF-Token",
|
||||
* description="CSRF token header required for state-changing requests"
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response="Unauthorized",
|
||||
* description="Unauthorized (no session)",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="Unauthorized")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="Forbidden",
|
||||
* description="Forbidden (not enough privileges)",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="Invalid CSRF token.")
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="SimpleSuccess",
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="boolean", example=true)
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* schema="SimpleError",
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="Something went wrong")
|
||||
* ),
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="LoginOptionsPublic",
|
||||
* type="object",
|
||||
* @OA\Property(property="disableFormLogin", type="boolean"),
|
||||
* @OA\Property(property="disableBasicAuth", type="boolean"),
|
||||
* @OA\Property(property="disableOIDCLogin", type="boolean")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* schema="LoginOptionsAdminExtra",
|
||||
* type="object",
|
||||
* @OA\Property(property="authBypass", type="boolean", nullable=true),
|
||||
* @OA\Property(property="authHeaderName", type="string", nullable=true, example="X-Remote-User")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* schema="OIDCConfigPublic",
|
||||
* type="object",
|
||||
* @OA\Property(property="providerUrl", type="string", example="https://accounts.example.com"),
|
||||
* @OA\Property(property="redirectUri", type="string", example="https://your.filerise.app/callback")
|
||||
* ),
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="AdminGetConfigPublic",
|
||||
* type="object",
|
||||
* required={"header_title","loginOptions","globalOtpauthUrl","enableWebDAV","sharedMaxUploadSize","oidc"},
|
||||
* @OA\Property(property="header_title", type="string", example="FileRise"),
|
||||
* @OA\Property(property="loginOptions", ref="#/components/schemas/LoginOptionsPublic"),
|
||||
* @OA\Property(property="globalOtpauthUrl", type="string"),
|
||||
* @OA\Property(property="enableWebDAV", type="boolean"),
|
||||
* @OA\Property(property="sharedMaxUploadSize", type="integer", format="int64"),
|
||||
* @OA\Property(property="oidc", ref="#/components/schemas/OIDCConfigPublic")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* schema="AdminGetConfigAdmin",
|
||||
* allOf={
|
||||
* @OA\Schema(ref="#/components/schemas/AdminGetConfigPublic"),
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="loginOptions",
|
||||
* allOf={
|
||||
* @OA\Schema(ref="#/components/schemas/LoginOptionsPublic"),
|
||||
* @OA\Schema(ref="#/components/schemas/LoginOptionsAdminExtra")
|
||||
* }
|
||||
* )
|
||||
* )
|
||||
* }
|
||||
* ),
|
||||
*
|
||||
* @OA\Schema(
|
||||
* schema="AdminUpdateConfigRequest",
|
||||
* type="object",
|
||||
* additionalProperties=false,
|
||||
* @OA\Property(property="header_title", type="string", maxLength=100, example="FileRise"),
|
||||
* @OA\Property(
|
||||
* property="loginOptions",
|
||||
* type="object",
|
||||
* additionalProperties=false,
|
||||
* @OA\Property(property="disableFormLogin", type="boolean", example=false),
|
||||
* @OA\Property(property="disableBasicAuth", type="boolean", example=false),
|
||||
* @OA\Property(property="disableOIDCLogin", type="boolean", example=true, description="false = OIDC enabled"),
|
||||
* @OA\Property(property="authBypass", type="boolean", example=false),
|
||||
* @OA\Property(
|
||||
* property="authHeaderName",
|
||||
* type="string",
|
||||
* pattern="^[A-Za-z0-9\\-]+$",
|
||||
* example="X-Remote-User",
|
||||
* description="Letters/numbers/dashes only"
|
||||
* )
|
||||
* ),
|
||||
* @OA\Property(property="globalOtpauthUrl", type="string", example="otpauth://totp/{label}?secret={secret}&issuer=FileRise"),
|
||||
* @OA\Property(property="enableWebDAV", type="boolean", example=false),
|
||||
* @OA\Property(property="sharedMaxUploadSize", type="integer", format="int64", minimum=0, example=52428800),
|
||||
* @OA\Property(
|
||||
* property="oidc",
|
||||
* type="object",
|
||||
* additionalProperties=false,
|
||||
* description="When disableOIDCLogin=false (OIDC enabled), providerUrl, redirectUri, and clientId are required.",
|
||||
* @OA\Property(property="providerUrl", type="string", format="uri", example="https://issuer.example.com"),
|
||||
* @OA\Property(property="clientId", type="string", example="my-client-id"),
|
||||
* @OA\Property(property="clientSecret", type="string", writeOnly=true, example="***"),
|
||||
* @OA\Property(property="redirectUri", type="string", format="uri", example="https://app.example.com/auth/callback")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @OA\RequestBody(
|
||||
* request="MoveFilesRequest",
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* required={"source","destination","files"},
|
||||
* @OA\Property(property="source", type="string", example="inbox"),
|
||||
* @OA\Property(property="destination", type="string", example="archive"),
|
||||
* @OA\Property(property="files", type="array", @OA\Items(type="string"))
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
|
||||
final class OpenAPIComponents {}
|
||||
Reference in New Issue
Block a user