Admin panel and REST API built with Laravel 13, Filament 5 and Laravel Sanctum. It manages Pages, Product Categories and Products.
- PHP 8.3+ with the
intl,fileinfoandpdo_mysqlextensions - Composer
- MySQL
git clone https://github.com/abdulloh-id/shop-admin.git
cd shop-admin
composer install
cp .env.example .env
php artisan key:generateCreate an empty MySQL database (for example shop_admin) and set the DB_* values in .env. Then run:
php artisan migrate --seed
php artisan storage:link
php artisan serveAPP_URL in .env must match the address you open in the browser (default http://127.0.0.1:8000). Otherwise image previews and image URLs in the API will be wrong.
Open http://127.0.0.1:8000/admin.
| Password | |
|---|---|
admin@example.com |
Admin123 |
The seeder also creates sample categories, products and a page. The panel has full CRUD for Pages, Categories and Products, with validation, search, sorting and pagination. Uploaded images are stored in storage/app/public.
Base URL: http://127.0.0.1:8000/api. Send Accept: application/json with every request. Responses are JSON.
| Method | URL | Description |
|---|---|---|
| GET | /api/categories |
List categories (paginated, 10 per page) |
| GET | /api/categories/{id} |
Get one category |
| GET | /api/products |
List products (paginated, optional ?category_id=) |
| GET | /api/products/{id} |
Get one product |
| POST | /api/login |
Get a Bearer token |
| Method | URL | Description |
|---|---|---|
| POST | /api/categories |
Create a category |
| PUT | /api/categories/{id} |
Update a category |
| DELETE | /api/categories/{id} |
Delete a category |
| POST | /api/logout |
Revoke the current token |
Get a token:
curl -X POST http://127.0.0.1:8000/api/login -H "Accept: application/json" -d "email=admin@example.com&password=Admin123"Create a category (multipart, with an image):
curl -X POST http://127.0.0.1:8000/api/categories -H "Accept: application/json" -H "Authorization: Bearer YOUR_TOKEN" --form-string "name=Toys" --form-string "short_description=Fun things" --form-string "full_description=<p>All the toys</p>" -F "image=@photo.jpg"Update a category (text only):
curl -X PUT http://127.0.0.1:8000/api/categories/1 -H "Accept: application/json" -H "Authorization: Bearer YOUR_TOKEN" -d "name=Toys and Games"Update a category with a new image. PHP does not parse files from a real PUT request, so send a POST with _method=PUT:
curl -X POST http://127.0.0.1:8000/api/categories/1 -H "Accept: application/json" -H "Authorization: Bearer YOUR_TOKEN" --form-string "_method=PUT" -F "image=@photo.jpg"Delete a category:
curl -X DELETE http://127.0.0.1:8000/api/categories/1 -H "Accept: application/json" -H "Authorization: Bearer YOUR_TOKEN"On Windows PowerShell, use curl.exe instead of curl.
Validation errors return 422 with an errors object. Missing or invalid tokens return 401. Missing records return 404.
- Deleting a category keeps its products, which become uncategorized (
nullOnDelete). In the admin panel, a category is required when saving a product. - Images are stored on the
publicdisk incategories/andproducts/. The API returns full image URLs. Old files are deleted when an image is replaced or a record is deleted (model events). full_descriptionis raw HTML from the rich editor and is returned unchanged by the API. Clients must sanitize it before rendering it.PUTupdates accept partial data: only the fields you send are validated and changed.- Login is rate-limited to 5 attempts per minute.
- Images pasted into the rich text editor are not removed when the description changes.
- Deletes that bypass Eloquent (raw queries) do not remove image files.
- Upload fails, or the image never loads: check
APP_URL, runphp artisan storage:link, and checkupload_max_filesizeandpost_max_sizeinphp.ini. intlextension error oncomposer install: enableextension=intlinphp.ini.