Full-featured web application for cloud file storage with the ability to create folders, upload files, manage access, administer users and password reset system.
CloudStorageApp is a modern solution for storing and managing files in the cloud. The application provides users with the ability to:
- Register and authenticate in the system
- 🔐 Two-factor authentication (Email codes and Google Authenticator)
- Create hierarchical folder structure
- Upload files of various formats
- Manage file access (private/received from another user)
- Preview files (images, PDF, video)
- View video of any format in modal window
- Auto-play video in tile mode (like in Viber)
- Download and share files
- User administration (for admins)
- Activity logging
- Password reset via email
- PHP: version 7.4 or higher
- MySQL: version 5.7 or higher / MariaDB: version 10.3 or higher
- Web server: Apache 2.4+ or Nginx 1.18+
- SMTP server: for sending email notifications (optional)
pdo- for database workpdo_mysql- MySQL driver for PDOmbstring- for working with multibyte stringsfileinfo- for determining file typesgdorimagick- for working with images (optional)json- for working with JSON datasession- for session managementfilter- for data validationopenssl- for generating password reset tokenscurl- for sending HTTP requests (optional)
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
memory_limit = 256M
max_input_vars = 3000# Download project archive or clone repository
git clone https://gitlab.skillbox.ru/artur_zelenco/php-developer-base/-/tree/dev13/Final%20work%20the%20course%20%C2%ABPHP-developer.%20Basic%20level%C2%BB
cd CloudStorageApp# Execute this command to create database and all tables (PowerShell or CMD). For example, if your application is located at this address, then in CMD instead of YourUserName enter your username and execute this command:
mysql -u YourUserName -p <C:\xampp\htdocs\welcome\CloudStorageApp\database.sql
# Then enter your password and press Enter:
Enter password: ***********
# Done, your database is created!This command automatically:
- Creates
cloud_storagedatabase - Creates all necessary tables (
directories,files,shared_items,users,password_reset_tokens) - Creates 2FA tables (
two_factor_codes,system_settings,two_factor_logs,trusted_devices) - Adds 2FA fields to
userstable - Sets up all relationships and indexes
- Sets correct UTF-8 encoding
From system command line:
mysql -u root -p -e "USE cloud_storage; SHOW TABLES;"Or from MySQL command line:
# Enter MySQL
mysql -u root -p
# In MySQL console execute:
USE cloud_storage;
SHOW TABLES;Should display tables:
+-------------------------+
| Tables_in_cloud_storage |
+-------------------------+
| directories |
| files |
| password_reset_tokens |
| shared_items |
| users |
+-------------------------+mysql -u root -psource /path/to/your/project/database.sql
mysql -u root -p -e "USE cloud_storage; SHOW TABLES;"Edit config/config.php file:
<?php
return [
'database' => [
'host' => 'localhost',
'dbname' => 'cloud_storage',
'username' => 'your_db_username',
'password' => 'your_db_password',
'charset' => 'utf8mb4'
],
'app' => [
'name' => 'CloudStorageApp',
'url' => 'http://localhost',
'upload_path' => __DIR__ . '/../uploads/',
'max_file_size' => 104857600, // 100MB in bytes
'allowed_extensions' => ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'txt', 'zip', 'rar', 'mp4', 'avi', 'mkv', 'webm', 'mov', 'wmv', 'flv', '3gp', 'ts', 'asf', 'ogg'],
'timezone' => 'Europe/Moscow'
],
'security' => [
'session_lifetime' => 3600, // 1 hour
'password_min_length' => 6,
'max_login_attempts' => 5
],
'email' => [
'smtp_host' => 'smtp.gmail.com',
'smtp_port' => 587,
'smtp_username' => 'your_email@gmail.com',
'smtp_password' => 'your_app_password',
'smtp_secure' => 'tls',
'from_email' => 'your_email@gmail.com',
'from_name' => 'CloudStorageApp'
]
];For password reset functionality, configure SMTP in config/config.php:
- Enable two-factor authentication
- Create app password
- Use settings above
- Yandex: smtp.yandex.ru, port 587
- Mail.ru: smtp.mail.ru, port 465
- Outlook: smtp-mail.outlook.com, port 587
.htaccess file is already configured in public/ folder.
Installation and setup:
-
Place project in
C:\xampp\htdocs\welcome\folder -
Add or modify configuration in httpd-vhosts.conf file in
C:\xampp\apache\conf\extra\folder and if needed in httpd.conf file in C:\xampp\apache\conf\ with following content:<VirtualHost *:8080> DocumentRoot "C:/xampp/htdocs/welcome/CloudStorageApp/public" ServerName localhost <Directory "C:/xampp/htdocs/welcome/CloudStorageApp/public"> AllowOverride All Require all granted </Directory> </VirtualHost>
-
Start Apache and MySQL in XAMPP control panel
-
Open in browser:
http://localhost:8080/login.html
Structure should be:
C:\xampp\htdocs
├── phpmyadmin
└── welcome
└── CloudStorageApp
├── public
│ └── index.php
├── database.sql
└── README.md
If you want to place project in htdocs root:
C:\xampp\htdocs
├── phpmyadmin
└── CloudStorageApp
├── public
└── ...
Add or modify configuration in httpd-vhosts.conf file in C:\xampp\apache\conf\extra\ folder and if needed in httpd.conf file in C:\xampp\apache\conf\ with following content:
<VirtualHost *:8080>
DocumentRoot "C:/xampp/htdocs/CloudStorageApp/public"
ServerName localhost
<Directory "C:/xampp/htdocs/CloudStorageApp/public">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Then address will also be: http://localhost:8080/login.html
- PHP 7.4 or higher
mod_rewritemodule enabled.htaccessfile support- MySQL/MariaDB via phpMyAdmin
If using other local servers (WAMP, MAMP, Laragon):
- Place project in corresponding folder (
www,htdocs) - Ensure Apache and MySQL are running
- Open
http://localhost/login.html
- PHP 7.4 or higher
mod_rewritemodule enabled.htaccessfile support- MySQL/MariaDB
Open browser and go to: http://localhost:8080/login.html
CloudStorageApp/ ├── App/ │ ├── Controllers/ │ │ ├── AdminController.php # Administration controller │ │ ├── AuthController.php # Authentication controller │ │ ├── BaseController.php # Base controller │ │ ├── DirectoryController.php # Folder management controller │ │ ├── FileController.php # File management controller │ │ └── UserController.php # User controller │ ├── Core/ │ │ ├── App.php # Main application class │ │ ├── AuthMiddleware.php # Authentication middleware │ │ ├── BaseController.php # Base controller │ │ ├── Container.php # DI container │ │ ├── Db.php # Database class │ │ ├── Logger.php # Logging system │ │ ├── Repository.php # Base repository │ │ ├── Request.php # HTTP request class │ │ ├── Response.php # HTTP response class │ │ ├── Router.php # Router │ │ └── Validator.php # Base validator │ ├── Repositories/ │ │ ├── AdminRepository.php # Admin repository │ │ ├── DirectoryRepository.php # Folder repository │ │ ├── FileRepository.php # File repository │ │ ├── IFileRepository.php # File repository interface │ │ ├── IPasswordResetRepository.php # Password reset interface │ │ ├── IUserRepository.php # User repository interface │ │ ├── PasswordResetRepository.php # Password reset repository │ │ └── UserRepository.php # User repository │ ├── Services/ │ │ ├── AdminService.php # Administration service │ │ ├── AuthService.php # Authentication service │ │ ├── DirectoryService.php # Folder management service │ │ ├── EmailService.php # Email sending service │ │ ├── FileResponseService.php # File sending service │ │ ├── FileService.php # File management service │ │ ├── FileTypeService.php # File type detection service │ │ ├── IDirectoryService.php # Folder service interface │ │ ├── IEmailService.php # Email service interface │ │ ├── IFileService.php # File service interface │ │ ├── IUserService.php # User service interface │ │ └── UserService.php # User service │ ├── Utils/ │ │ └── FileUtils.php # File utilities │ ├── Validators/ │ │ ├── AuthValidator.php # Authentication validator │ │ └── DirectoryValidator.php # Folder validator │ ├── logs/ # Logs folder │ └── uploads/ # Uploaded files folder │ ├── files/ # Files subfolder │ └── folders/ # Folders subfolder ├── config/ │ └── config.php # Application configuration ├── public/ │ ├── css/ # CSS styles │ │ ├── Admin.css # Admin panel styles │ │ ├── login.css # Login page styles │ │ ├── reset-password.css # Password reset styles │ │ ├── upload.css # Main page styles │ │ └── video-tiles.css # Video tiles styles │ ├── js/ # JavaScript files │ │ ├── admin.js # Admin panel JavaScript │ │ └── app.js # Main JavaScript │ ├── .htaccess # Apache configuration │ ├── Admins.html # Administrator template │ ├── CloudIcon.png # CloudStorageApp login icon │ ├── CoveringCloudIcon.png # Animation │ ├── ElibraryIcon.png # My Files page icon │ ├── index.php # Entry point │ ├── login.html # Login template │ ├── reset-password.html # Password reset page │ └── upload.html # User page ├── database.sql # SQL scripts for DB creation └── README.md # Project documentation
For registration, you need to fill in the following mandatory fields (marked with red asterisk in user interface):
- First Name - user's first name
- Last Name - user's last name
- Email - email address
- Password - system login password
- Repeat Password - password confirmation
To log into the system use:
- Email - email address specified during registration
- Password - password set during registration
If you forgot your password:
- On login page click "Forgot password?"
- Enter your email address
- Check your email - you will receive a letter with reset link
- Follow the link and set new password
- Log into system with new password
Important: Link is valid for 1 hour.
System supports viewing video of any format:
- MP4 (video/mp4) - recommended
- AVI (video/x-msvideo)
- MKV (video/x-matroska)
- WebM (video/webm)
- MOV (video/quicktime)
- WMV (video/x-ms-wmv)
- FLV (video/x-flv)
- 3GP (video/3gpp)
- TS (video/mp2t)
- ASF (video/x-ms-asf)
- OGG (video/ogg)
- MPEG (video/mpeg)
-
In tile mode (auto-play):
- Switch to "Tiles" view mode
- Videos automatically play on hover
- Like in Viber feed - without sound, with looping
-
In modal window:
- Click on video tile or select "Preview" in menu
- Video opens in modal window with controls
- Built-in browser video player with full functionality
- Built into modal window - like image preview
- Standard browser controls - familiar interface
- Auto-load with muted sound - no unexpected sounds
- Adaptive size - adjusts to window size
- Playback error handling - graceful degradation
- File information in modal window header
Backend changes:
- isVideoFile() function - video file detection by MIME type
- Video preview - built into modal window like images
Frontend improvements:
- public/css/video-tiles.css - styles for video in tile mode
- Updated public/js/app.js with auto-play functions
- Updated public/js/admin.js for video support in admin panel
- Style connection in upload.html and Admins.html
JavaScript functions:
- isVideoFile(mimeType) - video file detection
- Canvas animation - video frame rendering in tiles
- Lazy video loading for performance optimization
- Auto-play with 300ms delay (like in Viber)
- Hover event handling with timeouts
Visual elements:
- "VIDEO" indicator in bottom right corner of card
- Animated play button with pulsing effect
- Canvas preview - video frame display in tiles
- Smooth transitions and hover effects
- Loading and error state handling
- Modal window for video viewing
Performance optimization:
- preload="metadata" - only metadata loaded for Canvas
- Canvas rendering - frame display without full video loading
- Playback stop when cursor leaves
- Return to beginning of video when stopped
- DOM initialization delay (100ms) for stability
- Error handling with fallback to static icons
For users:
- Upload video file via "Choose files" button
- Switch to "Tiles" mode (grid icon button)
- Hover cursor over video - it will automatically start playing
- Click on video to open in modal window
- Use menu (three dots) → "Preview" for quick access
For administrators:
- Go to "Files" in admin panel side menu
- Find video file in table
- Click "eye" button (View file)
- Video opens in modal window with controls
Video controls:
- Use standard browser controls
- Pause/play - play/pause button
- Volume - sound control
- Fullscreen mode - button in browser controls
Auto-play features:
- 300ms delay before start (like in Viber)
- Playback without sound with looping
- Automatic stop when cursor leaves
- Lazy loading for traffic saving
CloudStorageApp includes a complete two-factor authentication system to enhance user security. The system supports two authentication methods and provides flexible settings for both users and administrators.
- Registration toggle - ability to enable 2FA immediately when creating account
- Two authentication methods:
- Email codes - 6-digit codes sent to email (valid for 10 minutes)
- TOTP codes - codes from Google Authenticator, Authy and other apps
- QR codes - for quick mobile app setup
- Backup codes - 10 one-time codes for access recovery
- Flexible setup - can be enabled/disabled at any time
- Forced 2FA - 2FA requirement for all users
- Usage statistics - number of users with 2FA by methods
- Action logging - complete audit of all 2FA operations
- Flexible logic - users with their own 2FA are not affected by admin settings
When executing database.sql automatically creates:
- 2FA fields in
userstable two_factor_codestable for temporary codessystem_settingstable for global settingstwo_factor_logstable for audittrusted_devicestable for future features
Ensure SMTP parameters are correctly configured in config/config.php:
'email' => [
'method' => 'smtp',
'smtp_host' => 'smtp.gmail.com',
'smtp_port' => 587,
'smtp_secure' => 'tls',
'smtp_username' => 'your@gmail.com',
'smtp_password' => 'your_app_password',
'from_email' => 'your@gmail.com',
'from_name' => 'CloudStorageApp Support'
]- Open http://localhost:8080/login.html
- Go to "Registration" tab
- Fill all required fields
- Enable "Two-factor authentication" toggle
- Read 2FA information block
- Click "Register"
- Log in with new credentials
- Automatically redirected to 2FA setup
- Select "Email code" on setup page
- Click "Continue"
- Check email - test code will arrive
- Enter code from email
- Save backup codes (download or print)
- Complete setup
- Install Google Authenticator on phone
- Select "Authenticator App" on setup page
- Scan QR code or enter key manually:
- Account name: your email
- Issuer: Cloud Storage
- Enter 6-digit code from app
- Save backup codes
- Complete setup
- Enter email and password as usual
- 2FA code form appears
- Enter code:
- From email (if Email method selected)
- From Authenticator app (if TOTP selected)
- Or use backup code
- Automatic verification when entering 6 digits
- Successful login to system
- Log in as administrator
- Go to "System" in side menu
- Find "Security Settings" section
- Enable "Forced two-factor authentication"
- View 2FA usage statistics
- Users with own 2FA - not affected (continue using their settings)
- Users without 2FA - redirected to 2FA setup on login
- New users - must set up 2FA after first login
- Disabling forced 2FA - doesn't affect users who enabled 2FA themselves
For testing two-factor authentication (2FA) via Postman, follow the request sequence. After successful login, if 2FA is required, you will receive a response indicating the 2FA method (two_factor_method: "email" or "totp"). Important: do not clear session/cookies between login request and 2FA confirmation request.
-
Method:
POST -
URL:
/users/login -
Request body (JSON):
{ "email": "your_email@example.com", "password": "your_password" } -
Expected response (if 2FA enabled for user):
{ "success": true, "requires_2fa_verification": true, "two_factor_method": "totp", // or "email" "user_email": "your_email@example.com" }After this response, user session will contain temporary data needed for next 2FA confirmation step.
-
POST /api/2fa/generate-secret- generate TOTP secret and QR code-
Description: Used for TOTP setup (Authenticator app). Generates unique secret key and QR code URL.
-
Request body: (empty)
-
Example response:
{ "success": true, "secret": "JBSWY3DPEHPK3PXP...", "qr_url": "otpauth://totp/...", "qr_image": "https://api.qrserver.com/v1/create-qr-code/...", "account_name": "artzlc@yandex.ru", "issuer": "Cloud Storage" }
-
-
POST /api/2fa/send-email-code- send code to email-
Description: Sends 6-digit confirmation code to user's registered email. Used for email 2FA setup or code resend during login.
-
Request body: (empty)
-
Example response:
{ "success": true, "message": "Code sent to email" }
-
-
POST /api/2fa/verify-totp- verify TOTP code during setup-
Description: Verifies 6-digit code from Authenticator app with generated secret.
-
Request body (JSON):
{ "code": "123456", // Code from Authenticator app "secret": "JBSWY3DPEHPK3PXP..." // Secret from /api/2fa/generate-secret }
-
-
POST /api/2fa/verify-email- verify email code during setup-
Description: Verifies 6-digit code sent to email to complete email 2FA setup.
-
Request body (JSON):
{ "code": "654321" // Code received by email }
-
-
POST /api/2fa/generate-backup-codes- generate backup codes-
Description: Generates list of one-time backup codes for access recovery.
-
Request body: (empty)
-
Example response:
{ "success": true, "backup_codes": ["CODE1", "CODE2", "CODE3", ...] }
-
-
POST /api/2fa/complete-setup- complete 2FA setup-
Description: Finalizes 2FA setup process, saving selected method and backup codes.
-
Request body (JSON):
{ "method": "email", // or "totp" "backup_codes": ["CODE1", "CODE2", ...] // List of generated backup codes }
-
-
POST /api/2fa/verify-email-login- verify email code during login-
Description: Used to confirm login if user's 2FA method is email. Code is sent automatically after
/users/loginif 2FA is enabled. -
Request body (JSON):
{ "code": "123456" // Code received by email } -
Example successful response:
{ "success": true, "message": "Login successful", "user": { /* user data */ } }
-
-
POST /api/2fa/verify-totp-login- verify TOTP code during login-
Description: Used to confirm login if user's 2FA method is Authenticator app.
-
Request body (JSON):
{ "code": "654321" // Code from Authenticator app } -
Example successful response:
{ "success": true, "message": "Login successful", "user": { /* user data */ } }
-
-
POST /api/2fa/verify-backup-code- verify backup code during login-
Description: Used as alternative login method if main 2FA method is unavailable. Backup code is one-time use.
-
Request body (JSON):
{ "code": "YOUR_BACKUP_CODE" // One of generated backup codes } -
Example successful response:
{ "success": true, "message": "Login successful with backup code", "user": { /* user data */ }, "remaining_backup_codes": 9 // Number of remaining backup codes }
-
-
GET /api/2fa/status- get user's 2FA status-
Description: Returns current 2FA status for authorized user.
-
Request body: (empty)
-
Example response:
{ "success": true, "enabled": true, "method": "totp", // or "email" "setup_completed": true }
-
-
GET /api/admin/2fa/status- get 2FA statistics (admin)- Description: (Requires admin rights) Returns overall 2FA usage statistics in system.
- Request body: (empty)
-
POST /api/admin/2fa/toggle-forced- toggle forced 2FA (admin)-
Description: (Requires admin rights) Enables or disables forced 2FA for all users.
-
Request body (JSON):
{ "enable": true // or false }
-
- Email codes valid for 10 minutes
- TOTP codes valid for 30 seconds (standard)
- Backup codes one-time use (deleted after use)
All 2FA actions are recorded in two_factor_logs table:
- 2FA setup
- Login attempts (successful and failed)
- Code generation and usage
- Administrative changes
- TOTP secrets stored encrypted
- Backup codes hashed before saving
- Temporary codes automatically deleted after expiration
- Google Authenticator (iOS/Android)
- Microsoft Authenticator (iOS/Android)
- Authy (iOS/Android/Desktop)
- 1Password (with TOTP support)
- Bitwarden (with TOTP support)
- LastPass Authenticator
- Check SMTP settings in
config.php - Check "Spam" folder
- Ensure email address is correct
- Check internet connection
- Try entering key manually
- Refresh page
- Check server and device time
- Ensure code hasn't expired
- Check correct input (6 digits)
- Use backup code if necessary
- Ensure complete
database.sqlexecuted - Check database access rights
- Check PHP logs for errors
File sharing issues:
- ✅ Fixed: Error 500 when uploading files - restored correct
getFilesInRootDirectory()call - ✅ Fixed: Shared files from any folders now display for recipient
- ✅ Improved: Removed
d.parent_id IS NULLrestrictions in SQL queries - ✅ Added: Getting
sharedRootIdsfor correct method operation
To access administrative panel:
UPDATE users SET role = 'admin', is_admin = 1 WHERE email = your@email.com;
UPDATE users SET role = 'admin', is_admin = 1 WHERE id = userID;base_url: http://localhost:8080/login.html
POST /register
Register new user
Headers: Content-Type: application/json Body → raw → JSON:
{
"first_name": "John",
"middle_name": "John",
"last_name": "Johnson",
"gender": "male",
"age": 25,
"email": "john@example.com",
"password": "secure_password"
}
{
"first_name": "Arthur",
"middle_name": "Arthur",
"last_name": "Arthurs",
"gender": "male",
"age": 27,
"email": "arthur@example.com",
"password": "secure_password1"
}
{
"first_name": "Alex",
"middle_name": "Alex",
"last_name": "Alexson",
"gender": "male",
"age": 29,
"email": "alex@example.com",
"password": "secure_password2"
}POST /users/login
User authorization
Headers: Content-Type: application/json Body → raw → JSON:
{
"email": "john@example.com",
"password": "secure_password"
}GET /users/logout
System logout (requires authorization)
POST /users/reset_password
Request password reset via email
Headers: Content-Type: application/json Body → raw → JSON:
{
"email": "user@example.com"
}POST /users/password-reset-validate
Check reset token validity
Headers: Content-Type: application/json Body → raw → JSON:
{
"token": "your_reset_token_here"
}POST /users/password-reset-confirm
Confirm password reset with new password
Headers: Content-Type: application/json Body → raw → JSON:
{
"token": "your_reset_token_here",
"password": "new_secure_password",
"confirm_password": "new_secure_password"
}GET /users/list
Get user list
GET /users/get/{id}
Get user information by ID
PUT /users/update
Update current user data
Headers: Content-Type: application/json Body → raw → JSON:
{
"first_name": "New name",
"last_name": "New surname",
"email": "new@example.com",
"old_password": "",
"new_password": "",
"confirm_new_password": ""
}
Field descriptions:
- `first_name` — new user first name.
- `last_name` — new user last name.
- `email` — new user email.
- `old_password` — current password (optional field, required only when changing password).
- `new_password` — new password (optional field).
- `confirm_new_password` — new password confirmation (optional field).
If password change fields (`old_password`, `new_password`, `confirm_new_password`) are not provided, password remains unchanged.
For successful password change, all three fields must be specified, with `new_password` and `confirm_new_password` matching, and `old_password` being correct current user password.
POST /files/add
Add file (alias for upload)
Content-Type: multipart/form-data
Form parameters:
files[]- array of files to upload (required)directory_id- destination folder ID (default "root")paths- JSON string with relative paths for folder structure creation (optional)
GET /files/list
Get list of files and folders
GET /files/get/{id} Get file information by ID
PUT /files/rename Rename file
Headers: Content-Type: application/json Body → raw → JSON:
{
"file_id": 123,
"new_name": "New file name"
}DELETE /files/remove/{id}
Delete file by ID
POST /directories/add
Create folder
Headers: Content-Type: application/json Body → raw → JSON:
{
"name": "New folder",
"parent_id": "root"
}PUT /directories/rename
Rename folder
Headers: Content-Type: application/json Body → raw → JSON:
{
"id": 123,
"new_name": "New folder name"
}GET /directories/get/{id}
Get folder information and its contents
DELETE /directories/delete/{id}
Delete folder by ID
PUT /files/share/{id}/{user_id}
Share file with specific user
GET /files/share/{id}
Get file sharing information
DELETE /files/share/{id}/{user_id}
Remove file sharing from specific user
POST /files/upload
Upload file or multiple files
Content-Type: multipart/form-data
Form parameters:
files[]- array of files to upload (required)directory_id- destination folder ID (default "root")paths- JSON string with relative paths for folder structure creation (optional)
Content-Type: multipart/form-data
Form data:
files[]: document.pdf directory_id: root
Content-Type: multipart/form-data
Form data:
files[]: project/docs/readme.txt files[]: project/images/logo.png files[]: project/src/main.js directory_id: root paths: ["project/docs/readme.txt", "project/images/logo.png", "project/src/main.js"]
Example form-data setup in Postman:
Key: files[] Type: File Value: [Select Files]
Key: directory_id Type: Text Value: root
Key: paths Type: Text Value: ["docs/readme.txt", "images/logo.png"]
JSON structure for paths parameter:
[
"folder1/subfolder1/file1.txt",
"folder1/subfolder2/file2.jpg",
"folder2/file3.pdf"
]Limitations:
- Maximum file size: 50MB
- Supported formats: jpg, jpeg, png, gif, pdf, doc, docx, txt, zip, rar
- Maximum files per request: 20
Success response (200 OK):
{
"success": true,
"message": "Uploaded 3 of 3 files",
"results": [
{
"file": "document.pdf",
"success": true,
"file_id": 123
},
{
"file": "photo.jpg",
"success": true,
"file_id": 124
},
{
"file": "large_file.zip",
"success": false,
"error": "File too large (maximum 50MB)"
}
],
"total": 3,
"success_count": 2
}POST /directories/share
Share folder with user
Headers: Content-Type: application/json Body → raw → JSON:
{
"directory_id": 123,
"email": "arthur@example.com"
}-
Cancel folder sharing by owner
-
Decline access to folder received from another user
**
Headers: Content-Type: application/json Body → raw → JSON:
{
"directory_id": 123
}PUT /directories/move
Move folder
Headers: Content-Type: application/json Body → raw → JSON:
{
"directory_id": 123,
"target_parent_id": 456
}GET /directories/download/{id}
Download folder as archive
GET /directories/list
Get list of all user folders
POST /users/create-first-admin
Assign first administrator (public endpoint for testing)
Headers: Content-Type: application/json Body → raw → JSON:
{
"email": "john@example.com"
}POST /users/login
Administrator authorization (same endpoint as for users)
{
"email": "john@example.com",
"password": "secure_password"
}**
GET /admin/users/list
Get list of all users
GET /admin/users/get/{id}
Get detailed user information by ID
PUT /admin/users/update/{id}
Update user data
Headers: Content-Type: application/json Body → raw → JSON:
{
"first_name": "New name",
"last_name": "New surname",
"email": "new@example.com",
"role": "user"
}DELETE /admin/users/delete/{id}
Delete user by ID
GET /admin/stats
Get system statistics
POST /admin/users/{id}/make-admin
Administrator assigns another user as administrator
PATCH /admin/users/{id}/remove-admin
Revoke rights from another administrator
DELETE /admin/users/bulk-delete
Bulk user deletion
Headers: Content-Type: application/json Body → raw → JSON:
{
"user_ids": [1, 2, 3, 4]
}GET /admin/files/list
Get list of all files in system
DELETE /admin/files/{id}
Delete any file by ID (admin access)
DELETE /admin/directories/delete/{id}
Delete any folder by ID (admin access)
DELETE /admin/files/cleanup
Cleanup unused files
DELETE /admin/files/clear
Delete all files in system
GET /admin/logs
Get system logs
Query parameters:
- level: all|info|warning|error (default: all)
- limit: number of records (default: 100)
DELETE /admin/logs/clear
Clear all logs from previous days
GET /admin/system/health
Get system health information
GET /admin/security/report
Get security report
Cause: Missing authorization cookies
Solution: Execute POST /users/login and ensure cookies are saved
Cause: Trying to access admin endpoint without administrator rights
Solution: Use /users/create-first-admin to assign rights
Cause: Trying to assign administrator to non-existent user
Solution: First register user via /users/register
Cause: Incorrect URL or request method
Solution: Check URL and HTTP method correctness
Cause: Administrator already exists in system
Solution: Use /remove-admin to reset or authorize with existing admin
"Invalid or expired token"
Cause: Token expired (valid for 1 hour) or already used
Solution: Request new token via /users/password-reset-request
"Email not sending"
Cause: Incorrect SMTP settings
Solution: Check email settings in config/config.php
"Token not found in database"
Cause: Issues with token saving
Solution: Check password_reset_tokens table in database
When testing issues occur:
- Check logs in
/logs/folder - Ensure database is configured correctly
- Check access rights to
/uploads/and/logs/folders - For email issues check SMTP settings
- Password hashing: Using password_hash() for password storage
- File validation: Checking types and sizes of uploaded files
- SQL Injection protection: Using prepared statements
- XSS protection: Escaping user input
- Access restriction: Middleware for authorization checking
- Logging: Maintaining log of all user actions
- Password reset tokens: Secure one-time tokens with limited validity
- Temporary tokens: Password reset tokens valid for only 1 hour
- One-time tokens: Each token can only be used once
- Token cleanup: Automatic deletion of expired tokens
- Email validation: Checking user existence before sending
- Secure generation: Using cryptographically strong generators
I. Check folder access rights:
chmod 755 uploads/
chmod 755 uploads/files/
chmod 755 uploads/folders/II. Increase PHP limits in php.ini:
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
memory_limit = 256MIII. Check that folders exist:
mkdir -p uploads/files
mkdir -p uploads/folders
mkdir -p logsIV. Check web server settings:
- Ensure Apache/Nginx has access to uploads folder
- Check that .htaccess file doesn't block uploads
I. Video not displaying in tiles:
- Check browser console (F12) for errors
- Ensure files have correct MIME type
- Check that video files are not corrupted
II. JavaScript cache issues (Chrome):
- Use Ctrl+F5 for forced refresh
- Clear browser cache via settings
- Try opening in incognito mode
III. Video not playing:
- Ensure file is actually video
- Check format support by browser
- Try different browser (Edge, Firefox)
- Check file size (50MB limit)
Database connection failure → Check connection:
mysql -u username -p -h localhost database_nameNo tables in database → Ensure all tables are created:
SHOW TABLES;
-- Should be:
+-------------------------+
| Tables_in_cloud_storage |
+-------------------------+
| directories |
| files |
| password_reset_tokens |
| shared_items |
| system_settings |
| trusted_devices |
| two_factor_codes |
| two_factor_logs |
| users |
+-------------------------+
9 rows in setEmail not sending → Check SMTP settings:
// In config/config.php
'email' => [
'smtp_host' => 'smtp.gmail.com',
'smtp_port' => 587,
'smtp_username' => 'your_email@gmail.com',
'smtp_password' => 'your_app_password',
'smtp_secure' => 'tls',
'from_email' => 'your_email@gmail.com',
'from_name' => 'CloudStorageApp'
]For Gmail:
- Enable two-factor authentication
- Create app password in Google settings
- Use app password, not regular password
SMTP errors → Check logs:
# View PHP logs
tail -f /var/log/php_errors.log
# Or application logs
tail -f logs/app_YYYY-MM-DD.logV. Check error logs:
# View PHP logs (first navigate to web server or PHP environment logs folder)
#Unix/Linux:
tail -f /var/log/php_errors.log
#Windows PowerShell or Command Prompt:
Get-Content -Path .\logs\php_errors.log
# Or application logs (first navigate to application address)
#Unix/Linux:
tail -f logs/app_YYYY-MM-DD.log
#Windows PowerShell or Command Prompt:
Get-Content -Path .\logs\app_YYYY-MM-DD.log (example: app_2025-07-10.log)VI. Common errors and solutions:
**"File too large"** → increase `upload_max_filesize`
**"Execution time exceeded"** → increase `max_execution_time`
**"Insufficient memory"** → increase `memory_limit`
**"Folder not found"** → check access rights and folder existence
**"SMTP Authentication failed"** → check SMTP login/password
**"Connection refused"** → check SMTP host and port
**"Token not found"** → check password_reset_tokens table
chmod -R 755 CloudStorageApp/
chmod -R 777 uploads/
chmod -R 777 logs/A: Execute SQL query:
UPDATE users SET role = 'admin', is_admin = 1 WHERE email = your@email.com;
UPDATE users SET role = 'admin', is_admin = 1 WHERE id = userID;Then user must clear browser cache and log in again. (CTRL+SHIFT+R)
A: First Name, Last Name, Email, Password and Repeat Password (marked with red asterisk in user interface).
A: Yes, Composer is required for application to work, as project uses dependencies and autoloading according to PSR-4 standard. To install all dependencies execute command:
composer installThis will install PHPMailer and other necessary libraries.
A: By default limited by PHP settings. Can be changed in php.ini.
A: In uploads/files/ folder in project root.
A: In uploads/folders/ folder in project root.
A:
- User requests reset via email
- System generates unique token and saves in DB
- Token is sent to user's email
- User follows link and sets new password
- Token is marked as used
A: Token is valid for 1 hour from creation. After use token becomes invalid.
- Check "Spam" folder
- Ensure SMTP settings are correct
- Check application logs for errors
- For Gmail use app password, not regular password
Use:
PUT /files/share/{file_id}/{user_id}- share file with specific userDELETE /files/share/{file_id}/{user_id}- remove sharingGET /files/share/{file_id}- see who file is shared with
All routes starting with /admin/:
/admin/users/list/admin/users/get/{id}/admin/users/update/{id}/admin/users/delete/{id}/admin/stats/admin/files/admin/logs
For email functions it's recommended to install PHPMailer:
composer require phpmailer/phpmailerOr download manually and place in vendor/ folder.
# Get user list
GET /users/list
# Get specific user
GET /users/get/123
# Update your data
PUT /users/update
{
"first_name": "New name",
"email": "new@email.com"
}# 1. Request password reset
POST /users/password-reset-request
{
"email": "user@example.com"
}
# 2. Validate token (received from email)
POST /users/password-reset-validate
{
"token": "abc123def456"
}
# 3. Set new password
POST /users/password-reset-confirm
{
"token": "abc123def456",
"password": "new_password123",
"confirm_password": "new_password123"
}# Share file with user ID 456
PUT /files/share/123/456
# Remove sharing
DELETE /files/share/123/456
# View who has access to the file
GET /files/share/123# Get all users (admin)
GET /admin/users/list
# Get user (admin)
GET /admin/users/get/123
# Update user (admin)
PUT /admin/users/update/123
{
"first_name": "New name",
"role": "admin"
}
# Delete user (admin)
DELETE /admin/users/delete/123
# Get system statistics
GET /admin/stats
# Get system logs
GET /admin/logs?level=error&limit=50# Testing email sending
POST /users/password-reset-request
{
"email": "test@example.com"
}
# Success response:
{
"success": true,
"message": "Password reset link sent to your email"
}
# SMTP error response:
{
"success": false,
"error": "Error sending email"
}- Support for 12+ video formats: MP4, AVI, MKV, WebM, MOV, WMV, FLV, 3GP, TS, ASF, OGG, MPEG
- Auto-play in tiles: Videos play on hover (like in Viber)
- Canvas animation: Video frame rendering in tiles for preview
- Modal viewing: Built-in video player in modal window
- Standard controls: Using native browser controls
- Error handling: Graceful degradation for unsupported formats
- Secure tokens: Cryptographically strong tokens
- Limited time: Tokens valid for 1 hour
- Single use: Each token can only be used once
- Email notifications: Beautiful HTML emails with instructions
- Auto-cleanup: Expired tokens automatically deleted
- SMTP support: Sending through external SMTP servers
- HTML templates: Beautiful branded emails
- Security: Protected TLS/SSL connections
- Logging: All email operations logged
- Fallback: Backup sending methods
- Token validation: Multi-level token verification
- Spam protection: Request frequency limits
- Security audit: Detailed logging of all operations
- Data cleanup: Automatic removal of outdated data
# In project root execute:
composer require phpmailer/phpmailer-
Download PHPMailer from GitHub: [https://github.com/PHPMailer/PHPMailer]
-
Extract to folder
vendor/phpmailer/phpmailer/ -
Structure should be:
CloudStorageApp/
├── vendor/
│ └── phpmailer/
│ └── phpmailer/
│ ├── src/
│ └── ...
// Create test_email.php file in project root:
<?php
require_once 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
echo "PHPMailer installed correctly!";'email' => [
'smtp_host' => 'smtp.gmail.com',
'smtp_port' => 587,
'smtp_username' => 'your_email@gmail.com',
'smtp_password' => 'your_app_password', // App password!
'smtp_secure' => 'tls',
'from_email' => 'your_email@gmail.com',
'from_name' => 'CloudStorageApp'
]'email' => [
'smtp_host' => 'smtp.yandex.ru',
'smtp_port' => 587,
'smtp_username' => 'your_email@yandex.ru',
'smtp_password' => 'your_password',
'smtp_secure' => 'tls',
'from_email' => 'your_email@yandex.ru',
'from_name' => 'CloudStorageApp'
]'email' => [
'smtp_host' => 'smtp.mail.ru',
'smtp_port' => 465,
'smtp_username' => 'your_email@mail.ru',
'smtp_password' => 'your_password',
'smtp_secure' => 'tls',
'from_email' => 'your_email@mail.ru',
'from_name' => 'CloudStorageApp'
]'email' => [
'smtp_host' => 'smtp-mail.outlook.com',
'smtp_port' => 587,
'smtp_username' => 'your_email@outlook.com',
'smtp_password' => 'your_password',
'smtp_secure' => 'tls',
'from_email' => 'your_email@outlook.com',
'from_name' => 'CloudStorageApp'
]- Register test user
- Configure SMTP in config.php
- Request password reset:
POST /users/password-reset-request
{
"email": "test@example.com"
}-
Check email (and spam folder)
-
Copy token from email
-
Validate token:
POST /users/password-reset-validate
{
"token": "your_token_here"
}- Set new password:
POST /users/password-reset-confirm
{
"token": "your_token_here",
"password": "new_password",
"confirm_password": "new_password"
}-- Check tokens table
SELECT * FROM password_reset_tokens;
-- Check that tokens are cleaned up
SELECT COUNT(*) FROM password_reset_tokens WHERE expires_at < UNIX_TIMESTAMP();- Cleanup expired tokens (can set up cron):
# Add to crontab for daily cleanup:
0 2 * * * php /path/to/your/project/cleanup_tokens.php- Log monitoring
# Check logs for email errors:
grep -i "email\|smtp\|mail" logs/app_*.log- Disk space check
# Monitor uploads folder size:
du -sh uploads/This project is created for educational purposes and is intended to demonstrate development skills.
This project is developed as a demonstration of web application development skills using PHP with:
- Clean Architecture - controllers (2-3 lines), services (business logic), repositories (data access)
- Repository and Service patterns with interfaces
- Dependency Injection through container
- Specialized services:
FileResponseService- file delivery with Range request supportFileTypeService- file type detection and preview capabilitiesFileUtils- file handling utilities
- MySQL database work
- REST API creation
- Web application security
- Modern routing approaches
- Secure password reset
- SMTP integration for email notifications
To check system functionality:
-
Browser console check (F12):
- Open developer tools
- Check Console tab for errors
- Check Network tab for HTTP requests
-
Authorization check:
- Ensure you are logged into the system
- Check file access permissions
- Re-login if necessary
-
Browser cache clearing:
- Use Ctrl+F5 for forced refresh
- Clear cache through browser settings
- Try incognito mode
- 🎬 Video files: blue play icon
- 🖼️ Images: blue image icon
- 📄 PDF: red PDF icon
- 🎵 Audio: yellow music icon
- 📝 Word: blue Word icon
- 📊 Excel: green Excel icon
- 📋 PowerPoint: yellow presentation icon
- 📄 Text files: light blue text icon
- 🗜️ Archives: gray archive icon
- 📄 Other: gray file icon
- Upload video files (MP4, AVI, MOV)
- Switch to tile mode
- Check Canvas animation on hover
- Open preview by clicking tile
- Ensure video player works correctly
- Check icons in list mode
Project created for educational purposes and technical skills demonstration.