From 060eb79c094e24a62c15dad8b2fa68ff513942f5 Mon Sep 17 00:00:00 2001
From: lucanlee
Date: Sat, 8 Aug 2026 18:27:38 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.example | 34 +
.gitignore | 32 +-
DEPLOY.md | 85 +
README.md | 154 +-
SECURITY_REPORT.md | 104 +
app/Exceptions/Handler.php | 30 +
.../Controllers/Admin/ArticleController.php | 100 +
.../Controllers/Admin/CategoryController.php | 65 +
.../Controllers/Admin/ContactController.php | 29 +
.../Controllers/Admin/DashboardController.php | 33 +
.../Controllers/Admin/DatabaseController.php | 53 +
.../Admin/ForumBoardController.php | 63 +
.../Admin/ForumThreadController.php | 26 +
.../Controllers/Admin/GlossaryController.php | 65 +
.../Controllers/Admin/HeroSlideController.php | 136 +
app/Http/Controllers/Admin/MenuController.php | 111 +
.../Admin/ModerationController.php | 95 +
app/Http/Controllers/Admin/NewsController.php | 73 +
.../Controllers/Admin/ProductController.php | 80 +
.../Controllers/Admin/SettingsController.php | 45 +
.../Controllers/Admin/SolutionController.php | 79 +
app/Http/Controllers/Admin/UserController.php | 228 +
app/Http/Controllers/ArticleController.php | 114 +
.../Controllers/Auth/AdminLoginController.php | 92 +
.../Controllers/Auth/CaptchaController.php | 77 +
app/Http/Controllers/Auth/LoginController.php | 91 +
.../Controllers/Auth/RegisterController.php | 47 +
.../Controllers/Auth/SettingsController.php | 32 +
app/Http/Controllers/ContactController.php | 61 +
app/Http/Controllers/Controller.php | 12 +
app/Http/Controllers/ForumController.php | 113 +
app/Http/Controllers/GlossaryController.php | 33 +
app/Http/Controllers/HealthController.php | 29 +
app/Http/Controllers/HomeController.php | 34 +
app/Http/Controllers/NewsController.php | 31 +
app/Http/Controllers/ProductController.php | 42 +
app/Http/Controllers/SearchController.php | 80 +
app/Http/Controllers/SitemapController.php | 155 +
app/Http/Controllers/SolutionsController.php | 34 +
app/Http/Controllers/UploadController.php | 27 +
app/Http/Middleware/Admin.php | 23 +
app/Http/Middleware/Can.php | 31 +
app/Http/Middleware/Role.php | 25 +
app/Http/Middleware/SecurityHeaders.php | 70 +
app/Models/Article.php | 100 +
app/Models/Category.php | 37 +
app/Models/ContactMessage.php | 22 +
app/Models/ForumBoard.php | 25 +
app/Models/ForumReply.php | 60 +
app/Models/ForumThread.php | 75 +
app/Models/GlossaryTerm.php | 21 +
app/Models/HeroSlide.php | 41 +
app/Models/NavigationMenu.php | 29 +
app/Models/News.php | 56 +
app/Models/Product.php | 52 +
app/Models/Setting.php | 33 +
app/Models/Solution.php | 89 +
app/Models/User.php | 177 +
app/Providers/AppServiceProvider.php | 24 +
artisan | 16 +
bootstrap/app.php | 26 +
bootstrap/cache/.gitkeep | 0
bootstrap/cache/packages.php | 45 +
bootstrap/cache/services.php | 255 +
bootstrap/providers.php | 5 +
composer.json | 62 +
composer.lock | 8322 +++++++++++++++++
config/app.php | 31 +
config/auth.php | 35 +
config/cache.php | 36 +
config/cors.php | 21 +
config/database.php | 81 +
config/filesystems.php | 39 +
config/logging.php | 61 +
config/mail.php | 62 +
config/permissions.php | 138 +
config/purifier.php | 46 +
config/queue.php | 40 +
config/services.php | 22 +
config/session.php | 35 +
config/view.php | 14 +
.../2014_10_12_000000_create_users_table.php | 26 +
...000_create_password_reset_tokens_table.php | 22 +
..._08_19_000000_create_failed_jobs_table.php | 26 +
...1_000001_create_contact_messages_table.php | 30 +
..._01_000002_add_is_admin_to_users_table.php | 22 +
...6_08_01_000003_create_categories_table.php | 26 +
...026_08_01_000004_create_products_table.php | 34 +
...026_08_01_000005_create_articles_table.php | 33 +
..._01_000006_create_glossary_terms_table.php | 26 +
...08_01_000007_create_forum_boards_table.php | 25 +
...8_01_000008_create_forum_threads_table.php | 33 +
...8_01_000009_create_forum_replies_table.php | 28 +
...026_08_01_000010_create_settings_table.php | 24 +
...08_01_000011_add_points_to_users_table.php | 22 +
...add_template_and_seo_to_articles_table.php | 24 +
...08_01_000013_add_seo_to_products_table.php | 23 +
...dd_role_and_permissions_to_users_table.php | 27 +
...8_01_100002_add_status_to_forum_tables.php | 35 +
...d_user_id_and_status_to_articles_table.php | 46 +
...0001_add_profile_fields_to_users_table.php | 32 +
...26_08_04_000001_create_solutions_table.php | 36 +
.../2026_08_04_000002_create_news_table.php | 32 +
...000003_add_company_to_contact_messages.php | 25 +
...4_000100_create_navigation_menus_table.php | 27 +
..._08_04_000101_create_hero_slides_table.php | 32 +
database/seeders/AdminUserSeeder.php | 25 +
database/seeders/ArticleSeeder.php | 42 +
database/seeders/CategorySeeder.php | 28 +
database/seeders/DatabaseSeeder.php | 26 +
database/seeders/ForumBoardSeeder.php | 37 +
database/seeders/GlossaryTermSeeder.php | 54 +
database/seeders/HeroSlideSeeder.php | 109 +
database/seeders/NavigationMenuSeeder.php | 35 +
database/seeders/NewsSeeder.php | 68 +
database/seeders/PdContentSeeder.php | 80 +
database/seeders/ProductSeeder.php | 57 +
database/seeders/SettingsSeeder.php | 50 +
database/seeders/SolutionsSeeder.php | 75 +
database/seeders/data/site_content.json | 526 ++
install.sh | 172 +
public/.htaccess | 21 +
public/css/app.css | 1096 +++
public/favicon.ico | Bin 0 -> 27886 bytes
public/images/ccc-icon.ico | Bin 0 -> 27886 bytes
public/images/gongan_badge.png | Bin 0 -> 11143 bytes
public/images/logo-56.png | Bin 0 -> 5128 bytes
public/images/logo-animated.gif | Bin 0 -> 2523 bytes
public/images/logo-dark.png | Bin 0 -> 10452 bytes
public/images/logo.ico | Bin 0 -> 123262 bytes
public/images/logo.png | Bin 0 -> 19218 bytes
public/index.php | 23 +
public/js/app.js | 95 +
public/js/vendor/vue.global.prod.js | 9 +
public/vendor/quill/quill.js | 3 +
public/vendor/quill/quill.snow.css | 10 +
resources/views/about.blade.php | 140 +
resources/views/admin/articles/form.blade.php | 84 +
.../views/admin/articles/index.blade.php | 39 +
.../views/admin/categories/form.blade.php | 38 +
.../views/admin/categories/index.blade.php | 32 +
.../views/admin/contacts/index.blade.php | 30 +
resources/views/admin/contacts/show.blade.php | 24 +
resources/views/admin/dashboard.blade.php | 41 +
resources/views/admin/database.blade.php | 56 +
.../views/admin/forum/board-form.blade.php | 35 +
resources/views/admin/forum/boards.blade.php | 32 +
resources/views/admin/forum/threads.blade.php | 31 +
resources/views/admin/glossary/form.blade.php | 39 +
.../views/admin/glossary/index.blade.php | 31 +
.../views/admin/hero-slides/form.blade.php | 65 +
.../views/admin/hero-slides/index.blade.php | 70 +
resources/views/admin/layout.blade.php | 46 +
resources/views/admin/menus/form.blade.php | 47 +
resources/views/admin/menus/index.blade.php | 60 +
.../views/admin/moderation/index.blade.php | 103 +
resources/views/admin/products/form.blade.php | 68 +
.../views/admin/products/index.blade.php | 33 +
resources/views/admin/settings.blade.php | 105 +
resources/views/admin/users/create.blade.php | 87 +
resources/views/admin/users/edit.blade.php | 143 +
resources/views/admin/users/index.blade.php | 58 +
resources/views/articles/index.blade.php | 59 +
resources/views/articles/mine.blade.php | 61 +
resources/views/articles/show.blade.php | 58 +
resources/views/articles/submit.blade.php | 67 +
resources/views/auth/admin-login.blade.php | 51 +
resources/views/auth/login.blade.php | 44 +
resources/views/auth/register.blade.php | 56 +
resources/views/auth/settings.blade.php | 50 +
resources/views/components/icon.blade.php | 46 +
.../views/components/level-badge.blade.php | 8 +
.../views/components/rich-editor.blade.php | 95 +
resources/views/contact.blade.php | 136 +
resources/views/forum/board.blade.php | 75 +
resources/views/forum/index.blade.php | 44 +
resources/views/forum/thread.blade.php | 68 +
resources/views/glossary/index.blade.php | 49 +
resources/views/glossary/show.blade.php | 36 +
resources/views/home.blade.php | 235 +
resources/views/layouts/app.blade.php | 299 +
resources/views/news/index.blade.php | 43 +
resources/views/news/show.blade.php | 35 +
resources/views/partials/hero-slide.blade.php | 28 +
resources/views/products/index.blade.php | 57 +
resources/views/products/show.blade.php | 45 +
resources/views/search/index.blade.php | 107 +
resources/views/sitemap.blade.php | 131 +
resources/views/solutions/index.blade.php | 61 +
resources/views/solutions/show.blade.php | 56 +
router.php | 56 +
routes/api.php | 6 +
routes/console.php | 8 +
routes/web.php | 158 +
storage/app/.gitkeep | 0
storage/framework/cache/.gitkeep | 0
.../1a5ab75a2650ac3516829f9db1af97dbe94d0db7 | 1 +
.../4c1fe5387048dd142b24044f83e6e920c7cb9fea | 1 +
.../a7c2cef6848acb2036c43a618d19df0456f8aaae | 1 +
.../db68ee7e615502bfcd3bb03f0be329bffd5b000d | 1 +
storage/framework/sessions/.gitkeep | 0
.../0LvKlcOCOr2O5Pm8V2tWrL0qrYbufKZvjQUIzXF5 | 1 +
.../0X8nNF3bRuMaVk13RwsUK2A21dEM6hHbbU0bH6bn | 1 +
.../39QoTZUXGJDR2DaTvvfDJ2SoRXUdpSI4jrrQywTW | 1 +
.../3HNVdNbwWC7dMcmkK1gw5SghZSShR415naoYzudN | 1 +
.../3pmDLferkhgM1PODQVBNL9DhZWUv6ZlKoTAUE4sb | 1 +
.../4AgfQMBmECxRM26CdaCnLSXHTTWsh0Lq6iMeSGwt | 1 +
.../4phpNsA3mAqgGcuC8qQt0kW38EJUOFa1J1pFEFy5 | 1 +
.../59D6cp1xekyIF8chch2TkFBod5Hjibg9sbdB0otu | 1 +
.../5d3bndSBF8ml3AUteJSWQnyiXa04PFMhJC5fR1VE | 1 +
.../636kEcZekVeAy71gpuNhcNiOsqOmHSDxBdYa97oP | 1 +
.../7BJ4Z7wc6gWzKC5wGOEKDZn9IEZX5PHlINsv8JQQ | 1 +
.../7GulP4ndB4HP1NxQQjKyY3Dzjon6YVKKFEzWMuvG | 1 +
.../8YYoqxebggDQ6L2kpxHg8twz9rlr3yq0EtzITtcH | 1 +
.../99kVWOjz8qLghuTInK9z6SUAbj6LaSFdyddlgnLV | 1 +
.../9fkgMuLaQnePsVHLrGZKu82NWADq8DXrzoi6BN8E | 1 +
.../9mWgx1PsuNTPclch8JSeSjblJ5FRMHMrzNTOu9TD | 1 +
.../A0YydUfOuS75nvy2c8i3LyE7jXOiL4oVzjEEAUWn | 1 +
.../AJr2isfYK8PMY9ySmdoSqnMbacrs1mi3DPWZv5pF | 1 +
.../By4oINi5NSiRkBzQrBholpPEcw7KXp2sp1wLvBvG | 1 +
.../CFxocOhrqyoHJUU1HwzSAzavBg8YvUvqIUfN4pxb | 1 +
.../CL2xgP11gSJs9dRCTfaqQ25DJ0df6CSMAyPtyS9r | 1 +
.../D5ODQ2Xdm2zP2E6OZjeWEvo6zquoRZjzoVwxWVBs | 1 +
.../DeBUqmjuy1FleIL933EKO6ebI0YUVWXFW6LzSDQs | 1 +
.../FsBSO7p2mUHfnG0yY2S5tYZtwayMlxlHUcmDmKxO | 1 +
.../HH2WCYBqqu8j1wVcoTHmqkQAMtcnBoLT22fRjlal | 1 +
.../HMu8UpCTK9dPuNNpiEmam5nzNZC57yKCCn2wS3pr | 1 +
.../I0iH5L4TkEmjfSsqweOP1Z2NPd9XRc1DpxkPTPbo | 1 +
.../IQ7m85uibM59CNwe05opHN3Opo2TyI0fuVuNG2tf | 1 +
.../IkSKj8vK6NAGki1BVV7dMOlMkXvIARImrBBeB4Lf | 1 +
.../J74ObvmaBWYjTTQTs7tAIg7uZr5TeZIaDaWJw25k | 1 +
.../LPLbJtWV5Hfd0bYMot7pxneqiNUFK8PGReFpUofb | 1 +
.../LwzP7qaUj366VcdHPKJuDClU5pCI1KysVwygAjRQ | 1 +
.../MRNVNNXWWkevJYsiMeAULcgVHnb94gnLqE14C6pj | 1 +
.../MmJPr9Sol62jK5GbLXOwXdaAM9x7sVxYXayNDVoT | 1 +
.../MsybeD4uApyagBolAq9dtR1hfZwOYGfjVhdxkA0S | 1 +
.../OoidY45sTSGctMOngxmZr3gKxKBEvm7TRP8scPgr | 1 +
.../OuggSkTJzXJSzlSuNATZleF0DFeIVQOeOWhumnPV | 1 +
.../PFN6pXvtYe8ao4BQkXzgwg5LHSsAsXVIW0EQTZtB | 1 +
.../QpNwLCyBf1JWXuhLQ2IojVgNDaJpnSJxlE6bgPOI | 1 +
.../R0ifJwQlh8ZXKuo6jNdr2J22SqJ8j5xNrf1OhRfA | 1 +
.../RPD8MycdhWAsgByuW6nH5YjRXUJdVadlBAK9qmKX | 1 +
.../SjHpF9bDYZdkrAhbFyaJ80Kp74KsYjdLIQN0BdCR | 1 +
.../Sv2LxGJQASyhP30hnrzFNZuM6kCtUGUwI4fwzYd3 | 1 +
.../TYALLNCEiSFjjWLxSNJvmZIciKDmGgCIy2HQ90Ea | 1 +
.../TcjaGaLB3lEB958eyt62oFLDOzRz8BpRP7FQKUP5 | 1 +
.../TqzJYtKkIiros1zTMQXJT5EEKXkGVnkjRtmR4XGd | 1 +
.../TufCurfCrgLpISCXqHeogyuXuEn5MLnqWvVUFGw3 | 1 +
.../U6tEMgpGADfu26GxuN80xg5AUwkZ32jw39FzmMmZ | 1 +
.../UVyIW3QKPdI3x6YbG7NLn8ha4bpiN4JOG0xelNQd | 1 +
.../VZ89yuloDTTI6uD6A68TARpqMVpAjm0WJ81Irs1g | 1 +
.../VcqsqDPXyPZqEtk6dbYWwUrUAnwTtcvC0vwEgByQ | 1 +
.../WSeWmshAu84lrwpDBMWglVZSkFXLYyfGfYAZa7ZG | 1 +
.../WlNzRs7HSGqKEg352yi2u5T3GmfR5PNpdijBMd5q | 1 +
.../XaOKa8LEi5wtlnx8OkwmfxmBtczdIRoV9aB8uJRS | 1 +
.../YjknNvfmm8CLaBurhStwQSGZ8Zkuv0XAljXrSUKt | 1 +
.../ZTyQcSHCKUSvEimbPRKkuXMTVLkZ5EXkBcKMgf6H | 1 +
.../aDNQ5yBzwHdZr1g5dZu6wg8f3jsB8q8IQjj0Yxjk | 1 +
.../aMdfplTM56k6Un3xcRQImwQnRZbQu4Pci3Ha4HF3 | 1 +
.../byBEhnytxxGFbbMoAIJlKMFmyLMB7m06bNQTqiBn | 1 +
.../cVHrJcSo8llpgM0gMoL1TvIizFDIGV6zeHlGfTTz | 1 +
.../dOxnEmqKLaE9yUQUw9vsTCJHyazbGg9jrHisc4VL | 1 +
.../dWguL9ssSVnIePckwSdyDSB2yefPyz9xQqZrRpkq | 1 +
.../drKgdyHJkEKnsTDfgpsNfg8Q2g2sVllBZG0bLG9F | 1 +
.../eH7SXZuvuPxPFbgpDA99dv2N48E0obFvsritDYz7 | 1 +
.../eNhjpa1lQ1WJ81j2lVGZ2bqi2Kr1qH98yFLjnm7k | 1 +
.../fMOtXSHVqdmuzhddGvwjyFpdp89WhVUsihUBVkTJ | 1 +
.../fVINnk8rIDswjuZE2TNg1loatQ892GZwnEb2qlKU | 1 +
.../gyddBMpqW1hNSNkMx7twLAq9MBGH8cFmoiWCDJkN | 1 +
.../i0M4xP47SovRPBFgvNsCIAe5Bm8a2q79c5GmksUJ | 1 +
.../iwcit142KwkSQeU9LMMs5jqMnIWzMh6rQZ1CIWyH | 1 +
.../j2SeIoihXlXz5HL5806vBtAzEJ0tx1Ox1TeETVcV | 1 +
.../jmo8VjhXjULBZL7pMzSvCxZigoTzsql8WGDP6Tf5 | 1 +
.../jw9GTFOs7zxIBzZT6A0TDJs0LGgcpDlNxeA83DKb | 1 +
.../l8retQpc9nY3dwci84aLKbHNwwNZfCgKkXDZF1Mo | 1 +
.../lEQz8AMN4UrfaSkz6M902RMOhzjLeINAZqE13qPy | 1 +
.../lK265IOfDsvnDzjOWCzY62DEvguaopjVrz3JmL9l | 1 +
.../lkowNf8mIhlY9ZQb0YIgF2JwQjKCPIDGQlQqEujY | 1 +
.../luHGYELgXitDiFYFpJMl60o2ETz5CgZnRVL8DoWo | 1 +
.../m4CTy6sjcIxR46gnBfj83SZWoXwyAQTJsc2jLJ0f | 1 +
.../mED9Kc5tv4cVX0OwVLTYgp9fLJ5tKScquMSr3MiF | 1 +
.../mwuphfOLozdCa3CW9HZwWpSdRpYInuiOivymoC55 | 1 +
.../nBrcQVEDavhMrhzmOQ1y9rsZGtOwdGMoTJWEi0nZ | 1 +
.../nFKRWRCIIWaXfeFSs3ppOq5ywdbolOh2aK8gvC5D | 1 +
.../nPjc9Q0qG1v1viqrfrgPSVeeYnw5XaNRlk0aZSIA | 1 +
.../omMrN1E7AFpX7mxdghKBSYuWVxPD6WEyUPG3LRC8 | 1 +
.../osWo6PJtxn7cmxLyNsVPQbfejoJzXENp92NhYKQM | 1 +
.../ouK3b4kovOi51Cd06JnKjgGTzkKqsAiftlgvNjlx | 1 +
.../qSVGKs3nVfuCD4glcmuxyp7jwMRAGmPd0jIL5XYl | 1 +
.../qvxuO6EhncjZoB0mlbqMWMl8kAHmpiw8U36dPv4q | 1 +
.../rgmSAY1IlTAlsOHBtDem2BDG6viWUtaEMmxeaFpA | 1 +
.../s13OCD8tF6RDC8zNlAWynINjBJOzoY8UElTfpku5 | 1 +
.../sPewv2LlIsqmF9vSvEbzTLBY01BmfDMsf7Waumys | 1 +
.../ssPobPGAjcOUrKF5RpiyYrIazYCppjAujEhw6o6F | 1 +
.../v4yp2NUBWCqyaKTvyZG6OWrDJ7WSlsBjXDykITnV | 1 +
.../vWAlv4dexYMkfnITO1bkZ3oe9XHwWQTta1OIJpCe | 1 +
.../x9Zsl91G4CUFBbVvECC88eypbJGRorQx3oYFiNG6 | 1 +
.../x9eQYs4AY0jXyFbECBywxFVYsDusTzNH4vQW5X5o | 1 +
.../xpZdDEuKqhQWD4fe8agC7CSR2YmaTqOIsXv889go | 1 +
.../ycXVBKZfkwDRGmG86eR7S7Sq5sZMTSW5vpsnxBoY | 1 +
.../zOPbsIA4iPGjVlW5eNpWj8OIfvc3QnQ6sPqxgGls | 1 +
.../zbKnAr79blajOaOjUSPDcmNR1fsVzyGKgWucXxRa | 1 +
storage/framework/testing/.gitkeep | 0
storage/framework/views/.gitkeep | 0
.../0a8789f139de15f847d69c49efc44f49.php | 41 +
.../15b9d4e02c209a2f1e3c28a8dc50ac02.php | 35 +
.../16089911eeeb602832250302c2cd049f.php | 64 +
.../18e917f0524fac30e8812a95bd8a01cd.php | 46 +
.../1d856a721bacf5700d599ae513cfc39a.php | 91 +
.../24f475e10c5f5dd12359f46205641a8a.php | 370 +
.../253e0b0ecbba0b334a479f2567a92523.php | 293 +
.../2b514aa22b5d10200f82764d1e8b1262.php | 58 +
.../2ff4acda1568e1d72077e7b05cbb05dd.php | 134 +
.../324d075a03ec465c04e7c7edf5aff085.php | 75 +
.../40a52fb7dced61faf2e5d69224ad395c.php | 84 +
.../4cbda21650a03c74ba295822590317e0.php | 58 +
.../548477a6a585ec49a2f27085bfcb8cba.php | 124 +
.../5496242fd46c49a1eb73fc7d342b6f39.php | 5 +
.../5d039872216afd2de7e2649de9d82baf.php | 101 +
.../6309104b2bdbede64f99945b56b14858.php | 153 +
.../635f14b24b41c4f62756dec86d8259e3.php | 38 +
.../6d97f08c22bb906fbb4b73b4c34a4f5e.php | 389 +
.../76a517bf0b8aee43086be6a6d309df9b.php | 67 +
.../7a66ca3440694de3668ee2cffe656869.php | 117 +
.../893e48106319282548a1ee6ea39d98d9.php | 96 +
.../97ffa5d8d55c45773efadada907b861f.php | 93 +
.../989ddd6b1f599a426eca38b293f52998.php | 124 +
.../c7d7628f707fbc8bb91e302f4d299ef1.php | 276 +
.../cfa2208064bda481ba2008da7932a6a6.php | 82 +
.../d7fe0f07512af77fe6ff8ccf46c415ff.php | 56 +
.../e44e13153d7da70ab3bc55bfbc81a97d.php | 109 +
.../e903b7765e8c413b9a7390aa81fd1685.php | 119 +
.../f88d0e84d3df42bb93a6b055b8263a49.php | 51 +
.../fb732d386deb347bc804e258e49d1979.php | 474 +
storage/logs/.gitkeep | 0
storage/logs/laravel.log | 272 +
336 files changed, 24613 insertions(+), 15 deletions(-)
create mode 100644 .env.example
create mode 100644 DEPLOY.md
create mode 100644 SECURITY_REPORT.md
create mode 100644 app/Exceptions/Handler.php
create mode 100644 app/Http/Controllers/Admin/ArticleController.php
create mode 100644 app/Http/Controllers/Admin/CategoryController.php
create mode 100644 app/Http/Controllers/Admin/ContactController.php
create mode 100644 app/Http/Controllers/Admin/DashboardController.php
create mode 100644 app/Http/Controllers/Admin/DatabaseController.php
create mode 100644 app/Http/Controllers/Admin/ForumBoardController.php
create mode 100644 app/Http/Controllers/Admin/ForumThreadController.php
create mode 100644 app/Http/Controllers/Admin/GlossaryController.php
create mode 100644 app/Http/Controllers/Admin/HeroSlideController.php
create mode 100644 app/Http/Controllers/Admin/MenuController.php
create mode 100644 app/Http/Controllers/Admin/ModerationController.php
create mode 100644 app/Http/Controllers/Admin/NewsController.php
create mode 100644 app/Http/Controllers/Admin/ProductController.php
create mode 100644 app/Http/Controllers/Admin/SettingsController.php
create mode 100644 app/Http/Controllers/Admin/SolutionController.php
create mode 100644 app/Http/Controllers/Admin/UserController.php
create mode 100644 app/Http/Controllers/ArticleController.php
create mode 100644 app/Http/Controllers/Auth/AdminLoginController.php
create mode 100644 app/Http/Controllers/Auth/CaptchaController.php
create mode 100644 app/Http/Controllers/Auth/LoginController.php
create mode 100644 app/Http/Controllers/Auth/RegisterController.php
create mode 100644 app/Http/Controllers/Auth/SettingsController.php
create mode 100644 app/Http/Controllers/ContactController.php
create mode 100644 app/Http/Controllers/Controller.php
create mode 100644 app/Http/Controllers/ForumController.php
create mode 100644 app/Http/Controllers/GlossaryController.php
create mode 100644 app/Http/Controllers/HealthController.php
create mode 100644 app/Http/Controllers/HomeController.php
create mode 100644 app/Http/Controllers/NewsController.php
create mode 100644 app/Http/Controllers/ProductController.php
create mode 100644 app/Http/Controllers/SearchController.php
create mode 100644 app/Http/Controllers/SitemapController.php
create mode 100644 app/Http/Controllers/SolutionsController.php
create mode 100644 app/Http/Controllers/UploadController.php
create mode 100644 app/Http/Middleware/Admin.php
create mode 100644 app/Http/Middleware/Can.php
create mode 100644 app/Http/Middleware/Role.php
create mode 100644 app/Http/Middleware/SecurityHeaders.php
create mode 100644 app/Models/Article.php
create mode 100644 app/Models/Category.php
create mode 100644 app/Models/ContactMessage.php
create mode 100644 app/Models/ForumBoard.php
create mode 100644 app/Models/ForumReply.php
create mode 100644 app/Models/ForumThread.php
create mode 100644 app/Models/GlossaryTerm.php
create mode 100644 app/Models/HeroSlide.php
create mode 100644 app/Models/NavigationMenu.php
create mode 100644 app/Models/News.php
create mode 100644 app/Models/Product.php
create mode 100644 app/Models/Setting.php
create mode 100644 app/Models/Solution.php
create mode 100644 app/Models/User.php
create mode 100644 app/Providers/AppServiceProvider.php
create mode 100644 artisan
create mode 100644 bootstrap/app.php
create mode 100644 bootstrap/cache/.gitkeep
create mode 100644 bootstrap/cache/packages.php
create mode 100644 bootstrap/cache/services.php
create mode 100644 bootstrap/providers.php
create mode 100644 composer.json
create mode 100644 composer.lock
create mode 100644 config/app.php
create mode 100644 config/auth.php
create mode 100644 config/cache.php
create mode 100644 config/cors.php
create mode 100644 config/database.php
create mode 100644 config/filesystems.php
create mode 100644 config/logging.php
create mode 100644 config/mail.php
create mode 100644 config/permissions.php
create mode 100644 config/purifier.php
create mode 100644 config/queue.php
create mode 100644 config/services.php
create mode 100644 config/session.php
create mode 100644 config/view.php
create mode 100644 database/migrations/2014_10_12_000000_create_users_table.php
create mode 100644 database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php
create mode 100644 database/migrations/2019_08_19_000000_create_failed_jobs_table.php
create mode 100644 database/migrations/2026_08_01_000001_create_contact_messages_table.php
create mode 100644 database/migrations/2026_08_01_000002_add_is_admin_to_users_table.php
create mode 100644 database/migrations/2026_08_01_000003_create_categories_table.php
create mode 100644 database/migrations/2026_08_01_000004_create_products_table.php
create mode 100644 database/migrations/2026_08_01_000005_create_articles_table.php
create mode 100644 database/migrations/2026_08_01_000006_create_glossary_terms_table.php
create mode 100644 database/migrations/2026_08_01_000007_create_forum_boards_table.php
create mode 100644 database/migrations/2026_08_01_000008_create_forum_threads_table.php
create mode 100644 database/migrations/2026_08_01_000009_create_forum_replies_table.php
create mode 100644 database/migrations/2026_08_01_000010_create_settings_table.php
create mode 100644 database/migrations/2026_08_01_000011_add_points_to_users_table.php
create mode 100644 database/migrations/2026_08_01_000012_add_template_and_seo_to_articles_table.php
create mode 100644 database/migrations/2026_08_01_000013_add_seo_to_products_table.php
create mode 100644 database/migrations/2026_08_01_100001_add_role_and_permissions_to_users_table.php
create mode 100644 database/migrations/2026_08_01_100002_add_status_to_forum_tables.php
create mode 100644 database/migrations/2026_08_01_100003_add_user_id_and_status_to_articles_table.php
create mode 100644 database/migrations/2026_08_02_000001_add_profile_fields_to_users_table.php
create mode 100644 database/migrations/2026_08_04_000001_create_solutions_table.php
create mode 100644 database/migrations/2026_08_04_000002_create_news_table.php
create mode 100644 database/migrations/2026_08_04_000003_add_company_to_contact_messages.php
create mode 100644 database/migrations/2026_08_04_000100_create_navigation_menus_table.php
create mode 100644 database/migrations/2026_08_04_000101_create_hero_slides_table.php
create mode 100644 database/seeders/AdminUserSeeder.php
create mode 100644 database/seeders/ArticleSeeder.php
create mode 100644 database/seeders/CategorySeeder.php
create mode 100644 database/seeders/DatabaseSeeder.php
create mode 100644 database/seeders/ForumBoardSeeder.php
create mode 100644 database/seeders/GlossaryTermSeeder.php
create mode 100644 database/seeders/HeroSlideSeeder.php
create mode 100644 database/seeders/NavigationMenuSeeder.php
create mode 100644 database/seeders/NewsSeeder.php
create mode 100644 database/seeders/PdContentSeeder.php
create mode 100644 database/seeders/ProductSeeder.php
create mode 100644 database/seeders/SettingsSeeder.php
create mode 100644 database/seeders/SolutionsSeeder.php
create mode 100644 database/seeders/data/site_content.json
create mode 100644 install.sh
create mode 100644 public/.htaccess
create mode 100644 public/css/app.css
create mode 100644 public/favicon.ico
create mode 100644 public/images/ccc-icon.ico
create mode 100644 public/images/gongan_badge.png
create mode 100644 public/images/logo-56.png
create mode 100644 public/images/logo-animated.gif
create mode 100644 public/images/logo-dark.png
create mode 100644 public/images/logo.ico
create mode 100644 public/images/logo.png
create mode 100644 public/index.php
create mode 100644 public/js/app.js
create mode 100644 public/js/vendor/vue.global.prod.js
create mode 100644 public/vendor/quill/quill.js
create mode 100644 public/vendor/quill/quill.snow.css
create mode 100644 resources/views/about.blade.php
create mode 100644 resources/views/admin/articles/form.blade.php
create mode 100644 resources/views/admin/articles/index.blade.php
create mode 100644 resources/views/admin/categories/form.blade.php
create mode 100644 resources/views/admin/categories/index.blade.php
create mode 100644 resources/views/admin/contacts/index.blade.php
create mode 100644 resources/views/admin/contacts/show.blade.php
create mode 100644 resources/views/admin/dashboard.blade.php
create mode 100644 resources/views/admin/database.blade.php
create mode 100644 resources/views/admin/forum/board-form.blade.php
create mode 100644 resources/views/admin/forum/boards.blade.php
create mode 100644 resources/views/admin/forum/threads.blade.php
create mode 100644 resources/views/admin/glossary/form.blade.php
create mode 100644 resources/views/admin/glossary/index.blade.php
create mode 100644 resources/views/admin/hero-slides/form.blade.php
create mode 100644 resources/views/admin/hero-slides/index.blade.php
create mode 100644 resources/views/admin/layout.blade.php
create mode 100644 resources/views/admin/menus/form.blade.php
create mode 100644 resources/views/admin/menus/index.blade.php
create mode 100644 resources/views/admin/moderation/index.blade.php
create mode 100644 resources/views/admin/products/form.blade.php
create mode 100644 resources/views/admin/products/index.blade.php
create mode 100644 resources/views/admin/settings.blade.php
create mode 100644 resources/views/admin/users/create.blade.php
create mode 100644 resources/views/admin/users/edit.blade.php
create mode 100644 resources/views/admin/users/index.blade.php
create mode 100644 resources/views/articles/index.blade.php
create mode 100644 resources/views/articles/mine.blade.php
create mode 100644 resources/views/articles/show.blade.php
create mode 100644 resources/views/articles/submit.blade.php
create mode 100644 resources/views/auth/admin-login.blade.php
create mode 100644 resources/views/auth/login.blade.php
create mode 100644 resources/views/auth/register.blade.php
create mode 100644 resources/views/auth/settings.blade.php
create mode 100644 resources/views/components/icon.blade.php
create mode 100644 resources/views/components/level-badge.blade.php
create mode 100644 resources/views/components/rich-editor.blade.php
create mode 100644 resources/views/contact.blade.php
create mode 100644 resources/views/forum/board.blade.php
create mode 100644 resources/views/forum/index.blade.php
create mode 100644 resources/views/forum/thread.blade.php
create mode 100644 resources/views/glossary/index.blade.php
create mode 100644 resources/views/glossary/show.blade.php
create mode 100644 resources/views/home.blade.php
create mode 100644 resources/views/layouts/app.blade.php
create mode 100644 resources/views/news/index.blade.php
create mode 100644 resources/views/news/show.blade.php
create mode 100644 resources/views/partials/hero-slide.blade.php
create mode 100644 resources/views/products/index.blade.php
create mode 100644 resources/views/products/show.blade.php
create mode 100644 resources/views/search/index.blade.php
create mode 100644 resources/views/sitemap.blade.php
create mode 100644 resources/views/solutions/index.blade.php
create mode 100644 resources/views/solutions/show.blade.php
create mode 100644 router.php
create mode 100644 routes/api.php
create mode 100644 routes/console.php
create mode 100644 routes/web.php
create mode 100644 storage/app/.gitkeep
create mode 100644 storage/framework/cache/.gitkeep
create mode 100644 storage/framework/cache/data/1a/5a/1a5ab75a2650ac3516829f9db1af97dbe94d0db7
create mode 100644 storage/framework/cache/data/4c/1f/4c1fe5387048dd142b24044f83e6e920c7cb9fea
create mode 100644 storage/framework/cache/data/a7/c2/a7c2cef6848acb2036c43a618d19df0456f8aaae
create mode 100644 storage/framework/cache/data/db/68/db68ee7e615502bfcd3bb03f0be329bffd5b000d
create mode 100644 storage/framework/sessions/.gitkeep
create mode 100644 storage/framework/sessions/0LvKlcOCOr2O5Pm8V2tWrL0qrYbufKZvjQUIzXF5
create mode 100644 storage/framework/sessions/0X8nNF3bRuMaVk13RwsUK2A21dEM6hHbbU0bH6bn
create mode 100644 storage/framework/sessions/39QoTZUXGJDR2DaTvvfDJ2SoRXUdpSI4jrrQywTW
create mode 100644 storage/framework/sessions/3HNVdNbwWC7dMcmkK1gw5SghZSShR415naoYzudN
create mode 100644 storage/framework/sessions/3pmDLferkhgM1PODQVBNL9DhZWUv6ZlKoTAUE4sb
create mode 100644 storage/framework/sessions/4AgfQMBmECxRM26CdaCnLSXHTTWsh0Lq6iMeSGwt
create mode 100644 storage/framework/sessions/4phpNsA3mAqgGcuC8qQt0kW38EJUOFa1J1pFEFy5
create mode 100644 storage/framework/sessions/59D6cp1xekyIF8chch2TkFBod5Hjibg9sbdB0otu
create mode 100644 storage/framework/sessions/5d3bndSBF8ml3AUteJSWQnyiXa04PFMhJC5fR1VE
create mode 100644 storage/framework/sessions/636kEcZekVeAy71gpuNhcNiOsqOmHSDxBdYa97oP
create mode 100644 storage/framework/sessions/7BJ4Z7wc6gWzKC5wGOEKDZn9IEZX5PHlINsv8JQQ
create mode 100644 storage/framework/sessions/7GulP4ndB4HP1NxQQjKyY3Dzjon6YVKKFEzWMuvG
create mode 100644 storage/framework/sessions/8YYoqxebggDQ6L2kpxHg8twz9rlr3yq0EtzITtcH
create mode 100644 storage/framework/sessions/99kVWOjz8qLghuTInK9z6SUAbj6LaSFdyddlgnLV
create mode 100644 storage/framework/sessions/9fkgMuLaQnePsVHLrGZKu82NWADq8DXrzoi6BN8E
create mode 100644 storage/framework/sessions/9mWgx1PsuNTPclch8JSeSjblJ5FRMHMrzNTOu9TD
create mode 100644 storage/framework/sessions/A0YydUfOuS75nvy2c8i3LyE7jXOiL4oVzjEEAUWn
create mode 100644 storage/framework/sessions/AJr2isfYK8PMY9ySmdoSqnMbacrs1mi3DPWZv5pF
create mode 100644 storage/framework/sessions/By4oINi5NSiRkBzQrBholpPEcw7KXp2sp1wLvBvG
create mode 100644 storage/framework/sessions/CFxocOhrqyoHJUU1HwzSAzavBg8YvUvqIUfN4pxb
create mode 100644 storage/framework/sessions/CL2xgP11gSJs9dRCTfaqQ25DJ0df6CSMAyPtyS9r
create mode 100644 storage/framework/sessions/D5ODQ2Xdm2zP2E6OZjeWEvo6zquoRZjzoVwxWVBs
create mode 100644 storage/framework/sessions/DeBUqmjuy1FleIL933EKO6ebI0YUVWXFW6LzSDQs
create mode 100644 storage/framework/sessions/FsBSO7p2mUHfnG0yY2S5tYZtwayMlxlHUcmDmKxO
create mode 100644 storage/framework/sessions/HH2WCYBqqu8j1wVcoTHmqkQAMtcnBoLT22fRjlal
create mode 100644 storage/framework/sessions/HMu8UpCTK9dPuNNpiEmam5nzNZC57yKCCn2wS3pr
create mode 100644 storage/framework/sessions/I0iH5L4TkEmjfSsqweOP1Z2NPd9XRc1DpxkPTPbo
create mode 100644 storage/framework/sessions/IQ7m85uibM59CNwe05opHN3Opo2TyI0fuVuNG2tf
create mode 100644 storage/framework/sessions/IkSKj8vK6NAGki1BVV7dMOlMkXvIARImrBBeB4Lf
create mode 100644 storage/framework/sessions/J74ObvmaBWYjTTQTs7tAIg7uZr5TeZIaDaWJw25k
create mode 100644 storage/framework/sessions/LPLbJtWV5Hfd0bYMot7pxneqiNUFK8PGReFpUofb
create mode 100644 storage/framework/sessions/LwzP7qaUj366VcdHPKJuDClU5pCI1KysVwygAjRQ
create mode 100644 storage/framework/sessions/MRNVNNXWWkevJYsiMeAULcgVHnb94gnLqE14C6pj
create mode 100644 storage/framework/sessions/MmJPr9Sol62jK5GbLXOwXdaAM9x7sVxYXayNDVoT
create mode 100644 storage/framework/sessions/MsybeD4uApyagBolAq9dtR1hfZwOYGfjVhdxkA0S
create mode 100644 storage/framework/sessions/OoidY45sTSGctMOngxmZr3gKxKBEvm7TRP8scPgr
create mode 100644 storage/framework/sessions/OuggSkTJzXJSzlSuNATZleF0DFeIVQOeOWhumnPV
create mode 100644 storage/framework/sessions/PFN6pXvtYe8ao4BQkXzgwg5LHSsAsXVIW0EQTZtB
create mode 100644 storage/framework/sessions/QpNwLCyBf1JWXuhLQ2IojVgNDaJpnSJxlE6bgPOI
create mode 100644 storage/framework/sessions/R0ifJwQlh8ZXKuo6jNdr2J22SqJ8j5xNrf1OhRfA
create mode 100644 storage/framework/sessions/RPD8MycdhWAsgByuW6nH5YjRXUJdVadlBAK9qmKX
create mode 100644 storage/framework/sessions/SjHpF9bDYZdkrAhbFyaJ80Kp74KsYjdLIQN0BdCR
create mode 100644 storage/framework/sessions/Sv2LxGJQASyhP30hnrzFNZuM6kCtUGUwI4fwzYd3
create mode 100644 storage/framework/sessions/TYALLNCEiSFjjWLxSNJvmZIciKDmGgCIy2HQ90Ea
create mode 100644 storage/framework/sessions/TcjaGaLB3lEB958eyt62oFLDOzRz8BpRP7FQKUP5
create mode 100644 storage/framework/sessions/TqzJYtKkIiros1zTMQXJT5EEKXkGVnkjRtmR4XGd
create mode 100644 storage/framework/sessions/TufCurfCrgLpISCXqHeogyuXuEn5MLnqWvVUFGw3
create mode 100644 storage/framework/sessions/U6tEMgpGADfu26GxuN80xg5AUwkZ32jw39FzmMmZ
create mode 100644 storage/framework/sessions/UVyIW3QKPdI3x6YbG7NLn8ha4bpiN4JOG0xelNQd
create mode 100644 storage/framework/sessions/VZ89yuloDTTI6uD6A68TARpqMVpAjm0WJ81Irs1g
create mode 100644 storage/framework/sessions/VcqsqDPXyPZqEtk6dbYWwUrUAnwTtcvC0vwEgByQ
create mode 100644 storage/framework/sessions/WSeWmshAu84lrwpDBMWglVZSkFXLYyfGfYAZa7ZG
create mode 100644 storage/framework/sessions/WlNzRs7HSGqKEg352yi2u5T3GmfR5PNpdijBMd5q
create mode 100644 storage/framework/sessions/XaOKa8LEi5wtlnx8OkwmfxmBtczdIRoV9aB8uJRS
create mode 100644 storage/framework/sessions/YjknNvfmm8CLaBurhStwQSGZ8Zkuv0XAljXrSUKt
create mode 100644 storage/framework/sessions/ZTyQcSHCKUSvEimbPRKkuXMTVLkZ5EXkBcKMgf6H
create mode 100644 storage/framework/sessions/aDNQ5yBzwHdZr1g5dZu6wg8f3jsB8q8IQjj0Yxjk
create mode 100644 storage/framework/sessions/aMdfplTM56k6Un3xcRQImwQnRZbQu4Pci3Ha4HF3
create mode 100644 storage/framework/sessions/byBEhnytxxGFbbMoAIJlKMFmyLMB7m06bNQTqiBn
create mode 100644 storage/framework/sessions/cVHrJcSo8llpgM0gMoL1TvIizFDIGV6zeHlGfTTz
create mode 100644 storage/framework/sessions/dOxnEmqKLaE9yUQUw9vsTCJHyazbGg9jrHisc4VL
create mode 100644 storage/framework/sessions/dWguL9ssSVnIePckwSdyDSB2yefPyz9xQqZrRpkq
create mode 100644 storage/framework/sessions/drKgdyHJkEKnsTDfgpsNfg8Q2g2sVllBZG0bLG9F
create mode 100644 storage/framework/sessions/eH7SXZuvuPxPFbgpDA99dv2N48E0obFvsritDYz7
create mode 100644 storage/framework/sessions/eNhjpa1lQ1WJ81j2lVGZ2bqi2Kr1qH98yFLjnm7k
create mode 100644 storage/framework/sessions/fMOtXSHVqdmuzhddGvwjyFpdp89WhVUsihUBVkTJ
create mode 100644 storage/framework/sessions/fVINnk8rIDswjuZE2TNg1loatQ892GZwnEb2qlKU
create mode 100644 storage/framework/sessions/gyddBMpqW1hNSNkMx7twLAq9MBGH8cFmoiWCDJkN
create mode 100644 storage/framework/sessions/i0M4xP47SovRPBFgvNsCIAe5Bm8a2q79c5GmksUJ
create mode 100644 storage/framework/sessions/iwcit142KwkSQeU9LMMs5jqMnIWzMh6rQZ1CIWyH
create mode 100644 storage/framework/sessions/j2SeIoihXlXz5HL5806vBtAzEJ0tx1Ox1TeETVcV
create mode 100644 storage/framework/sessions/jmo8VjhXjULBZL7pMzSvCxZigoTzsql8WGDP6Tf5
create mode 100644 storage/framework/sessions/jw9GTFOs7zxIBzZT6A0TDJs0LGgcpDlNxeA83DKb
create mode 100644 storage/framework/sessions/l8retQpc9nY3dwci84aLKbHNwwNZfCgKkXDZF1Mo
create mode 100644 storage/framework/sessions/lEQz8AMN4UrfaSkz6M902RMOhzjLeINAZqE13qPy
create mode 100644 storage/framework/sessions/lK265IOfDsvnDzjOWCzY62DEvguaopjVrz3JmL9l
create mode 100644 storage/framework/sessions/lkowNf8mIhlY9ZQb0YIgF2JwQjKCPIDGQlQqEujY
create mode 100644 storage/framework/sessions/luHGYELgXitDiFYFpJMl60o2ETz5CgZnRVL8DoWo
create mode 100644 storage/framework/sessions/m4CTy6sjcIxR46gnBfj83SZWoXwyAQTJsc2jLJ0f
create mode 100644 storage/framework/sessions/mED9Kc5tv4cVX0OwVLTYgp9fLJ5tKScquMSr3MiF
create mode 100644 storage/framework/sessions/mwuphfOLozdCa3CW9HZwWpSdRpYInuiOivymoC55
create mode 100644 storage/framework/sessions/nBrcQVEDavhMrhzmOQ1y9rsZGtOwdGMoTJWEi0nZ
create mode 100644 storage/framework/sessions/nFKRWRCIIWaXfeFSs3ppOq5ywdbolOh2aK8gvC5D
create mode 100644 storage/framework/sessions/nPjc9Q0qG1v1viqrfrgPSVeeYnw5XaNRlk0aZSIA
create mode 100644 storage/framework/sessions/omMrN1E7AFpX7mxdghKBSYuWVxPD6WEyUPG3LRC8
create mode 100644 storage/framework/sessions/osWo6PJtxn7cmxLyNsVPQbfejoJzXENp92NhYKQM
create mode 100644 storage/framework/sessions/ouK3b4kovOi51Cd06JnKjgGTzkKqsAiftlgvNjlx
create mode 100644 storage/framework/sessions/qSVGKs3nVfuCD4glcmuxyp7jwMRAGmPd0jIL5XYl
create mode 100644 storage/framework/sessions/qvxuO6EhncjZoB0mlbqMWMl8kAHmpiw8U36dPv4q
create mode 100644 storage/framework/sessions/rgmSAY1IlTAlsOHBtDem2BDG6viWUtaEMmxeaFpA
create mode 100644 storage/framework/sessions/s13OCD8tF6RDC8zNlAWynINjBJOzoY8UElTfpku5
create mode 100644 storage/framework/sessions/sPewv2LlIsqmF9vSvEbzTLBY01BmfDMsf7Waumys
create mode 100644 storage/framework/sessions/ssPobPGAjcOUrKF5RpiyYrIazYCppjAujEhw6o6F
create mode 100644 storage/framework/sessions/v4yp2NUBWCqyaKTvyZG6OWrDJ7WSlsBjXDykITnV
create mode 100644 storage/framework/sessions/vWAlv4dexYMkfnITO1bkZ3oe9XHwWQTta1OIJpCe
create mode 100644 storage/framework/sessions/x9Zsl91G4CUFBbVvECC88eypbJGRorQx3oYFiNG6
create mode 100644 storage/framework/sessions/x9eQYs4AY0jXyFbECBywxFVYsDusTzNH4vQW5X5o
create mode 100644 storage/framework/sessions/xpZdDEuKqhQWD4fe8agC7CSR2YmaTqOIsXv889go
create mode 100644 storage/framework/sessions/ycXVBKZfkwDRGmG86eR7S7Sq5sZMTSW5vpsnxBoY
create mode 100644 storage/framework/sessions/zOPbsIA4iPGjVlW5eNpWj8OIfvc3QnQ6sPqxgGls
create mode 100644 storage/framework/sessions/zbKnAr79blajOaOjUSPDcmNR1fsVzyGKgWucXxRa
create mode 100644 storage/framework/testing/.gitkeep
create mode 100644 storage/framework/views/.gitkeep
create mode 100644 storage/framework/views/0a8789f139de15f847d69c49efc44f49.php
create mode 100644 storage/framework/views/15b9d4e02c209a2f1e3c28a8dc50ac02.php
create mode 100644 storage/framework/views/16089911eeeb602832250302c2cd049f.php
create mode 100644 storage/framework/views/18e917f0524fac30e8812a95bd8a01cd.php
create mode 100644 storage/framework/views/1d856a721bacf5700d599ae513cfc39a.php
create mode 100644 storage/framework/views/24f475e10c5f5dd12359f46205641a8a.php
create mode 100644 storage/framework/views/253e0b0ecbba0b334a479f2567a92523.php
create mode 100644 storage/framework/views/2b514aa22b5d10200f82764d1e8b1262.php
create mode 100644 storage/framework/views/2ff4acda1568e1d72077e7b05cbb05dd.php
create mode 100644 storage/framework/views/324d075a03ec465c04e7c7edf5aff085.php
create mode 100644 storage/framework/views/40a52fb7dced61faf2e5d69224ad395c.php
create mode 100644 storage/framework/views/4cbda21650a03c74ba295822590317e0.php
create mode 100644 storage/framework/views/548477a6a585ec49a2f27085bfcb8cba.php
create mode 100644 storage/framework/views/5496242fd46c49a1eb73fc7d342b6f39.php
create mode 100644 storage/framework/views/5d039872216afd2de7e2649de9d82baf.php
create mode 100644 storage/framework/views/6309104b2bdbede64f99945b56b14858.php
create mode 100644 storage/framework/views/635f14b24b41c4f62756dec86d8259e3.php
create mode 100644 storage/framework/views/6d97f08c22bb906fbb4b73b4c34a4f5e.php
create mode 100644 storage/framework/views/76a517bf0b8aee43086be6a6d309df9b.php
create mode 100644 storage/framework/views/7a66ca3440694de3668ee2cffe656869.php
create mode 100644 storage/framework/views/893e48106319282548a1ee6ea39d98d9.php
create mode 100644 storage/framework/views/97ffa5d8d55c45773efadada907b861f.php
create mode 100644 storage/framework/views/989ddd6b1f599a426eca38b293f52998.php
create mode 100644 storage/framework/views/c7d7628f707fbc8bb91e302f4d299ef1.php
create mode 100644 storage/framework/views/cfa2208064bda481ba2008da7932a6a6.php
create mode 100644 storage/framework/views/d7fe0f07512af77fe6ff8ccf46c415ff.php
create mode 100644 storage/framework/views/e44e13153d7da70ab3bc55bfbc81a97d.php
create mode 100644 storage/framework/views/e903b7765e8c413b9a7390aa81fd1685.php
create mode 100644 storage/framework/views/f88d0e84d3df42bb93a6b055b8263a49.php
create mode 100644 storage/framework/views/fb732d386deb347bc804e258e49d1979.php
create mode 100644 storage/logs/.gitkeep
create mode 100644 storage/logs/laravel.log
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..7bccc48
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,34 @@
+APP_NAME=CloudChip
+APP_ENV=production
+APP_KEY=
+APP_DEBUG=false
+APP_URL=https://cloud-chip.cn
+
+# 宝塔 PHP8.3 + MySQL5.7 适配:collation 必须用 utf8mb4_unicode_ci(5.7 不支持 0900_ai_ci)
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=cloudchip_cn
+DB_USERNAME=cloudchip_cn
+DB_PASSWORD=
+DB_CHARSET=utf8mb4
+DB_COLLATION=utf8mb4_unicode_ci
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+FILESYSTEM_DISK=local
+QUEUE_CONNECTION=sync
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+SESSION_SECURE_COOKIE=true
+
+LOG_CHANNEL=stack
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=warning
+
+# 宝塔 Redis 若已安装可改用 redis,默认 file 即可跑通
+# CACHE_DRIVER=redis
+# SESSION_DRIVER=redis
+# REDIS_HOST=127.0.0.1
+# REDIS_PASSWORD=null
+# REDIS_PORT=6379
diff --git a/.gitignore b/.gitignore
index 60d7971..fc8a632 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,19 @@
-# ---> AL
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-*.code-workspace
-
-# Local History for Visual Studio Code
-.history/
-*.app
-.snapshots/*
-
+/node_modules
+/public/build
+/public/hot
+/public/storage
+/storage/*.key
+/vendor
+.env
+.env.backup
+.env.production
+.phpunit.result.cache
+Homestead.json
+Homestead.yaml
+auth.json
+npm-debug.log
+yarn-error.log
+/.fleet
+/.idea
+/.vscode
+*.zip
diff --git a/DEPLOY.md b/DEPLOY.md
new file mode 100644
index 0000000..a17cbfa
--- /dev/null
+++ b/DEPLOY.md
@@ -0,0 +1,85 @@
+# 部署指南(深圳市云创芯电子有限公司 · Laravel 12)
+
+本包已包含 `vendor/`(Composer 依赖已安装),上传到服务器后**无需再跑 composer install** 即可运行。
+如需自行重装依赖,删除 `vendor/` 后执行 `composer install --no-dev --optimize-autoloader`。
+
+## 1. 上传与解压
+- 将 `cloud-chip-laravel-vue.tar.gz` 上传到服务器网站根目录(如 Baota 的 `/www/wwwroot/cloud-chip.cn/`)。
+- 解压:
+ ```bash
+ tar -xzf cloud-chip-laravel-vue.tar.gz -C /www/wwwroot/cloud-chip.cn/
+ ```
+
+## 2. 配置环境变量
+```bash
+cp .env.example .env
+# 用编辑器打开 .env,至少修改以下项:
+# APP_URL=https://你的域名
+# DB_DATABASE / DB_USERNAME / DB_PASSWORD
+# APP_DEBUG=false
+# SESSION_SECURE_COOKIE=true # 启用了 HTTPS 才设 true
+```
+生成应用密钥:
+```bash
+php artisan key:generate
+```
+
+## 3. 数据库
+```bash
+php artisan migrate --force
+# 如需初始演示数据(产品/文章/术语等):
+php artisan db:seed --force
+```
+> 本次发布包含新增迁移 `2026_08_02_000001_add_profile_fields_to_users_table`:
+> 在 `users` 表追加 `real_name / phone / company / industry` 四个资料字段(均允许为空)。
+> **首次部署或升级时务必执行 `migrate --force`**,否则详细注册收集到的字段无法落库。
+
+## 4. 存储软链(文章图片上传需要)
+```bash
+php artisan storage:link
+```
+这会把 `storage/app/public` 映射到 `public/storage`,使上传的图片可通过网页访问。
+
+## 5. 目录权限(Baota 面板或命令行)
+确保以下目录**可写**(命令行示例):
+```bash
+chmod -R 755 storage bootstrap/cache
+# 若仍报权限错,再放宽到 775 / 777(仅限这两个目录)
+```
+> 文章配图、封面图通过 `/upload` 接口写入 `storage/app/public/uploads/{年}/{月}/`,
+> 该目录由上传时自动创建,且已随包带 `storage/app/public/.gitkeep`。
+> 上一步 `chmod 755 storage` 已覆盖此路径;若上传报「无法写入」,单独放宽:
+> `chmod -R 755 storage/app/public`
+
+## 6. Web 服务器(关键:docroot 必须是 public/)
+- **Nginx**:`root` 指向 `.../public;` 并加 Laravel 重写:
+ ```nginx
+ location / {
+ try_files $uri $uri/ /index.php?$query_string;
+ }
+ ```
+- **Apache**:本包已带 `public/.htaccess`,只要把站点根目录设为 `public/` 即可,无需额外配置。
+- ⚠️ **不要**用项目根的 `router.php` 跑生产(`router.php` 仅本地 `php -S` 调试用)。
+
+## 7. 上线检查
+- 访问首页、产品、文章、论坛、联系我们页均返回 200。
+- 后台入口 `/admin`(需管理员账号,任意后台角色可登录)。
+- 前台用户注册/登录在「论坛」页内;管理员登录在独立页面 `/admin/login`。
+
+## 8. SEO 与搜索引擎收录(sitemap / robots)
+上线后建议提交站点地图,加快收录:
+- **robots 协议**:访问 `/robots.txt` 自动返回(动态读取 `APP_URL`,无需手改域名),
+ 已对前台全开放,并屏蔽 `/admin`、`/login`、`/register`、`/settings`、`/upload`、`/api` 等后台与用户接口。
+- **站点地图**:访问 `/sitemap.xml`(机读 XML,含首页/关于我们/产品服务/文章/术语库/论坛/联系我们 + 全部已发布产品、文章、术语)。
+- **提交地址**:
+ - 百度搜索资源平台:https://ziyuan.baidu.com/ → 站点管理 → 链接提交 → 自动提交(sitemap)填入 `https://你的域名/sitemap.xml`
+ - Google Search Console:https://search.google.com/search-console → Sitemaps 填入 `https://你的域名/sitemap.xml`
+- 上线检查项补一条:访问 `/robots.txt` 与 `/sitemap.xml` 均返回 200,且 `Sitemap:` 行域名与 `APP_URL` 一致。
+
+## 备注
+- 图标采用 x-icon(纯 SVG,无 emoji);配色走 CSS 变量 token,未硬编码颜色。
+- **本次已上线功能**:
+ - 文章富文本编辑(Quill)+ 图片上传(/upload 接口)+ HTMLPurifier 净化,正文安全渲染 HTML。
+ - 详细注册:注册表单新增「真实姓名 / 手机号 / 公司名称 / 行业领域」四项(选填)。
+ - 会员等级徽章(`level-badge` 组件):论坛帖子与回复署名、导航(桌面+移动端)、账户设置页均展示等级。
+- **仍待办**:微信扫码注册(本期未排期,暂未实现)。
diff --git a/README.md b/README.md
index 80aa271..5683699 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,153 @@
-# cloud-chip.cn
+# 深圳市云创芯电子有限公司 · Laravel 12 + Vue3(CDN) 官网
-pd网站
\ No newline at end of file
+一套**零构建、零 Node**的轻量全栈站点,专为宝塔面板(Nginx + PHP 8.3 + MySQL 5.7)设计,强调安全、稳定、易移植。
+
+## 功能模块
+
+| 模块 | 前台 | 后台管理 |
+|------|------|----------|
+| 产品中心 | 列表 + 搜索 + 分类筛选 + 详情页 | CRUD + 分类 + 草稿/发布 |
+| 技术文章 | 列表 + 搜索 + 分类筛选 + 详情页 | CRUD + 分类 + 作者 + 发布时间 |
+| 术语库 | 列表 + 搜索 + 详情页 | CRUD + 别名 + 分类 |
+| 技术论坛 | 版块 + 帖子 + 回复 + 发帖/回复 | 版块管理 + 帖子删除 + 锁帖 |
+| 联系我们 | Vue 异步表单 + CSRF + 限流 | 留言列表 + 详情 + 删除 |
+| 用户系统 | 注册 + 登录 + 退出 + 个人设置 | 用户列表 + 管理员提权/降权 + 删除 |
+| 用户等级 | 积分驱动 L1–L5 徽章 + 进度条 | 用户列表可见积分 |
+| 账号安全 | `/settings` 自助修改密码(需验证当前密码) | - |
+| 站点设置 | 站名 / Logo / Favicon / ICP 全站生效 | `/admin/settings` 统一维护 |
+| SEO | 每页独立 title + description,缺省回落全局 | 文章/产品表单内逐条填写 |
+| 文章版式 | 三种阅读版式(标准 / 宽栏 / 文档) | 发布时下拉选择 |
+| 仪表盘 | - | 统计概览 + 最新留言/帖子 |
+
+## 技术选型
+
+| 项 | 方案 |
+|----|------|
+| 后端 | Laravel 12(PHP 8.3) |
+| 前端交互 | Vue 3.5.13 全局构建,本地自托管(无 CDN 依赖) |
+| 样式 | 纯 CSS + 设计令牌(:root 变量),全站只用 var() |
+| 视图 | Blade 服务端渲染(SEO 友好,首屏快) |
+| 图标 | 内联 SVG 组件(统一描边,16/20/24 三档尺寸) |
+| 前端构建 | **无构建**(无 npm / Vite / Tailwind) |
+| 数据库 | MySQL 5.7(utf8mb4_unicode_ci) |
+| RBAC | is_admin 布尔标志位 + Admin 中间件 + /admin 路由组 |
+
+## 目录结构
+
+```
+app/
+ Http/Controllers/
+ HomeController.php 首页(产品+文章)
+ ProductController.php 产品列表/详情
+ ArticleController.php 文章列表/详情
+ GlossaryController.php 术语列表/详情
+ ForumController.php 论坛版块/帖子/回复
+ ContactController.php 联系表单(Vue 异步 + 限流)
+ HealthController.php /api/health 健康检查
+ Auth/LoginController.php 登录 / 退出
+ Auth/RegisterController.php 注册
+ Auth/SettingsController.php 个人设置(修改密码)
+ Admin/ 后台管理(10 个控制器,含站点设置)
+ Http/Middleware/Admin.php 管理员中间件
+ Models/ 10 个模型(含关系和 scope)
+database/
+ migrations/ 16 个迁移
+ seeders/ 7 个 seeder(全部幂等,可重复执行)
+public/
+ css/app.css 设计令牌 + 完整组件样式
+ js/app.js Vue 挂载(联系表单)
+ js/vendor/vue.global.prod.js 自托管 Vue 3.5.13
+resources/views/
+ layouts/app.blade.php 前台布局
+ components/icon.blade.php 内联 SVG 图标组件
+ home.blade.php 首页
+ products/ articles/ glossary/ 前台展示页
+ forum/ 论坛页面
+ auth/ 登录/注册/个人设置
+ admin/ 后台管理页面(layout + 16 个子页面)
+routes/web.php 前台 + /admin 路由组
+```
+
+## 安全要点
+
+- 全站 CSRF(`@csrf` + 前端 `X-CSRF-TOKEN`)
+- 表单服务端校验(`validate`),Vue 仅做体验增强
+- 联系表单频率限制:同一 IP 60 秒内最多 5 次
+- 密码 `bcrypt`(`hashed` cast),会话 cookie `secure` + `http_only` + `encrypt`
+- Blade `{{ }}` 自动转义,防 XSS
+- Eloquent 参数化查询,防 SQL 注入
+- Admin 中间件保护所有后台路由
+
+## 宝塔部署(3 步)
+
+### 第 1 步:建站点 + 数据库
+
+宝塔 → 网站 → 添加站点:
+- 根目录指向 `public/`
+- PHP 版本:8.3
+- 同时建好 MySQL 数据库(排序规则 `utf8mb4_unicode_ci`)
+
+### 第 2 步:传代码 + 安装
+
+```bash
+cd /www/wwwroot/cloud-chip.cn
+
+# 配置 Composer 镜像(国内服务器)
+composer config -g repos.packagist.org composer https://mirrors.aliyun.com/composer/
+
+# 安装依赖(必须先于 artisan 命令)
+composer install --no-dev --optimize-autoloader --no-interaction
+
+# 配置环境
+cp .env.example .env
+# 编辑 .env,填写:
+# DB_DATABASE=你的数据库名
+# DB_USERNAME=你的数据库用户
+# DB_PASSWORD=你的数据库密码
+# APP_URL=https://cloud-chip.cn
+
+# 生成密钥 + 迁移 + 种子数据
+php artisan key:generate
+php artisan migrate --force
+php artisan db:seed --force
+
+# 权限
+chmod -R 755 storage bootstrap/cache
+php artisan storage:link
+```
+
+或直接运行 `./install.sh`(自动完成上述全部步骤)。
+
+### 第 3 步:配置伪静态
+
+宝塔 → 站点设置 → 伪静态 → 选择 **Laravel** 模板。
+
+### 注意事项
+
+- **宝塔禁用函数**:PHP-8.3 设置 → 禁用函数,删除 `putenv, proc_open, proc_get_status, proc_close, popen` 并重启 PHP-FPM
+- **运行时目录**:确保 `storage/` 和 `bootstrap/cache/` 对 www 用户可写(755)
+
+## 默认管理员账号
+
+Seeder 会自动创建管理员账号:
+- 邮箱:`admin@cloud-chip.cn`
+- 密码:`cloudchip2026`
+
+**请在首次登录后立即修改密码**:登录后访问 `/settings` → 修改密码(需输入当前密码校验)。
+
+## 数据库表
+
+| 表名 | 说明 |
+|------|------|
+| users | 用户(is_admin 布尔 + points 积分) |
+| settings | 站点级 key-value 配置(ICP / Logo / SEO 等) |
+| password_reset_tokens | 密码重置令牌 |
+| failed_jobs | 失败任务队列 |
+| contact_messages | 联系表单留言 |
+| categories | 分类(type: product/article) |
+| products | 产品(软删) |
+| articles | 文章(软删 + 发布时间) |
+| glossary_terms | 术语 |
+| forum_boards | 论坛版块 |
+| forum_threads | 论坛帖子(软删 + 置顶 + 锁定) |
+| forum_replies | 论坛回复(软删) |
diff --git a/SECURITY_REPORT.md b/SECURITY_REPORT.md
new file mode 100644
index 0000000..ad6fa6c
--- /dev/null
+++ b/SECURITY_REPORT.md
@@ -0,0 +1,104 @@
+# 企业官网 · 安全等级评估报告
+
+> 评估对象:cloud-chip-laravel-vue(Laravel 12 + Vue3 CDN 零构建栈)
+> 评估日期:2026-08-03
+> 评估方式:源码审计 + 本地运行验证(PHP 8.3 / MySQL 5.7),覆盖路由、控制器、中间件、配置、视图与依赖。
+
+---
+
+## 一、综合评级
+
+| 场景 | 综合等级 | 说明 |
+| --- | --- | --- |
+| 当前(本地开发配置) | **B** | 框架与代码安全基线良好;但 `.env` 仍是开发值(`APP_DEBUG=true`、`SESSION_SECURE_COOKIE=false`),且此前缺少响应头与登录节流(本次已修复)。 |
+| 部署合规后(修正配置 + HTTPS) | **A‑** | 达到企业官网安全基线。剩余项均为可选增强。 |
+
+> 评级说明:本项目基于 Laravel 12「安全优先」框架,代码层已具备 CSRF、参数化查询、输出转义、严格上传校验等能力;风险主要集中在**部署配置**与**纵深防御响应头**,均已给出整改项。
+
+---
+
+## 二、各安全维度评分
+
+| 维度 | 评级 | 关键依据 |
+| --- | --- | --- |
+| 注入防护(SQLi / XSS) | **A** | Eloquent 全程参数化查询,杜绝 SQL 注入;视图默认 `{{ }}` 转义;富文本经 `mews/purifier` 净化(default/article 两档)。 |
+| 身份认证与会话 | **A‑** | 密码 bcrypt 哈希;登录成功 `session()->regenerate()` 防会话固定;新增登录节流(5 次/分钟);后台具备 RBAC 五角色 + 权限点门禁。 |
+| Cookie 安全 | **B+** | `http_only=true`、`encrypt=true`、`same_site=lax` 均已开启;`secure` 在 `.env` 中为 `false`(本地值,上线须置 `true`)。 |
+| 配置安全 | **C+** | 当前 `.env`:`APP_DEBUG=true`、`APP_ENV=local`、数据库弱口令 `cloudchip_cn/cloudchip_cn`——**均为本地开发值,上线前必须修改**。 |
+| 传输与响应头 | **A‑** | 本次新增 `SecurityHeaders` 中间件:CSP、X-Frame-Options(DENY)、X-Content-Type-Options(nosniff)、Referrer-Policy、Permissions-Policy;HSTS 待生产 HTTPS 启用。 |
+| 文件上传 | **A** | `image` + `mimes:jpg,jpeg,png,gif,webp` + `max:5120` 严格校验;存储于 `storage/app/public`(经 `storage:link` 暴露,非 PHP 执行目录),命名随机防遍历。 |
+| 防滥用 / 限流 | **A‑** | 联系表单 `RateLimiter` 5 次/60 秒;登录接口新增 `ThrottlesLogins` 5 次/分钟(按 IP+邮箱)。 |
+| 依赖与供应链 | **B** | 仅引入 `mews/purifier`、`simple-qrcode` 等少量可信依赖;建议上线前执行 `composer audit`。 |
+| 日志与错误处理 | **B+** | `LOG_CHANNEL=stack`;只要 `APP_DEBUG=false`,异常不会向用户泄露堆栈与环境信息。 |
+
+---
+
+## 三、本次已实施的安全加固(代码层)
+
+1. **新增 `App\Http\Middleware\SecurityHeaders` 并注册到 `web` 中间件组**
+ - 下发 `Content-Security-Policy`(限制脚本/样式/连接/表单来源,显著缩小 XSS 影响面)、`X-Frame-Options: DENY`(防点击劫持)、`X-Content-Type-Options: nosniff`(防 MIME 嗅探)、`Referrer-Policy`、`Permissions-Policy`。
+ - 生产 + HTTPS 环境下自动追加 `Strict-Transport-Security`(HSTS,含 `preload`)。
+ - CSP 针对本项目的零构建架构做了适配:放行 `cdn.tailwindcss.com` 与内联脚本/样式(否则 Tailwind Play CDN 与内联 `tailwind.config` 会被拦截)。
+
+2. **登录接口增加防暴力破解节流**
+ - `LoginController` 引入 `ThrottlesLogins`,同一 IP+邮箱 每分钟最多 5 次,超限返回校验错误并锁定。
+
+3. **修复联系表单 CSRF 失效缺陷(功能性 + 安全性)**
+ - 发现布局缺少 ``,导致 Vue 联系表单取不到 token、提交被 Laravel 拦截返回 419(原表单实际不可用)。
+ - 已在布局 `` 补齐该 meta 标签,并经端到端验证:取 token → POST → 返回 `{"ok":true}`,HTTP 200。
+
+---
+
+## 四、残余风险与整改建议(按优先级)
+
+### P0 · 上线前必须完成
+- [ ] **关闭调试模式**:`.env` 设 `APP_DEBUG=false`、`APP_ENV=production`。否则将向访客泄露堆栈、环境变量等敏感信息。
+- [ ] **启用安全 Cookie**:`SESSION_SECURE_COOKIE=true`(HTTPS 站点必须),防止会话被明文中间人窃取。
+- [ ] **强制 HTTPS**:在反向代理/Web 服务器层 301 跳转 HTTP→HTTPS,并启用 HSTS(中间件已在生产自动下发)。
+- [ ] **数据库强口令 + 最小权限**:更换为高强度独立口令,账号仅授予本站库所需权限;切勿使用本地弱口令 `cloudchip_cn`。
+- [ ] **保护敏感文件**:确保 `.env`、`.gitignore`、`.workbuddy` 等不被 Web 直接访问(当前项目结构 `.env` 在 `public/` 之外,已天然隔离;部署时复核 Nginx/Apache 配置,禁止访问点号开头的隐藏文件)。
+
+### P1 · 强烈建议
+- [ ] **配置可信代理 `TrustProxies`**:若站点置于 CDN / 负载均衡之后,需在 `bootstrap/app.php` 声明可信代理,否则 `secure` cookie 与 `isSecure()` 会误判,影响 HTTPS 与会话安全。
+- [ ] **依赖审计**:上线前执行 `composer audit` 并升级存在 CVE 的包。
+- [ ] **后台安全增强**:管理员登录增加失败告警 / 锁定,条件允许时启用 2FA;对 `/admin` 异常访问做监控。
+- [ ] **异常监控**:对批量联系提交、异常登录、5xx 突增配置告警。
+
+### P2 · 可选增强
+- [ ] **收紧 CSP**:将 Tailwind 改为构建产物 + 为内联脚本加 `nonce`,从而移除 `unsafe-inline`,把 XSS 防御从「依赖转义」升级为「双层防护」。
+- [ ] **合规页面**:补充 `security.txt`、隐私政策与备案信息展示(页脚已预留 ICP / 公安备案位)。
+- [ ] **外部资源 SRI**:若后续引入任何第三方脚本/样式,添加子资源完整性校验。
+
+---
+
+## 五、部署上线前必检清单
+
+| 检查项 | 命令 / 位置 | 期望 |
+| --- | --- | --- |
+| 调试关闭 | `.env` `APP_DEBUG` | `false` |
+| 环境 | `.env` `APP_ENV` | `production` |
+| Cookie 安全 | `.env` `SESSION_SECURE_COOKIE` | `true` |
+| APP_KEY | `php artisan key:generate`(如未生成) | 已设置且唯一 |
+| 路由缓存 | `php artisan route:cache` | 无冲突 |
+| 视图缓存 | `php artisan view:cache` | 编译通过 |
+| 数据库迁移 | `php artisan migrate --force` | 成功 |
+| 依赖审计 | `composer audit` | 无高危 |
+| HTTPS | Web 服务器 / CDN | 已启用 + 跳转 |
+| 静态资源软链 | `php artisan storage:link` | `/storage` 可访问 |
+
+---
+
+## 六、验证记录(本地)
+
+| 页面 | 路由 | 状态码 |
+| --- | --- | --- |
+| 首页 | `/` | 200 |
+| 关于我们 | `/about` | 200 |
+| 产品服务 | `/products` | 200 |
+| 联系我们 | `/contact` | 200 |
+| 联系表单提交(带 CSRF) | `POST /contact` | 200 `{"ok":true}` |
+| 安全响应头 | 所有页面 | CSP / XFO / NoSniff / Referrer / Permissions 均已下发 |
+
+---
+
+*报告由 WorkBuddy 基于真实源码与本地运行结果生成,非泛泛模板。所有 P0 项均为部署配置,不依赖代码改动即可完成。*
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
new file mode 100644
index 0000000..402b633
--- /dev/null
+++ b/app/Exceptions/Handler.php
@@ -0,0 +1,30 @@
+
+ */
+ protected $dontFlash = [
+ 'current_password',
+ 'password',
+ 'password_confirmation',
+ ];
+
+ /**
+ * Register the exception handling callbacks for the application.
+ */
+ public function register(): void
+ {
+ $this->reportable(function (Throwable $e) {
+ //
+ });
+ }
+}
diff --git a/app/Http/Controllers/Admin/ArticleController.php b/app/Http/Controllers/Admin/ArticleController.php
new file mode 100644
index 0000000..9f1af47
--- /dev/null
+++ b/app/Http/Controllers/Admin/ArticleController.php
@@ -0,0 +1,100 @@
+orderByDesc('created_at')->paginate(20);
+
+ return view('admin.articles.index', compact('articles'));
+ }
+
+ public function create(): View
+ {
+ $categories = Category::ofType('article')->orderBy('sort')->orderBy('name')->get();
+
+ return view('admin.articles.form', ['article' => null, 'categories' => $categories]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'category_id' => ['nullable', 'exists:categories,id'],
+ 'title' => ['required', 'string', 'max:160'],
+ 'slug' => ['required', 'string', 'max:160', 'unique:articles,slug'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'body' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'author' => ['nullable', 'string', 'max:60'],
+ 'status' => ['required', 'in:approved,draft'],
+ 'template' => ['required', 'in:standard,tech,feature'],
+ 'seo_title' => ['nullable', 'string', 'max:160'],
+ 'seo_description' => ['nullable', 'string', 'max:255'],
+ 'published_at' => ['nullable', 'date'],
+ ]);
+
+ if (! empty($data['body'])) {
+ $data['body'] = \Purifier::clean($data['body'], 'article');
+ }
+
+ if (empty($data['published_at']) && $data['status'] === 'approved') {
+ $data['published_at'] = now();
+ }
+
+ Article::create($data);
+
+ return redirect()->route('admin.articles.index')->with('success', '文章已创建');
+ }
+
+ public function edit(Article $article): View
+ {
+ $categories = Category::ofType('article')->orderBy('sort')->orderBy('name')->get();
+
+ return view('admin.articles.form', compact('article', 'categories'));
+ }
+
+ public function update(Request $request, Article $article)
+ {
+ $data = $request->validate([
+ 'category_id' => ['nullable', 'exists:categories,id'],
+ 'title' => ['required', 'string', 'max:160'],
+ 'slug' => ['required', 'string', 'max:160', 'unique:articles,slug,' . $article->id],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'body' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'author' => ['nullable', 'string', 'max:60'],
+ 'status' => ['required', 'in:approved,draft'],
+ 'template' => ['required', 'in:standard,tech,feature'],
+ 'seo_title' => ['nullable', 'string', 'max:160'],
+ 'seo_description' => ['nullable', 'string', 'max:255'],
+ 'published_at' => ['nullable', 'date'],
+ ]);
+
+ if (! empty($data['body'])) {
+ $data['body'] = \Purifier::clean($data['body'], 'article');
+ }
+
+ if (empty($data['published_at']) && $data['status'] === 'approved' && $article->published_at === null) {
+ $data['published_at'] = now();
+ }
+
+ $article->update($data);
+
+ return redirect()->route('admin.articles.index')->with('success', '文章已更新');
+ }
+
+ public function destroy(Article $article)
+ {
+ $article->delete();
+
+ return redirect()->route('admin.articles.index')->with('success', '文章已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/CategoryController.php b/app/Http/Controllers/Admin/CategoryController.php
new file mode 100644
index 0000000..9f53913
--- /dev/null
+++ b/app/Http/Controllers/Admin/CategoryController.php
@@ -0,0 +1,65 @@
+orderBy('sort')->orderBy('name')->paginate(20);
+
+ return view('admin.categories.index', compact('categories'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.categories.form', ['category' => null]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:60'],
+ 'slug' => ['required', 'string', 'max:80', 'unique:categories,slug'],
+ 'type' => ['required', 'in:product,article'],
+ 'parent_id' => ['nullable', 'integer'],
+ 'sort' => ['integer'],
+ ]);
+
+ Category::create($data);
+
+ return redirect()->route('admin.categories.index')->with('success', '分类已创建');
+ }
+
+ public function edit(Category $category): View
+ {
+ return view('admin.categories.form', compact('category'));
+ }
+
+ public function update(Request $request, Category $category)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:60'],
+ 'slug' => ['required', 'string', 'max:80', 'unique:categories,slug,' . $category->id],
+ 'type' => ['required', 'in:product,article'],
+ 'parent_id' => ['nullable', 'integer'],
+ 'sort' => ['integer'],
+ ]);
+
+ $category->update($data);
+
+ return redirect()->route('admin.categories.index')->with('success', '分类已更新');
+ }
+
+ public function destroy(Category $category)
+ {
+ $category->delete();
+
+ return redirect()->route('admin.categories.index')->with('success', '分类已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/ContactController.php b/app/Http/Controllers/Admin/ContactController.php
new file mode 100644
index 0000000..511b424
--- /dev/null
+++ b/app/Http/Controllers/Admin/ContactController.php
@@ -0,0 +1,29 @@
+paginate(20);
+
+ return view('admin.contacts.index', compact('messages'));
+ }
+
+ public function show(ContactMessage $contactMessage): View
+ {
+ return view('admin.contacts.show', compact('contactMessage'));
+ }
+
+ public function destroy(ContactMessage $contactMessage)
+ {
+ $contactMessage->delete();
+
+ return redirect()->route('admin.contacts.index')->with('success', '留言已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
new file mode 100644
index 0000000..a773418
--- /dev/null
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -0,0 +1,33 @@
+ Product::count(),
+ 'articles' => Article::count(),
+ 'categories' => \App\Models\Category::count(),
+ 'glossary' => \App\Models\GlossaryTerm::count(),
+ 'threads' => ForumThread::count(),
+ 'users' => User::count(),
+ 'messages' => ContactMessage::count(),
+ ];
+
+ $recentMessages = ContactMessage::orderByDesc('created_at')->limit(5)->get();
+ $recentThreads = ForumThread::with('user', 'board')->orderByDesc('created_at')->limit(5)->get();
+
+ return view('admin.dashboard', compact('stats', 'recentMessages', 'recentThreads'));
+ }
+}
diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php
new file mode 100644
index 0000000..fc2fff4
--- /dev/null
+++ b/app/Http/Controllers/Admin/DatabaseController.php
@@ -0,0 +1,53 @@
+pluck('migration')->toArray();
+ }
+
+ $files = glob(database_path('migrations/*.php'));
+ $all = array_map(fn ($f) => basename($f, '.php'), $files);
+ sort($all);
+
+ $done = array_values(array_intersect($all, $ran));
+ $pending = array_values(array_diff($all, $ran));
+
+ return view('admin.database', compact('connection', 'dbName', 'pending', 'done'));
+ }
+
+ public function upgrade(Request $request): RedirectResponse
+ {
+ try {
+ Artisan::call('migrate', ['--force' => true]);
+ $output = Artisan::output();
+
+ return redirect()->route('admin.database')
+ ->with('success', '数据库升级已执行完成。')
+ ->with('migrate_output', $output);
+ } catch (\Throwable $e) {
+ return redirect()->route('admin.database')
+ ->with('error', '升级失败:' . $e->getMessage());
+ }
+ }
+}
diff --git a/app/Http/Controllers/Admin/ForumBoardController.php b/app/Http/Controllers/Admin/ForumBoardController.php
new file mode 100644
index 0000000..85441f8
--- /dev/null
+++ b/app/Http/Controllers/Admin/ForumBoardController.php
@@ -0,0 +1,63 @@
+orderBy('name')->paginate(20);
+
+ return view('admin.forum.boards', compact('boards'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.forum.board-form', ['board' => null]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:60'],
+ 'slug' => ['required', 'string', 'max:80', 'unique:forum_boards,slug'],
+ 'description' => ['nullable', 'string'],
+ 'sort' => ['integer'],
+ ]);
+
+ ForumBoard::create($data);
+
+ return redirect()->route('admin.forum.boards.index')->with('success', '版块已创建');
+ }
+
+ public function edit(ForumBoard $board): View
+ {
+ return view('admin.forum.board-form', compact('board'));
+ }
+
+ public function update(Request $request, ForumBoard $board)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:60'],
+ 'slug' => ['required', 'string', 'max:80', 'unique:forum_boards,slug,' . $board->id],
+ 'description' => ['nullable', 'string'],
+ 'sort' => ['integer'],
+ ]);
+
+ $board->update($data);
+
+ return redirect()->route('admin.forum.boards.index')->with('success', '版块已更新');
+ }
+
+ public function destroy(ForumBoard $board)
+ {
+ $board->delete();
+
+ return redirect()->route('admin.forum.boards.index')->with('success', '版块已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/ForumThreadController.php b/app/Http/Controllers/Admin/ForumThreadController.php
new file mode 100644
index 0000000..5f76135
--- /dev/null
+++ b/app/Http/Controllers/Admin/ForumThreadController.php
@@ -0,0 +1,26 @@
+orderByDesc('created_at')
+ ->paginate(20);
+
+ return view('admin.forum.threads', compact('threads'));
+ }
+
+ public function destroy(ForumThread $thread)
+ {
+ $thread->delete();
+
+ return redirect()->route('admin.forum.threads.index')->with('success', '帖子已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/GlossaryController.php b/app/Http/Controllers/Admin/GlossaryController.php
new file mode 100644
index 0000000..f7117fe
--- /dev/null
+++ b/app/Http/Controllers/Admin/GlossaryController.php
@@ -0,0 +1,65 @@
+paginate(20);
+
+ return view('admin.glossary.index', compact('terms'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.glossary.form', ['term' => null]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'term' => ['required', 'string', 'max:80'],
+ 'slug' => ['required', 'string', 'max:90', 'unique:glossary_terms,slug'],
+ 'definition' => ['required', 'string'],
+ 'aliases' => ['nullable', 'string', 'max:255'],
+ 'category' => ['nullable', 'string', 'max:60'],
+ ]);
+
+ GlossaryTerm::create($data);
+
+ return redirect()->route('admin.glossary.index')->with('success', '术语已创建');
+ }
+
+ public function edit(GlossaryTerm $glossaryTerm): View
+ {
+ return view('admin.glossary.form', ['term' => $glossaryTerm]);
+ }
+
+ public function update(Request $request, GlossaryTerm $glossaryTerm)
+ {
+ $data = $request->validate([
+ 'term' => ['required', 'string', 'max:80'],
+ 'slug' => ['required', 'string', 'max:90', 'unique:glossary_terms,slug,' . $glossaryTerm->id],
+ 'definition' => ['required', 'string'],
+ 'aliases' => ['nullable', 'string', 'max:255'],
+ 'category' => ['nullable', 'string', 'max:60'],
+ ]);
+
+ $glossaryTerm->update($data);
+
+ return redirect()->route('admin.glossary.index')->with('success', '术语已更新');
+ }
+
+ public function destroy(GlossaryTerm $glossaryTerm)
+ {
+ $glossaryTerm->delete();
+
+ return redirect()->route('admin.glossary.index')->with('success', '术语已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/HeroSlideController.php b/app/Http/Controllers/Admin/HeroSlideController.php
new file mode 100644
index 0000000..ecbd4a7
--- /dev/null
+++ b/app/Http/Controllers/Admin/HeroSlideController.php
@@ -0,0 +1,136 @@
+ordered()->get();
+ $mode = Setting::get('hero_mode', 'carousel');
+
+ return view('admin.hero-slides.index', compact('slides', 'mode'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.hero-slides.form', ['slide' => null]);
+ }
+
+ public function store(Request $request): RedirectResponse
+ {
+ $data = $request->validate([
+ 'title' => ['required', 'string', 'max:120'],
+ 'content' => ['nullable', 'string'],
+ 'cta_text' => ['nullable', 'string', 'max:80'],
+ 'cta_url' => ['nullable', 'string', 'max:255'],
+ 'cta2_text' => ['nullable', 'string', 'max:80'],
+ 'cta2_url' => ['nullable', 'string', 'max:255'],
+ 'is_active' => ['boolean'],
+ 'sort_order' => ['integer'],
+ ]);
+
+ $data['location'] = 'home';
+ $data['is_active'] = $request->boolean('is_active');
+ $data['sort_order'] = $data['sort_order'] ?? 0;
+
+ HeroSlide::create($data);
+
+ return redirect()->route('admin.hero-slides.index')->with('success', '轮播项已添加');
+ }
+
+ public function edit(HeroSlide $hero_slide): View
+ {
+ return view('admin.hero-slides.form', ['slide' => $hero_slide]);
+ }
+
+ public function update(Request $request, HeroSlide $hero_slide): RedirectResponse
+ {
+ $data = $request->validate([
+ 'title' => ['required', 'string', 'max:120'],
+ 'content' => ['nullable', 'string'],
+ 'cta_text' => ['nullable', 'string', 'max:80'],
+ 'cta_url' => ['nullable', 'string', 'max:255'],
+ 'cta2_text' => ['nullable', 'string', 'max:80'],
+ 'cta2_url' => ['nullable', 'string', 'max:255'],
+ 'is_active' => ['boolean'],
+ 'sort_order' => ['integer'],
+ ]);
+
+ $data['is_active'] = $request->boolean('is_active');
+ $data['sort_order'] = $data['sort_order'] ?? $hero_slide->sort_order;
+
+ $hero_slide->update($data);
+
+ return redirect()->route('admin.hero-slides.index')->with('success', '轮播项已更新');
+ }
+
+ public function destroy(HeroSlide $hero_slide): RedirectResponse
+ {
+ $hero_slide->delete();
+
+ return redirect()->route('admin.hero-slides.index')->with('success', '轮播项已删除');
+ }
+
+ /**
+ * 上移 / 下移排序。
+ */
+ public function move(Request $request, HeroSlide $hero_slide): RedirectResponse
+ {
+ $direction = $request->input('direction');
+
+ if ($direction === 'up') {
+ $sibling = HeroSlide::where('location', 'home')->ordered()
+ ->where('sort_order', '<', $hero_slide->sort_order)
+ ->orderByDesc('sort_order')
+ ->first();
+ if ($sibling) {
+ [$hero_slide->sort_order, $sibling->sort_order] = [$sibling->sort_order, $hero_slide->sort_order];
+ $hero_slide->save();
+ $sibling->save();
+ }
+ } elseif ($direction === 'down') {
+ $sibling = HeroSlide::where('location', 'home')->ordered()
+ ->where('sort_order', '>', $hero_slide->sort_order)
+ ->first();
+ if ($sibling) {
+ [$hero_slide->sort_order, $sibling->sort_order] = [$sibling->sort_order, $hero_slide->sort_order];
+ $hero_slide->save();
+ $sibling->save();
+ }
+ }
+
+ return redirect()->route('admin.hero-slides.index');
+ }
+
+ /**
+ * 一键启用 / 停用。
+ */
+ public function toggle(HeroSlide $hero_slide): RedirectResponse
+ {
+ $hero_slide->update(['is_active' => ! $hero_slide->is_active]);
+
+ return redirect()->route('admin.hero-slides.index');
+ }
+
+ /**
+ * 全局 Hero 显示模式:carousel(轮播)/ single(独立单图)。
+ */
+ public function setMode(Request $request): RedirectResponse
+ {
+ $data = $request->validate([
+ 'hero_mode' => ['required', 'in:carousel,single'],
+ ]);
+
+ Setting::set('hero_mode', $data['hero_mode'], 'appearance');
+
+ return redirect()->route('admin.hero-slides.index')->with('success', 'Hero 显示模式已更新');
+ }
+}
diff --git a/app/Http/Controllers/Admin/MenuController.php b/app/Http/Controllers/Admin/MenuController.php
new file mode 100644
index 0000000..2e820c2
--- /dev/null
+++ b/app/Http/Controllers/Admin/MenuController.php
@@ -0,0 +1,111 @@
+get();
+
+ return view('admin.menus.index', compact('menus'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.menus.form', ['menu' => null]);
+ }
+
+ public function store(Request $request): RedirectResponse
+ {
+ $data = $request->validate([
+ 'label' => ['required', 'string', 'max:60'],
+ 'url' => ['required', 'string', 'max:255'],
+ 'is_visible' => ['boolean'],
+ 'sort_order' => ['integer'],
+ ]);
+
+ $data['is_visible'] = $request->boolean('is_visible');
+ $data['sort_order'] = $data['sort_order'] ?? 0;
+
+ NavigationMenu::create($data);
+
+ return redirect()->route('admin.menus.index')->with('success', '菜单项已添加');
+ }
+
+ public function edit(NavigationMenu $menu): View
+ {
+ return view('admin.menus.form', compact('menu'));
+ }
+
+ public function update(Request $request, NavigationMenu $menu): RedirectResponse
+ {
+ $data = $request->validate([
+ 'label' => ['required', 'string', 'max:60'],
+ 'url' => ['required', 'string', 'max:255'],
+ 'is_visible' => ['boolean'],
+ 'sort_order' => ['integer'],
+ ]);
+
+ $data['is_visible'] = $request->boolean('is_visible');
+ $data['sort_order'] = $data['sort_order'] ?? $menu->sort_order;
+
+ $menu->update($data);
+
+ return redirect()->route('admin.menus.index')->with('success', '菜单项已更新');
+ }
+
+ public function destroy(NavigationMenu $menu): RedirectResponse
+ {
+ $menu->delete();
+
+ return redirect()->route('admin.menus.index')->with('success', '菜单项已删除');
+ }
+
+ /**
+ * 上移 / 下移排序。
+ */
+ public function move(Request $request, NavigationMenu $menu): RedirectResponse
+ {
+ $direction = $request->input('direction');
+
+ if ($direction === 'up') {
+ $sibling = NavigationMenu::ordered()
+ ->where('sort_order', '<', $menu->sort_order)
+ ->orderByDesc('sort_order')
+ ->first();
+ if ($sibling) {
+ [$menu->sort_order, $sibling->sort_order] = [$sibling->sort_order, $menu->sort_order];
+ $menu->save();
+ $sibling->save();
+ }
+ } elseif ($direction === 'down') {
+ $sibling = NavigationMenu::ordered()
+ ->where('sort_order', '>', $menu->sort_order)
+ ->first();
+ if ($sibling) {
+ [$menu->sort_order, $sibling->sort_order] = [$sibling->sort_order, $menu->sort_order];
+ $menu->save();
+ $sibling->save();
+ }
+ }
+
+ return redirect()->route('admin.menus.index');
+ }
+
+ /**
+ * 一键显示 / 隐藏。
+ */
+ public function toggle(NavigationMenu $menu): RedirectResponse
+ {
+ $menu->update(['is_visible' => ! $menu->is_visible]);
+
+ return redirect()->route('admin.menus.index');
+ }
+}
diff --git a/app/Http/Controllers/Admin/ModerationController.php b/app/Http/Controllers/Admin/ModerationController.php
new file mode 100644
index 0000000..f1e7ddf
--- /dev/null
+++ b/app/Http/Controllers/Admin/ModerationController.php
@@ -0,0 +1,95 @@
+user();
+ $tab = $request->query('tab', 'articles');
+
+ $canArticles = $user->canPerm('articles.moderate');
+ $canForum = $user->canPerm('forum.moderate');
+
+ if (! $canArticles && ! $canForum) {
+ abort(403, '无审核权限');
+ }
+
+ if ($tab === 'threads' && ! $canForum) {
+ $tab = $canArticles ? 'articles' : 'replies';
+ }
+ if ($tab === 'replies' && ! $canForum) {
+ $tab = $canArticles ? 'articles' : 'threads';
+ }
+
+ $articles = $canArticles
+ ? Article::where('status', 'pending')->with('user', 'category')->orderByDesc('created_at')->paginate(10, ['*'], 'ap')
+ : null;
+
+ $threads = $canForum
+ ? ForumThread::where('status', 'pending')->with('user', 'board')->orderByDesc('created_at')->paginate(10, ['*'], 'tp')
+ : null;
+
+ $replies = $canForum
+ ? ForumReply::where('status', 'pending')->with('user', 'thread')->orderByDesc('created_at')->paginate(10, ['*'], 'rp')
+ : null;
+
+ return view('admin.moderation.index', compact('tab', 'articles', 'threads', 'replies', 'canArticles', 'canForum'));
+ }
+
+ public function approveArticle(Article $article)
+ {
+ $article->update([
+ 'status' => 'approved',
+ 'published_at' => $article->published_at ?? now(),
+ ]);
+
+ return back()->with('success', "已通过文章《{$article->title}》");
+ }
+
+ public function rejectArticle(Article $article)
+ {
+ $article->update(['status' => 'rejected']);
+
+ return back()->with('success', "已驳回文章《{$article->title}》");
+ }
+
+ public function approveThread(ForumThread $thread)
+ {
+ $thread->update(['status' => ForumThread::STATUS_APPROVED]);
+
+ return back()->with('success', "已通过帖子《{$thread->title}》");
+ }
+
+ public function rejectThread(ForumThread $thread)
+ {
+ $thread->update(['status' => ForumThread::STATUS_REJECTED]);
+
+ return back()->with('success', "已驳回帖子《{$thread->title}》");
+ }
+
+ public function approveReply(ForumReply $reply)
+ {
+ $reply->update(['status' => ForumReply::STATUS_APPROVED]);
+
+ return back()->with('success', '已通过该回复');
+ }
+
+ public function rejectReply(ForumReply $reply)
+ {
+ $reply->update(['status' => ForumReply::STATUS_REJECTED]);
+
+ return back()->with('success', '已驳回该回复');
+ }
+}
diff --git a/app/Http/Controllers/Admin/NewsController.php b/app/Http/Controllers/Admin/NewsController.php
new file mode 100644
index 0000000..c04e92d
--- /dev/null
+++ b/app/Http/Controllers/Admin/NewsController.php
@@ -0,0 +1,73 @@
+paginate(20);
+
+ return view('admin.news.index', compact('news'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.news.form', ['news' => null]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'title' => ['required', 'string', 'max:160'],
+ 'slug' => ['required', 'string', 'max:160', 'unique:news,slug'],
+ 'category' => ['required', 'in:industry,company,event'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'body' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'published_at' => ['nullable', 'date'],
+ ]);
+
+ $data['published_at'] = $data['published_at'] ?? now();
+
+ News::create($data);
+
+ return redirect()->route('admin.news.index')->with('success', '新闻已创建');
+ }
+
+ public function edit(News $news): View
+ {
+ return view('admin.news.form', compact('news'));
+ }
+
+ public function update(Request $request, News $news)
+ {
+ $data = $request->validate([
+ 'title' => ['required', 'string', 'max:160'],
+ 'slug' => ['required', 'string', 'max:160', 'unique:news,slug,' . $news->id],
+ 'category' => ['required', 'in:industry,company,event'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'body' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'published_at' => ['nullable', 'date'],
+ ]);
+
+ $news->update($data);
+
+ return redirect()->route('admin.news.index')->with('success', '新闻已更新');
+ }
+
+ public function destroy(News $news)
+ {
+ $news->delete();
+
+ return redirect()->route('admin.news.index')->with('success', '新闻已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php
new file mode 100644
index 0000000..86e8aa9
--- /dev/null
+++ b/app/Http/Controllers/Admin/ProductController.php
@@ -0,0 +1,80 @@
+orderBy('sort')->orderBy('name')->paginate(20);
+
+ return view('admin.products.index', compact('products'));
+ }
+
+ public function create(): View
+ {
+ $categories = Category::ofType('product')->orderBy('sort')->orderBy('name')->get();
+
+ return view('admin.products.form', ['product' => null, 'categories' => $categories]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'category_id' => ['nullable', 'exists:categories,id'],
+ 'name' => ['required', 'string', 'max:120'],
+ 'slug' => ['required', 'string', 'max:120', 'unique:products,slug'],
+ 'model' => ['nullable', 'string', 'max:60'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'description' => ['nullable', 'string'],
+ 'specs' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'sort' => ['integer'],
+ ]);
+
+ Product::create($data);
+
+ return redirect()->route('admin.products.index')->with('success', '产品已创建');
+ }
+
+ public function edit(Product $product): View
+ {
+ $categories = Category::ofType('product')->orderBy('sort')->orderBy('name')->get();
+
+ return view('admin.products.form', compact('product', 'categories'));
+ }
+
+ public function update(Request $request, Product $product)
+ {
+ $data = $request->validate([
+ 'category_id' => ['nullable', 'exists:categories,id'],
+ 'name' => ['required', 'string', 'max:120'],
+ 'slug' => ['required', 'string', 'max:120', 'unique:products,slug,' . $product->id],
+ 'model' => ['nullable', 'string', 'max:60'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'description' => ['nullable', 'string'],
+ 'specs' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'sort' => ['integer'],
+ ]);
+
+ $product->update($data);
+
+ return redirect()->route('admin.products.index')->with('success', '产品已更新');
+ }
+
+ public function destroy(Product $product)
+ {
+ $product->delete();
+
+ return redirect()->route('admin.products.index')->with('success', '产品已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/SettingsController.php b/app/Http/Controllers/Admin/SettingsController.php
new file mode 100644
index 0000000..5517c2f
--- /dev/null
+++ b/app/Http/Controllers/Admin/SettingsController.php
@@ -0,0 +1,45 @@
+toArray();
+
+ return view('admin.settings', compact('settings'));
+ }
+
+ public function update(Request $request): RedirectResponse
+ {
+ $data = $request->validate([
+ 'site_name' => ['required', 'string', 'max:80'],
+ 'site_description' => ['nullable', 'string', 'max:255'],
+ 'seo_title_default' => ['nullable', 'string', 'max:160'],
+ 'seo_description_default' => ['nullable', 'string', 'max:255'],
+ 'icp_no' => ['nullable', 'string', 'max:60'],
+ 'security_filing_no' => ['nullable', 'string', 'max:60'],
+ 'logo_url' => ['nullable', 'string', 'max:255'],
+ 'favicon_url' => ['nullable', 'string', 'max:255'],
+ 'contact_email' => ['nullable', 'email', 'max:120'],
+ 'theme' => ['nullable', 'string', 'in:azure,slate,indigo,teal,amber'],
+ 'footer_content' => ['nullable', 'string', 'max:20000'],
+ ]);
+
+ $seoKeys = ['site_description', 'seo_title_default', 'seo_description_default'];
+
+ foreach ($data as $key => $value) {
+ $group = in_array($key, $seoKeys, true) ? 'seo' : 'general';
+ Setting::set($key, $value ?: null, $group);
+ }
+
+ return redirect()->route('admin.settings')->with('success', '站点设置已保存');
+ }
+}
diff --git a/app/Http/Controllers/Admin/SolutionController.php b/app/Http/Controllers/Admin/SolutionController.php
new file mode 100644
index 0000000..4294d54
--- /dev/null
+++ b/app/Http/Controllers/Admin/SolutionController.php
@@ -0,0 +1,79 @@
+orderBy('name')->paginate(20);
+
+ return view('admin.solutions.index', compact('solutions'));
+ }
+
+ public function create(): View
+ {
+ return view('admin.solutions.form', ['solution' => null]);
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:120'],
+ 'slug' => ['required', 'string', 'max:120', 'unique:solutions,slug'],
+ 'power_segment' => ['nullable', 'in:lt30,30-65,65-100,gt100'],
+ 'application' => ['nullable', 'in:charger,powerbank,car,dock'],
+ 'chip_platform' => ['nullable', 'in:protocol,soc,rectifier'],
+ 'power' => ['nullable', 'string', 'max:40'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'advantages' => ['nullable', 'string'],
+ 'description' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'sort' => ['integer'],
+ ]);
+
+ Solution::create($data);
+
+ return redirect()->route('admin.solutions.index')->with('success', '方案已创建');
+ }
+
+ public function edit(Solution $solution): View
+ {
+ return view('admin.solutions.form', compact('solution'));
+ }
+
+ public function update(Request $request, Solution $solution)
+ {
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:120'],
+ 'slug' => ['required', 'string', 'max:120', 'unique:solutions,slug,' . $solution->id],
+ 'power_segment' => ['nullable', 'in:lt30,30-65,65-100,gt100'],
+ 'application' => ['nullable', 'in:charger,powerbank,car,dock'],
+ 'chip_platform' => ['nullable', 'in:protocol,soc,rectifier'],
+ 'power' => ['nullable', 'string', 'max:40'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'advantages' => ['nullable', 'string'],
+ 'description' => ['nullable', 'string'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ 'status' => ['required', 'in:published,draft'],
+ 'sort' => ['integer'],
+ ]);
+
+ $solution->update($data);
+
+ return redirect()->route('admin.solutions.index')->with('success', '方案已更新');
+ }
+
+ public function destroy(Solution $solution)
+ {
+ $solution->delete();
+
+ return redirect()->route('admin.solutions.index')->with('success', '方案已删除');
+ }
+}
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
new file mode 100644
index 0000000..c59a28f
--- /dev/null
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -0,0 +1,228 @@
+query('q', ''));
+
+ $users = User::query()
+ ->when($q, fn ($query) => $query->where(function ($sub) use ($q) {
+ $sub->where('name', 'like', "%{$q}%")->orWhere('email', 'like', "%{$q}%");
+ }))
+ ->orderByDesc('created_at')
+ ->paginate(20)
+ ->withQueryString();
+
+ return view('admin.users.index', compact('users', 'q'));
+ }
+
+ /**
+ * 新建用户表单(角色 + 权限分配)。
+ */
+ public function create(): View
+ {
+ $roles = config('permissions.roles');
+ $groups = config('permissions.groups');
+ $permissions = config('permissions.permissions');
+
+ return view('admin.users.create', compact('roles', 'groups', 'permissions'));
+ }
+
+ /**
+ * 新建用户,受层级约束:不能创建权限高于或等于自身角色的用户。
+ */
+ public function store(Request $request): RedirectResponse
+ {
+ $current = $request->user();
+
+ $allPermKeys = implode(',', array_keys(config('permissions.permissions')));
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:60'],
+ 'email' => ['required', 'email', 'unique:users,email'],
+ 'password' => ['required', 'string', 'min:8'],
+ 'role' => ['required', 'in:super_admin,admin,forum_admin,staff,user'],
+ 'permissions' => ['nullable', 'array'],
+ 'permissions.*' => ['string', "in:{$allPermKeys}"],
+ ]);
+
+ // 层级防护:不能创建高于或等于自身权限的角色
+ if (! $current->isSuperAdmin() && $current->roleLevel() <= $this->roleLevelOf($data['role'])) {
+ return back()->withErrors(['role' => '您只能创建权限低于自身的用户'])->withInput();
+ }
+ if ($data['role'] === 'super_admin' && ! $current->isSuperAdmin()) {
+ return back()->withErrors(['role' => '只有超级管理员可以创建超级管理员'])->withInput();
+ }
+
+ $permissions = $data['role'] === 'staff'
+ ? array_values(array_unique($data['permissions'] ?? []))
+ : null;
+
+ $user = User::create([
+ 'name' => $data['name'],
+ 'email' => $data['email'],
+ 'password' => Hash::make($data['password']),
+ 'role' => $data['role'],
+ 'is_admin' => $data['role'] !== 'user',
+ 'permissions' => $permissions,
+ 'points' => 0,
+ ]);
+
+ return redirect()->route('admin.users.index')
+ ->with('success', "已创建用户 {$user->name}");
+ }
+
+ /**
+ * 编辑用户角色与权限(内部员工可勾选细粒度权限)。
+ */
+ public function edit(User $user)
+ {
+ $roles = config('permissions.roles');
+ $groups = config('permissions.groups');
+ $permissions = config('permissions.permissions');
+ $userPerms = is_array($user->permissions) ? $user->permissions : [];
+
+ $canManage = $this->canManage($user);
+
+ return view('admin.users.edit', compact('user', 'roles', 'groups', 'permissions', 'userPerms', 'canManage'));
+ }
+
+ /**
+ * 更新用户角色与权限,受层级约束。
+ */
+ public function update(Request $request, User $user): RedirectResponse
+ {
+ $current = $request->user();
+
+ // 防锁死:禁止改动自己
+ if ($user->id === $current->id) {
+ return back()->withErrors(['role' => '不能修改当前登录账号的角色/权限']);
+ }
+
+ // 层级防护:非超管不能触碰同级或更高权限用户
+ if (! $current->isSuperAdmin() && $current->roleLevel() <= $user->roleLevel()) {
+ return back()->withErrors(['role' => '您只能修改权限低于自身的用户']);
+ }
+ if ($user->isSuperAdmin() && ! $current->isSuperAdmin()) {
+ return back()->withErrors(['role' => '只有超级管理员可以调整超级管理员']);
+ }
+
+ $allPermKeys = implode(',', array_keys(config('permissions.permissions')));
+ $data = $request->validate([
+ 'role' => ['required', 'in:super_admin,admin,forum_admin,staff,user'],
+ 'permissions' => ['nullable', 'array'],
+ 'permissions.*' => ['string', "in:{$allPermKeys}"],
+ ]);
+
+ // 越权防护:不能分配高于或等于自身权限的角色
+ if ($data['role'] === 'super_admin' && ! $current->isSuperAdmin()) {
+ return back()->withErrors(['role' => '只有超级管理员可以分配超级管理员角色']);
+ }
+ if (! $current->isSuperAdmin() && $current->roleLevel() <= $this->roleLevelOf($data['role'])) {
+ return back()->withErrors(['role' => '您只能分配权限低于自身的角色']);
+ }
+
+ $permissions = $data['role'] === 'staff'
+ ? array_values(array_unique($data['permissions'] ?? []))
+ : null;
+
+ $user->update([
+ 'role' => $data['role'],
+ 'is_admin' => $data['role'] !== 'user',
+ 'permissions' => $permissions,
+ ]);
+
+ return redirect()->route('admin.users.index')
+ ->with('success', "已更新 {$user->name} 的角色与权限");
+ }
+
+ /**
+ * 重置指定用户密码(管理员代操作):可指定新密码,留空则生成随机密码并回显。
+ * 受层级约束,且禁止重置当前登录账号(请用「修改密码」)。
+ */
+ public function resetPassword(Request $request, User $user): RedirectResponse
+ {
+ $current = $request->user();
+
+ if ($user->id === $current->id) {
+ return back()->withErrors(['password' => '不能重置当前登录账号的密码,请到「修改密码」操作']);
+ }
+ if (! $current->isSuperAdmin() && $current->roleLevel() <= $user->roleLevel()) {
+ return back()->withErrors(['password' => '您只能重置权限低于自身的用户密码']);
+ }
+ if ($user->isSuperAdmin() && ! $current->isSuperAdmin()) {
+ return back()->withErrors(['password' => '只有超级管理员可以重置超级管理员密码']);
+ }
+
+ $data = $request->validate([
+ 'password' => ['nullable', 'string', 'min:8', 'confirmed'],
+ ]);
+
+ if (! empty($data['password'])) {
+ $new = $data['password'];
+ $msg = '密码已重置';
+ } else {
+ $new = $this->generatePassword(12);
+ $msg = '密码已重置为随机密码:' . $new . '(请妥善告知用户,并建议其尽快修改)';
+ }
+
+ $user->update(['password' => Hash::make($new)]);
+
+ return redirect()->route('admin.users.edit', $user)->with('success', $msg);
+ }
+
+ public function destroy(User $user): RedirectResponse
+ {
+ $current = request()->user();
+
+ if ($user->id === $current->id) {
+ return back()->withErrors(['name' => '不能删除当前登录账号']);
+ }
+ if (! $current->isSuperAdmin() && $current->roleLevel() <= $user->roleLevel()) {
+ return back()->withErrors(['name' => '您只能删除权限低于自身的用户']);
+ }
+ if ($user->isSuperAdmin() && ! $current->isSuperAdmin()) {
+ return back()->withErrors(['name' => '只有超级管理员可以删除超级管理员']);
+ }
+
+ $user->delete();
+
+ return redirect()->route('admin.users.index')->with('success', '用户已删除');
+ }
+
+ /**
+ * 当前用户是否可管理目标用户(用于视图显隐操作按钮)。
+ */
+ protected function canManage(User $user): bool
+ {
+ $current = request()->user();
+ if ($current->isSuperAdmin()) {
+ return true;
+ }
+ return $current->roleLevel() > $user->roleLevel();
+ }
+
+ protected function roleLevelOf(string $role): int
+ {
+ return (int) config("permissions.roles.{$role}.level", 1);
+ }
+
+ protected function generatePassword(int $len): string
+ {
+ $pool = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
+ $out = '';
+ for ($i = 0; $i < $len; $i++) {
+ $out .= $pool[random_int(0, strlen($pool) - 1)];
+ }
+ return $out;
+ }
+}
diff --git a/app/Http/Controllers/ArticleController.php b/app/Http/Controllers/ArticleController.php
new file mode 100644
index 0000000..b4cf296
--- /dev/null
+++ b/app/Http/Controllers/ArticleController.php
@@ -0,0 +1,114 @@
+query('category');
+ $q = trim((string) $request->query('q', ''));
+
+ $categories = Category::ofType('article')->orderBy('sort')->orderBy('name')->get();
+
+ $articles = Article::published()
+ ->when($categoryId, fn ($query) => $query->where('category_id', $categoryId))
+ ->when($q, function ($query) use ($q) {
+ $query->where(function ($sub) use ($q) {
+ $sub->where('title', 'like', "%{$q}%")
+ ->orWhere('summary', 'like', "%{$q}%");
+ });
+ })
+ ->paginate(10)
+ ->withQueryString();
+
+ return view('articles.index', compact('articles', 'categories', 'q', 'categoryId'));
+ }
+
+ public function show(Article $article): View
+ {
+ $user = request()->user();
+
+ // 公开:已审且已发布;作者本人或审核员:可看自己投稿(含待审/驳回)
+ $canView = $article->status === 'approved' && $article->published_at !== null;
+ if (! $canView && $user) {
+ $canView = $article->user_id === $user->id || $user->canPerm('articles.moderate');
+ }
+ if (! $canView) {
+ abort(404);
+ }
+
+ return view('articles.show', compact('article'));
+ }
+
+ /**
+ * 用户投稿表单(需登录)。
+ */
+ public function create(): View
+ {
+ $categories = Category::ofType('article')->orderBy('sort')->orderBy('name')->get();
+
+ return view('articles.submit', compact('categories'));
+ }
+
+ /**
+ * 提交用户投稿:写入 articles(user_id + status=pending),审核前仅作者可见。
+ */
+ public function storeSubmission(Request $request)
+ {
+ $data = $request->validate([
+ 'category_id' => ['nullable', 'exists:categories,id'],
+ 'title' => ['required', 'string', 'max:160'],
+ 'summary' => ['nullable', 'string', 'max:255'],
+ 'body' => ['required', 'string'],
+ 'template' => ['required', 'in:standard,tech,feature'],
+ 'cover' => ['nullable', 'string', 'max:255'],
+ ]);
+
+ $data['body'] = \Purifier::clean($data['body'], 'article');
+
+ $base = Str::slug(Str::ascii($data['title'])) ?: ('article-' . date('YmdHis'));
+ $slug = $base;
+ $i = 1;
+ // withTrashed:软删除行仍占用 slug 唯一索引,须一并检查避免 Duplicate entry
+ while (Article::withTrashed()->where('slug', $slug)->exists()) {
+ $slug = $base . '-' . $i++;
+ }
+
+ Article::create([
+ 'category_id' => $data['category_id'] ?? null,
+ 'user_id' => Auth::id(),
+ 'title' => $data['title'],
+ 'slug' => $slug,
+ 'summary' => $data['summary'] ?? null,
+ 'body' => $data['body'],
+ 'template' => $data['template'],
+ 'cover' => $data['cover'] ?? null,
+ 'author' => Auth::user()->name,
+ 'status' => 'pending',
+ 'published_at' => null,
+ ]);
+
+ return redirect()->route('articles.mine')
+ ->with('success', '投稿已提交,管理员审核通过后将公开展示');
+ }
+
+ /**
+ * 我的投稿:当前用户自己的全部文章(含待审/驳回)。
+ */
+ public function mine(): View
+ {
+ $articles = Article::where('user_id', Auth::id())
+ ->orderByDesc('created_at')
+ ->paginate(10);
+
+ return view('articles.mine', compact('articles'));
+ }
+}
diff --git a/app/Http/Controllers/Auth/AdminLoginController.php b/app/Http/Controllers/Auth/AdminLoginController.php
new file mode 100644
index 0000000..4bff789
--- /dev/null
+++ b/app/Http/Controllers/Auth/AdminLoginController.php
@@ -0,0 +1,92 @@
+throttleKey($request);
+
+ if (RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
+ $seconds = RateLimiter::availableIn($key);
+ $request->session()->forget('captcha');
+ throw ValidationException::withMessages([
+ 'email' => '登录尝试过于频繁,出于安全考虑已临时锁定。请在约 '.ceil($seconds / 60).' 分钟('.ceil($seconds).' 秒)后重试。',
+ ]);
+ }
+
+ $data = $request->validate([
+ 'email' => ['required', 'email'],
+ 'password' => ['required', 'string'],
+ 'captcha' => ['required', 'string', function ($attribute, $value, $fail) use ($request) {
+ if (strtolower(trim((string) $value)) !== strtolower((string) $request->session()->get('captcha', ''))) {
+ $fail('图形验证码不正确,请重新输入。');
+ }
+ }],
+ ]);
+
+ if (! Auth::attempt(['email' => $data['email'], 'password' => $data['password']], $request->boolean('remember'))) {
+ RateLimiter::hit($key, self::DECAY_SECONDS);
+ $attempts = RateLimiter::attempts($key);
+ $left = max(0, self::MAX_ATTEMPTS - $attempts);
+ $request->session()->forget('captcha');
+
+ $msg = '账号或密码不正确。';
+ if ($left > 0) {
+ $msg .= "(已错误 {$attempts} 次,再错 {$left} 次将锁定 10 分钟)";
+ } else {
+ $msg .= '(错误次数过多,已锁定 10 分钟)';
+ }
+
+ return back()->withErrors([
+ 'email' => $msg,
+ ])->onlyInput('email');
+ }
+
+ $user = Auth::user();
+
+ if (! $user->isAdmin()) {
+ Auth::logout();
+ $request->session()->invalidate();
+ $request->session()->regenerateToken();
+ $request->session()->forget('captcha');
+
+ return back()->withErrors([
+ 'email' => '该账户没有后台管理权限。',
+ ])->onlyInput('email');
+ }
+
+ RateLimiter::clear($key);
+ $request->session()->forget('captcha');
+ $request->session()->regenerate();
+
+ return redirect()->intended('/admin');
+ }
+
+ protected function throttleKey(Request $request): string
+ {
+ return 'admin-login:'.strtolower($request->input('email', '')).':'.$request->ip();
+ }
+}
diff --git a/app/Http/Controllers/Auth/CaptchaController.php b/app/Http/Controllers/Auth/CaptchaController.php
new file mode 100644
index 0000000..2629ded
--- /dev/null
+++ b/app/Http/Controllers/Auth/CaptchaController.php
@@ -0,0 +1,77 @@
+generateCode(4);
+ Session::put('captcha', $code);
+
+ $width = 120;
+ $height = 44;
+ $img = imagecreatetruecolor($width, $height);
+
+ // 背景
+ $bg = imagecolorallocate($img, 244, 247, 250);
+ imagefilledrectangle($img, 0, 0, $width, $height, $bg);
+
+ // 干扰线
+ for ($i = 0; $i < 6; $i++) {
+ $c = imagecolorallocate($img, rand(160, 210), rand(160, 210), rand(160, 210));
+ imageline($img, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $c);
+ }
+
+ // 字符(随机色 + 轻微纵向抖动)
+ $palette = [[74, 138, 244], [110, 140, 176], [43, 182, 164], [123, 126, 240], [200, 120, 60]];
+ $len = strlen($code);
+ $step = (int) ($width / ($len + 1));
+ for ($i = 0; $i < $len; $i++) {
+ $col = $palette[array_rand($palette)];
+ $c = imagecolorallocate($img, $col[0], $col[1], $col[2]);
+ $x = $step * ($i + 1) - 10 + rand(-3, 3);
+ $y = rand(8, 18);
+ imagestring($img, 5, $x, $y, $code[$i], $c);
+ }
+
+ // 噪点
+ for ($i = 0; $i < 50; $i++) {
+ $c = imagecolorallocate($img, rand(180, 225), rand(180, 225), rand(180, 225));
+ imagesetpixel($img, rand(0, $width), rand(0, $height), $c);
+ }
+
+ ob_start();
+ imagepng($img);
+ $data = ob_get_clean();
+ imagedestroy($img);
+
+ return response($data, 200)
+ ->header('Content-Type', 'image/png')
+ ->header('Cache-Control', 'no-store, no-cache, must-revalidate, private')
+ ->header('Pragma', 'no-cache');
+ }
+
+ /**
+ * 生成验证码字符(去除易混淆的 0/O/1/I/L)。
+ */
+ protected function generateCode(int $len): string
+ {
+ $pool = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
+ $out = '';
+ for ($i = 0; $i < $len; $i++) {
+ $out .= $pool[random_int(0, strlen($pool) - 1)];
+ }
+ return $out;
+ }
+}
diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php
new file mode 100644
index 0000000..dde5995
--- /dev/null
+++ b/app/Http/Controllers/Auth/LoginController.php
@@ -0,0 +1,91 @@
+throttleKey($request);
+
+ // 锁定检查:10 分钟内错误满 5 次即锁定,提示等待时长
+ if (RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
+ $seconds = RateLimiter::availableIn($key);
+ $request->session()->forget('captcha');
+ throw ValidationException::withMessages([
+ 'email' => '登录尝试过于频繁,出于安全考虑已临时锁定。请在约 '.ceil($seconds / 60).' 分钟('.ceil($seconds).' 秒)后重试。',
+ ]);
+ }
+
+ $credentials = $request->validate([
+ 'email' => ['required', 'email'],
+ 'password' => ['required', 'string'],
+ 'captcha' => ['required', 'string', function ($attribute, $value, $fail) use ($request) {
+ if (strtolower(trim((string) $value)) !== strtolower((string) $request->session()->get('captcha', ''))) {
+ $fail('图形验证码不正确,请重新输入。');
+ }
+ }],
+ ]);
+
+ if (Auth::attempt(['email' => $credentials['email'], 'password' => $credentials['password']], $request->boolean('remember'))) {
+ RateLimiter::clear($key);
+ $request->session()->forget('captcha');
+ $request->session()->regenerate();
+
+ return redirect()->intended('/');
+ }
+
+ // 失败:记录一次错误(10 分钟滚动窗口),并给出剩余次数提醒
+ RateLimiter::hit($key, self::DECAY_SECONDS);
+ $attempts = RateLimiter::attempts($key);
+ $left = max(0, self::MAX_ATTEMPTS - $attempts);
+ $request->session()->forget('captcha');
+
+ $msg = '账号或密码不正确。';
+ if ($left > 0) {
+ $msg .= "(已错误 {$attempts} 次,再错 {$left} 次将锁定 10 分钟)";
+ } else {
+ $msg .= '(错误次数过多,已锁定 10 分钟)';
+ }
+
+ return back()->withErrors([
+ 'email' => $msg,
+ ])->onlyInput('email');
+ }
+
+ protected function throttleKey(Request $request): string
+ {
+ return 'login:'.strtolower($request->input('email', '')).':'.$request->ip();
+ }
+
+ /**
+ * 退出登录。定义为控制器方法而非闭包,
+ * 以保证生产环境 `php artisan route:cache` 可用。
+ */
+ public function destroy(Request $request): RedirectResponse
+ {
+ Auth::guard('web')->logout();
+
+ $request->session()->invalidate();
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
+}
diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php
new file mode 100644
index 0000000..c7c4c9f
--- /dev/null
+++ b/app/Http/Controllers/Auth/RegisterController.php
@@ -0,0 +1,47 @@
+validate([
+ 'name' => ['required', 'string', 'max:40'],
+ 'email' => ['required', 'email', 'max:120', 'unique:users,email'],
+ 'password' => ['required', 'string', 'min:8', 'confirmed'],
+ 'real_name' => ['nullable', 'string', 'max:40'],
+ 'phone' => ['nullable', 'string', 'max:30'],
+ 'company' => ['nullable', 'string', 'max:120'],
+ 'industry' => ['nullable', 'string', 'max:60'],
+ ]);
+
+ $user = User::create([
+ 'name' => $data['name'],
+ 'email' => $data['email'],
+ 'password' => $data['password'],
+ 'real_name' => $data['real_name'] ?? null,
+ 'phone' => $data['phone'] ?? null,
+ 'company' => $data['company'] ?? null,
+ 'industry' => $data['industry'] ?? null,
+ ]);
+
+ Auth::login($user);
+
+ $user->awardPoints(20);
+
+ return redirect('/');
+ }
+}
diff --git a/app/Http/Controllers/Auth/SettingsController.php b/app/Http/Controllers/Auth/SettingsController.php
new file mode 100644
index 0000000..c0ffda5
--- /dev/null
+++ b/app/Http/Controllers/Auth/SettingsController.php
@@ -0,0 +1,32 @@
+validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', 'confirmed', Password::min(8)],
+ ]);
+
+ $request->user()->update([
+ 'password' => Hash::make($data['password']),
+ ]);
+
+ return redirect()->route('auth.settings')->with('success', '密码已修改');
+ }
+}
diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php
new file mode 100644
index 0000000..7fc37ab
--- /dev/null
+++ b/app/Http/Controllers/ContactController.php
@@ -0,0 +1,61 @@
+ip();
+
+ if (RateLimiter::tooManyAttempts($key, 5, 60)) {
+ $seconds = RateLimiter::availableIn($key);
+ $msg = "提交过于频繁,请 {$seconds} 秒后再试。";
+
+ if ($request->wantsJson()) {
+ return response()->json(['ok' => false, 'message' => $msg], 429);
+ }
+
+ return back()->withErrors(['message' => $msg])->withInput();
+ }
+
+ $data = $request->validate([
+ 'name' => ['required', 'string', 'max:40'],
+ 'email' => ['required', 'email', 'max:120'],
+ 'phone' => ['nullable', 'string', 'max:20', 'regex:/^[0-9+\-()\s]+$/'],
+ 'company' => ['nullable', 'string', 'max:120'],
+ 'subject' => ['required', 'string', 'max:80'],
+ 'message' => ['required', 'string', 'max:1000'],
+ ]);
+
+ ContactMessage::create([
+ 'name' => $data['name'],
+ 'email' => $data['email'],
+ 'phone' => $data['phone'] ?? null,
+ 'company' => $data['company'] ?? null,
+ 'subject' => $data['subject'],
+ 'message' => $data['message'],
+ 'ip' => $request->ip(),
+ ]);
+
+ RateLimiter::hit($key, 60);
+
+ if ($request->wantsJson()) {
+ return response()->json(['ok' => true, 'message' => '提交成功,我们会尽快与您联系。']);
+ }
+
+ return back()->with('success', '提交成功,我们会尽快与您联系。');
+ }
+}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
new file mode 100644
index 0000000..77ec359
--- /dev/null
+++ b/app/Http/Controllers/Controller.php
@@ -0,0 +1,12 @@
+orderBy('name')
+ ->withCount(['threads' => fn ($q) => $q->where('status', ForumThread::STATUS_APPROVED)])
+ ->get();
+
+ return view('forum.index', compact('boards'));
+ }
+
+ public function board(ForumBoard $board): View
+ {
+ $threads = $board->threads()
+ ->visible(Auth::user())
+ ->with('user')
+ ->orderByDesc('is_pinned')
+ ->orderByDesc('last_reply_at')
+ ->orderByDesc('created_at')
+ ->paginate(15);
+
+ return view('forum.board', compact('board', 'threads'));
+ }
+
+ public function thread(ForumThread $thread): View
+ {
+ if ($thread->trashed()) {
+ abort(404);
+ }
+
+ $user = Auth::user();
+ $isMod = $user && $user->canPerm('forum.moderate');
+ $isOwner = $user && $thread->user_id === $user->id;
+
+ // 待审且非作者、非审核员:不可见
+ if ($thread->status !== ForumThread::STATUS_APPROVED && ! $isMod && ! $isOwner) {
+ abort(404);
+ }
+
+ $thread->increment('views');
+
+ $replies = $thread->replies()
+ ->visible($user)
+ ->with('user')
+ ->orderBy('created_at')
+ ->paginate(20);
+
+ return view('forum.thread', compact('thread', 'replies'));
+ }
+
+ public function store(Request $request)
+ {
+ $data = $request->validate([
+ 'board_id' => ['required', 'exists:forum_boards,id'],
+ 'title' => ['required', 'string', 'max:120'],
+ 'body' => ['required', 'string', 'max:8000'],
+ ]);
+
+ $thread = ForumThread::create([
+ 'board_id' => $data['board_id'],
+ 'user_id' => Auth::id(),
+ 'title' => $data['title'],
+ 'body' => $data['body'],
+ 'status' => ForumThread::STATUS_PENDING,
+ 'last_reply_at' => now(),
+ ]);
+
+ $request->user()->awardPoints(10);
+
+ return redirect()->route('forum.thread', $thread)
+ ->with('success', '帖子已提交,管理员审核通过后将公开展示');
+ }
+
+ public function reply(Request $request, ForumThread $thread)
+ {
+ if ($thread->is_locked) {
+ return back()->withErrors(['body' => '该帖子已锁定,无法回复。']);
+ }
+
+ // 待审帖子的回复入口对普通用户不可见(thread() 已拦截),此处仅作兜底
+ if ($thread->status !== ForumThread::STATUS_APPROVED && ! Auth::user()?->canPerm('forum.moderate')) {
+ return back()->withErrors(['body' => '该帖子尚未通过审核,暂不可回复。']);
+ }
+
+ $data = $request->validate([
+ 'body' => ['required', 'string', 'max:8000'],
+ ]);
+
+ ForumReply::create([
+ 'thread_id' => $thread->id,
+ 'user_id' => Auth::id(),
+ 'body' => $data['body'],
+ 'status' => ForumReply::STATUS_PENDING,
+ ]);
+
+ $thread->update(['last_reply_at' => now()]);
+
+ $request->user()->awardPoints(5);
+
+ return back()->with('success', '回复已提交,审核通过后将公开展示');
+ }
+}
diff --git a/app/Http/Controllers/GlossaryController.php b/app/Http/Controllers/GlossaryController.php
new file mode 100644
index 0000000..2650e73
--- /dev/null
+++ b/app/Http/Controllers/GlossaryController.php
@@ -0,0 +1,33 @@
+query('q', ''));
+
+ $terms = GlossaryTerm::ordered()
+ ->when($q, function ($query) use ($q) {
+ $query->where(function ($sub) use ($q) {
+ $sub->where('term', 'like', "%{$q}%")
+ ->orWhere('definition', 'like', "%{$q}%")
+ ->orWhere('aliases', 'like', "%{$q}%");
+ });
+ })
+ ->paginate(20)
+ ->withQueryString();
+
+ return view('glossary.index', compact('terms', 'q'));
+ }
+
+ public function show(GlossaryTerm $glossaryTerm): View
+ {
+ return view('glossary.show', compact('glossaryTerm'));
+ }
+}
diff --git a/app/Http/Controllers/HealthController.php b/app/Http/Controllers/HealthController.php
new file mode 100644
index 0000000..b9bb467
--- /dev/null
+++ b/app/Http/Controllers/HealthController.php
@@ -0,0 +1,29 @@
+getPdo();
+ } catch (\Throwable) {
+ $dbOk = false;
+ }
+
+ return response()->json([
+ 'status' => $dbOk ? 'ok' : 'degraded',
+ 'database' => $dbOk ? 'up' : 'down',
+ 'time' => now()->toIso8601String(),
+ ], $dbOk ? 200 : 503);
+ }
+}
diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
new file mode 100644
index 0000000..f9510c1
--- /dev/null
+++ b/app/Http/Controllers/HomeController.php
@@ -0,0 +1,34 @@
+take(3)->get();
+
+ $articles = Article::published()->take(3)->get();
+
+ $solutions = Solution::published()->orderBy('sort')->take(4)->get();
+
+ $news = News::published()->take(3)->get();
+
+ // 首页 Hero 轮播(后台「首页轮播」管理;mode=single 时为独立单图)
+ $heroMode = \App\Models\Setting::get('hero_mode', 'carousel');
+ $heroSlides = \App\Models\HeroSlide::location('home')->active()->ordered()->get();
+
+ return view('home', compact('products', 'articles', 'solutions', 'news', 'heroSlides', 'heroMode'));
+ }
+
+ public function about(): View
+ {
+ return view('about');
+ }
+}
diff --git a/app/Http/Controllers/NewsController.php b/app/Http/Controllers/NewsController.php
new file mode 100644
index 0000000..29029d1
--- /dev/null
+++ b/app/Http/Controllers/NewsController.php
@@ -0,0 +1,31 @@
+query('category');
+
+ $news = News::published()
+ ->when($category, fn ($q) => $q->where('category', $category))
+ ->paginate(9)
+ ->withQueryString();
+
+ return view('news.index', compact('news', 'category'));
+ }
+
+ public function show(News $news): View
+ {
+ if ($news->status !== 'published' || $news->published_at === null) {
+ abort(404);
+ }
+
+ return view('news.show', compact('news'));
+ }
+}
diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php
new file mode 100644
index 0000000..cfc2c0a
--- /dev/null
+++ b/app/Http/Controllers/ProductController.php
@@ -0,0 +1,42 @@
+query('category');
+ $q = trim((string) $request->query('q', ''));
+
+ $categories = Category::ofType('product')->orderBy('sort')->orderBy('name')->get();
+
+ $products = Product::published()
+ ->when($categoryId, fn ($query) => $query->where('category_id', $categoryId))
+ ->when($q, function ($query) use ($q) {
+ $query->where(function ($sub) use ($q) {
+ $sub->where('name', 'like', "%{$q}%")
+ ->orWhere('model', 'like', "%{$q}%")
+ ->orWhere('summary', 'like', "%{$q}%");
+ });
+ })
+ ->paginate(12)
+ ->withQueryString();
+
+ return view('products.index', compact('products', 'categories', 'q', 'categoryId'));
+ }
+
+ public function show(Product $product): View
+ {
+ if ($product->status !== 'published') {
+ abort(404);
+ }
+
+ return view('products.show', compact('product'));
+ }
+}
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
new file mode 100644
index 0000000..81b4c12
--- /dev/null
+++ b/app/Http/Controllers/SearchController.php
@@ -0,0 +1,80 @@
+query('q', ''));
+ $user = $request->user();
+
+ $results = [
+ 'articles' => collect(),
+ 'products' => collect(),
+ 'glossary' => collect(),
+ 'threads' => collect(),
+ ];
+ $total = 0;
+
+ if ($q !== '') {
+ $like = '%' . $q . '%';
+
+ $results['articles'] = Article::visiblePublic()
+ ->where(function ($query) use ($like) {
+ $query->where('title', 'like', $like)
+ ->orWhere('summary', 'like', $like)
+ ->orWhere('body', 'like', $like);
+ })
+ ->orderByDesc('published_at')
+ ->limit(10)
+ ->get();
+
+ $results['products'] = Product::published()
+ ->where(function ($query) use ($like) {
+ $query->where('name', 'like', $like)
+ ->orWhere('summary', 'like', $like)
+ ->orWhere('description', 'like', $like)
+ ->orWhere('model', 'like', $like);
+ })
+ ->limit(10)
+ ->get();
+
+ $results['glossary'] = GlossaryTerm::query()
+ ->where(function ($query) use ($like) {
+ $query->where('term', 'like', $like)
+ ->orWhere('definition', 'like', $like)
+ ->orWhere('aliases', 'like', $like);
+ })
+ ->orderBy('term')
+ ->limit(10)
+ ->get();
+
+ $results['threads'] = ForumThread::visible($user)
+ ->where(function ($query) use ($like) {
+ $query->where('title', 'like', $like)
+ ->orWhere('body', 'like', $like);
+ })
+ ->orderByDesc('last_reply_at')
+ ->limit(10)
+ ->get();
+
+ $total = $results['articles']->count()
+ + $results['products']->count()
+ + $results['glossary']->count()
+ + $results['threads']->count();
+ }
+
+ return view('search.index', compact('q', 'results', 'total'));
+ }
+}
diff --git a/app/Http/Controllers/SitemapController.php b/app/Http/Controllers/SitemapController.php
new file mode 100644
index 0000000..424bd06
--- /dev/null
+++ b/app/Http/Controllers/SitemapController.php
@@ -0,0 +1,155 @@
+ ['freq' => 'daily', 'pri' => '1.0'],
+ '/about' => ['freq' => 'monthly', 'pri' => '0.8'],
+ '/products' => ['freq' => 'weekly', 'pri' => '0.8'],
+ '/articles' => ['freq' => 'weekly', 'pri' => '0.8'],
+ '/solutions' => ['freq' => 'weekly', 'pri' => '0.8'],
+ '/news' => ['freq' => 'weekly', 'pri' => '0.8'],
+ '/glossary' => ['freq' => 'weekly', 'pri' => '0.8'],
+ '/forum' => ['freq' => 'daily', 'pri' => '0.7'],
+ '/contact' => ['freq' => 'yearly', 'pri' => '0.6'],
+ '/sitemap' => ['freq' => 'monthly', 'pri' => '0.3'],
+ ];
+
+ foreach ($core as $path => $meta) {
+ $urls[] = [
+ 'loc' => url($path),
+ 'freq' => $meta['freq'],
+ 'pri' => $meta['pri'],
+ ];
+ }
+
+ // ── 动态内容:产品 ────────────────────────────
+ foreach (Product::published()->get() as $p) {
+ $urls[] = [
+ 'loc' => route('products.show', $p->slug),
+ 'last' => $p->updated_at?->toW3CString(),
+ 'freq' => 'weekly',
+ 'pri' => '0.7',
+ ];
+ }
+
+ // ── 动态内容:技术文章 ────────────────────────
+ foreach (Article::published()->get() as $a) {
+ $urls[] = [
+ 'loc' => route('articles.show', $a->slug),
+ 'last' => $a->published_at?->toW3CString(),
+ 'freq' => 'monthly',
+ 'pri' => '0.6',
+ ];
+ }
+
+ // ── 动态内容:术语库 ──────────────────────────
+ foreach (GlossaryTerm::ordered()->get() as $g) {
+ $urls[] = [
+ 'loc' => route('glossary.show', $g->slug),
+ 'freq' => 'monthly',
+ 'pri' => '0.4',
+ ];
+ }
+
+ // ── 动态内容:解决方案 ────────────────────────
+ foreach (Solution::published()->get() as $s) {
+ $urls[] = [
+ 'loc' => route('solutions.show', $s->slug),
+ 'last' => $s->updated_at?->toW3CString(),
+ 'freq' => 'weekly',
+ 'pri' => '0.7',
+ ];
+ }
+
+ // ── 动态内容:新闻动态 ────────────────────────
+ foreach (News::published()->get() as $n) {
+ $urls[] = [
+ 'loc' => route('news.show', $n->slug),
+ 'last' => $n->published_at?->toW3CString(),
+ 'freq' => 'monthly',
+ 'pri' => '0.6',
+ ];
+ }
+
+ $xml = '' . "\n";
+ $xml .= '' . "\n";
+
+ foreach ($urls as $u) {
+ $xml .= ' ' . "\n";
+ $xml .= ' ' . e($u['loc']) . '' . "\n";
+
+ if (!empty($u['last'])) {
+ $xml .= ' ' . e($u['last']) . '' . "\n";
+ }
+
+ $xml .= ' ' . e($u['freq']) . '' . "\n";
+ $xml .= ' ' . e($u['pri']) . '' . "\n";
+ $xml .= ' ' . "\n";
+ }
+
+ $xml .= '';
+
+ return response($xml, 200, [
+ 'Content-Type' => 'application/xml; charset=UTF-8',
+ ]);
+ }
+
+ /**
+ * robots.txt:开放前台收录,屏蔽后台与用户接口,并指向 sitemap.xml。
+ * 访问 /robots.txt 获取(动态读取 APP_URL,域名随环境自动变化)。
+ */
+ public function robots()
+ {
+ $base = rtrim(config('app.url'), '/');
+
+ $content = "User-agent: *\n";
+ $content .= "Allow: /\n\n";
+ $content .= "# 后台与管理 / 用户接口不向搜索引擎开放\n";
+ $content .= "Disallow: /admin/\n";
+ $content .= "Disallow: /login\n";
+ $content .= "Disallow: /register\n";
+ $content .= "Disallow: /logout\n";
+ $content .= "Disallow: /settings\n";
+ $content .= "Disallow: /upload\n";
+ $content .= "Disallow: /forum/threads\n";
+ $content .= "Disallow: /api/\n\n";
+ $content .= "Sitemap: {$base}/sitemap.xml\n";
+
+ return response($content, 200, [
+ 'Content-Type' => 'text/plain; charset=UTF-8',
+ ]);
+ }
+
+ /**
+ * 可视化「网站地图」页面(用户可见,列出全站主要页面与内容)。
+ * 访问 /sitemap 获取。
+ */
+ public function index()
+ {
+ $products = Product::published()->get();
+ $articles = Article::published()->get();
+ $terms = GlossaryTerm::ordered()->get();
+ $solutions = Solution::published()->get();
+ $news = News::published()->get();
+
+ return view('sitemap', compact('products', 'articles', 'terms', 'solutions', 'news'));
+ }
+}
diff --git a/app/Http/Controllers/SolutionsController.php b/app/Http/Controllers/SolutionsController.php
new file mode 100644
index 0000000..37e008f
--- /dev/null
+++ b/app/Http/Controllers/SolutionsController.php
@@ -0,0 +1,34 @@
+query('power');
+ $application = $request->query('application');
+ $platform = $request->query('platform');
+
+ $solutions = Solution::published()
+ ->when($power, fn ($q) => $q->where('power_segment', $power))
+ ->when($application, fn ($q) => $q->where('application', $application))
+ ->when($platform, fn ($q) => $q->where('chip_platform', $platform))
+ ->get();
+
+ return view('solutions.index', compact('solutions', 'power', 'application', 'platform'));
+ }
+
+ public function show(Solution $solution): View
+ {
+ if ($solution->status !== 'published') {
+ abort(404);
+ }
+
+ return view('solutions.show', compact('solution'));
+ }
+}
diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php
new file mode 100644
index 0000000..fc2bff9
--- /dev/null
+++ b/app/Http/Controllers/UploadController.php
@@ -0,0 +1,27 @@
+validate([
+ 'file' => ['required', 'file', 'image', 'mimes:jpg,jpeg,png,gif,webp', 'max:5120'],
+ ]);
+
+ $path = $request->file('file')->store('uploads/' . date('Y/m'), 'public');
+
+ return response()->json([
+ 'url' => Storage::disk('public')->url($path),
+ 'path' => $path,
+ ]);
+ }
+}
diff --git a/app/Http/Middleware/Admin.php b/app/Http/Middleware/Admin.php
new file mode 100644
index 0000000..9f78f71
--- /dev/null
+++ b/app/Http/Middleware/Admin.php
@@ -0,0 +1,23 @@
+user()) {
+ return redirect()->route('admin.login');
+ }
+
+ if (! $request->user()->isAdmin()) {
+ abort(403, '需要管理员权限');
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/Can.php b/app/Http/Middleware/Can.php
new file mode 100644
index 0000000..1a00257
--- /dev/null
+++ b/app/Http/Middleware/Can.php
@@ -0,0 +1,31 @@
+user();
+
+ if (! $user) {
+ abort(403, '需要登录');
+ }
+
+ foreach ($permissions as $permission) {
+ if ($user->canPerm($permission)) {
+ return $next($request);
+ }
+ }
+
+ abort(403, '无访问权限');
+ }
+}
diff --git a/app/Http/Middleware/Role.php b/app/Http/Middleware/Role.php
new file mode 100644
index 0000000..5eb07de
--- /dev/null
+++ b/app/Http/Middleware/Role.php
@@ -0,0 +1,25 @@
+user();
+
+ if (! $user || ! in_array($user->role, $roles, true)) {
+ abort(403, '无访问权限');
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/SecurityHeaders.php b/app/Http/Middleware/SecurityHeaders.php
new file mode 100644
index 0000000..8182003
--- /dev/null
+++ b/app/Http/Middleware/SecurityHeaders.php
@@ -0,0 +1,70 @@
+headers->set('X-Content-Type-Options', 'nosniff');
+
+ // 防点击劫持(现代浏览器看 frame-ancestors,旧浏览器看 X-Frame-Options)
+ $response->headers->set('X-Frame-Options', 'DENY');
+
+ // Referrer 泄露控制
+ $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
+
+ // 关闭不必要的浏览器敏感能力
+ $response->headers->set(
+ 'Permissions-Policy',
+ "geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()"
+ );
+
+ // 内容安全策略:限制脚本/样式/连接/表单来源,缩小 XSS 影响面
+ $csp = implode('; ', [
+ "default-src 'self'",
+ "script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com",
+ "style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com",
+ "img-src 'self' data: https:",
+ "font-src 'self' data:",
+ "connect-src 'self'",
+ "frame-ancestors 'none'",
+ "base-uri 'self'",
+ "form-action 'self'",
+ ]);
+ $response->headers->set('Content-Security-Policy', $csp);
+
+ // HSTS:仅生产 + HTTPS 时下发
+ if ($request->isSecure() && app()->environment('production')) {
+ $response->headers->set(
+ 'Strict-Transport-Security',
+ 'max-age=31536000; includeSubDomains; preload'
+ );
+ }
+
+ // 禁止代理/浏览器缓存动态页面(含 CSRF token 的表单页)。
+ // 否则 nginx/浏览器缓存的旧 HTML 会携带过期 _token,提交即触发 419 Page Expired。
+ // 静态资源由 web 服务器直接服务、不经过本中间件,不受影响。
+ $response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
+ $response->headers->set('Pragma', 'no-cache');
+ $response->headers->set('Expires', '0');
+
+ return $response;
+ }
+}
diff --git a/app/Models/Article.php b/app/Models/Article.php
new file mode 100644
index 0000000..86e4465
--- /dev/null
+++ b/app/Models/Article.php
@@ -0,0 +1,100 @@
+ 'integer',
+ 'published_at' => 'datetime',
+ ];
+
+ public const TEMPLATES = [
+ 'standard' => '标准文章',
+ 'tech' => '技术文档',
+ 'feature' => '专题报道',
+ ];
+
+ public function category(): BelongsTo
+ {
+ return $this->belongsTo(Category::class, 'category_id');
+ }
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+
+ /**
+ * 可见性作用域(与 forum 一致):
+ * - 审核员(articles.moderate):看全部
+ * - 登录用户:看已审 + 自己投稿(无论状态)
+ * - 游客:仅看已审
+ */
+ public function scopeVisibleTo($query, ?User $user = null)
+ {
+ if ($user && $user->canPerm('articles.moderate')) {
+ return $query;
+ }
+
+ if ($user) {
+ return $query->where(function ($q) use ($user) {
+ $q->where('status', 'approved')->orWhere('user_id', $user->id);
+ });
+ }
+
+ return $query->where('status', 'approved');
+ }
+
+ public function scopePublished($query)
+ {
+ return $query->where('status', 'approved')
+ ->whereNotNull('published_at')
+ ->orderByDesc('published_at');
+ }
+
+ /**
+ * 前台可见文章:已审核通过(approved) 且已设定发布时间。
+ * 用户自己的待审(pending)/驳回(rejected) 由控制器单独查询,不进此作用域。
+ */
+ public function scopeVisiblePublic($query)
+ {
+ return $query->where('status', 'approved')
+ ->whereNotNull('published_at');
+ }
+
+ /**
+ * 前台 SEO 标题:优先用文章自定义,否则回退到标题。
+ */
+ public function seoTitle(): string
+ {
+ return $this->seo_title ?: $this->title;
+ }
+
+ public function seoDescription(): string
+ {
+ return $this->seo_description ?: ($this->summary ?: $this->title);
+ }
+}
diff --git a/app/Models/Category.php b/app/Models/Category.php
new file mode 100644
index 0000000..c48e2c0
--- /dev/null
+++ b/app/Models/Category.php
@@ -0,0 +1,37 @@
+ 'integer',
+ 'sort' => 'integer',
+ ];
+
+ public function products(): HasMany
+ {
+ return $this->hasMany(Product::class, 'category_id');
+ }
+
+ public function articles(): HasMany
+ {
+ return $this->hasMany(Article::class, 'category_id');
+ }
+
+ public function scopeOfType($query, string $type)
+ {
+ return $query->where('type', $type)->orderBy('sort')->orderBy('name');
+ }
+}
diff --git a/app/Models/ContactMessage.php b/app/Models/ContactMessage.php
new file mode 100644
index 0000000..283658c
--- /dev/null
+++ b/app/Models/ContactMessage.php
@@ -0,0 +1,22 @@
+ 'integer',
+ ];
+
+ public function threads(): HasMany
+ {
+ return $this->hasMany(ForumThread::class, 'board_id');
+ }
+}
diff --git a/app/Models/ForumReply.php b/app/Models/ForumReply.php
new file mode 100644
index 0000000..b49f28f
--- /dev/null
+++ b/app/Models/ForumReply.php
@@ -0,0 +1,60 @@
+ 'integer',
+ 'user_id' => 'integer',
+ ];
+
+ public const STATUS_PENDING = 'pending';
+ public const STATUS_APPROVED = 'approved';
+ public const STATUS_REJECTED = 'rejected';
+
+ /**
+ * 可见性作用域(同 ForumThread):
+ * - 审核员:全部
+ * - 登录用户:已审 + 自己发的
+ * - 游客:仅已审
+ */
+ public function scopeVisible($query, ?User $user = null)
+ {
+ if ($user && $user->canPerm('forum.moderate')) {
+ return $query;
+ }
+
+ if ($user) {
+ return $query->where(function ($q) use ($user) {
+ $q->where('status', self::STATUS_APPROVED)
+ ->orWhere('user_id', $user->id);
+ });
+ }
+
+ return $query->where('status', self::STATUS_APPROVED);
+ }
+
+ public function thread(): BelongsTo
+ {
+ return $this->belongsTo(ForumThread::class, 'thread_id');
+ }
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+}
diff --git a/app/Models/ForumThread.php b/app/Models/ForumThread.php
new file mode 100644
index 0000000..203d963
--- /dev/null
+++ b/app/Models/ForumThread.php
@@ -0,0 +1,75 @@
+ 'integer',
+ 'user_id' => 'integer',
+ 'is_pinned' => 'boolean',
+ 'is_locked' => 'boolean',
+ 'views' => 'integer',
+ 'last_reply_at' => 'datetime',
+ ];
+
+ public const STATUS_PENDING = 'pending';
+ public const STATUS_APPROVED = 'approved';
+ public const STATUS_REJECTED = 'rejected';
+
+ /**
+ * 可见性作用域:
+ * - 审核员(forum.moderate):看全部(含待审/驳回)
+ * - 登录用户:看已审 + 自己发的(无论状态)
+ * - 游客:仅看已审
+ */
+ public function scopeVisible($query, ?User $user = null)
+ {
+ if ($user && $user->canPerm('forum.moderate')) {
+ return $query;
+ }
+
+ if ($user) {
+ return $query->where(function ($q) use ($user) {
+ $q->where('status', self::STATUS_APPROVED)
+ ->orWhere('user_id', $user->id);
+ });
+ }
+
+ return $query->where('status', self::STATUS_APPROVED);
+ }
+
+ public function board(): BelongsTo
+ {
+ return $this->belongsTo(ForumBoard::class, 'board_id');
+ }
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+
+ public function replies(): HasMany
+ {
+ return $this->hasMany(ForumReply::class, 'thread_id');
+ }
+}
diff --git a/app/Models/GlossaryTerm.php b/app/Models/GlossaryTerm.php
new file mode 100644
index 0000000..0ccd39a
--- /dev/null
+++ b/app/Models/GlossaryTerm.php
@@ -0,0 +1,21 @@
+orderBy('term');
+ }
+}
diff --git a/app/Models/HeroSlide.php b/app/Models/HeroSlide.php
new file mode 100644
index 0000000..a980767
--- /dev/null
+++ b/app/Models/HeroSlide.php
@@ -0,0 +1,41 @@
+ 'boolean',
+ 'sort_order' => 'integer',
+ ];
+
+ public function scopeLocation(Builder $query, string $location): Builder
+ {
+ return $query->where('location', $location);
+ }
+
+ public function scopeActive(Builder $query): Builder
+ {
+ return $query->where('is_active', true);
+ }
+
+ public function scopeOrdered(Builder $query): Builder
+ {
+ return $query->orderBy('sort_order')->orderBy('id');
+ }
+}
diff --git a/app/Models/NavigationMenu.php b/app/Models/NavigationMenu.php
new file mode 100644
index 0000000..9a3406e
--- /dev/null
+++ b/app/Models/NavigationMenu.php
@@ -0,0 +1,29 @@
+ 'boolean',
+ 'sort_order' => 'integer',
+ ];
+
+ /**
+ * 前台可见菜单:按排序返回。
+ */
+ public function scopeVisible(Builder $query): Builder
+ {
+ return $query->where('is_visible', true);
+ }
+
+ public function scopeOrdered(Builder $query): Builder
+ {
+ return $query->orderBy('sort_order')->orderBy('id');
+ }
+}
diff --git a/app/Models/News.php b/app/Models/News.php
new file mode 100644
index 0000000..019696c
--- /dev/null
+++ b/app/Models/News.php
@@ -0,0 +1,56 @@
+ 'datetime',
+ ];
+
+ public const CATEGORIES = [
+ 'industry' => '行业资讯',
+ 'company' => '公司动态',
+ 'event' => '展会活动',
+ ];
+
+ public function scopePublished($query)
+ {
+ return $query->where('status', 'published')
+ ->whereNotNull('published_at')
+ ->orderByDesc('published_at');
+ }
+
+ public function categoryLabel(): string
+ {
+ return self::CATEGORIES[$this->category] ?? $this->category ?? '—';
+ }
+
+ public function seoTitle(): string
+ {
+ return $this->seo_title ?: $this->title;
+ }
+
+ public function seoDescription(): string
+ {
+ return $this->seo_description ?: ($this->summary ?: $this->title);
+ }
+}
diff --git a/app/Models/Product.php b/app/Models/Product.php
new file mode 100644
index 0000000..0ee77ef
--- /dev/null
+++ b/app/Models/Product.php
@@ -0,0 +1,52 @@
+ 'integer',
+ 'sort' => 'integer',
+ ];
+
+ public function category(): BelongsTo
+ {
+ return $this->belongsTo(Category::class, 'category_id');
+ }
+
+ public function scopePublished($query)
+ {
+ return $query->where('status', 'published')->orderBy('sort')->orderBy('name');
+ }
+
+ public function seoTitle(): string
+ {
+ return $this->seo_title ?: ($this->name . ' - 深圳市云创芯电子有限公司');
+ }
+
+ public function seoDescription(): string
+ {
+ return $this->seo_description ?: ($this->summary ?: $this->name);
+ }
+}
diff --git a/app/Models/Setting.php b/app/Models/Setting.php
new file mode 100644
index 0000000..834575f
--- /dev/null
+++ b/app/Models/Setting.php
@@ -0,0 +1,33 @@
+first();
+ if (!$row) {
+ return $default;
+ }
+ return $row->value;
+ }
+
+ /**
+ * 写入或更新设置值。
+ */
+ public static function set(string $key, ?string $value, string $group = 'general'): void
+ {
+ static::updateOrCreate(
+ ['key' => $key],
+ ['value' => $value, 'group' => $group]
+ );
+ }
+}
diff --git a/app/Models/Solution.php b/app/Models/Solution.php
new file mode 100644
index 0000000..da15a06
--- /dev/null
+++ b/app/Models/Solution.php
@@ -0,0 +1,89 @@
+ '30W 以下',
+ '30-65' => '30–65W',
+ '65-100' => '65–100W',
+ 'gt100' => '100W 以上',
+ ];
+
+ public const APPLICATIONS = [
+ 'charger' => '充电器',
+ 'powerbank' => '移动电源',
+ 'car' => '车载充电',
+ 'dock' => '扩展坞 / 多口排插',
+ ];
+
+ public const CHIP_PLATFORMS = [
+ 'protocol' => '协议芯片',
+ 'soc' => '高集成 SoC',
+ 'rectifier' => '同步整流',
+ ];
+
+ public function scopePublished($query)
+ {
+ return $query->where('status', 'published')->orderBy('sort')->orderBy('name');
+ }
+
+ public function powerSegmentLabel(): string
+ {
+ return self::POWER_SEGMENTS[$this->power_segment] ?? $this->power_segment ?? '—';
+ }
+
+ public function applicationLabel(): string
+ {
+ return self::APPLICATIONS[$this->application] ?? $this->application ?? '—';
+ }
+
+ public function chipPlatformLabel(): string
+ {
+ return self::CHIP_PLATFORMS[$this->chip_platform] ?? $this->chip_platform ?? '—';
+ }
+
+ public function advantagesList(): array
+ {
+ if (! $this->advantages) {
+ return [];
+ }
+ return collect(explode("\n", $this->advantages))
+ ->map(fn ($line) => trim($line))
+ ->filter()
+ ->all();
+ }
+
+ public function seoTitle(): string
+ {
+ return $this->seo_title ?: ($this->name . ' - 深圳市云创芯电子有限公司');
+ }
+
+ public function seoDescription(): string
+ {
+ return $this->seo_description ?: ($this->summary ?: $this->name);
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
new file mode 100644
index 0000000..5d190d5
--- /dev/null
+++ b/app/Models/User.php
@@ -0,0 +1,177 @@
+ 'datetime',
+ 'password' => 'hashed',
+ 'is_admin' => 'boolean',
+ 'role' => 'string',
+ 'permissions' => 'array',
+ 'points' => 'integer',
+ ];
+ }
+
+ /**
+ * 等级阈值(积分下限),键名即等级 L1..L5
+ */
+ public const LEVELS = [
+ 1 => 0,
+ 2 => 50,
+ 3 => 200,
+ 4 => 600,
+ 5 => 1500,
+ ];
+
+ public const LEVEL_NAMES = [
+ 1 => '新芯用户',
+ 2 => '活跃用户',
+ 3 => '资深用户',
+ 4 => '核心用户',
+ 5 => '云创芯达人',
+ ];
+
+ /**
+ * 兼容旧逻辑:有后台角色即视为「管理员」(用于前台「后台」入口判断)。
+ * 新逻辑请使用 role() / canPerm()。
+ */
+ public function isAdmin(): bool
+ {
+ return $this->role !== 'user';
+ }
+
+ /**
+ * 角色等级(1-5),用于层级判断。
+ */
+ public function roleLevel(): int
+ {
+ return (int) config("permissions.roles.{$this->role}.level", 1);
+ }
+
+ public function roleLabel(): string
+ {
+ return config("permissions.roles.{$this->role}.label", '注册用户');
+ }
+
+ public function isSuperAdmin(): bool
+ {
+ return $this->role === 'super_admin';
+ }
+
+ public function isStaff(): bool
+ {
+ return $this->role === 'staff';
+ }
+
+ /**
+ * 细粒度权限判定。
+ * - 超级管理员:恒 true
+ * - 内部员工:查 users.permissions(管理员勾选分配)
+ * - 其他角色:查 config/permissions.php 中该角色的默认权限集
+ */
+ public function canPerm(string $key): bool
+ {
+ if ($this->role === 'super_admin') {
+ return true;
+ }
+
+ if ($this->role === 'staff') {
+ $perms = is_array($this->permissions) ? $this->permissions : [];
+ return in_array($key, $perms, true);
+ }
+
+ $def = config("permissions.roles.{$this->role}.permissions", []);
+ if (in_array('*', $def, true)) {
+ return true;
+ }
+
+ return in_array($key, $def, true);
+ }
+
+ public function threads()
+ {
+ return $this->hasMany(ForumThread::class);
+ }
+
+ public function replies()
+ {
+ return $this->hasMany(ForumReply::class);
+ }
+
+ /**
+ * 增加积分(用于注册、发帖、回帖等动作)。
+ */
+ public function awardPoints(int $amount): void
+ {
+ $this->increment('points', max(0, $amount));
+ }
+
+ /**
+ * 当前等级(1-5)。
+ */
+ public function level(): int
+ {
+ $points = $this->points ?? 0;
+ $level = 1;
+ foreach (self::LEVELS as $lv => $threshold) {
+ if ($points >= $threshold) {
+ $level = $lv;
+ }
+ }
+ return $level;
+ }
+
+ public function levelName(): string
+ {
+ return self::LEVEL_NAMES[$this->level()] ?? '用户';
+ }
+
+ /**
+ * 当前等级进度(用于前台进度条展示)。
+ */
+ public function levelProgress(): array
+ {
+ $level = $this->level();
+ $current = self::LEVELS[$level] ?? 0;
+ $next = self::LEVELS[$level + 1] ?? null;
+
+ if ($next === null) {
+ return ['level' => $level, 'current' => $this->points, 'next' => null, 'percent' => 100];
+ }
+
+ $span = $next - $current;
+ $done = $this->points - $current;
+ $percent = $span > 0 ? (int) min(100, round($done / $span * 100)) : 100;
+
+ return ['level' => $level, 'current' => $this->points, 'next' => $next, 'percent' => $percent];
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
new file mode 100644
index 0000000..c4ae2cf
--- /dev/null
+++ b/app/Providers/AppServiceProvider.php
@@ -0,0 +1,24 @@
+user();
+ if (! $user) {
+ return false;
+ }
+ return $permission === null ? $user->role !== 'user' : $user->canPerm($permission);
+ });
+ }
+}
diff --git a/artisan b/artisan
new file mode 100644
index 0000000..a965db8
--- /dev/null
+++ b/artisan
@@ -0,0 +1,16 @@
+#!/usr/bin/env php
+make(Illuminate\Contracts\Console\Kernel::class);
+
+exit($kernel->handle(new Symfony\Component\Console\Input\ArgvInput));
diff --git a/bootstrap/app.php b/bootstrap/app.php
new file mode 100644
index 0000000..95f16ce
--- /dev/null
+++ b/bootstrap/app.php
@@ -0,0 +1,26 @@
+withRouting(
+ web: __DIR__.'/../routes/web.php',
+ api: __DIR__.'/../routes/api.php',
+ commands: __DIR__.'/../routes/console.php',
+ health: '/up',
+ )
+ ->withMiddleware(function (Middleware $middleware) {
+ $middleware->alias([
+ 'admin' => \App\Http\Middleware\Admin::class,
+ 'role' => \App\Http\Middleware\Role::class,
+ 'can' => \App\Http\Middleware\Can::class,
+ ]);
+
+ // 统一安全响应头(纵深防御):对所有 web 路由生效
+ $middleware->appendToGroup('web', \App\Http\Middleware\SecurityHeaders::class);
+ })
+ ->withExceptions(function (Exceptions $exceptions) {
+ //
+ })->create();
diff --git a/bootstrap/cache/.gitkeep b/bootstrap/cache/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/bootstrap/cache/packages.php b/bootstrap/cache/packages.php
new file mode 100644
index 0000000..f3e4fb7
--- /dev/null
+++ b/bootstrap/cache/packages.php
@@ -0,0 +1,45 @@
+
+ array (
+ 'providers' =>
+ array (
+ 0 => 'Laravel\\Tinker\\TinkerServiceProvider',
+ ),
+ ),
+ 'mews/purifier' =>
+ array (
+ 'aliases' =>
+ array (
+ 'Purifier' => 'Mews\\Purifier\\Facades\\Purifier',
+ ),
+ 'providers' =>
+ array (
+ 0 => 'Mews\\Purifier\\PurifierServiceProvider',
+ ),
+ ),
+ 'nesbot/carbon' =>
+ array (
+ 'providers' =>
+ array (
+ 0 => 'Carbon\\Laravel\\ServiceProvider',
+ ),
+ ),
+ 'nunomaduro/termwind' =>
+ array (
+ 'providers' =>
+ array (
+ 0 => 'Termwind\\Laravel\\TermwindServiceProvider',
+ ),
+ ),
+ 'simplesoftwareio/simple-qrcode' =>
+ array (
+ 'aliases' =>
+ array (
+ 'QrCode' => 'SimpleSoftwareIO\\QrCode\\Facades\\QrCode',
+ ),
+ 'providers' =>
+ array (
+ 0 => 'SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider',
+ ),
+ ),
+);
\ No newline at end of file
diff --git a/bootstrap/cache/services.php b/bootstrap/cache/services.php
new file mode 100644
index 0000000..f854760
--- /dev/null
+++ b/bootstrap/cache/services.php
@@ -0,0 +1,255 @@
+
+ array (
+ 0 => 'Illuminate\\Auth\\AuthServiceProvider',
+ 1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
+ 2 => 'Illuminate\\Bus\\BusServiceProvider',
+ 3 => 'Illuminate\\Cache\\CacheServiceProvider',
+ 4 => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 5 => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
+ 6 => 'Illuminate\\Cookie\\CookieServiceProvider',
+ 7 => 'Illuminate\\Database\\DatabaseServiceProvider',
+ 8 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
+ 9 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
+ 10 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
+ 11 => 'Illuminate\\Hashing\\HashServiceProvider',
+ 12 => 'Illuminate\\Mail\\MailServiceProvider',
+ 13 => 'Illuminate\\Notifications\\NotificationServiceProvider',
+ 14 => 'Illuminate\\Pagination\\PaginationServiceProvider',
+ 15 => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
+ 16 => 'Illuminate\\Pipeline\\PipelineServiceProvider',
+ 17 => 'Illuminate\\Queue\\QueueServiceProvider',
+ 18 => 'Illuminate\\Redis\\RedisServiceProvider',
+ 19 => 'Illuminate\\Session\\SessionServiceProvider',
+ 20 => 'Illuminate\\Translation\\TranslationServiceProvider',
+ 21 => 'Illuminate\\Validation\\ValidationServiceProvider',
+ 22 => 'Illuminate\\View\\ViewServiceProvider',
+ 23 => 'Laravel\\Tinker\\TinkerServiceProvider',
+ 24 => 'Mews\\Purifier\\PurifierServiceProvider',
+ 25 => 'Carbon\\Laravel\\ServiceProvider',
+ 26 => 'Termwind\\Laravel\\TermwindServiceProvider',
+ 27 => 'SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider',
+ 28 => 'App\\Providers\\AppServiceProvider',
+ ),
+ 'eager' =>
+ array (
+ 0 => 'Illuminate\\Auth\\AuthServiceProvider',
+ 1 => 'Illuminate\\Cookie\\CookieServiceProvider',
+ 2 => 'Illuminate\\Database\\DatabaseServiceProvider',
+ 3 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
+ 4 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
+ 5 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
+ 6 => 'Illuminate\\Notifications\\NotificationServiceProvider',
+ 7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
+ 8 => 'Illuminate\\Session\\SessionServiceProvider',
+ 9 => 'Illuminate\\View\\ViewServiceProvider',
+ 10 => 'Mews\\Purifier\\PurifierServiceProvider',
+ 11 => 'Carbon\\Laravel\\ServiceProvider',
+ 12 => 'Termwind\\Laravel\\TermwindServiceProvider',
+ 13 => 'SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider',
+ 14 => 'App\\Providers\\AppServiceProvider',
+ ),
+ 'deferred' =>
+ array (
+ 'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
+ 'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
+ 'Illuminate\\Contracts\\Broadcasting\\Broadcaster' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
+ 'Illuminate\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
+ 'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
+ 'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
+ 'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
+ 'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
+ 'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
+ 'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
+ 'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
+ 'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider',
+ 'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider',
+ 'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConfigShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Concurrency\\Console\\InvokeSerializedClosureCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Cache\\Console\\PruneStaleTagsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\PauseCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\ResumeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ReloadCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Console\\Scheduling\\ScheduleInterruptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\StorageUnlinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ApiInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\BroadcastingInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ChannelListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ClassMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConfigMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConfigPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EnumMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\InterfaceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\JobMiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\LangPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\TraitMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Foundation\\Console\\ViewMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Migrations\\Migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
+ 'Illuminate\\Concurrency\\ConcurrencyManager' => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
+ 'hash' => 'Illuminate\\Hashing\\HashServiceProvider',
+ 'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider',
+ 'mail.manager' => 'Illuminate\\Mail\\MailServiceProvider',
+ 'mailer' => 'Illuminate\\Mail\\MailServiceProvider',
+ 'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider',
+ 'auth.password' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
+ 'auth.password.broker' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
+ 'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
+ 'pipeline' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
+ 'queue' => 'Illuminate\\Queue\\QueueServiceProvider',
+ 'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider',
+ 'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider',
+ 'queue.listener' => 'Illuminate\\Queue\\QueueServiceProvider',
+ 'queue.worker' => 'Illuminate\\Queue\\QueueServiceProvider',
+ 'redis' => 'Illuminate\\Redis\\RedisServiceProvider',
+ 'redis.connection' => 'Illuminate\\Redis\\RedisServiceProvider',
+ 'translator' => 'Illuminate\\Translation\\TranslationServiceProvider',
+ 'translation.loader' => 'Illuminate\\Translation\\TranslationServiceProvider',
+ 'validator' => 'Illuminate\\Validation\\ValidationServiceProvider',
+ 'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
+ 'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => 'Illuminate\\Validation\\ValidationServiceProvider',
+ 'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
+ ),
+ 'when' =>
+ array (
+ 'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Bus\\BusServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Cache\\CacheServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Concurrency\\ConcurrencyServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Hashing\\HashServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Mail\\MailServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Pipeline\\PipelineServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Queue\\QueueServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Redis\\RedisServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Translation\\TranslationServiceProvider' =>
+ array (
+ ),
+ 'Illuminate\\Validation\\ValidationServiceProvider' =>
+ array (
+ ),
+ 'Laravel\\Tinker\\TinkerServiceProvider' =>
+ array (
+ ),
+ ),
+);
\ No newline at end of file
diff --git a/bootstrap/providers.php b/bootstrap/providers.php
new file mode 100644
index 0000000..38b258d
--- /dev/null
+++ b/bootstrap/providers.php
@@ -0,0 +1,5 @@
+=5.0.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^4.0.0",
+ "nesbot/carbon": "^2.71.0 || ^3.0.0",
+ "phpunit/phpunit": "^10.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "KyleKatarn",
+ "email": "kylekatarnls@gmail.com"
+ }
+ ],
+ "description": "Types to use Carbon in Doctrine",
+ "keywords": [
+ "carbon",
+ "date",
+ "datetime",
+ "doctrine",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+ "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-09T16:56:22+00:00"
+ },
+ {
+ "name": "dasprid/enum",
+ "version": "1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/DASPRiD/Enum.git",
+ "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
+ "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1 <9.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
+ "squizlabs/php_codesniffer": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DASPRiD\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ben Scholzen 'DASPRiD'",
+ "email": "mail@dasprids.de",
+ "homepage": "https://dasprids.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP 7.1 enum implementation",
+ "keywords": [
+ "enum",
+ "map"
+ ],
+ "support": {
+ "issues": "https://github.com/DASPRiD/Enum/issues",
+ "source": "https://github.com/DASPRiD/Enum/tree/1.0.7"
+ },
+ "time": "2025-09-16T12:23:56+00:00"
+ },
+ {
+ "name": "dflydev/dot-access-data",
+ "version": "v3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com"
+ },
+ {
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com"
+ }
+ ],
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+ "keywords": [
+ "access",
+ "data",
+ "dot",
+ "notation"
+ ],
+ "support": {
+ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+ "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
+ },
+ "time": "2024-07-08T12:26:09+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b",
+ "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12.0 || ^13.0",
+ "phpstan/phpstan": "^1.12 || ^2.0",
+ "phpstan/phpstan-phpunit": "^1.4 || ^2.0",
+ "phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
+ "phpunit/phpunit": "^8.5 || ^12.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T19:31:58+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^10.5",
+ "psalm/plugin-phpunit": "^0.18.3",
+ "vimeo/psalm": "^5.21"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-05T11:56:58+00:00"
+ },
+ {
+ "name": "dragonmantank/cron-expression",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dragonmantank/cron-expression.git",
+ "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013",
+ "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.2|^8.3|^8.4|^8.5"
+ },
+ "replace": {
+ "mtdowling/cron-expression": "^1.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.32|^2.1.31",
+ "phpunit/phpunit": "^8.5.48|^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Cron\\": "src/Cron/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Tankersley",
+ "email": "chris@ctankersley.com",
+ "homepage": "https://github.com/dragonmantank"
+ }
+ ],
+ "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+ "keywords": [
+ "cron",
+ "schedule"
+ ],
+ "support": {
+ "issues": "https://github.com/dragonmantank/cron-expression/issues",
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/dragonmantank",
+ "type": "github"
+ }
+ ],
+ "time": "2025-10-31T18:51:33+00:00"
+ },
+ {
+ "name": "egulias/email-validator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+ "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2.0 || ^3.0",
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.26"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.2",
+ "vimeo/psalm": "^5.12"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Egulias\\EmailValidator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eduardo Gulias Davis"
+ }
+ ],
+ "description": "A library for validating emails against several RFCs",
+ "homepage": "https://github.com/egulias/EmailValidator",
+ "keywords": [
+ "email",
+ "emailvalidation",
+ "emailvalidator",
+ "validation",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-06T22:45:56+00:00"
+ },
+ {
+ "name": "ezyang/htmlpurifier",
+ "version": "v4.19.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf",
+ "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+ },
+ "require-dev": {
+ "cerdic/css-tidy": "^1.7 || ^2.0",
+ "simpletest/simpletest": "dev-master"
+ },
+ "suggest": {
+ "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
+ "ext-bcmath": "Used for unit conversion and imagecrash protection",
+ "ext-iconv": "Converts text to and from non-UTF-8 encodings",
+ "ext-tidy": "Used for pretty-printing HTML"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/HTMLPurifier.composer.php"
+ ],
+ "psr-0": {
+ "HTMLPurifier": "library/"
+ },
+ "exclude-from-classmap": [
+ "/library/HTMLPurifier/Language/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Edward Z. Yang",
+ "email": "admin@htmlpurifier.org",
+ "homepage": "http://ezyang.com"
+ }
+ ],
+ "description": "Standards compliant HTML filter written in PHP",
+ "homepage": "http://htmlpurifier.org/",
+ "keywords": [
+ "html"
+ ],
+ "support": {
+ "issues": "https://github.com/ezyang/htmlpurifier/issues",
+ "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0"
+ },
+ "time": "2025-10-17T16:34:55+00:00"
+ },
+ {
+ "name": "fruitcake/php-cors",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fruitcake/php-cors.git",
+ "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379",
+ "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1",
+ "symfony/http-foundation": "^5.4|^6.4|^7.3|^8"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2",
+ "phpunit/phpunit": "^9",
+ "squizlabs/php_codesniffer": "^4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Fruitcake\\Cors\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fruitcake",
+ "homepage": "https://fruitcake.nl"
+ },
+ {
+ "name": "Barryvdh",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "Cross-origin resource sharing library for the Symfony HttpFoundation",
+ "homepage": "https://github.com/fruitcake/php-cors",
+ "keywords": [
+ "cors",
+ "laravel",
+ "symfony"
+ ],
+ "support": {
+ "issues": "https://github.com/fruitcake/php-cors/issues",
+ "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://fruitcake.nl",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/barryvdh",
+ "type": "github"
+ }
+ ],
+ "time": "2025-12-03T09:33:47+00:00"
+ },
+ {
+ "name": "graham-campbell/result-type",
+ "version": "v1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
+ "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-27T19:43:20+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.15.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "744101956d78b7c1384d0cbf379db13e859167bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/744101956d78b7c1384d0cbf379db13e859167bf",
+ "reference": "744101956d78b7c1384d0cbf379db13e859167bf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^2.5.1",
+ "guzzlehttp/psr7": "^2.13",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0",
+ "symfony/polyfill-php80": "^1.25"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-curl": "*",
+ "guzzle/client-integration-tests": "3.0.3",
+ "guzzlehttp/test-server": "^0.7",
+ "php-http/message-factory": "^1.1",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.15.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-26T23:23:20+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "2.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29",
+ "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/2.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-08T15:48:39+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4",
+ "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0",
+ "symfony/deprecation-contracts": "^2.5 || ^3.0",
+ "symfony/polyfill-php80": "^1.25"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "1.1.0",
+ "jshttp/mime-db": "1.54.0.1",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.13.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-16T22:23:49+00:00"
+ },
+ {
+ "name": "guzzlehttp/uri-template",
+ "version": "v1.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/uri-template.git",
+ "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/uri-template/zipball/f6c24c21f42b990e9a58912b332d0874df6ba839",
+ "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "symfony/polyfill-php80": "^1.25"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34",
+ "uri-template/tests": "1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\UriTemplate\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ }
+ ],
+ "description": "A polyfill class for uri_template of PHP",
+ "keywords": [
+ "guzzlehttp",
+ "uri-template"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/uri-template/issues",
+ "source": "https://github.com/guzzle/uri-template/tree/v1.0.10"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-17T13:53:03+00:00"
+ },
+ {
+ "name": "laravel/framework",
+ "version": "v12.64.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/framework.git",
+ "reference": "727a8ea2949c23ca8b5316b86a00984b6017b7a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/727a8ea2949c23ca8b5316b86a00984b6017b7a0",
+ "reference": "727a8ea2949c23ca8b5316b86a00984b6017b7a0",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": "^0.11|^0.12|^0.13|^0.14",
+ "composer-runtime-api": "^2.2",
+ "doctrine/inflector": "^2.0.5",
+ "dragonmantank/cron-expression": "^3.4",
+ "egulias/email-validator": "^3.2.1|^4.0",
+ "ext-ctype": "*",
+ "ext-filter": "*",
+ "ext-hash": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-session": "*",
+ "ext-tokenizer": "*",
+ "fruitcake/php-cors": "^1.3",
+ "guzzlehttp/guzzle": "^7.8.2",
+ "guzzlehttp/uri-template": "^1.0",
+ "laravel/prompts": "^0.3.0",
+ "laravel/serializable-closure": "^1.3|^2.0",
+ "league/commonmark": "^2.8.1",
+ "league/flysystem": "^3.25.1",
+ "league/flysystem-local": "^3.25.1",
+ "league/uri": "^7.5.1",
+ "monolog/monolog": "^3.0",
+ "nesbot/carbon": "^3.8.4",
+ "nunomaduro/termwind": "^2.0",
+ "php": "^8.2",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/log": "^1.0|^2.0|^3.0",
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "ramsey/uuid": "^4.7",
+ "symfony/console": "^7.2.0",
+ "symfony/error-handler": "^7.2.0",
+ "symfony/finder": "^7.2.0",
+ "symfony/http-foundation": "^7.2.0",
+ "symfony/http-kernel": "^7.2.0",
+ "symfony/mailer": "^7.2.0",
+ "symfony/mime": "^7.2.0",
+ "symfony/polyfill-php83": "^1.33",
+ "symfony/polyfill-php84": "^1.34",
+ "symfony/polyfill-php85": "^1.34",
+ "symfony/process": "^7.2.0",
+ "symfony/routing": "^7.2.0",
+ "symfony/uid": "^7.2.0",
+ "symfony/var-dumper": "^7.2.0",
+ "tijsverkoyen/css-to-inline-styles": "^2.2.5",
+ "vlucas/phpdotenv": "^5.6.1",
+ "voku/portable-ascii": "^2.0.2"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "psr/log-implementation": "1.0|2.0|3.0",
+ "psr/simple-cache-implementation": "1.0|2.0|3.0"
+ },
+ "replace": {
+ "illuminate/auth": "self.version",
+ "illuminate/broadcasting": "self.version",
+ "illuminate/bus": "self.version",
+ "illuminate/cache": "self.version",
+ "illuminate/collections": "self.version",
+ "illuminate/concurrency": "self.version",
+ "illuminate/conditionable": "self.version",
+ "illuminate/config": "self.version",
+ "illuminate/console": "self.version",
+ "illuminate/container": "self.version",
+ "illuminate/contracts": "self.version",
+ "illuminate/cookie": "self.version",
+ "illuminate/database": "self.version",
+ "illuminate/encryption": "self.version",
+ "illuminate/events": "self.version",
+ "illuminate/filesystem": "self.version",
+ "illuminate/hashing": "self.version",
+ "illuminate/http": "self.version",
+ "illuminate/json-schema": "self.version",
+ "illuminate/log": "self.version",
+ "illuminate/macroable": "self.version",
+ "illuminate/mail": "self.version",
+ "illuminate/notifications": "self.version",
+ "illuminate/pagination": "self.version",
+ "illuminate/pipeline": "self.version",
+ "illuminate/process": "self.version",
+ "illuminate/queue": "self.version",
+ "illuminate/redis": "self.version",
+ "illuminate/reflection": "self.version",
+ "illuminate/routing": "self.version",
+ "illuminate/session": "self.version",
+ "illuminate/support": "self.version",
+ "illuminate/testing": "self.version",
+ "illuminate/translation": "self.version",
+ "illuminate/validation": "self.version",
+ "illuminate/view": "self.version",
+ "spatie/once": "*"
+ },
+ "require-dev": {
+ "ably/ably-php": "^1.0",
+ "aws/aws-sdk-php": "^3.322.9",
+ "ext-gmp": "*",
+ "fakerphp/faker": "^1.24",
+ "guzzlehttp/promises": "^2.0.3",
+ "guzzlehttp/psr7": "^2.4",
+ "laravel/pint": "^1.18",
+ "league/flysystem-aws-s3-v3": "^3.25.1",
+ "league/flysystem-ftp": "^3.25.1",
+ "league/flysystem-path-prefixing": "^3.25.1",
+ "league/flysystem-read-only": "^3.25.1",
+ "league/flysystem-sftp-v3": "^3.25.1",
+ "mockery/mockery": "^1.6.10",
+ "opis/json-schema": "^2.4.1",
+ "orchestra/testbench-core": "^10.9.0",
+ "pda/pheanstalk": "^5.0.6|^7.0.0",
+ "php-http/discovery": "^1.15",
+ "phpstan/phpstan": "^2.1.41",
+ "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
+ "predis/predis": "^2.3|^3.0",
+ "resend/resend-php": "^0.10.0|^1.0",
+ "symfony/cache": "^7.2.0",
+ "symfony/http-client": "^7.2.0",
+ "symfony/psr-http-message-bridge": "^7.2.0",
+ "symfony/translation": "^7.2.0"
+ },
+ "suggest": {
+ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).",
+ "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).",
+ "ext-apcu": "Required to use the APC cache driver.",
+ "ext-fileinfo": "Required to use the Filesystem class.",
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.",
+ "ext-pdo": "Required to use all database features.",
+ "ext-posix": "Required to use all features of the queue worker.",
+ "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).",
+ "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).",
+ "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+ "laravel/tinker": "Required to use the tinker console command (^2.0).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
+ "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).",
+ "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).",
+ "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
+ "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
+ "mockery/mockery": "Required to use mocking (^1.6).",
+ "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
+ "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
+ "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).",
+ "predis/predis": "Required to use the predis connector (^2.3|^3.0).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
+ "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).",
+ "symfony/cache": "Required to PSR-6 cache bridge (^7.2).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).",
+ "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).",
+ "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).",
+ "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).",
+ "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Illuminate/Collections/functions.php",
+ "src/Illuminate/Collections/helpers.php",
+ "src/Illuminate/Events/functions.php",
+ "src/Illuminate/Filesystem/functions.php",
+ "src/Illuminate/Foundation/helpers.php",
+ "src/Illuminate/Log/functions.php",
+ "src/Illuminate/Reflection/helpers.php",
+ "src/Illuminate/Support/functions.php",
+ "src/Illuminate/Support/helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\": "src/Illuminate/",
+ "Illuminate\\Support\\": [
+ "src/Illuminate/Macroable/",
+ "src/Illuminate/Collections/",
+ "src/Illuminate/Conditionable/",
+ "src/Illuminate/Reflection/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Laravel Framework.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "framework",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2026-07-14T14:25:37+00:00"
+ },
+ {
+ "name": "laravel/prompts",
+ "version": "v0.3.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/prompts.git",
+ "reference": "7753c65c281c2550c7c183f14e18062073b7d821"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/7753c65c281c2550c7c183f14e18062073b7d821",
+ "reference": "7753c65c281c2550c7c183f14e18062073b7d821",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2.2",
+ "ext-mbstring": "*",
+ "php": "^8.1",
+ "symfony/console": "^6.2|^7.0|^8.0"
+ },
+ "conflict": {
+ "illuminate/console": ">=10.17.0 <10.25.0",
+ "laravel/framework": ">=10.17.0 <10.25.0"
+ },
+ "require-dev": {
+ "illuminate/collections": "^10.0|^11.0|^12.0|^13.0",
+ "mockery/mockery": "^1.5",
+ "pestphp/pest": "^2.3|^3.4|^4.0",
+ "phpstan/phpstan": "^1.12.28",
+ "phpstan/phpstan-mockery": "^1.1.3"
+ },
+ "suggest": {
+ "ext-pcntl": "Required for the spinner to be animated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.3.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Laravel\\Prompts\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Add beautiful and user-friendly forms to your command-line applications.",
+ "support": {
+ "issues": "https://github.com/laravel/prompts/issues",
+ "source": "https://github.com/laravel/prompts/tree/v0.3.21"
+ },
+ "time": "2026-06-26T00:11:25+00:00"
+ },
+ {
+ "name": "laravel/serializable-closure",
+ "version": "v2.0.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/serializable-closure.git",
+ "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661",
+ "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "illuminate/support": "^10.0|^11.0|^12.0|^13.0",
+ "nesbot/carbon": "^2.67|^3.0",
+ "pestphp/pest": "^2.36|^3.0|^4.0",
+ "phpstan/phpstan": "^2.0",
+ "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\SerializableClosure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ },
+ {
+ "name": "Nuno Maduro",
+ "email": "nuno@laravel.com"
+ }
+ ],
+ "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
+ "keywords": [
+ "closure",
+ "laravel",
+ "serializable"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/serializable-closure/issues",
+ "source": "https://github.com/laravel/serializable-closure"
+ },
+ "time": "2026-07-21T16:49:22+00:00"
+ },
+ {
+ "name": "laravel/tinker",
+ "version": "v2.11.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/tinker.git",
+ "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741",
+ "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
+ "php": "^7.2.5|^8.0",
+ "psy/psysh": "^0.11.1|^0.12.0",
+ "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.3|^1.4.2",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0"
+ },
+ "suggest": {
+ "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Tinker\\TinkerServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Tinker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Powerful REPL for the Laravel framework.",
+ "keywords": [
+ "REPL",
+ "Tinker",
+ "laravel",
+ "psysh"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/tinker/issues",
+ "source": "https://github.com/laravel/tinker/tree/v2.11.1"
+ },
+ "time": "2026-02-06T14:12:35+00:00"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "2.8.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/1902f60f984235023acbe03db6ad614a37b3c3e7",
+ "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "php": "^7.4 || ^8.0",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^2.0.0",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0 || ^12.0.0 || ^13.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
+ },
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-12T15:29:16+00:00"
+ },
+ {
+ "name": "league/config",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/config.git",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "shasum": ""
+ },
+ "require": {
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "homepage": "https://config.thephpleague.com",
+ "keywords": [
+ "array",
+ "config",
+ "configuration",
+ "dot",
+ "dot-access",
+ "nested",
+ "schema"
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-11T20:36:23+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "3.35.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "b277b5dc3d56650b68904117124e79c851e12376"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376",
+ "reference": "b277b5dc3d56650b68904117124e79c851e12376",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem-local": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "conflict": {
+ "async-aws/core": "<1.19.0",
+ "async-aws/s3": "<1.14.0",
+ "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+ "guzzlehttp/guzzle": "<7.0",
+ "guzzlehttp/ringphp": "<1.1.1",
+ "phpseclib/phpseclib": "3.0.15",
+ "symfony/http-client": "<5.2"
+ },
+ "require-dev": {
+ "async-aws/s3": "^1.5 || ^2.0",
+ "async-aws/simple-s3": "^1.1 || ^2.0",
+ "aws/aws-sdk-php": "^3.295.10",
+ "composer/semver": "^3.0",
+ "ext-fileinfo": "*",
+ "ext-ftp": "*",
+ "ext-mongodb": "^1.3|^2",
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.5",
+ "google/cloud-storage": "^1.23",
+ "guzzlehttp/psr7": "^2.6",
+ "microsoft/azure-storage-blob": "^1.1",
+ "mongodb/mongodb": "^1.2|^2",
+ "phpseclib/phpseclib": "^3.0.36",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^9.5.11|^10.0",
+ "sabre/dav": "^4.6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "File storage abstraction for PHP",
+ "keywords": [
+ "WebDAV",
+ "aws",
+ "cloud",
+ "file",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/3.35.2"
+ },
+ "time": "2026-07-06T14:42:07+00:00"
+ },
+ {
+ "name": "league/flysystem-local",
+ "version": "3.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-local.git",
+ "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+ "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/flysystem": "^3.0.0",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Local\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Local filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "local"
+ ],
+ "support": {
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0"
+ },
+ "time": "2026-01-23T15:30:45+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76",
+ "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-09T11:49:27+00:00"
+ },
+ {
+ "name": "league/uri",
+ "version": "7.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri.git",
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4",
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4",
+ "shasum": ""
+ },
+ "require": {
+ "league/uri-interfaces": "^7.8.1",
+ "php": "^8.1",
+ "psr/http-factory": "^1"
+ },
+ "conflict": {
+ "league/uri-schemes": "^1.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-dom": "to convert the URI into an HTML anchor tag",
+ "ext-fileinfo": "to create Data URI from file contennts",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "ext-uri": "to use the PHP native URI class",
+ "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
+ "league/uri-components": "to provide additional tools to manipulate URI objects components",
+ "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
+ "php-64bit": "to improve IPV4 host parsing",
+ "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "URI manipulation library",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "URN",
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "middleware",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc2141",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "rfc8141",
+ "uri",
+ "uri-template",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri/tree/7.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2026-03-15T20:22:25+00:00"
+ },
+ {
+ "name": "league/uri-interfaces",
+ "version": "7.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri-interfaces.git",
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928",
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^8.1",
+ "psr/http-message": "^1.1 || ^2.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "php-64bit": "to improve IPV4 host parsing",
+ "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2026-03-08T20:05:35+00:00"
+ },
+ {
+ "name": "mews/purifier",
+ "version": "3.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mewebstudio/Purifier.git",
+ "reference": "b2705cc6c832ce7229373418e191d71b6c037841"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mewebstudio/Purifier/zipball/b2705cc6c832ce7229373418e191d71b6c037841",
+ "reference": "b2705cc6c832ce7229373418e191d71b6c037841",
+ "shasum": ""
+ },
+ "require": {
+ "ezyang/htmlpurifier": "^4.16.0",
+ "illuminate/config": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+ "illuminate/filesystem": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+ "illuminate/support": "^5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
+ "php": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "graham-campbell/testbench": "^3.2|^5.5.1|^6.1",
+ "mockery/mockery": "^1.3.3",
+ "phpunit/phpunit": "^8.0|^9.0|^10.0"
+ },
+ "suggest": {
+ "laravel/framework": "To test the Laravel bindings",
+ "laravel/lumen-framework": "To test the Lumen bindings"
+ },
+ "type": "package",
+ "extra": {
+ "laravel": {
+ "aliases": {
+ "Purifier": "Mews\\Purifier\\Facades\\Purifier"
+ },
+ "providers": [
+ "Mews\\Purifier\\PurifierServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Mews\\Purifier\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Muharrem ERİN",
+ "email": "me@mewebstudio.com",
+ "homepage": "https://github.com/mewebstudio",
+ "role": "Developer"
+ }
+ ],
+ "description": "Laravel 5/6/7/8/9/10 HtmlPurifier Package",
+ "homepage": "https://github.com/mewebstudio/purifier",
+ "keywords": [
+ "Laravel Purifier",
+ "Laravel Security",
+ "Purifier",
+ "htmlpurifier",
+ "laravel HtmlPurifier",
+ "security",
+ "xss"
+ ],
+ "support": {
+ "issues": "https://github.com/mewebstudio/Purifier/issues",
+ "source": "https://github.com/mewebstudio/Purifier/tree/3.4.4"
+ },
+ "time": "2026-04-15T16:41:08+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "3.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0",
+ "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/log": "^2.0 || ^3.0"
+ },
+ "provide": {
+ "psr/log-implementation": "3.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7 || ^8",
+ "ext-json": "*",
+ "graylog2/gelf-php": "^1.4.2 || ^2.0",
+ "guzzlehttp/guzzle": "^7.4.5",
+ "guzzlehttp/psr7": "^2.2",
+ "mongodb/mongodb": "^1.8 || ^2.0",
+ "php-amqplib/php-amqplib": "~2.4 || ^3",
+ "php-console/php-console": "^3.1.8",
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpunit/phpunit": "^10.5.17 || ^11.0.7",
+ "predis/predis": "^1.1 || ^2",
+ "rollbar/rollbar": "^4.0",
+ "ruflin/elastica": "^7 || ^8",
+ "symfony/mailer": "^5.4 || ^6",
+ "symfony/mime": "^5.4 || ^6"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/3.10.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-02T08:56:05+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "3.13.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/CarbonPHP/carbon.git",
+ "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2",
+ "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2",
+ "shasum": ""
+ },
+ "require": {
+ "carbonphp/carbon-doctrine-types": "<100.0",
+ "ext-json": "*",
+ "php": "^8.1",
+ "psr/clock": "^1.0",
+ "symfony/clock": "^6.3.12 || ^7.0 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6.3 || ^4.0",
+ "doctrine/orm": "^2.15.2 || ^3.0",
+ "friendsofphp/php-cs-fixer": "^v3.87.1",
+ "kylekatarnls/multi-tester": "^2.5.3",
+ "phpmd/phpmd": "^2.15.0",
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^2.1.22",
+ "phpunit/phpunit": "^10.5.53",
+ "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev",
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbonphp.github.io/carbon/",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html",
+ "issues": "https://github.com/CarbonPHP/carbon/issues",
+ "source": "https://github.com/CarbonPHP/carbon"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/kylekatarnls",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/Carbon#sponsor",
+ "type": "opencollective"
+ },
+ {
+ "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-09T18:23:49+00:00"
+ },
+ {
+ "name": "nette/schema",
+ "version": "v1.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/schema.git",
+ "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002",
+ "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^4.0",
+ "php": "8.1 - 8.5"
+ },
+ "require-dev": {
+ "nette/phpstan-rules": "^1.0",
+ "nette/tester": "^2.6",
+ "phpstan/extension-installer": "^1.4@stable",
+ "phpstan/phpstan": "^2.1.39@stable",
+ "tracy/tracy": "^2.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "📐 Nette Schema: validating data structures against a given Schema.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "config",
+ "nette"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/schema/issues",
+ "source": "https://github.com/nette/schema/tree/v1.3.5"
+ },
+ "time": "2026-02-23T03:47:12+00:00"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/b043439dbdf954e6c28b5ea7e34b0100f83165e0",
+ "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.2 - 8.5"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "^1.2",
+ "nette/phpstan-rules": "^1.0",
+ "nette/tester": "^2.5",
+ "phpstan/extension-installer": "^1.4@stable",
+ "phpstan/phpstan": "^2.1@stable",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::chr(), ord() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.1.5"
+ },
+ "time": "2026-07-17T23:02:45+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0"
+ },
+ "time": "2026-07-04T14:30:18+00:00"
+ },
+ {
+ "name": "nunomaduro/termwind",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nunomaduro/termwind.git",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8",
+ "reference": "712a31b768f5daea284c2169a7d227031001b9a8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^8.2",
+ "symfony/console": "^7.4.4 || ^8.0.4"
+ },
+ "require-dev": {
+ "illuminate/console": "^11.47.0",
+ "laravel/pint": "^1.27.1",
+ "mockery/mockery": "^1.6.12",
+ "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2",
+ "phpstan/phpstan": "^1.12.32",
+ "phpstan/phpstan-strict-rules": "^1.6.2",
+ "symfony/var-dumper": "^7.3.5 || ^8.0.4",
+ "thecodingmachine/phpstan-strict-rules": "^1.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Termwind\\Laravel\\TermwindServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Functions.php"
+ ],
+ "psr-4": {
+ "Termwind\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "It's like Tailwind CSS, but for the console.",
+ "keywords": [
+ "cli",
+ "console",
+ "css",
+ "package",
+ "php",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/nunomaduro/termwind/issues",
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/paypalme/enunomaduro",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/xiCO2k",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T23:10:27+00:00"
+ },
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.9.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
+ "reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-27T19:41:33+00:00"
+ },
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client"
+ },
+ "time": "2023-09-23T14:17:50+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "psy/psysh",
+ "version": "v0.12.24",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bobthecow/psysh.git",
+ "reference": "ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1",
+ "reference": "ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "nikic/php-parser": "^5.0 || ^4.0",
+ "php": "^8.0 || ^7.4",
+ "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
+ "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
+ },
+ "conflict": {
+ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2",
+ "composer/class-map-generator": "^1.6"
+ },
+ "suggest": {
+ "composer/class-map-generator": "Improved tab completion performance with better class discovery.",
+ "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+ "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well."
+ },
+ "bin": [
+ "bin/psysh"
+ ],
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": false,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-main": "0.12.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Psy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info"
+ }
+ ],
+ "description": "An interactive shell for modern PHP.",
+ "homepage": "https://psysh.org",
+ "keywords": [
+ "REPL",
+ "console",
+ "interactive",
+ "shell"
+ ],
+ "support": {
+ "issues": "https://github.com/bobthecow/psysh/issues",
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.24"
+ },
+ "time": "2026-06-29T15:41:09+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "ramsey/collection",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/collection.git",
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+ "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "captainhook/plugin-composer": "^5.3",
+ "ergebnis/composer-normalize": "^2.45",
+ "fakerphp/faker": "^1.24",
+ "hamcrest/hamcrest-php": "^2.0",
+ "jangregor/phpstan-prophecy": "^2.1",
+ "mockery/mockery": "^1.6",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpspec/prophecy-phpunit": "^2.3",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^10.5",
+ "ramsey/coding-standard": "^2.3",
+ "ramsey/conventional-commits": "^1.6",
+ "roave/security-advisories": "dev-latest"
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ },
+ "ramsey/conventional-commits": {
+ "configFile": "conventional-commits.json"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ramsey\\Collection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
+ }
+ ],
+ "description": "A PHP library for representing and manipulating collections.",
+ "keywords": [
+ "array",
+ "collection",
+ "hash",
+ "map",
+ "queue",
+ "set"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/collection/issues",
+ "source": "https://github.com/ramsey/collection/tree/2.1.1"
+ },
+ "time": "2025-03-22T05:38:12+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "4.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8",
+ "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8",
+ "shasum": ""
+ },
+ "require": {
+ "brick/math": ">=0.8.16 <=0.18",
+ "php": "^8.0",
+ "ramsey/collection": "^1.2 || ^2.0"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "captainhook/captainhook": "^5.25",
+ "captainhook/plugin-composer": "^5.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "ergebnis/composer-normalize": "^2.47",
+ "mockery/mockery": "^1.6",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock": "^2.6",
+ "php-mock/php-mock-mockery": "^1.5",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpbench/phpbench": "^1.2.14",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "slevomat/coding-standard": "^8.18",
+ "squizlabs/php_codesniffer": "^3.13"
+ },
+ "suggest": {
+ "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+ "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+ "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
+ "type": "library",
+ "extra": {
+ "captainhook": {
+ "force-install": true
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+ "keywords": [
+ "guid",
+ "identifier",
+ "uuid"
+ ],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "source": "https://github.com/ramsey/uuid/tree/4.9.3"
+ },
+ "time": "2026-06-18T03:57:49+00:00"
+ },
+ {
+ "name": "simplesoftwareio/simple-qrcode",
+ "version": "4.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/SimpleSoftwareIO/simple-qrcode.git",
+ "reference": "916db7948ca6772d54bb617259c768c9cdc8d537"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/SimpleSoftwareIO/simple-qrcode/zipball/916db7948ca6772d54bb617259c768c9cdc8d537",
+ "reference": "916db7948ca6772d54bb617259c768c9cdc8d537",
+ "shasum": ""
+ },
+ "require": {
+ "bacon/bacon-qr-code": "^2.0",
+ "ext-gd": "*",
+ "php": ">=7.2|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1",
+ "phpunit/phpunit": "~9"
+ },
+ "suggest": {
+ "ext-imagick": "Allows the generation of PNG QrCodes.",
+ "illuminate/support": "Allows for use within Laravel."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "aliases": {
+ "QrCode": "SimpleSoftwareIO\\QrCode\\Facades\\QrCode"
+ },
+ "providers": [
+ "SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SimpleSoftwareIO\\QrCode\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Simple Software LLC",
+ "email": "support@simplesoftware.io"
+ }
+ ],
+ "description": "Simple QrCode is a QR code generator made for Laravel.",
+ "homepage": "https://www.simplesoftware.io/#/docs/simple-qrcode",
+ "keywords": [
+ "Simple",
+ "generator",
+ "laravel",
+ "qrcode",
+ "wrapper"
+ ],
+ "support": {
+ "issues": "https://github.com/SimpleSoftwareIO/simple-qrcode/issues",
+ "source": "https://github.com/SimpleSoftwareIO/simple-qrcode/tree/4.2.0"
+ },
+ "time": "2021-02-08T20:43:55+00:00"
+ },
+ {
+ "name": "symfony/clock",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clock",
+ "psr20",
+ "time"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/clock/tree/v7.4.8"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-03-24T13:12:05+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930",
+ "reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/lock": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-27T13:51:00+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v7.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "b75663ed96cf4756e28e3105476f220f92886cc4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/b75663ed96cf4756e28e3105476f220f92886cc4",
+ "reference": "b75663ed96cf4756e28e3105476f220f92886cc4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v7.4.9"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-18T13:18:21+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-05T06:23:12+00:00"
+ },
+ {
+ "name": "symfony/error-handler",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/d49f6a19f326db41ae7103bdc38e3eb35a791261",
+ "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/polyfill-php85": "^1.32",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
+ },
+ "conflict": {
+ "symfony/deprecation-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4|^7.0|^8.0",
+ "symfony/webpack-encore-bundle": "^1.0|^2.0"
+ },
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-21T15:13:06+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/336e7f3b9e95aba04f93ea9143920c2186abfbb9",
+ "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/framework-bundle": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-21T15:13:06+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e",
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-05T06:23:12+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v7.4.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/13b38720174286f55d1761152b575a8d1436fc25",
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v7.4.14"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-27T08:31:18+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1f898ee8188adda9417fb52cf8425a8342c254e7",
+ "reference": "1f898ee8188adda9417fb52cf8425a8342c254e7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "^1.1"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.6",
+ "symfony/cache": "<6.4.12|>=7.0,<7.1.5"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^3.6|^4",
+ "predis/predis": "^1.1|^2.0",
+ "symfony/cache": "^6.4.12|^7.1.5|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^6.4|^7.0|^8.0",
+ "symfony/rate-limiter": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-29T07:12:33+00:00"
+ },
+ {
+ "name": "symfony/http-kernel",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "403275d94f94d5626c3288c599b3b48093ba24f7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/403275d94f94d5626c3288c599b3b48093ba24f7",
+ "reference": "403275d94f94d5626c3288c599b3b48093ba24f7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/event-dispatcher": "^7.3|^8.0",
+ "symfony/http-foundation": "^7.4|^8.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/cache": "<6.4",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/doctrine-bridge": "<6.4",
+ "symfony/flex": "<2.10",
+ "symfony/form": "<6.4",
+ "symfony/http-client": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/mailer": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/translation": "<6.4",
+ "symfony/translation-contracts": "<2.5",
+ "symfony/twig-bridge": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/var-dumper": "<6.4",
+ "twig/twig": "<3.12"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^6.4|^7.0|^8.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/css-selector": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0",
+ "symfony/dom-crawler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
+ "symfony/http-client-contracts": "^2.5|^3",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^7.1|^8.0",
+ "symfony/routing": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^7.1|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0",
+ "symfony/translation": "^6.4|^7.0|^8.0",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "symfony/validator": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12|^4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-29T11:40:42+00:00"
+ },
+ {
+ "name": "symfony/mailer",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "68c1f27c97edd0222eb8d440a6c8c4da5354ab46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/68c1f27c97edd0222eb8d440a6c8c4da5354ab46",
+ "reference": "68c1f27c97edd0222eb8d440a6c8c4da5354ab46",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^7.2|^8.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/twig-bridge": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps sending emails",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailer/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-28T07:33:02+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
+ "reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<5.2|>=7",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^6.4|^7.0|^8.0",
+ "symfony/property-info": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4.3|^7.0.3|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-29T07:59:49+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
+ "reference": "141046a8f9477948ff284fa65be2095baafb94f2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T16:19:22+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.41.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-28T08:25:59+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.38.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "dc21118016c039a66235cf93d96b435ffb282412"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
+ "reference": "dc21118016c039a66235cf93d96b435ffb282412",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2",
+ "symfony/polyfill-intl-normalizer": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-25T15:22:23+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.38.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+ "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-25T13:48:31+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.38.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-27T06:59:30+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T16:19:22+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php83",
+ "version": "v1.41.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/5ea99087fb99c273a9b9236ed4c31e78b16103c6",
+ "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php83\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.41.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-01T12:47:55+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.38.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa",
+ "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-26T12:51:13+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php85",
+ "version": "v1.41.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php85.git",
+ "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a",
+ "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php85\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-01T12:47:55+00:00"
+ },
+ {
+ "name": "symfony/polyfill-uuid",
+ "version": "v1.37.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-uuid.git",
+ "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
+ "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-uuid": "*"
+ },
+ "suggest": {
+ "ext-uuid": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Uuid\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for uuid functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-10T16:19:22+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v7.4.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "f5804be144caceb570f6747519999636b664f24c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c",
+ "reference": "f5804be144caceb570f6747519999636b664f24c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v7.4.13"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-23T16:05:06+00:00"
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/80c0a93d3f8e7499f716204a1fb38ead942a7a2b",
+ "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/config": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Maps an HTTP request to a set of configuration variables",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-21T15:13:06+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-16T09:55:08+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b",
+ "reference": "e394af32256bf9e7bf80849d95e589167c10097b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.33",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-28T07:33:02+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v7.4.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^2.5.3|^3.3"
+ },
+ "conflict": {
+ "nikic/php-parser": "<5.0",
+ "symfony/config": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/service-contracts": "<2.5",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3|3.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^5.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/finder": "^6.4|^7.0|^8.0",
+ "symfony/http-client-contracts": "^2.5|^3.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/routing": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v7.4.14"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-06T09:33:19+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621",
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-06-05T06:23:12+00:00"
+ },
+ {
+ "name": "symfony/uid",
+ "version": "v7.4.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/uid.git",
+ "reference": "2676b524340abcfe4d6151ec698463cebafee439"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439",
+ "reference": "2676b524340abcfe4d6151ec698463cebafee439",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-uuid": "^1.15"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Uid\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to generate and represent UIDs",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "UID",
+ "ulid",
+ "uuid"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/uid/tree/v7.4.9"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-30T15:19:22+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v7.4.15",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
+ "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/uid": "^6.4|^7.0|^8.0",
+ "twig/twig": "^3.12|^4.0"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v7.4.15"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-21T15:13:06+00:00"
+ },
+ {
+ "name": "tijsverkoyen/css-to-inline-styles",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
+ "reference": "f0292ccf0ec75843d65027214426b6b163b48b41"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41",
+ "reference": "f0292ccf0ec75843d65027214426b6b163b48b41",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": "^7.4 || ^8.0",
+ "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^8.5.21 || ^9.5.10"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "TijsVerkoyen\\CssToInlineStyles\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Tijs Verkoyen",
+ "email": "css_to_inline_styles@verkoyen.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
+ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+ "support": {
+ "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+ "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0"
+ },
+ "time": "2025-12-02T11:56:42+00:00"
+ },
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v5.6.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/416df702837983f8d5ff48c9c3fee4f5f57b980b",
+ "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.1.4",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.5",
+ "symfony/polyfill-ctype": "^1.26",
+ "symfony/polyfill-mbstring": "^1.26",
+ "symfony/polyfill-php80": "^1.26"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-filter": "*",
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "5.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "https://github.com/vlucas"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-07-06T19:11:50+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "8e1051fe39379367aecf014f41744ce7539a856f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f",
+ "reference": "8e1051fe39379367aecf014f41744ce7539a856f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "https://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/2.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-26T05:33:54+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "fakerphp/faker",
+ "version": "v1.24.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "psr/container": "^1.0 || ^2.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "conflict": {
+ "fzaninotto/faker": "*"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "doctrine/persistence": "^1.3 || ^2.0",
+ "ext-intl": "*",
+ "phpunit/phpunit": "^9.5.26",
+ "symfony/phpunit-bridge": "^5.4.16"
+ },
+ "suggest": {
+ "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+ "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+ "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+ "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+ "ext-mbstring": "Required for multibyte Unicode string functionality."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Faker\\": "src/Faker/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "François Zaninotto"
+ }
+ ],
+ "description": "Faker is a PHP library that generates fake data for you.",
+ "keywords": [
+ "data",
+ "faker",
+ "fixtures"
+ ],
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1"
+ },
+ "time": "2024-11-21T13:46:39+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
+ },
+ "time": "2025-04-30T06:54:44+00:00"
+ },
+ {
+ "name": "laravel/pint",
+ "version": "v1.30.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/pint.git",
+ "reference": "de440809cb86055b43e7ae979bed3baf8d29ef5a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/de440809cb86055b43e7ae979bed3baf8d29ef5a",
+ "reference": "de440809cb86055b43e7ae979bed3baf8d29ef5a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-tokenizer": "*",
+ "ext-xml": "*",
+ "php": "^8.2.0"
+ },
+ "require-dev": {
+ "composer/semver": "^3.4.4",
+ "friendsofphp/php-cs-fixer": "^3.95.18",
+ "illuminate/view": "^12.64.0",
+ "larastan/larastan": "^3.10.0",
+ "laravel-zero/framework": "^12.1.0",
+ "laravel/agent-detector": "^2.0.2",
+ "laravel/prompts": "^0.3.21",
+ "mockery/mockery": "^1.6.12",
+ "nunomaduro/termwind": "^2.4.0",
+ "pestphp/pest": "^3.8.7"
+ },
+ "bin": [
+ "builds/pint"
+ ],
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "App\\": "app/",
+ "Database\\Seeders\\": "database/seeders/",
+ "Database\\Factories\\": "database/factories/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nuno Maduro",
+ "email": "enunomaduro@gmail.com"
+ }
+ ],
+ "description": "An opinionated code formatter for PHP.",
+ "homepage": "https://laravel.com",
+ "keywords": [
+ "dev",
+ "format",
+ "formatter",
+ "lint",
+ "linter",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/pint/issues",
+ "source": "https://github.com/laravel/pint"
+ },
+ "time": "2026-08-01T01:14:35+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/helpers.php",
+ "library/Mockery.php"
+ ],
+ "psr-4": {
+ "Mockery\\": "library/Mockery"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
+ },
+ "time": "2024-05-16T03:13:13+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56",
+ "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-text-template": "^4.0.1",
+ "sebastian/code-unit-reverse-lookup": "^4.0.1",
+ "sebastian/complexity": "^4.0.1",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/lines-of-code": "^3.0.1",
+ "sebastian/version": "^5.0.2",
+ "theseer/tokenizer": "^1.3.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.46"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-24T07:01:01+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903",
+ "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-02-02T13:52:54+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:07:44+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:08:43+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:09:35+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.5.56",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256",
+ "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-filter": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0.12",
+ "phpunit/php-file-iterator": "^5.1.1",
+ "phpunit/php-invoker": "^5.0.1",
+ "phpunit/php-text-template": "^4.0.1",
+ "phpunit/php-timer": "^7.0.1",
+ "sebastian/cli-parser": "^3.0.2",
+ "sebastian/code-unit": "^3.0.3",
+ "sebastian/comparator": "^6.3.3",
+ "sebastian/diff": "^6.0.2",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/exporter": "^6.3.2",
+ "sebastian/global-state": "^7.0.2",
+ "sebastian/object-enumerator": "^6.0.1",
+ "sebastian/recursion-context": "^6.0.3",
+ "sebastian/type": "^5.1.3",
+ "sebastian/version": "^5.0.2",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsoring.html",
+ "type": "other"
+ }
+ ],
+ "time": "2026-07-06T14:52:39+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:41:36+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-19T07:56:08+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:45:54+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9",
+ "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.4"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-24T09:26:40+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:49:50+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-05-21T11:55:47+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:12:51+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:57:36+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:58:38+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:00:13+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:01:32+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T04:42:22+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-09T06:55:48+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T05:16:32+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-17T20:03:58+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "^8.2"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.2.0"
+}
diff --git a/config/app.php b/config/app.php
new file mode 100644
index 0000000..eb658a1
--- /dev/null
+++ b/config/app.php
@@ -0,0 +1,31 @@
+ env('APP_NAME', 'CloudChip'),
+
+ 'env' => env('APP_ENV', 'production'),
+
+ 'debug' => (bool) env('APP_DEBUG', false),
+
+ 'url' => env('APP_URL', 'http://localhost'),
+
+ 'asset_url' => env('ASSET_URL'),
+
+ 'timezone' => 'Asia/Shanghai',
+
+ 'locale' => 'zh_CN',
+
+ 'fallback_locale' => 'en',
+
+ 'faker_locale' => 'zh_CN',
+
+ 'key' => env('APP_KEY'),
+
+ 'cipher' => 'AES-256-CBC',
+
+ 'maintenance' => [
+ 'driver' => 'file',
+ ],
+
+];
diff --git a/config/auth.php b/config/auth.php
new file mode 100644
index 0000000..e57e5c5
--- /dev/null
+++ b/config/auth.php
@@ -0,0 +1,35 @@
+ [
+ 'guard' => 'web',
+ 'passwords' => 'users',
+ ],
+
+ 'guards' => [
+ 'web' => [
+ 'driver' => 'session',
+ 'provider' => 'users',
+ ],
+ ],
+
+ 'providers' => [
+ 'users' => [
+ 'driver' => 'eloquent',
+ 'model' => App\Models\User::class,
+ ],
+ ],
+
+ 'passwords' => [
+ 'users' => [
+ 'provider' => 'users',
+ 'table' => 'password_reset_tokens',
+ 'expire' => 60,
+ 'throttle' => 60,
+ ],
+ ],
+
+ 'password_timeout' => 10800,
+
+];
diff --git a/config/cache.php b/config/cache.php
new file mode 100644
index 0000000..b8c3a9f
--- /dev/null
+++ b/config/cache.php
@@ -0,0 +1,36 @@
+ env('CACHE_DRIVER', 'file'),
+
+ 'stores' => [
+
+ 'array' => [
+ 'driver' => 'array',
+ 'serialize' => false,
+ ],
+
+ 'database' => [
+ 'driver' => 'database',
+ 'table' => 'cache',
+ 'connection' => null,
+ 'lock_connection' => null,
+ ],
+
+ 'file' => [
+ 'driver' => 'file',
+ 'path' => storage_path('framework/cache/data'),
+ ],
+
+ 'redis' => [
+ 'driver' => 'redis',
+ 'connection' => 'cache',
+ 'lock_connection' => 'default',
+ ],
+
+ ],
+
+ 'prefix' => env('CACHE_PREFIX', 'cloudchip_cache'),
+
+];
diff --git a/config/cors.php b/config/cors.php
new file mode 100644
index 0000000..22b86b8
--- /dev/null
+++ b/config/cors.php
@@ -0,0 +1,21 @@
+ ['api/*', 'sanctum/csrf-cookie'],
+
+ 'allowed_methods' => ['*'],
+
+ 'allowed_origins' => [env('APP_URL', 'http://localhost')],
+
+ 'allowed_origins_patterns' => [],
+
+ 'allowed_headers' => ['*'],
+
+ 'exposed_headers' => [],
+
+ 'max_age' => 0,
+
+ 'supports_credentials' => true,
+
+];
diff --git a/config/database.php b/config/database.php
new file mode 100644
index 0000000..3b419ce
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,81 @@
+ env('DB_CONNECTION', 'mysql'),
+
+ 'connections' => [
+
+ 'sqlite' => [
+ 'driver' => 'sqlite',
+ 'url' => env('DB_URL'),
+ 'database' => env('DB_DATABASE', database_path('database.sqlite')),
+ 'prefix' => '',
+ 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
+ ],
+
+ 'mysql' => [
+ 'driver' => 'mysql',
+ 'url' => env('DB_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '3306'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => env('DB_CHARSET', 'utf8mb4'),
+ // 宝塔 MySQL5.7 不支持 utf8mb4_0900_ai_ci,必须用 utf8mb4_unicode_ci
+ 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => true,
+ 'engine' => null,
+ 'options' => extension_loaded('pdo_mysql') ? array_filter([
+ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ ]) : [],
+ ],
+
+ 'pgsql' => [
+ 'driver' => 'pgsql',
+ 'url' => env('DB_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '5432'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'charset' => env('DB_CHARSET', 'utf8'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'search_path' => 'public',
+ 'sslmode' => 'prefer',
+ ],
+
+ ],
+
+ 'migrations' => 'migrations',
+
+ 'redis' => [
+ 'client' => env('REDIS_CLIENT', 'phpredis'),
+ 'options' => [
+ 'cluster' => env('REDIS_CLUSTER', 'redis'),
+ 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
+ ],
+ 'default' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'password' => env('REDIS_PASSWORD'),
+ 'port' => env('REDIS_PORT', '6379'),
+ 'database' => env('REDIS_DB', '0'),
+ ],
+ 'cache' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'password' => env('REDIS_PASSWORD'),
+ 'port' => env('REDIS_PORT', '6379'),
+ 'database' => env('REDIS_CACHE_DB', '1'),
+ ],
+ ],
+
+];
diff --git a/config/filesystems.php b/config/filesystems.php
new file mode 100644
index 0000000..5ed5663
--- /dev/null
+++ b/config/filesystems.php
@@ -0,0 +1,39 @@
+ env('FILESYSTEM_DISK', 'local'),
+
+ 'disks' => [
+
+ 'local' => [
+ 'driver' => 'local',
+ 'root' => storage_path('app'),
+ 'throw' => false,
+ ],
+
+ 'public' => [
+ 'driver' => 'local',
+ 'root' => storage_path('app/public'),
+ 'url' => env('APP_URL').'/storage',
+ 'visibility' => 'public',
+ 'throw' => false,
+ ],
+
+ 's3' => [
+ 'driver' => 's3',
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_DEFAULT_REGION'),
+ 'bucket' => env('AWS_BUCKET'),
+ 'url' => env('AWS_URL'),
+ 'endpoint' => env('AWS_ENDPOINT'),
+ ],
+
+ ],
+
+ 'links' => [
+ public_path('storage') => storage_path('app/public'),
+ ],
+
+];
diff --git a/config/logging.php b/config/logging.php
new file mode 100644
index 0000000..f3941cf
--- /dev/null
+++ b/config/logging.php
@@ -0,0 +1,61 @@
+ env('LOG_CHANNEL', 'stack'),
+
+ 'deprecations' => [
+ 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
+ 'trace' => false,
+ ],
+
+ 'channels' => [
+
+ 'stack' => [
+ 'driver' => 'stack',
+ 'channels' => ['single'],
+ 'ignore_exceptions' => false,
+ ],
+
+ 'single' => [
+ 'driver' => 'single',
+ 'path' => storage_path('logs/laravel.log'),
+ 'level' => env('LOG_LEVEL', 'warning'),
+ 'replace_placeholders' => true,
+ ],
+
+ 'daily' => [
+ 'driver' => 'daily',
+ 'path' => storage_path('logs/laravel.log'),
+ 'level' => env('LOG_LEVEL', 'warning'),
+ 'days' => 14,
+ 'replace_placeholders' => true,
+ ],
+
+ 'stderr' => [
+ 'driver' => 'monolog',
+ 'level' => env('LOG_LEVEL', 'warning'),
+ 'handler' => StreamHandler::class,
+ 'with' => [
+ 'stream' => 'php://stderr',
+ ],
+ 'processors' => [PsrLogMessageProcessor::class],
+ ],
+
+ 'null' => [
+ 'driver' => 'monolog',
+ 'handler' => NullHandler::class,
+ ],
+
+ 'emergency' => [
+ 'path' => storage_path('logs/laravel.log'),
+ ],
+
+ ],
+
+];
diff --git a/config/mail.php b/config/mail.php
new file mode 100644
index 0000000..fbca8fb
--- /dev/null
+++ b/config/mail.php
@@ -0,0 +1,62 @@
+ env('MAIL_MAILER', 'log'),
+
+ 'mailers' => [
+ 'smtp' => [
+ 'transport' => 'smtp',
+ 'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
+ 'port' => env('MAIL_PORT', 587),
+ 'encryption' => env('MAIL_ENCRYPTION', 'tls'),
+ 'username' => env('MAIL_USERNAME'),
+ 'password' => env('MAIL_PASSWORD'),
+ 'timeout' => null,
+ 'local_domain' => env('MAIL_EHLO_DOMAIN'),
+ ],
+
+ 'ses' => [
+ 'transport' => 'ses',
+ ],
+
+ 'mailgun' => [
+ 'transport' => 'mailgun',
+ ],
+
+ 'postmark' => [
+ 'transport' => 'postmark',
+ ],
+
+ 'sendmail' => [
+ 'transport' => 'sendmail',
+ 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
+ ],
+
+ 'log' => [
+ 'transport' => 'log',
+ 'channel' => env('MAIL_LOG_CHANNEL'),
+ ],
+
+ 'array' => [
+ 'transport' => 'array',
+ ],
+
+ 'failover' => [
+ 'transport' => 'failover',
+ 'mailers' => ['smtp', 'log'],
+ ],
+ ],
+
+ 'from' => [
+ 'address' => env('MAIL_FROM_ADDRESS', 'hello@cloud-chip.cn'),
+ 'name' => env('MAIL_FROM_NAME', 'CloudChip'),
+ ],
+
+ 'markdown' => [
+ 'theme' => 'default',
+ 'paths' => [
+ resource_path('views/vendor/mail'),
+ ],
+ ],
+];
diff --git a/config/permissions.php b/config/permissions.php
new file mode 100644
index 0000000..e2bca1c
--- /dev/null
+++ b/config/permissions.php
@@ -0,0 +1,138 @@
+ label + group)。
+ * - roles:角色默认权限。super_admin 用 '*' 表示全部;
+ * staff 默认空,由管理员在用户维度勾选分配(写入 users.permissions JSON)。
+ *
+ * 解析规则见 App\Models\User::canPerm()。
+ */
+
+return [
+ 'groups' => [
+ 'dashboard' => '仪表盘',
+ 'content' => '内容管理',
+ 'forum' => '论坛管理',
+ 'appearance'=> '外观管理',
+ 'users' => '用户与权限',
+ 'settings' => '站点设置',
+ ],
+
+ 'permissions' => [
+ // 仪表盘
+ 'dashboard.view' => ['label' => '查看仪表盘', 'group' => 'dashboard'],
+
+ // 产品
+ 'products.view' => ['label' => '查看产品', 'group' => 'content'],
+ 'products.create' => ['label' => '新建产品', 'group' => 'content'],
+ 'products.update' => ['label' => '编辑产品', 'group' => 'content'],
+ 'products.delete' => ['label' => '删除产品', 'group' => 'content'],
+
+ // 分类
+ 'categories.view' => ['label' => '查看分类', 'group' => 'content'],
+ 'categories.create' => ['label' => '新建分类', 'group' => 'content'],
+ 'categories.update' => ['label' => '编辑分类', 'group' => 'content'],
+ 'categories.delete' => ['label' => '删除分类', 'group' => 'content'],
+
+ // 文章
+ 'articles.view' => ['label' => '查看文章', 'group' => 'content'],
+ 'articles.create' => ['label' => '新建文章', 'group' => 'content'],
+ 'articles.update' => ['label' => '编辑文章', 'group' => 'content'],
+ 'articles.delete' => ['label' => '删除文章', 'group' => 'content'],
+ 'articles.moderate' => ['label' => '审核用户投稿', 'group' => 'content'],
+
+ // 解决方案
+ 'solutions.view' => ['label' => '查看方案', 'group' => 'content'],
+ 'solutions.create' => ['label' => '新建方案', 'group' => 'content'],
+ 'solutions.update' => ['label' => '编辑方案', 'group' => 'content'],
+ 'solutions.delete' => ['label' => '删除方案', 'group' => 'content'],
+
+ // 新闻动态
+ 'news.view' => ['label' => '查看新闻', 'group' => 'content'],
+ 'news.create' => ['label' => '新建新闻', 'group' => 'content'],
+ 'news.update' => ['label' => '编辑新闻', 'group' => 'content'],
+ 'news.delete' => ['label' => '删除新闻', 'group' => 'content'],
+
+ // 术语库
+ 'glossary.view' => ['label' => '查看术语', 'group' => 'content'],
+ 'glossary.create' => ['label' => '新建术语', 'group' => 'content'],
+ 'glossary.update' => ['label' => '编辑术语', 'group' => 'content'],
+ 'glossary.delete' => ['label' => '删除术语', 'group' => 'content'],
+
+ // 论坛
+ 'forum.view' => ['label' => '查看论坛', 'group' => 'forum'],
+ 'forum.create' => ['label' => '新建板块', 'group' => 'forum'],
+ 'forum.update' => ['label' => '编辑板块', 'group' => 'forum'],
+ 'forum.delete' => ['label' => '删除板块', 'group' => 'forum'],
+ 'forum.moderate' => ['label' => '审核帖子/回复', 'group' => 'forum'],
+
+ // 留言
+ 'contacts.view' => ['label' => '查看留言', 'group' => 'users'],
+ 'contacts.delete' => ['label' => '删除留言', 'group' => 'users'],
+
+ // 外观管理(首页轮播 / 导航菜单)
+ 'appearance.view' => ['label' => '查看外观设置', 'group' => 'appearance'],
+ 'appearance.update' => ['label' => '修改外观设置', 'group' => 'appearance'],
+
+ // 用户与权限
+ 'users.view' => ['label' => '查看用户', 'group' => 'users'],
+ 'users.create' => ['label' => '新增用户', 'group' => 'users'],
+ 'users.update' => ['label' => '编辑用户', 'group' => 'users'],
+ 'users.delete' => ['label' => '删除用户', 'group' => 'users'],
+ 'users.assign_role' => ['label' => '分配角色/权限', 'group' => 'users'],
+ 'users.reset_password' => ['label' => '重置用户密码', 'group' => 'users'],
+
+ // 站点设置
+ 'settings.view' => ['label' => '查看站点设置', 'group' => 'settings'],
+ 'settings.update' => ['label' => '修改站点设置', 'group' => 'settings'],
+ ],
+
+ 'roles' => [
+ 'super_admin' => [
+ 'label' => '超级管理员',
+ 'level' => 5,
+ 'permissions' => ['*'],
+ ],
+ 'admin' => [
+ 'label' => '管理员',
+ 'level' => 4,
+ 'permissions' => [
+ 'dashboard.view',
+ 'products.view', 'products.create', 'products.update', 'products.delete',
+ 'categories.view', 'categories.create', 'categories.update', 'categories.delete',
+ 'articles.view', 'articles.create', 'articles.update', 'articles.delete', 'articles.moderate',
+ 'solutions.view', 'solutions.create', 'solutions.update', 'solutions.delete',
+ 'news.view', 'news.create', 'news.update', 'news.delete',
+ 'glossary.view', 'glossary.create', 'glossary.update', 'glossary.delete',
+ 'forum.view', 'forum.create', 'forum.update', 'forum.delete', 'forum.moderate',
+ 'contacts.view', 'contacts.delete',
+ 'appearance.view', 'appearance.update',
+ 'users.view', 'users.create', 'users.update', 'users.delete', 'users.assign_role', 'users.reset_password',
+ 'settings.view', 'settings.update',
+ ],
+ ],
+ 'forum_admin' => [
+ 'label' => '论坛管理员',
+ 'level' => 3,
+ 'permissions' => [
+ 'dashboard.view',
+ 'forum.view', 'forum.create', 'forum.update', 'forum.delete', 'forum.moderate',
+ 'articles.view', 'articles.moderate',
+ 'contacts.view',
+ ],
+ ],
+ 'staff' => [
+ 'label' => '内部员工',
+ 'level' => 2,
+ 'permissions' => [], // 由管理员在「用户权限」中勾选分配
+ ],
+ 'user' => [
+ 'label' => '注册用户',
+ 'level' => 1,
+ 'permissions' => [],
+ ],
+ ],
+];
diff --git a/config/purifier.php b/config/purifier.php
new file mode 100644
index 0000000..32882e2
--- /dev/null
+++ b/config/purifier.php
@@ -0,0 +1,46 @@
+ [
+ 'default' => [
+ 'HTML.Doctype' => 'HTML 4.01 Transitional',
+ 'HTML.Allowed' => 'div,b,strong,i,em,u,a[href|title|target|rel],ul,ol,li,p,br,span,img[src|alt|width|height|class],blockquote,pre,code,table,thead,tbody,tr,th,td,h1,h2,h3,h4,h5,h6,hr,small,sub,sup',
+ 'CSS.AllowedProperties' => 'text-align,float,margin,width,height,max-width,color,background-color,font-size,font-weight,font-style',
+ 'AutoFormat.AutoParagraph' => false,
+ 'AutoFormat.RemoveEmpty' => true,
+ 'Attr.AllowedFrameTargets' => ['_blank'],
+ 'Attr.AllowedRel' => ['nofollow', 'noopener', 'noreferrer'],
+ 'HTML.TargetBlank' => true,
+ 'URI.AllowedSchemes' => ['http', 'https', 'mailto', 'tel', 'data'],
+ 'URI.MakeAbsolute' => false,
+ 'Cache.DefinitionImpl' => null,
+ ],
+ 'article' => [
+ 'HTML.Doctype' => 'HTML 4.01 Transitional',
+ // 后台富文本(Quill)会产出 class:ql-align-*/ql-size-*/ql-indent-* 等,
+ // 同时首页 Hero 默认内容也用 class 排版;故对常用块级/行内标签放开 class,
+ // 仅保留白名单标签与安全的 CSS 属性(style 仍受 CSS.AllowedProperties 约束)。
+ 'HTML.Allowed' => 'div[class|style],b[style],strong[style],i[style],em[style],u[style],a[href|title|target|rel|class|style],ul[class|style],ol[class|style],li[class|style],p[class|style],br,span[class|style],img[src|alt|width|height|class|style],blockquote[class|style],pre[class|style],code[class|style],table[class|style],thead[class|style],tbody[class|style],tr[class|style],th[class|style],td[class|style],h1[class|style],h2[class|style],h3[class|style],h4[class|style],h5[class|style],h6[class|style],hr,small[class|style],sub,sup',
+ 'CSS.AllowedProperties' => 'text-align,float,margin,margin-left,margin-right,padding,padding-left,width,height,max-width,color,background-color,font-size,font-weight,font-style',
+ 'AutoFormat.AutoParagraph' => false,
+ 'AutoFormat.RemoveEmpty' => false,
+ 'Attr.AllowedFrameTargets' => ['_blank'],
+ 'Attr.AllowedRel' => ['nofollow', 'noopener', 'noreferrer'],
+ 'HTML.TargetBlank' => true,
+ 'URI.AllowedSchemes' => ['http', 'https', 'mailto', 'tel', 'data'],
+ 'URI.MakeAbsolute' => false,
+ 'Cache.DefinitionImpl' => null,
+ ],
+ ],
+ 'custom_definition' => [],
+];
diff --git a/config/queue.php b/config/queue.php
new file mode 100644
index 0000000..90220e1
--- /dev/null
+++ b/config/queue.php
@@ -0,0 +1,40 @@
+ env('QUEUE_CONNECTION', 'sync'),
+
+ 'connections' => [
+ 'sync' => [
+ 'driver' => 'sync',
+ ],
+
+ 'database' => [
+ 'driver' => 'database',
+ 'table' => 'jobs',
+ 'queue' => 'default',
+ 'retry_after' => 90,
+ 'after_commit' => false,
+ ],
+
+ 'redis' => [
+ 'driver' => 'redis',
+ 'connection' => 'default',
+ 'queue' => env('REDIS_QUEUE', 'default'),
+ 'retry_after' => 90,
+ 'block_for' => null,
+ 'after_commit' => false,
+ ],
+ ],
+
+ 'batching' => [
+ 'database' => 'mysql',
+ 'table' => 'job_batches',
+ ],
+
+ 'failed' => [
+ 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
+ 'database' => env('DB_CONNECTION', 'mysql'),
+ 'table' => 'failed_jobs',
+ ],
+];
diff --git a/config/services.php b/config/services.php
new file mode 100644
index 0000000..4fcc815
--- /dev/null
+++ b/config/services.php
@@ -0,0 +1,22 @@
+ [
+ 'domain' => env('MAILGUN_DOMAIN'),
+ 'secret' => env('MAILGUN_SECRET'),
+ 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
+ 'scheme' => 'https',
+ ],
+
+ 'postmark' => [
+ 'token' => env('POSTMARK_TOKEN'),
+ ],
+
+ 'ses' => [
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+ ],
+
+];
diff --git a/config/session.php b/config/session.php
new file mode 100644
index 0000000..088b501
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,35 @@
+ env('SESSION_DRIVER', 'file'),
+
+ 'lifetime' => (int) env('SESSION_LIFETIME', 120),
+
+ 'expire_on_close' => false,
+
+ 'encrypt' => true,
+
+ 'files' => storage_path('framework/sessions'),
+
+ 'connection' => env('SESSION_CONNECTION'),
+
+ 'table' => 'sessions',
+
+ 'store' => env('SESSION_STORE'),
+
+ 'lottery' => [2, 100],
+
+ 'cookie' => env('SESSION_COOKIE', 'cloudchip_session'),
+
+ 'path' => '/',
+
+ 'domain' => env('SESSION_DOMAIN'),
+
+ 'secure' => env('SESSION_SECURE_COOKIE', true),
+
+ 'http_only' => true,
+
+ 'same_site' => 'lax',
+
+];
diff --git a/config/view.php b/config/view.php
new file mode 100644
index 0000000..ca829bd
--- /dev/null
+++ b/config/view.php
@@ -0,0 +1,14 @@
+ [
+ resource_path('views'),
+ ],
+
+ 'compiled' => env(
+ 'VIEW_COMPILED_PATH',
+ realpath(storage_path('framework/views'))
+ ),
+
+];
diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php
new file mode 100644
index 0000000..750bc49
--- /dev/null
+++ b/database/migrations/2014_10_12_000000_create_users_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->string('name');
+ $table->string('email')->unique();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->string('password');
+ $table->rememberToken();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('users');
+ }
+};
diff --git a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php
new file mode 100644
index 0000000..3fa9275
--- /dev/null
+++ b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php
@@ -0,0 +1,22 @@
+string('email')->primary();
+ $table->string('token');
+ $table->timestamp('created_at')->nullable();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('password_reset_tokens');
+ }
+};
diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php
new file mode 100644
index 0000000..85264d8
--- /dev/null
+++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->string('uuid')->unique();
+ $table->text('connection');
+ $table->text('queue');
+ $table->longText('payload');
+ $table->longText('exception');
+ $table->timestamp('failed_at')->useCurrent();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('failed_jobs');
+ }
+};
diff --git a/database/migrations/2026_08_01_000001_create_contact_messages_table.php b/database/migrations/2026_08_01_000001_create_contact_messages_table.php
new file mode 100644
index 0000000..258aa33
--- /dev/null
+++ b/database/migrations/2026_08_01_000001_create_contact_messages_table.php
@@ -0,0 +1,30 @@
+id();
+ $table->string('name', 40);
+ $table->string('email', 120);
+ $table->string('phone', 20)->nullable();
+ $table->string('subject', 80);
+ $table->text('message');
+ $table->string('ip', 45)->nullable();
+ $table->timestamps();
+
+ $table->index('email');
+ $table->index('created_at');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('contact_messages');
+ }
+};
diff --git a/database/migrations/2026_08_01_000002_add_is_admin_to_users_table.php b/database/migrations/2026_08_01_000002_add_is_admin_to_users_table.php
new file mode 100644
index 0000000..1d1addd
--- /dev/null
+++ b/database/migrations/2026_08_01_000002_add_is_admin_to_users_table.php
@@ -0,0 +1,22 @@
+boolean('is_admin')->default(false)->after('password');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('is_admin');
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_000003_create_categories_table.php b/database/migrations/2026_08_01_000003_create_categories_table.php
new file mode 100644
index 0000000..3070b56
--- /dev/null
+++ b/database/migrations/2026_08_01_000003_create_categories_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->string('type')->default('product'); // product | article
+ $table->unsignedBigInteger('parent_id')->nullable();
+ $table->integer('sort')->default(0);
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('categories');
+ }
+};
diff --git a/database/migrations/2026_08_01_000004_create_products_table.php b/database/migrations/2026_08_01_000004_create_products_table.php
new file mode 100644
index 0000000..bb2a015
--- /dev/null
+++ b/database/migrations/2026_08_01_000004_create_products_table.php
@@ -0,0 +1,34 @@
+id();
+ $table->unsignedBigInteger('category_id')->nullable();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->string('model')->nullable();
+ $table->string('summary')->nullable();
+ $table->text('description')->nullable();
+ $table->text('specs')->nullable();
+ $table->string('cover')->nullable();
+ $table->string('status')->default('published'); // published | draft
+ $table->integer('sort')->default(0);
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->foreign('category_id')->references('id')->on('categories')->onDelete('set null');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('products');
+ }
+};
diff --git a/database/migrations/2026_08_01_000005_create_articles_table.php b/database/migrations/2026_08_01_000005_create_articles_table.php
new file mode 100644
index 0000000..bfa2230
--- /dev/null
+++ b/database/migrations/2026_08_01_000005_create_articles_table.php
@@ -0,0 +1,33 @@
+id();
+ $table->unsignedBigInteger('category_id')->nullable();
+ $table->string('title');
+ $table->string('slug')->unique();
+ $table->string('summary')->nullable();
+ $table->text('body')->nullable();
+ $table->string('cover')->nullable();
+ $table->string('author')->nullable();
+ $table->string('status')->default('published'); // published | draft
+ $table->timestamp('published_at')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->foreign('category_id')->references('id')->on('categories')->onDelete('set null');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('articles');
+ }
+};
diff --git a/database/migrations/2026_08_01_000006_create_glossary_terms_table.php b/database/migrations/2026_08_01_000006_create_glossary_terms_table.php
new file mode 100644
index 0000000..a2be4c3
--- /dev/null
+++ b/database/migrations/2026_08_01_000006_create_glossary_terms_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->string('term');
+ $table->string('slug')->unique();
+ $table->text('definition');
+ $table->string('aliases')->nullable();
+ $table->string('category')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('glossary_terms');
+ }
+};
diff --git a/database/migrations/2026_08_01_000007_create_forum_boards_table.php b/database/migrations/2026_08_01_000007_create_forum_boards_table.php
new file mode 100644
index 0000000..5a9d5c5
--- /dev/null
+++ b/database/migrations/2026_08_01_000007_create_forum_boards_table.php
@@ -0,0 +1,25 @@
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->text('description')->nullable();
+ $table->integer('sort')->default(0);
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('forum_boards');
+ }
+};
diff --git a/database/migrations/2026_08_01_000008_create_forum_threads_table.php b/database/migrations/2026_08_01_000008_create_forum_threads_table.php
new file mode 100644
index 0000000..24d17bb
--- /dev/null
+++ b/database/migrations/2026_08_01_000008_create_forum_threads_table.php
@@ -0,0 +1,33 @@
+id();
+ $table->unsignedBigInteger('board_id');
+ $table->unsignedBigInteger('user_id');
+ $table->string('title');
+ $table->text('body');
+ $table->boolean('is_pinned')->default(false);
+ $table->boolean('is_locked')->default(false);
+ $table->unsignedInteger('views')->default(0);
+ $table->timestamp('last_reply_at')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->foreign('board_id')->references('id')->on('forum_boards')->onDelete('cascade');
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('forum_threads');
+ }
+};
diff --git a/database/migrations/2026_08_01_000009_create_forum_replies_table.php b/database/migrations/2026_08_01_000009_create_forum_replies_table.php
new file mode 100644
index 0000000..5962bad
--- /dev/null
+++ b/database/migrations/2026_08_01_000009_create_forum_replies_table.php
@@ -0,0 +1,28 @@
+id();
+ $table->unsignedBigInteger('thread_id');
+ $table->unsignedBigInteger('user_id');
+ $table->text('body');
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->foreign('thread_id')->references('id')->on('forum_threads')->onDelete('cascade');
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('forum_replies');
+ }
+};
diff --git a/database/migrations/2026_08_01_000010_create_settings_table.php b/database/migrations/2026_08_01_000010_create_settings_table.php
new file mode 100644
index 0000000..44f91a2
--- /dev/null
+++ b/database/migrations/2026_08_01_000010_create_settings_table.php
@@ -0,0 +1,24 @@
+id();
+ $table->string('key')->unique();
+ $table->text('value')->nullable();
+ $table->string('group')->default('general');
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('settings');
+ }
+};
diff --git a/database/migrations/2026_08_01_000011_add_points_to_users_table.php b/database/migrations/2026_08_01_000011_add_points_to_users_table.php
new file mode 100644
index 0000000..a1ee6fa
--- /dev/null
+++ b/database/migrations/2026_08_01_000011_add_points_to_users_table.php
@@ -0,0 +1,22 @@
+unsignedInteger('points')->default(0)->after('is_admin');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('points');
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_000012_add_template_and_seo_to_articles_table.php b/database/migrations/2026_08_01_000012_add_template_and_seo_to_articles_table.php
new file mode 100644
index 0000000..d678909
--- /dev/null
+++ b/database/migrations/2026_08_01_000012_add_template_and_seo_to_articles_table.php
@@ -0,0 +1,24 @@
+string('template')->default('standard')->after('status');
+ $table->string('seo_title')->nullable()->after('template');
+ $table->string('seo_description')->nullable()->after('seo_title');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('articles', function (Blueprint $table) {
+ $table->dropColumn(['template', 'seo_title', 'seo_description']);
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_000013_add_seo_to_products_table.php b/database/migrations/2026_08_01_000013_add_seo_to_products_table.php
new file mode 100644
index 0000000..e91ded0
--- /dev/null
+++ b/database/migrations/2026_08_01_000013_add_seo_to_products_table.php
@@ -0,0 +1,23 @@
+string('seo_title')->nullable()->after('slug');
+ $table->string('seo_description')->nullable()->after('seo_title');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('products', function (Blueprint $table) {
+ $table->dropColumn(['seo_title', 'seo_description']);
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_100001_add_role_and_permissions_to_users_table.php b/database/migrations/2026_08_01_100001_add_role_and_permissions_to_users_table.php
new file mode 100644
index 0000000..1cf2872
--- /dev/null
+++ b/database/migrations/2026_08_01_100001_add_role_and_permissions_to_users_table.php
@@ -0,0 +1,27 @@
+string('role')->default('user')->after('is_admin');
+ $table->json('permissions')->nullable()->after('role');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn(['role', 'permissions']);
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_100002_add_status_to_forum_tables.php b/database/migrations/2026_08_01_100002_add_status_to_forum_tables.php
new file mode 100644
index 0000000..6bd4e8f
--- /dev/null
+++ b/database/migrations/2026_08_01_100002_add_status_to_forum_tables.php
@@ -0,0 +1,35 @@
+string('status')->default('approved')->after('body');
+ });
+
+ Schema::table('forum_replies', function (Blueprint $table) {
+ $table->string('status')->default('approved')->after('body');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('forum_threads', function (Blueprint $table) {
+ $table->dropColumn('status');
+ });
+
+ Schema::table('forum_replies', function (Blueprint $table) {
+ $table->dropColumn('status');
+ });
+ }
+};
diff --git a/database/migrations/2026_08_01_100003_add_user_id_and_status_to_articles_table.php b/database/migrations/2026_08_01_100003_add_user_id_and_status_to_articles_table.php
new file mode 100644
index 0000000..484e8e2
--- /dev/null
+++ b/database/migrations/2026_08_01_100003_add_user_id_and_status_to_articles_table.php
@@ -0,0 +1,46 @@
+foreignId('user_id')
+ ->nullable()
+ ->after('category_id')
+ ->constrained('users')
+ ->nullOnDelete();
+ });
+ }
+
+ DB::table('articles')
+ ->where('status', 'published')
+ ->update(['status' => 'approved']);
+ }
+
+ public function down(): void
+ {
+ if (Schema::hasColumn('articles', 'user_id')) {
+ Schema::table('articles', function (Blueprint $table) {
+ $table->dropConstrainedForeignId('user_id');
+ });
+ }
+ // 不回滚 status 取值,避免误伤已发布内容。
+ }
+};
diff --git a/database/migrations/2026_08_02_000001_add_profile_fields_to_users_table.php b/database/migrations/2026_08_02_000001_add_profile_fields_to_users_table.php
new file mode 100644
index 0000000..3b4316d
--- /dev/null
+++ b/database/migrations/2026_08_02_000001_add_profile_fields_to_users_table.php
@@ -0,0 +1,32 @@
+string('real_name')->nullable()->after('name');
+ $table->string('phone')->nullable()->after('real_name');
+ $table->string('company')->nullable()->after('phone');
+ $table->string('industry')->nullable()->after('company');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn(['real_name', 'phone', 'company', 'industry']);
+ });
+ }
+};
diff --git a/database/migrations/2026_08_04_000001_create_solutions_table.php b/database/migrations/2026_08_04_000001_create_solutions_table.php
new file mode 100644
index 0000000..0e5693b
--- /dev/null
+++ b/database/migrations/2026_08_04_000001_create_solutions_table.php
@@ -0,0 +1,36 @@
+id();
+ $table->string('name');
+ $table->string('slug')->unique();
+ $table->string('power_segment')->nullable(); // 功率段:lt30|30-65|65-100|gt100
+ $table->string('application')->nullable(); // 应用场景:charger|powerbank|car|dock
+ $table->string('chip_platform')->nullable(); // 芯片平台:protocol|soc|rectifier
+ $table->string('power')->nullable(); // 功率参数,如 65W
+ $table->string('summary')->nullable();
+ $table->text('advantages')->nullable(); // 方案优势,换行分隔
+ $table->text('description')->nullable();
+ $table->string('cover')->nullable();
+ $table->string('status')->default('published'); // published | draft
+ $table->integer('sort')->default(0);
+ $table->string('seo_title')->nullable();
+ $table->string('seo_description')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('solutions');
+ }
+};
diff --git a/database/migrations/2026_08_04_000002_create_news_table.php b/database/migrations/2026_08_04_000002_create_news_table.php
new file mode 100644
index 0000000..ef0dd6d
--- /dev/null
+++ b/database/migrations/2026_08_04_000002_create_news_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->string('title');
+ $table->string('slug')->unique();
+ $table->string('category')->default('industry'); // industry|company|event
+ $table->string('summary')->nullable();
+ $table->text('body')->nullable();
+ $table->string('cover')->nullable();
+ $table->string('status')->default('published'); // published | draft
+ $table->timestamp('published_at')->nullable();
+ $table->string('seo_title')->nullable();
+ $table->string('seo_description')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('news');
+ }
+};
diff --git a/database/migrations/2026_08_04_000003_add_company_to_contact_messages.php b/database/migrations/2026_08_04_000003_add_company_to_contact_messages.php
new file mode 100644
index 0000000..a8dab71
--- /dev/null
+++ b/database/migrations/2026_08_04_000003_add_company_to_contact_messages.php
@@ -0,0 +1,25 @@
+string('company', 120)->nullable()->after('phone');
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('contact_messages', function (Blueprint $table) {
+ $table->dropColumn('company');
+ });
+ }
+};
diff --git a/database/migrations/2026_08_04_000100_create_navigation_menus_table.php b/database/migrations/2026_08_04_000100_create_navigation_menus_table.php
new file mode 100644
index 0000000..64f7825
--- /dev/null
+++ b/database/migrations/2026_08_04_000100_create_navigation_menus_table.php
@@ -0,0 +1,27 @@
+id();
+ $table->string('label', 60)->comment('菜单显示文字');
+ $table->string('url', 255)->comment('链接地址(站内路径如 /about 或完整外链)');
+ $table->boolean('is_visible')->default(true)->comment('是否在前台显示');
+ $table->integer('sort_order')->default(0)->comment('排序,数字越小越靠前');
+ $table->timestamps();
+
+ $table->index(['is_visible', 'sort_order']);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('navigation_menus');
+ }
+};
diff --git a/database/migrations/2026_08_04_000101_create_hero_slides_table.php b/database/migrations/2026_08_04_000101_create_hero_slides_table.php
new file mode 100644
index 0000000..3930e5d
--- /dev/null
+++ b/database/migrations/2026_08_04_000101_create_hero_slides_table.php
@@ -0,0 +1,32 @@
+id();
+ $table->string('location', 40)->default('home')->comment('展示位置(预留多页扩展,当前仅 home)');
+ $table->string('title', 120)->comment('后台标识 / 无障碍标题');
+ $table->text('content')->nullable()->comment('富文本正文(Quill 产出的 HTML)');
+ $table->string('cta_text', 80)->nullable()->comment('主按钮文字');
+ $table->string('cta_url', 255)->nullable()->comment('主按钮链接');
+ $table->string('cta2_text', 80)->nullable()->comment('次按钮文字');
+ $table->string('cta2_url', 255)->nullable()->comment('次按钮链接');
+ $table->boolean('is_active')->default(true)->comment('是否启用');
+ $table->integer('sort_order')->default(0)->comment('排序,数字越小越靠前');
+ $table->timestamps();
+
+ $table->index(['location', 'is_active', 'sort_order']);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('hero_slides');
+ }
+};
diff --git a/database/seeders/AdminUserSeeder.php b/database/seeders/AdminUserSeeder.php
new file mode 100644
index 0000000..5b6c689
--- /dev/null
+++ b/database/seeders/AdminUserSeeder.php
@@ -0,0 +1,25 @@
+ 'admin@cloud-chip.cn'],
+ [
+ 'name' => '管理员',
+ 'email' => 'admin@cloud-chip.cn',
+ 'password' => 'cloudchip2026',
+ 'is_admin' => true,
+ 'role' => 'super_admin',
+ ]
+ );
+ }
+}
diff --git a/database/seeders/ArticleSeeder.php b/database/seeders/ArticleSeeder.php
new file mode 100644
index 0000000..2155ad1
--- /dev/null
+++ b/database/seeders/ArticleSeeder.php
@@ -0,0 +1,42 @@
+first();
+
+ $items = [
+ [
+ 'category_id' => $cat?->id,
+ 'title' => '多口 PD 充电器设计要点:端口优先级与功率分配策略',
+ 'slug' => 'multi-port-pd-design-guide',
+ 'summary' => '本文介绍多口 PD 充电器中端口优先级判定、动态功率分配和热管理的设计要点。',
+ 'body' => "## 端口优先级\n\n多口 PD 充电器需要根据接入设备类型和数量动态分配功率。常见策略包括:\n\n1. **均分策略**:各口平均分配总功率\n2. **优先级策略**:C1 口优先获得最大功率\n3. **自适应策略**:根据设备请求的 PD contract 动态调整\n\n## 功率分配\n\n以 CC-PD3168 为例,3 口总功率 100W:\n- 单口接入时:C1/C2/C3 均可输出 100W (20V/5A)\n- 双口接入时:C1=65W, C2=30W\n- 三口接入时:C1=65W, C2=20W, C3=15W\n\n## 热管理\n\n多口同时工作时热量集中,需要合理的 PCB 布局和散热设计。",
+ 'author' => '云创芯技术团队',
+ 'status' => 'published',
+ 'published_at' => now()->subDays(7),
+ ],
+ [
+ 'category_id' => $cat?->id,
+ 'title' => 'USB PD3.X 协议解析:PPS 与 SPRS 模式',
+ 'slug' => 'usb-pd3-pps-sprs-explained',
+ 'summary' => '深入解析 USB PD3.X 中的 PPS(可编程电源)和 SPRS(系统电源参考)模式。',
+ 'body' => "## PPS 模式\n\nPPS(Programmable Power Supply)允许电源在 3.3V 到 21V 之间以 20mV 步进调整输出电压,实现更精细的电压匹配。\n\n## SPRS 模式\n\nSPRS(System Power Reference)是 PD3.X 中新增的参考电压机制,用于多设备系统级电源管理。\n\n## 应用场景\n\n- 笔记本快充:PPS 可降低转换损耗约 5%\n- 移动电源:SPRS 简化多设备充电管理",
+ 'author' => '云创芯技术团队',
+ 'status' => 'published',
+ 'published_at' => now()->subDays(3),
+ ],
+ ];
+
+ foreach ($items as $item) {
+ Article::firstOrCreate(['slug' => $item['slug']], $item);
+ }
+ }
+}
diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php
new file mode 100644
index 0000000..5e36544
--- /dev/null
+++ b/database/seeders/CategorySeeder.php
@@ -0,0 +1,28 @@
+ 'PD 电源及其信号智能控制 IC', 'slug' => 'pd-phy-controller', 'type' => 'product', 'sort' => 1],
+ ['name' => '协议转换 IC', 'slug' => 'protocol-bridge', 'type' => 'product', 'sort' => 2],
+ ['name' => '同步整流 IC', 'slug' => 'sync-rectifier', 'type' => 'product', 'sort' => 3],
+ ];
+
+ $articles = [
+ ['name' => '设计指南', 'slug' => 'design-guide', 'type' => 'article', 'sort' => 1],
+ ['name' => '应用笔记', 'slug' => 'app-note', 'type' => 'article', 'sort' => 2],
+ ['name' => '行业方案', 'slug' => 'industry-solution', 'type' => 'article', 'sort' => 3],
+ ];
+
+ foreach (array_merge($products, $articles) as $cat) {
+ Category::firstOrCreate(['slug' => $cat['slug']], $cat);
+ }
+ }
+}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
new file mode 100644
index 0000000..d33b696
--- /dev/null
+++ b/database/seeders/DatabaseSeeder.php
@@ -0,0 +1,26 @@
+call([
+ CategorySeeder::class,
+ ProductSeeder::class,
+ ArticleSeeder::class,
+ GlossaryTermSeeder::class,
+ ForumBoardSeeder::class,
+ AdminUserSeeder::class,
+ SettingsSeeder::class,
+ SolutionsSeeder::class,
+ NewsSeeder::class,
+ NavigationMenuSeeder::class,
+ HeroSlideSeeder::class,
+ PdContentSeeder::class, // 最后灌入真实 PD 内容(幂等,跨库安全)
+ ]);
+ }
+}
diff --git a/database/seeders/ForumBoardSeeder.php b/database/seeders/ForumBoardSeeder.php
new file mode 100644
index 0000000..fd78f52
--- /dev/null
+++ b/database/seeders/ForumBoardSeeder.php
@@ -0,0 +1,37 @@
+ '选型咨询',
+ 'slug' => 'selection',
+ 'description' => '多口 TypeC 接口IC 选型、参数对比与技术方案讨论。',
+ 'sort' => 1,
+ ],
+ [
+ 'name' => '调试与量产',
+ 'slug' => 'debug',
+ 'description' => 'PCB 布局、EMI 整改、量产测试经验分享。',
+ 'sort' => 2,
+ ],
+ [
+ 'name' => '行业资讯',
+ 'slug' => 'news',
+ 'description' => 'TypeC 快速充电行业动态、标准更新与市场趋势。',
+ 'sort' => 3,
+ ],
+ ];
+
+ foreach ($boards as $board) {
+ ForumBoard::firstOrCreate(['slug' => $board['slug']], $board);
+ }
+ }
+}
diff --git a/database/seeders/GlossaryTermSeeder.php b/database/seeders/GlossaryTermSeeder.php
new file mode 100644
index 0000000..4bf62eb
--- /dev/null
+++ b/database/seeders/GlossaryTermSeeder.php
@@ -0,0 +1,54 @@
+ 'PD (Power Delivery)',
+ 'slug' => 'pd-power-delivery',
+ 'definition' => 'USB Power Delivery 协议,通过 USB-C 接口传输最高 240W (48V/5A) 的电力,支持双向供电和智能功率协商。',
+ 'aliases' => 'USB PD, Power Delivery, 快充协议',
+ 'category' => '协议',
+ ],
+ [
+ 'term' => 'PHY (物理层)',
+ 'slug' => 'phy-physical-layer',
+ 'definition' => 'PHY 是物理层(Physical Layer)的缩写,负责数据的物理传输,包括信号编码、调制解调、阻抗匹配等功能。',
+ 'aliases' => 'Physical Layer, 物理层收发器',
+ 'category' => '硬件',
+ ],
+ [
+ 'term' => 'PPS (可编程电源)',
+ 'slug' => 'pps-programmable-power-supply',
+ 'definition' => 'PPS 是 USB PD3.X 中的可编程电源模式,允许在 3.3V 到 21V 范围内以 20mV 步进调整输出电压,实现更高效的直充方案。',
+ 'aliases' => 'Programmable Power Supply',
+ 'category' => '协议',
+ ],
+ [
+ 'term' => 'BC1.2',
+ 'slug' => 'bc1-2',
+ 'definition' => 'Battery Charging Specification 1.2,USB-IF 制定的电池充电规范,定义了 DCP(专用充电口)、CDP(充电下行口)和 SDP(标准下行口)三种充电模式。',
+ 'aliases' => 'Battery Charging 1.2',
+ 'category' => '协议',
+ ],
+ [
+ 'term' => '同步整流',
+ 'slug' => 'synchronous-rectification',
+ 'definition' => '同步整流是用 MOSFET 替代肖特基二极管进行整流的技术,可大幅降低整流损耗,提升转换效率至 95% 以上。',
+ 'aliases' => 'Synchronous Rectification, SR',
+ 'category' => '硬件',
+ ],
+ ];
+
+ foreach ($terms as $item) {
+ GlossaryTerm::firstOrCreate(['slug' => $item['slug']], $item);
+ }
+ }
+}
diff --git a/database/seeders/HeroSlideSeeder.php b/database/seeders/HeroSlideSeeder.php
new file mode 100644
index 0000000..f1aef11
--- /dev/null
+++ b/database/seeders/HeroSlideSeeder.php
@@ -0,0 +1,109 @@
+delete();
+
+ // ── 轮播 1:品牌主视觉(复刻原首页,含三栏能力卡)──
+ $slide1 = <<TypeC 产品顾问 · 多口 TypeC 接口IC 方案提供商
+为充电与外设厂商提供稳定、易集成的接口芯片
+深圳市云创芯电子有限公司专注多口 TypeC 接口IC 与 PD 电源及其信号智能控制,支持标准 TypeC PD 协议(含快充、数据转换、功率智能分配),覆盖 PPS、SPRS 等快充协议,帮助客户缩短研发周期、提升量产良率。
+
+
+
多协议覆盖
+
PD3.X / PPS / SPRS 协议栈
+
+
+
+
+HTML;
+
+ // ── 轮播 2:产品矩阵(展示标题层级 + 行内高亮 + 列表 + 对齐)──
+ $slide2 = <<产品矩阵
+多口 TypeC 接口IC 全系选型
+从单口到多口、从低压到百瓦级,覆盖主流快充场景的接口与协议控制芯片。
+核心协议支持
+
+ - PD3.1 / PPS:宽范围电压调节,适配笔记本与移动设备
+ - SPR / EPR:28V/140W 扩展功率范围
+ - 数据角色切换:DFP/UFP/DRP 自适应
+ - 同步整流控制:高效率、低发热
+
+— 面向充电、外设与工业客户出货
+HTML;
+
+ // ── 轮播 3:方案能力(展示超大标题 + 居中 + 提示框)──
+ $slide3 = <<参考设计
+30W–140W 快充参考设计
+即拿即用的原理图、BOM 与调试指南,加速量产导入。
+
+
免费提供:选型手册、评估板与量产导入技术支持,由应用工程师一对一协助。
+
+HTML;
+
+ $slides = [
+ [
+ 'location' => 'home',
+ 'title' => '品牌主视觉',
+ 'content' => $slide1,
+ 'cta_text' => '获取选型支持',
+ 'cta_url' => '/contact',
+ 'cta2_text' => '浏览全部产品',
+ 'cta2_url' => '/products',
+ 'is_active' => true,
+ 'sort_order' => 0,
+ ],
+ [
+ 'location' => 'home',
+ 'title' => '产品矩阵',
+ 'content' => $slide2,
+ 'cta_text' => '查看产品中心',
+ 'cta_url' => '/products',
+ 'cta2_text' => '',
+ 'cta2_url' => null,
+ 'is_active' => true,
+ 'sort_order' => 1,
+ ],
+ [
+ 'location' => 'home',
+ 'title' => '方案能力',
+ 'content' => $slide3,
+ 'cta_text' => '下载参考设计',
+ 'cta_url' => '/solutions',
+ 'cta2_text' => '',
+ 'cta2_url' => null,
+ 'is_active' => true,
+ 'sort_order' => 2,
+ ],
+ ];
+
+ foreach ($slides as $s) {
+ HeroSlide::create($s);
+ }
+ }
+}
diff --git a/database/seeders/NavigationMenuSeeder.php b/database/seeders/NavigationMenuSeeder.php
new file mode 100644
index 0000000..72848e9
--- /dev/null
+++ b/database/seeders/NavigationMenuSeeder.php
@@ -0,0 +1,35 @@
+ '首页', 'url' => '/', 'sort_order' => 0],
+ ['label' => '关于我们', 'url' => '/about', 'sort_order' => 1],
+ ['label' => '产品服务', 'url' => '/products', 'sort_order' => 2],
+ ['label' => '解决方案', 'url' => '/solutions', 'sort_order' => 3],
+ ['label' => '文章', 'url' => '/articles', 'sort_order' => 4],
+ ['label' => '新闻动态', 'url' => '/news', 'sort_order' => 5],
+ ['label' => '术语库', 'url' => '/glossary', 'sort_order' => 6],
+ ['label' => '论坛', 'url' => '/forum', 'sort_order' => 7],
+ ['label' => '联系我们', 'url' => '/contact', 'sort_order' => 8],
+ ];
+
+ foreach ($items as $item) {
+ NavigationMenu::firstOrCreate(
+ ['label' => $item['label'], 'url' => $item['url']],
+ ['is_visible' => true, 'sort_order' => $item['sort_order']]
+ );
+ }
+ }
+}
diff --git a/database/seeders/NewsSeeder.php b/database/seeders/NewsSeeder.php
new file mode 100644
index 0000000..8f688eb
--- /dev/null
+++ b/database/seeders/NewsSeeder.php
@@ -0,0 +1,68 @@
+ 'USB-IF 发布新版 PD 规范,进一步统一快充生态',
+ 'slug' => 'usb-if-new-pd-spec',
+ 'category' => 'industry',
+ 'summary' => 'USB-IF 近期更新 USB PD 规范,强化 EPR 与多设备供电能力,推动跨品牌快充兼容。',
+ 'body' => "USB-IF 近期发布了 USB Power Delivery 规范的新一轮更新,进一步强化了 EPR(Extended Power Range)与多设备协同供电能力。\n\n对于配件与品牌厂商而言,规范的统一意味着更高的互操作性与更低的适配成本。深圳市云创芯电子有限公司将持续跟进标准演进,并在方案库中同步可落地的参考设计。",
+ 'status' => 'published',
+ 'published_at' => now()->subDays(2),
+ ],
+ [
+ 'title' => '深圳市云创芯电子有限公司方案库上线 30W–140W 全功率段参考设计',
+ 'slug' => 'pdtechhub-solution-library-launch',
+ 'category' => 'company',
+ 'summary' => '平台方案库正式上线,覆盖迷你充电、双口、桌面充电站到 140W 多设备方案。',
+ 'body' => "深圳市云创芯电子有限公司方案库现已上线,首批提供 30W、65W、100W、140W 四档功率段的参考设计,覆盖充电器、桌面充电站等多类场景。\n\n每套方案均附带功率分配策略、关键器件选型与保护机制说明,帮助工程师更快完成产品定义与评估。",
+ 'status' => 'published',
+ 'published_at' => now()->subDays(5),
+ ],
+ [
+ 'title' => '深圳市云创芯电子有限公司受邀参加 2026 亚洲充电展,现场演示 140W 多口方案',
+ 'slug' => 'pdtechhub-at-asia-charging-expo-2026',
+ 'category' => 'event',
+ 'summary' => '展会将集中展示 PD3.1 EPR 140W 多设备快充与氮化镓高集成方案。',
+ 'body' => "深圳市云创芯电子有限公司将亮相 2026 亚洲充电展,现场演示基于 PD3.1 EPR 的 140W 多设备快充方案,以及 65W 双口氮化镓高集成设计。\n\n欢迎配件与品牌厂商的工程师莅临交流,了解最新方案库资源与对接合作机会。",
+ 'status' => 'published',
+ 'published_at' => now()->subDays(9),
+ ],
+ [
+ 'title' => '氮化镓(GaN)器件成本下探,百瓦快充加速普及',
+ 'slug' => 'gan-cost-down-100w-fast-charging',
+ 'category' => 'industry',
+ 'summary' => '随着 GaN 器件规模量产,100W 级快充的 BOM 成本显著下降,普及拐点已至。',
+ 'body' => "近年来氮化镓(GaN)功率器件在规模量产下成本持续下探,使得 65W–100W 级快充的整机 BOM 明显下降。\n\n成本门槛降低叠加用户对大功率快充的强劲需求,正推动百瓦快充从高端走向主流,为配件厂商带来新的产品机会。",
+ 'status' => 'published',
+ 'published_at' => now()->subDays(14),
+ ],
+ [
+ 'title' => '平台合作厂商突破 80 家,资源对接效率显著提升',
+ 'slug' => 'pdtechhub-80-partners',
+ 'category' => 'company',
+ 'summary' => '深圳市云创芯电子有限公司资源对接网络持续扩大,芯片、模块与品牌厂商协作更高效。',
+ 'body' => "截至本月,深圳市云创芯电子有限公司的合作网络已汇聚超过 80 家芯片、模块与品牌厂商。\n\n通过标准化的方案库与供需匹配,平台显著缩短了从选型到量产导入的沟通链路,更多合作案例正在落地。",
+ 'status' => 'published',
+ 'published_at' => now()->subDays(20),
+ ],
+ ];
+
+ foreach ($items as $item) {
+ News::firstOrCreate(['slug' => $item['slug']], $item);
+ }
+ }
+}
diff --git a/database/seeders/PdContentSeeder.php b/database/seeders/PdContentSeeder.php
new file mode 100644
index 0000000..e746576
--- /dev/null
+++ b/database/seeders/PdContentSeeder.php
@@ -0,0 +1,80 @@
+ 新 id」slug 映射,跨库部署 id 错位也无妨。
+ * - 文章作者统一关联到 admin@cloud-chip.cn 账户(author 文本字段仍保留原作者署名)。
+ */
+class PdContentSeeder extends Seeder
+{
+ public function run(): void
+ {
+ $path = __DIR__ . '/data/site_content.json';
+ if (! file_exists($path)) {
+ return;
+ }
+
+ $data = json_decode(file_get_contents($path), true);
+ if (! is_array($data)) {
+ return;
+ }
+
+ // 1. 分类:按 slug 幂等,建立 原id -> 新id 映射(跨库 id 对齐)
+ $catMap = [];
+ foreach ($data['categories'] ?? [] as $c) {
+ $origId = $c['id'];
+ $existing = DB::table('categories')->where('slug', $c['slug'])->first();
+ if ($existing) {
+ $newId = $existing->id;
+ } else {
+ $row = $c;
+ unset($row['id']);
+ $newId = DB::table('categories')->insertGetId($row);
+ }
+ $catMap[$origId] = $newId;
+ }
+
+ // 文章作者关联到 admin 账户(若已存在)
+ $adminId = DB::table('users')->where('email', 'admin@cloud-chip.cn')->value('id');
+
+ // 2. 产品
+ foreach ($data['products'] ?? [] as $p) {
+ if (DB::table('products')->where('slug', $p['slug'])->exists()) {
+ continue;
+ }
+ if (! empty($p['category_id'])) {
+ $p['category_id'] = $catMap[$p['category_id']] ?? null;
+ }
+ DB::table('products')->insert($p);
+ }
+
+ // 3. 文章
+ foreach ($data['articles'] ?? [] as $a) {
+ if (DB::table('articles')->where('slug', $a['slug'])->exists()) {
+ continue;
+ }
+ if (! empty($a['category_id'])) {
+ $a['category_id'] = $catMap[$a['category_id']] ?? null;
+ }
+ $a['user_id'] = $adminId; // 弱关联;author 文本字段保留真实署名
+ DB::table('articles')->insert($a);
+ }
+
+ // 4. 术语库
+ foreach ($data['glossary_terms'] ?? [] as $g) {
+ if (DB::table('glossary_terms')->where('slug', $g['slug'])->exists()) {
+ continue;
+ }
+ DB::table('glossary_terms')->insert($g);
+ }
+ }
+}
diff --git a/database/seeders/ProductSeeder.php b/database/seeders/ProductSeeder.php
new file mode 100644
index 0000000..4195804
--- /dev/null
+++ b/database/seeders/ProductSeeder.php
@@ -0,0 +1,57 @@
+first();
+ $cat2 = Category::where('slug', 'protocol-bridge')->first();
+ $cat3 = Category::where('slug', 'sync-rectifier')->first();
+
+ $items = [
+ [
+ 'category_id' => $cat1?->id,
+ 'name' => 'CC-PD3168 多口 PD 电源及其信号智能控制 IC',
+ 'slug' => 'cc-pd3168',
+ 'model' => 'CC-PD3168',
+ 'summary' => '支持 3 口独立快充协商,内置 3168 协议栈,适用于桌面充与车充。',
+ 'description' => "CC-PD3168 是一款面向多口充电器的 PD 电源及其信号智能控制芯片,支持 PD 3.0 及其以上的接口协议。\n\n主要特性:\n- 3 口独立 PD 协商\n- 内置 32-bit MCU\n- 支持 PPS / AVR / SPRS\n- 待机功耗低于 30mW\n- 封装 QFN-32",
+ 'specs' => "接口: I2C / UART\n工作电压: 3.3V\n封装: QFN-32 (5x5mm)\n工作温度: -40~85C\n认证: USB-IF PD 3.0 Certified",
+ 'status' => 'published',
+ 'sort' => 1,
+ ],
+ [
+ 'category_id' => $cat2?->id,
+ 'name' => 'CC-PT6540 PHY 接口协议转换 IC',
+ 'slug' => 'cc-pt6540',
+ 'model' => 'CC-PT6540',
+ 'summary' => 'USB-C 到传统 PHY 接口桥接,向下兼容 BC1.2 与 Apple 2.4A。',
+ 'description' => "CC-PT6540 提供 USB-C 到传统 PHY 接口的桥接功能,支持多种快充协议自动切换。\n\n主要特性:\n- USB-C DFP/DRP 角色\n- BC1.2 DCP/CDP/SDP 兼容\n- Apple 2.4A 兼容\n- 高电压耐受 28V",
+ 'specs' => "接口: USB-C / D+/D-\n工作电压: 3.3V\n封装: QFN-24 (4x4mm)\n工作温度: -40~85C",
+ 'status' => 'published',
+ 'sort' => 2,
+ ],
+ [
+ 'category_id' => $cat3?->id,
+ 'name' => 'CC-SC128 同步整流控制器',
+ 'slug' => 'cc-sc128',
+ 'model' => 'CC-SC128',
+ 'summary' => '高效率同步整流,待机功耗低于 30mW,通过 CCC 认证。',
+ 'description' => "CC-SC128 是一款高效率同步整流控制器,适用于反激式和正激式变换器的副边整流。\n\n主要特性:\n- 超低待机功耗 30mW\n- 自适应导通时间控制\n- 支持 CCM/DCM/QM 三种模式\n- 内置 80V MOSFET 驱动\n- CCC 认证通过",
+ 'specs' => "驱动能力: 4A peak\n工作电压: 4.5~28V\n封装: SOP-8\n工作温度: -40~125C\n认证: CCC / CE / FCC",
+ 'status' => 'published',
+ 'sort' => 3,
+ ],
+ ];
+
+ foreach ($items as $item) {
+ Product::firstOrCreate(['slug' => $item['slug']], $item);
+ }
+ }
+}
diff --git a/database/seeders/SettingsSeeder.php b/database/seeders/SettingsSeeder.php
new file mode 100644
index 0000000..b6810bf
--- /dev/null
+++ b/database/seeders/SettingsSeeder.php
@@ -0,0 +1,50 @@
+ ['value' => '深圳市云创芯电子有限公司', 'group' => 'general'],
+ 'site_description' => [
+ 'value' => '深圳市云创芯电子有限公司提供多口 TypeC 接口IC、协议转换与同步整流控制芯片,面向充电与外设厂商;支持标准的TypeC PD协议,包含快充、数据转换、功率智能分配。',
+ 'group' => 'seo',
+ ],
+ 'seo_title_default' => ['value' => '深圳市云创芯电子有限公司 - 多口 TypeC 接口IC', 'group' => 'seo'],
+ 'seo_description_default' => [
+ 'value' => '深圳市云创芯电子有限公司提供多口 TypeC 接口IC、协议转换与同步整流控制芯片,面向充电与外设厂商;支持标准的TypeC PD协议,包含快充、数据转换、功率智能分配。',
+ 'group' => 'seo',
+ ],
+ 'icp_no' => ['value' => '粤ICP备16118477号-2', 'group' => 'general'],
+ // 公安联网备案号(管局强制悬挂,与 ICP 并列)。由管理员在后台填写真实号。
+ 'security_filing_no' => ['value' => '', 'group' => 'general'],
+ // 站内展示用横版 PNG(5KB,2x 高清屏适配),而非 123KB 的 ICO
+ 'logo_url' => ['value' => '/images/logo-dark.png', 'group' => 'general'],
+ // 浏览器标签页图标用方形 ICO
+ 'favicon_url' => ['value' => '/images/ccc-icon.ico', 'group' => 'general'],
+ 'contact_email' => ['value' => 'contact@cloud-chip.cn', 'group' => 'general'],
+ // 前台主题配色(后台「站点设置」可切换):azure / slate / indigo / teal / amber
+ 'theme' => ['value' => 'azure', 'group' => 'appearance'],
+ // 首页 Hero 显示模式:carousel(轮播)/ single(独立单图)
+ 'hero_mode' => ['value' => 'carousel', 'group' => 'appearance'],
+ // 页脚富文本内容(后台「站点设置」用富文本编辑器维护,留空则不显示)
+ 'footer_content' => ['value' => '', 'group' => 'general'],
+ ];
+
+ foreach ($defaults as $key => $item) {
+ Setting::firstOrCreate(
+ ['key' => $key],
+ ['value' => $item['value'], 'group' => $item['group']]
+ );
+ }
+ }
+}
diff --git a/database/seeders/SolutionsSeeder.php b/database/seeders/SolutionsSeeder.php
new file mode 100644
index 0000000..a8ac2dc
--- /dev/null
+++ b/database/seeders/SolutionsSeeder.php
@@ -0,0 +1,75 @@
+ '30W GaN 迷你快充方案',
+ 'slug' => '30w-gan-mini-charger',
+ 'power_segment' => '30-65',
+ 'application' => 'charger',
+ 'chip_platform' => 'protocol',
+ 'power' => '30W',
+ 'summary' => '面向手机/小设备的单口氮化镓迷你充电方案,高集成、低温升,适合便携式配件。',
+ 'advantages' => "高集成协议芯片,BOM 更精简\n氮化镓方案,体积较传统缩小约 45%\n全电压段效率 >92%,低温升",
+ 'description' => "本方案基于高集成 USB PD 协议芯片,搭配氮化镓(GaN)功率器件,提供 30W(20V/1.5A 或 15V/2A 等)单口输出。\n\n适用于手机、耳机、手表等小功率设备的便携快充配件,在保证功率密度的同时控制温升,便于通过各国安规与能效认证。",
+ 'status' => 'published',
+ 'sort' => 10,
+ ],
+ [
+ 'name' => '65W 双口氮化镓充电器方案',
+ 'slug' => '65w-dual-port-gan',
+ 'power_segment' => '30-65',
+ 'application' => 'charger',
+ 'chip_platform' => 'soc',
+ 'power' => '65W',
+ 'summary' => '双 Type-C 口动态功率分配方案,单口 65W、双口 45W+20W,适配笔电与手机同时快充。',
+ 'advantages' => "双口动态功率分配,智能识别接入设备\n单芯片 SOC 方案,降低外围复杂度\n支持 PPS,兼容主流品牌快充协议",
+ 'description' => "65W 双口氮化镓方案采用单芯片 SOC 架构,C1/C2 双 Type-C 口可动态分配功率:单口最高 65W(20V/3.25A),双口 45W+20W。\n\n内置 PPS 支持,广泛兼容笔记本、平板与手机,适合多设备用户的桌面与差旅场景。",
+ 'status' => 'published',
+ 'sort' => 20,
+ ],
+ [
+ 'name' => '100W 三口桌面充电站方案',
+ 'slug' => '100w-triple-port-dock',
+ 'power_segment' => '65-100',
+ 'application' => 'dock',
+ 'chip_platform' => 'soc',
+ 'power' => '100W',
+ 'summary' => '三口(2C1A)桌面充电站参考设计,总功率 100W,为多设备办公桌提供一站式快充。',
+ 'advantages' => "2C1A 三口,总功率 100W\n独立多路同步整流,端口互不干扰\n智能温控与过流保护,长时间满载稳定",
+ 'description' => "100W 桌面充电站方案提供 2×Type-C + 1×USB-A 三口输出,总功率 100W,可同时为笔记本、手机与配件充电。\n\n采用多路独立同步整流与智能功率调度,配合完善的保护机制,适合办公桌、床头等固定场景的一站式充电需求。",
+ 'status' => 'published',
+ 'sort' => 30,
+ ],
+ [
+ 'name' => '140W 多设备快充方案',
+ 'slug' => '140w-multi-device',
+ 'power_segment' => 'gt100',
+ 'application' => 'charger',
+ 'chip_platform' => 'rectifier',
+ 'power' => '140W',
+ 'summary' => '面向高性能笔电与多设备的 140W(28V/5A)PD3.1 EPR 方案,支持大电流同步整流。',
+ 'advantages' => "支持 PD3.1 EPR 28V/5A 达 140W\n大电流同步整流,效率与温升兼优\n面向游戏本、工作站等高性能设备",
+ 'description' => "140W 多设备方案基于 PD3.1 EPR 规范,支持 28V/5A 输出,可为高性能笔记本、移动工作站等大功耗设备供电。\n\n采用大电流同步整流平台,兼顾转换效率与温升控制,是高端快充与多设备供电场景的参考设计。",
+ 'status' => 'published',
+ 'sort' => 40,
+ ],
+ ];
+
+ foreach ($items as $item) {
+ Solution::firstOrCreate(['slug' => $item['slug']], $item);
+ }
+ }
+}
diff --git a/database/seeders/data/site_content.json b/database/seeders/data/site_content.json
new file mode 100644
index 0000000..2378c63
--- /dev/null
+++ b/database/seeders/data/site_content.json
@@ -0,0 +1,526 @@
+{
+ "categories": [
+ {
+ "id": 1,
+ "name": "PD PHY 控制 IC",
+ "slug": "pd-phy-controller",
+ "type": "product",
+ "parent_id": null,
+ "sort": 1,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ },
+ {
+ "id": 2,
+ "name": "协议转换 IC",
+ "slug": "protocol-bridge",
+ "type": "product",
+ "parent_id": null,
+ "sort": 2,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ },
+ {
+ "id": 3,
+ "name": "同步整流 IC",
+ "slug": "sync-rectifier",
+ "type": "product",
+ "parent_id": null,
+ "sort": 3,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ },
+ {
+ "id": 4,
+ "name": "设计指南",
+ "slug": "design-guide",
+ "type": "article",
+ "parent_id": null,
+ "sort": 1,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ },
+ {
+ "id": 5,
+ "name": "应用笔记",
+ "slug": "app-note",
+ "type": "article",
+ "parent_id": null,
+ "sort": 2,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ },
+ {
+ "id": 6,
+ "name": "行业方案",
+ "slug": "industry-solution",
+ "type": "article",
+ "parent_id": null,
+ "sort": 3,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02"
+ }
+ ],
+ "products": [
+ {
+ "category_id": 1,
+ "name": "CC-PD3168 多口 PD3.X PHY 控制 IC",
+ "slug": "cc-pd3168",
+ "seo_title": null,
+ "seo_description": null,
+ "model": "CC-PD3168",
+ "summary": "支持 3 口独立快充协商,内置 3168 协议栈,适用于桌面充与车充。",
+ "description": "CC-PD3168 是一款面向多口充电器的 PD3.X PHY 控制芯片,支持 USB PD Rev3.0 协议。\n\n主要特性:\n- 3 口独立 PD 协商\n- 内置 32-bit MCU\n- 支持 PPS / AVR / SPRS\n- 待机功耗低于 30mW\n- 封装 QFN-32",
+ "specs": "接口: I2C / UART\n工作电压: 3.3V\n封装: QFN-32 (5x5mm)\n工作温度: -40~85C\n认证: USB-IF PD3.X Certified",
+ "cover": null,
+ "status": "published",
+ "sort": 1,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02",
+ "deleted_at": null
+ },
+ {
+ "category_id": 2,
+ "name": "CC-PT6540 PHY 接口协议转换 IC",
+ "slug": "cc-pt6540",
+ "seo_title": null,
+ "seo_description": null,
+ "model": "CC-PT6540",
+ "summary": "USB-C 到传统 PHY 接口桥接,向下兼容 BC1.2 与 Apple 2.4A。",
+ "description": "CC-PT6540 提供 USB-C 到传统 PHY 接口的桥接功能,支持多种快充协议自动切换。\n\n主要特性:\n- USB-C DFP/DRP 角色\n- BC1.2 DCP/CDP/SDP 兼容\n- Apple 2.4A 兼容\n- 高电压耐受 28V",
+ "specs": "接口: USB-C / D+/D-\n工作电压: 3.3V\n封装: QFN-24 (4x4mm)\n工作温度: -40~85C",
+ "cover": null,
+ "status": "published",
+ "sort": 2,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02",
+ "deleted_at": null
+ },
+ {
+ "category_id": 3,
+ "name": "CC-SC128 同步整流控制器",
+ "slug": "cc-sc128",
+ "seo_title": null,
+ "seo_description": null,
+ "model": "CC-SC128",
+ "summary": "高效率同步整流,待机功耗低于 30mW,通过 CCC 认证。",
+ "description": "CC-SC128 是一款高效率同步整流控制器,适用于反激式和正激式变换器的副边整流。\n\n主要特性:\n- 超低待机功耗 30mW\n- 自适应导通时间控制\n- 支持 CCM/DCM/QM 三种模式\n- 内置 80V MOSFET 驱动\n- CCC 认证通过",
+ "specs": "驱动能力: 4A peak\n工作电压: 4.5~28V\n封装: SOP-8\n工作温度: -40~125C\n认证: CCC / CE / FCC",
+ "cover": null,
+ "status": "published",
+ "sort": 3,
+ "created_at": "2026-08-01 12:49:02",
+ "updated_at": "2026-08-01 12:49:02",
+ "deleted_at": null
+ }
+ ],
+ "articles": [
+ {
+ "category_id": 4,
+ "user_id": null,
+ "title": "多口 PD 充电器设计要点:端口优先级与功率分配策略",
+ "slug": "multi-port-pd-design-guide",
+ "summary": "本文介绍多口 PD 充电器中端口优先级判定、动态功率分配和热管理的设计要点。",
+ "body": "## 端口优先级\n\n多口 PD 充电器需要根据接入设备类型和数量动态分配功率。常见策略包括:\n\n1. **均分策略**:各口平均分配总功率\n2. **优先级策略**:C1 口优先获得最大功率\n3. **自适应策略**:根据设备请求的 PD contract 动态调整\n\n## 功率分配\n\n以 CC-PD3168 为例,3 口总功率 100W:\n- 单口接入时:C1/C2/C3 均可输出 100W (20V/5A)\n- 双口接入时:C1=65W, C2=30W\n- 三口接入时:C1=65W, C2=20W, C3=15W\n\n## 热管理\n\n多口同时工作时热量集中,需要合理的 PCB 布局和散热设计。",
+ "cover": null,
+ "author": "云创芯技术团队",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-07-25 20:54:40",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": null,
+ "title": "USB PD3.X 协议解析:PPS 与 SPRS 模式",
+ "slug": "usb-pd3-pps-sprs-explained",
+ "summary": "深入解析 USB PD3.X 中的 PPS(可编程电源)和 SPRS(系统电源参考)模式。",
+ "body": "## PPS 模式\n\nPPS(Programmable Power Supply)允许电源在 3.3V 到 21V 之间以 20mV 步进调整输出电压,实现更精细的电压匹配。\n\n## SPRS 模式\n\nSPRS(System Power Reference)是 PD3.X 中新增的参考电压机制,用于多设备系统级电源管理。\n\n## 应用场景\n\n- 笔记本快充:PPS 可降低转换损耗约 5%\n- 移动电源:SPRS 简化多设备充电管理",
+ "cover": null,
+ "author": "云创芯技术团队",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-07-29 20:54:40",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "一文读懂PD与QC充电协议",
+ "slug": "csdn-pd-qc-protocol",
+ "summary": "从 Type-C 接口、CC 配置通道到 PD 与 QC 快充协议的完整原理解析。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:tianpu2320959696),原文链接:https://blog.csdn.net/tianpu2320959696/article/details/145109516。内容按本站排版规范整理,版权归原作者所有。\n\nUSB-PD(Power Delivery)是基于 USB Type-C 的一种电源供电标准,最大供电功率可达 100 瓦(W)。随着 USB Type-C 的普及,越来越多的设备(手机、平板、显示器、工作站、充电器等)使用 USB-PD 快速充电方案。\n\n一、USB Type-C 简介\nType-C 是 USB 接口的一种形式,不分正反两面均可插入,支持 USB 标准的充电、数据传输、视频传输、音频传输、显示输出等功能。支持 USB-PD 后则可实现高达 100W 的电源供电。USB-PD 通过 Type-C 的“配置通道引脚 CC”进行通讯。\n\nUSB Type-C 引入了双角色能力。每根 USB Type-C 电缆的两端完全等同,连接起来的两台设备必须相互沟通以确定自己应作为主机还是外设。用于数据通讯的主机端口被称为下行端口(DFP),外设端口被称为上行端口(UFP)。电源方面,供电端被称为源端(Source),耗电端被称为吸端(Sink)。CC 线在两台设备连接期间起到定义电源角色的作用。\n\nUSB Type-C 系统带来的另一个好处是较高的供电能力。传统的 USB 电缆只容许提供 2.5W 功率,USB Type-C 电缆则容许提供高达 5V/3A 即 15W 的最大功率;如果采用了电源传输(PD)协议,电压电流指标就可以提升到 20V/5A 即 100W 的最大功率。新的 USB PD 3.0 协议还支持可编程电源(PPS),容许对总线电压和电流进行精确调节,电压可以低于 5V,实现高效直充。\n\n二、USB Type-C 标准进行数据和电源配置的方法\n未扭转、未翻转的直接连接:从左侧的插座到右侧的插座,RX1 线对连接到 TX1 线对,RX2 线对连接到 TX2 线对;D+ 与 D+ 连接,D- 与 D- 连接,SBU1 与 SBU2 连接,CC1 经由 CC 线与 CC1 连接。电缆两端的 VCONN 不需要连通。\n\n电缆扭转以后的连接:RX1 线对连接到 TX2 线对,RX2 线对连接到 TX1 线对。总共有 4 种可能的连接方式:插座翻转或不翻转,电缆扭转或不扭转。通过测量每个端子上 CC1/2 的状态即可了解电缆和插座的方向。\n\n三、电源传输\n在不采用电源传输协议的 USB Type-C 接口中,电源从源端传输到吸端由分压器 Rp/Rd 确定,但源端只会供应 5V 电压。引入电源传输(PD)协议以后,USB Type-C 系统的总线电压可以增加到最高 20V,源端和吸端之间关于总线电压和电流的交流通过在 CC 线上传输串行的 BMC 编码来完成。\n\n包含 PD 协议的 USB Type-C 系统中,源端内部包含了一个受源端 PD 控制器控制的电压转换器,可以是 Buck、Boost、Buck-Boost 或反激式转换器。当电缆连接建立好以后,PD 协议的 SOP 通讯就开始在 CC 线上进行以选择电源传输的规格:吸端询问源端能够提供的电源配置参数,从能力数据中选择适当的配置并发出请求,源端接受请求并将总线电压修改成相应参数。在总线电压变化期间,吸端的电流消耗会保持尽量小;源端提升总线电压按定义好的速度进行;达到数值后等待稳定再发送电源准备好信号。\n\nUSB PD 通讯使用的是双相标记码(BMC),每个数据包含有 0/1 交替的前置码、报文起始码(SOP)、报文头、信息数据字节、CRC 循环冗余编码和报文结束码(EOP)。\n\n四、电源配置清单\nUSB PD 3.0 规范定义了下列电源配置:有 4 个独立的电压值预先定义好:5V、9V、15V 和 20V。对于 5V、9V 和 15V,最大电流为 3A。在 20V 配置中,普通电缆最大容许 20V/3A 即 60W;使用含电子标签电缆可放大到 20V/5A 即 100W。系统支持最高电压功率等级时还必须同时支持所有较低的电压和功率等级。\n\n五、含有电子标签的电缆\nUSB Type-C 规范定义各种不同规格的电缆。支持超速数据传输或电流超过 3A 的电缆必须使用电子标签进行标识。含有电子标签 IC 的电缆中的 VCONN 上都含有 1kΩ 的下拉电阻 Ra,其值小于典型值 5.1kΩ 的 Rd。源端据此知道电缆中含有电子标签,于是将 5V 的 VCONN 电源接入 CC 线实现对电缆内部电路供电。其后发生的 PD 通讯包含源端和电子标签之间的通讯(SOP’/SOP’’)以及源端和吸端之间的通讯(SOP)。\n\n六、电源的双重角色\n有的 USB Type-C 设备既可做源端又可做吸端,被称为支持双重角色的设备(DRP)。电源角色的转换在连接期间也可以发生,只要有一台 DRP 设备发起角色变换的要求。\n\n七、QC2.0 协议\nQC2.0 是高通发布的快充技术 2.0,基于 D+/D- 输出电压信号给充电器,充电器内置 USB 输入解码芯片后输出目标电压。QC2.0 支持 5V、9V、12V、20V 四组电压(ClassA 支持 5V/9V/12V,ClassB 增加 20V)。\n\n八、QC3.0 协议\nQC3.0 是 QC2.0 的升级版,支持输出电压以 0.2V 为步长变化(3.6V 到 20V)。QC3.0 有 ClassA(3.6V-12V)和 ClassB(3.6V-20V)。最大负载电流限制为 3A,最高功率可达 60W。\n\nQC 快充原理:在充电电流限额的情况下来增加输出电压提高充电功率。普通手机 5V1A,支持快充可提高到 9V1A 或 12V1A。QC3.0 以 200mV 小步幅增加或降低 VBUS,让便携设备选择最适合的电压达到理想充电效率。",
+ "cover": null,
+ "author": "tianpu2320959696",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "一文读懂PD与QC充电协议",
+ "seo_description": "从 Type-C 接口、CC 配置通道到 PD 与 QC 快充协议的完整原理解析。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "基于 TPS25772-Q1 的 65W 车载 USB PD 充电器设计实战指南",
+ "slug": "csdn-tps25772q1-65w-car-pd",
+ "summary": "车载恶劣电气环境下,用 TI TPS25772-Q1 车规级 PD 控制器设计 65W 单口车载充电器的全流程实战。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:weixin_29174013),原文链接:https://blog.csdn.net/weixin_29174013/article/details/162863990。内容按本站排版规范整理,版权归原作者所有。\n\n在如今这个移动设备不离身的时代,一个高效、快速且可靠的充电方案,尤其是在汽车这样的移动场景中,变得前所未有的重要。USB Power Delivery(PD)协议的出现,彻底改变了我们为设备“加油”的方式。它不再局限于传统的 5V/2A,而是通过 USB Type-C 接口内的 CC 线进行智能握手,动态协商出高达 20V/5A(100W)的供电能力。对于车载环境而言,这意味着你可以在通勤途中,为高性能笔记本或平板电脑快速补充大量电量。\n\n然而,将这项技术搬上车,挑战也随之而来。汽车电气环境堪称“恶劣”:电池电压在冷启动时可能低至 6V,而在负载突降时又可能飙升至 36V 甚至更高;引擎舱内温度范围极宽(-40°C 到 +105°C);电磁干扰(EMI)无处不在。这就要求车载 PD 充电器的核心——电源管理芯片,必须具备宽输入电压范围、高效率、高可靠性以及强大的抗干扰能力。\n\n德州仪器(TI)的 TPS25772-Q1 正是为此而生的车规级解决方案。它集成了一个高效的同步 Buck-Boost 转换器,能够无缝地在降压、升压和升降压模式间切换,确保无论车辆电池电压高于、低于还是等于设备所需的充电电压,都能稳定输出。更重要的是,它内置了完整的 USB PD 协议控制器,符合最新的 PD 3.0 规范,支持 PPS 等高级特性。\n\n一、核心芯片与方案选型解析\nTPS25772-Q1 能脱颖而出,关键在于其“All-in-One”的集成度和车规级的可靠性设计。首先,它通过了 AEC-Q100 认证。其次,它将 Buck-Boost 功率级、MOSFET 驱动器、电流采样放大器、USB PD 协议栈、甚至微控制器(Cortex-M0)都集成在了一颗芯片里。这种高度集成带来了 BOM 更精简、PCB 面积更小、系统可靠性更高的直接好处。\n\n选择 65W 作为设计目标,是一个兼顾性能、成本和通用性的平衡点。65W 足以满足绝大多数轻薄型笔记本电脑的全速充电需求。从电气规格看,65W 输出通常对应 20V/3.25A 或 15V/4.33A 等组合。TPS25772-Q1 的同步 Buck-Boost 架构,其每个开关管的峰值电流能力是设计的关键瓶颈。\n\n二、关键外围电路设计与计算\nEN/UVLO 引脚决定了芯片何时开始工作。TPS25772-Q1 内部有一个固定的欠压锁定阈值(约 5V-5.5V)。但车载电池电压波动大,我们通常希望设置一个更高的启动电压(例如 6.5V),以避免在电池电压偏低时系统反复启停。\n\n设计方法是通过电阻分压网络(RENT, RENB)将输入电压分压后送入 EN/UVLO 引脚。当该引脚电压超过内部基准 VEN(OPER)(典型值 1.25V)时,芯片启动。计算过程:\n1. 确定目标开启电压 VON = 6.5V。\n2. 选择下偏置电阻 RENB = 22kΩ(折中起点,功耗与漏电流平衡)。\n3. 计算上偏置电阻 RENT = (VON / VEN(OPER) - 1) * RENB = (6.5/1.25 - 1) * 22kΩ ≈ 92.4kΩ。\n4. 选取标准值 91kΩ,复算实际开启电压 VON_actual = (91/22 + 1) * 1.25V ≈ 6.42V,可接受。\n5. 关闭电压 VOFF 因约 100mV 迟滞约为 5.91V,提供约 500mV 回差防止临界点振荡。\n6. 耐压检查:最大瞬态 36V 下 EN/UVLO 引脚电压约 7.0V,处于临界点,实际如需应对更高抛负载脉冲应增加稳压管或调整电阻比。\n\n实操心得:EN/UVLO 电路是系统的“守门员”,务必使用 1% 精度电阻,确保批量生产时启停电压一致。",
+ "cover": null,
+ "author": "weixin_29174013",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "基于 TPS25772-Q1 的 65W 车载 USB PD 充电器设计实战指南",
+ "seo_description": "车载恶劣电气环境下,用 TI TPS25772-Q1 车规级 PD 控制器设计 65W 单口车载充电器的全流程实战。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PD 协议:高压快充的智能协商过程",
+ "slug": "csdn-pd-negotiation-process",
+ "summary": "拆解 PD 协议四步电力协商,以及 LDR6020 芯片在 OTG 转接器中的三重角色。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:Legendary_008),原文链接:https://blog.csdn.net/Legendary_008/article/details/157583751。内容按本站排版规范整理,版权归原作者所有。\n\nUSB Power Delivery(PD)协议之所以能实现超越传统 USB 的高压快充,核心在于其标准化的双向协商机制,整个高压申请过程如同一场精密的“电力对话”,分为四个关键步骤:\n\n一、初始连接与身份识别\n当支持 PD 协议的设备通过 Type-C 接口连接充电器时,接口中的 CC(Configuration Channel)引脚立即启动通信。此时充电器作为“供电端(Source)”,设备作为“受电端(Sink)”,通过 CC 线建立双向数据链路,完成初始握手确认双方均支持 PD 协议版本(如 PD 2.0/3.0/3.1)。这一步就像充电设备与终端的“身份验证”。\n\n二、供电能力公示(PDO 交互)\n握手成功后,供电端通过 SOP 通信,向受电端发送 PDO(Power Data Object)数据包,清晰列出自身支持的所有供电规格——包括电压档位(5V、9V、12V、15V、20V,PD 3.1 最高可达 48V)、对应电流及功率上限。例如一款 65W PD 充电器会公示“5V/3A、9V/3A、12V/3A、15V/3A、20V/3.25A”等可选配置,相当于向设备提供“电力菜单”。\n\n三、高压需求申请(REQUEST 协商)\n受电端根据自身电池管理系统的需求,从“电力菜单”中选择匹配的高压规格,通过 REQUEST 数据包向供电端发送申请。比如手机可能请求“15V/3A”,笔记本电脑可能需要“20V/5A”。PD 协议支持 PPS 技术的芯片还能实现 20mV 级的电压微调。\n\n四、电压切换与稳定供电\n供电端接收到请求后,通过 PD 控制器调节内部电压转换器(如 Buck-Boost 电路),将 VBUS 电压从初始的 5V 平稳抬升至协商后的高压,并实时反馈确认信号。此时设备进入稳定快充状态,PD 芯片会持续监控电压、电流变化。\n\n五、OTG 转接器的核心:PD 芯片 LDR6020 的三重关键角色\nLDR6020 支持 USB PD 2.0 并兼容 PD 3.0,核心具备 PDO 与 REQUEST 数据包透传功能。在 OTG 转接场景中,芯片作为中间枢纽,将手机的高压快充请求精准传递给 PD 充电器,同时把充电器的供电规格反馈给手机。它具备双 USB-C DRP 接口,能自动识别连接设备类型,通过 DR SWAP 实现 UFP 模式切换,让手机同时作为“数据主机”和“受电设备”。此外还具备 VDM 协商功能,可引导智能设备进入 ALT 模式,支持 DP 信号输出等扩展应用。\n\n技术融合的实际价值:PD 协议的高压协商机制与 LDR6020 芯片的角色赋能,共同推动 OTG 转接器功能升级。手机可通过转接器实现 20V 高压快充的同时流畅传输 4K 视频;笔记本扩展外接设备时无需担忧电量消耗。从技术本质看,LDR6020 的核心价值在于“无缝整合”。",
+ "cover": null,
+ "author": "Legendary_008",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": "PD 协议:高压快充的智能协商过程",
+ "seo_description": "拆解 PD 协议四步电力协商,以及 LDR6020 芯片在 OTG 转接器中的三重角色。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "USB Type-C 技术全景",
+ "slug": "csdn-usb-typec-panorama",
+ "summary": "从接口形态、24 引脚、CC 配置通道到 USB-PD 协议与 Alt Mode 的完整技术地图。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:u011487024),原文链接:https://blog.csdn.net/u011487024/article/details/158320825。内容按本站排版规范整理,版权归原作者所有。\n\n本文系统介绍 USB Type-C 的接口形态演进、USB 协议版本、24 引脚硬件原理、CC 配置通道、USB-PD 通信协议及 Alt Mode 扩展功能,覆盖从接口定义到 PD 时序的关键知识点。\n\n一、接口形态演进\n早期 USB 接口形态多样、互不兼容:标准 USB-A(主机侧)、USB-B(打印机/显示器)、Micro-B(手机)等。USB Type-C 以其紧凑尺寸、正反盲插、高功能集成度,成为统一接口标准,可承载从 USB 2.0 到 USB4 的协议能力。\n\n二、USB 协议版本演进\n版本与速率:USB 2.0(480 Mbit/s,D+/D-)、USB 3.2 Gen1(5 Gbit/s)、USB 3.2 Gen2(10 Gbit/s,可双通道 20 Gbit/s)、USB4(20/40 Gbit/s,整合 Thunderbolt 3)。\n\n三、Type-C 硬件原理\nType-C 插座为 24-Pin 全功能接口,上下两排对称布局,是实现正反插的基础;插头通常为 16-Pin。按功能可分为电源、USB 2.0 数据、高速数据、配置通道与边带信号。\n- VBUS:供电线,默认 5V;经 USB-PD 协商可升至 9V/12V/15V/20V,支持 100W。\n- D+/D-:USB 2.0 差分数据线,最高 480 Mbit/s。\n- TX1±/TX2±、RX1±/RX2±:高速差分对,用于 USB 3.x 与 USB4,Alt Mode 下可复用为视频通道。\n- CC1/CC2:配置通道,承担连接检测、正反插识别、角色协商与 USB-PD 通信。\n- SBU1/SBU2:边带信号,用于 DisplayPort Alt Mode 的 AUX 通道、模拟音频等。\n\nCC 电阻典型值:Source(DFP)Rp 10k/22k/56kΩ 上拉表示电流能力;Sink(UFP)Rd 5.1kΩ;无源线缆 Ra 1kΩ。\n\n四、USB-PD 通信协议\nUSB-PD 在 CC 引脚上运行,采用 BMC(Biphase Mark Coding)调制与 4b/5b 编解码,在单根 CC 线上实现半双工数字通信。常见版本 PD 2.0/PD 3.0,后者支持 PPS。\n- 主要能力:高功率快充(典型 100W)、供电角色协商(Power Role Swap)、数据角色协商(Data Role Swap)、Alt Mode 协商。\n- 固定电压档位(典型 PDO):5V(2.5W~15W)、9V(15W~27W)、12V(18W~36W)、15V(30W~45W)、20V(45W~100W)。\n- 通信与编码:BMC 每位周期必有跳变;4b/5b 保证直流平衡;半双工需 GoodCRC 确认与重传。\n- 常见 PD 消息:Source_Capability、Request、Accept/Reject、PS_RDY、Get_Source_Cap、PR_Swap/DR_Swap。\n\n五、Alt Mode 扩展功能\n通过 PD 协议协商,Type-C 可进入不同 Alternate Mode:DisplayPort Alt Mode(4K/8K)、Thunderbolt(英特尔)、音频适配器模式、厂商调试/烧录。\n\n六、Type-C 24-Pin 引脚定义\nA1/A12 GND,A4/A9 VBUS,A5 CC1,B5 CC2,A6/A7 D+/D-,A2/A3/A10/A11 高速 TX1/RX1、TX2/RX2,A8/B8 SBU1/SBU2。正反插时仅一侧 CC 连通,主机据此判定方向并映射 TX/RX。\n\n七、USB-PD 通信时序\nPD 报文在 CC 线上以 BMC 编码传输,单帧含:Idle、Hard Reset、Soft Reset、Preamble(01 交替≥64 位)、SOP(SOP/SOP’/SOP’’)、Message Header、Data Object(s)(PDO/Request/VDM 等)、CRC、EOP。收到有效报文后接收方回复 GoodCRC,未收到则重传。\n\n示例:Sink 请求 9V\n1. Source→Sink:Source_Capability,宣告 5V/9V/15V 等。\n2. Sink→Source:Request,指定 9V 及电流。\n3. Source→Sink:Accept。\n4. Source 将 VBUS 调整至 9V。\n5. Source→Sink:PS_RDY,可安全使用新电压。\n\n术语速查:DFP 下行端口;UFP 上行端口;DRP 双角色端口;CC 配置通道;Rp/Rd/Ra 上拉/下拉/线缆电阻;BMC 双相标记编码;PDO/VDM/SVID 电源数据对象/厂商自定义消息/标准 ID;PS_RDY 电源就绪。",
+ "cover": null,
+ "author": "u011487024",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "USB Type-C 技术全景",
+ "seo_description": "从接口形态、24 引脚、CC 配置通道到 USB-PD 协议与 Alt Mode 的完整技术地图。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "USB PD 3.1 协议解析:从 100W 到 240W 的 3 个关键升级点",
+ "slug": "csdn-pd31-100w-240w",
+ "summary": "PD 3.1 如何通过 EPR、AVS 与 PPS 增强,把功率上限从 100W 推到 240W。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:weixin_30654583),原文链接:https://blog.csdn.net/weixin_30654583/article/details/97826512。内容按本站排版规范整理,版权归原作者所有。\n\n2021 年发布的 USB PD 3.1 不仅打破了传统电源适配器的功率限制,更重新定义了设备供电方式——从智能手机到工业设备,一套充电生态系统即可满足所有需求。\n\n一、功率革命:EPR 扩展功率范围的诞生\nUSB PD 3.1 最显著的突破在于将最大功率从 100W 提升至 240W,通过引入扩展功率范围(EPR)的全新架构实现。3.1 版本将功率划分为两个独立区间:\n- 标准功率(SPR):5V/9V/15V/20V,5A,100W,手机/平板/轻薄本。\n- 扩展功率(EPR):28V/36V/48V,5A,240W,工作站/服务器/电动工具。\n\n分级设计带来三个关键优势:高压低电流传输(48V 下仅需 5A 即可 240W,相比 20V/5A 减少 60% 线缆发热);兼容性保障(传统设备仍可识别 SPR 标准电压档);安全阈值控制(EPR 需专用含 E-Marker 芯片线缆)。\n\n实测:为 16 英寸 MacBook Pro(140W)充电时,PD 3.0(20V/5A)线损约 8-10W,PD 3.1(28V/5A)线损降至 3-4W,充电器表面温度降低约 15℃。\n\n二、电压革新:新增 28V/36V/48V 智能调节机制\n固定电压档应用场景:28V(4K 专业显示器)、36V(移动工作站)、48V(电动工具/服务器)。并引入可编程电压调节(AVS)技术,最小调节步进 0.1V,带来动态效率优化、温度控制、老化补偿三大价值。采用 AVS 的方案相比固定电压档:充电效率提升 3-5%,电池循环寿命延长 20%,满电时间缩短 8-12 分钟。\n\n三、协议升级:PPS 增强与多协议融合\nPD 3.1 对 PPS 做三项关键增强:调节精度从 20mV/step 提升到 10mV/step;响应速度从 100ms 缩短至 50ms;多协议握手通过 USB4 通道与其他快充协议共存(QC5、SCP、VOOC)。典型流程:设备发 EPR 能力请求 → 充电器返回最高电压档 → 协商 AVS 范围 → 建立加密认证链路 → 实时监测调整。\n\n四、应用场景与设备兼容性\n消费电子:游戏笔记本(200W+)、8K 便携显示器、VR 一体机。专业/工业:摄影补光灯、电动工具、移动服务器。PD 3.0 设备连接 PD 3.1 充电器时,部分反而快 15-20%,得益于 EPR 更高效电压转换。\n\n五、设计挑战与解决方案\n三大门槛:热管理(GaN + 3D 堆叠 PCB)、线缆损耗控制(5A 下电阻 <50mΩ,镀银线芯+双层屏蔽)、安全防护(数字证书认证 RSA-2048、毫秒级过压保护 <3ms、固件级熔断)。ST 的 STUSB02 在 240W 满载时转换效率达 98.2%,待机 <10mW。",
+ "cover": null,
+ "author": "weixin_30654583",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "USB PD 3.1 协议解析:从 100W 到 240W 的 3 个关键升级点",
+ "seo_description": "PD 3.1 如何通过 EPR、AVS 与 PPS 增强,把功率上限从 100W 推到 240W。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PD 充电头是怎么跟手机或笔记本“商量”出合适电压的?",
+ "slug": "csdn-pd-charger-negotiate-voltage",
+ "summary": "用 Source/Sink/线缆三角色模型,讲清 PD 充电头如何通过 CC 线协商多电压输出。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:CSDN文库),原文链接:https://wenku.csdn.net/answer/82r8b9t3i4ui。内容按本站排版规范整理,版权归原作者所有。\n\nPD 充电头实现多电压输出依赖 USB PD 协议驱动的智能协商机制:连接后,充电头通过 Type-C 接口的 CC 线向设备发送自身支持的电压电流组合(PDO);设备端根据电池状态选择最适档位并发出 Request 请求;充电头校验后通过 Accept 和 PS_RDY 消息确认,并控制内部开关电源电路精准切换至目标电压(如 9V/15V/20V)。整个过程在毫秒级完成,配合 OVP/OCP/OTP 等多重保护。E-Marker 芯片在线缆中协同保障高功率传输安全。\n\n一、连接与能力发现\n充电头内部的 Source 芯片通过 CC 线发送 Source Capabilities 消息(PDO),列出所有输出电压和电流组合(如 5V/3A、9V/3A、12V/3A、15V/3A、20V/5A)。\n\n二、请求与协商\n设备端的 PD Sink 芯片(如 ECP5701/ECP5702、XSP16)收到能力列表后,根据电池管理系统状态选择最合适的档位,通过 CC 线发送 Request 消息指定所选档位。\n\n三、电压切换与确认\n充电头 Source 芯片收到请求后安全校验,回复 Accept,内部控制电路(Buck 或 Flyback 拓扑)将输出调整至请求值,电压稳定后发送 PS_RDY 告知供电就绪。\n\n四、动态管理与保护\n供电过程中通信不停止,双方持续监控电压、电流、温度。设备需求变化可发新 Request 调整参数。协议芯片集成 OVP/OCP/OTP,异常时立即关闭输出或降功率。\n\n三角色芯片对比:\n- 供电端(Source):PD 协议控制器/Source 芯片,宣告能力、解析请求、控制电源切换。典型如 TI TPS65988。\n- 受电端(Sink):PD 取电/诱骗芯片,读取能力、发起电压请求。典型如 ECP5701/5702、XSP16、XSP04。\n- 连接媒介:E-Marker 芯片,宣告线缆承载能力,安全保障与能力中介。典型如慧能泰 HUSB332。\n\n硬件基础:PD 充电头核心是高效开关电源电路(反激式或 LLC 谐振拓扑)。协议芯片通过改变反馈网络参考电压或直接控制 PWM 占空比调节输出电压。总结:PD 实现多电压输出是“通信先行,电力跟随”的智能化过程。",
+ "cover": null,
+ "author": "CSDN文库",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": "PD 充电头是怎么跟手机或笔记本“商量”出合适电压的?",
+ "seo_description": "用 Source/Sink/线缆三角色模型,讲清 PD 充电头如何通过 CC 线协商多电压输出。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "USB-C PD 3.1 协议解析:从 5V/3A 到 48V/5A 的 240W 功率跃迁",
+ "slug": "csdn-pd31-240w-leap",
+ "summary": "从架构、线缆热管理到实测场景,拆解 PD 3.1 EPR 240W 的工程实践。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:CSDN文库),原文链接:https://wenku.csdn.net/column/kiiqowloqf3。内容按本站排版规范整理,版权归原作者所有。\n\n当你的笔记本电脑充电器体积缩小一半却功率翻倍时,背后是 USB Power Delivery 3.1 协议的革新。这场从 5V/3A 到 48V/5A 的电力革命,正在重塑电子设备的供电路径。\n\n一、PD 3.1 协议架构解析\nPD 3.1 通过引入扩展功率范围(EPR)将上限提升至 240W,包含三个关键技术创新:\n- 电压档位扩展:新增 28V、36V、48V 三个 EPR 电压档位,维持 SPR 的 5V/9V/15V/20V,动态电压切换响应时间 <50ms。\n- 线缆识别系统升级:EPR 模式下必须使用经过认证的 240W 线缆,普通 USB-C 线缆在 48V 高压下可能引发熔毁风险。\n- 安全防护体系:EPR 必须使用认证线缆。\n\n二、新旧协议技术指标对比\n参数 | PD 3.0 (SPR) | PD 3.1 (EPR) | 提升\n最大功率 | 100W | 240W | 140%\n最高电压 | 20V | 48V | 140%\n最大电流 | 5A | 5A | -\n线缆要求 | 3A/5A | 5A EPR 认证 | -\n\n三、高功率实现的工程挑战\n线缆设计与热管理:导体截面积 28AWG→24AWG(增加 58%);双绞 CC 线加入电压补偿算法;实时温度监控点间距 ≤15cm。EPR 方案采用镀锡铜编织屏蔽层、硅胶导热填充、铝箔反射层。\n端口电路设计:48V Buck-Boost 转换器控制逻辑需实时 ADC 采样、PID 调节 PWM 占空比。\n\n四、典型应用场景实测\n游戏本充电测试(ROG Zephyrus 2023):0-50% 充电 19 分钟(PD 3.1)vs 35 分钟(PD 3.0);满负载效率 89% @48V/5A。4 盘位 NAS 供电方案可通过功率分配控制器将 240W 分配给硬盘、主板、风扇等。\n\n五、开发选型指南\n芯片方案对比:TI TPS65988(支持 EPR 的完整 PD 控制器)、Cypress CCG6DF(双端口)、NXP PTN5150(低成本 SPR)。测试工具推荐 Total Phase USB PD Analyzer、Keysight 示波器。实际项目中 48V 系统 PCB 走线遵循 3W 原则,电源平面建议 2oz 铜厚。",
+ "cover": null,
+ "author": "CSDN文库",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "USB-C PD 3.1 协议解析:从 5V/3A 到 48V/5A 的 240W 功率跃迁",
+ "seo_description": "从架构、线缆热管理到实测场景,拆解 PD 3.1 EPR 240W 的工程实践。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "手把手教你用 VL822-QFN88 设计一个支持 TypeC 快速充电的 Type-C 四口 HUB",
+ "slug": "csdn-vl822-qfn88-pd-hub",
+ "summary": "基于 VL822-QFN88 的 10Gbps HUB 芯片,打造支持 TypeC 快速充电的 Type-C 四口扩展坞全流程实战。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:weixin_30832405),原文链接:https://blog.csdn.net/weixin_30832405/article/details/98115182。内容按本站排版规范整理,版权归原作者所有。\n\n在当今多设备协同工作的场景下,一款支持高速数据传输与快速充电的多端口扩展坞已成为工程师工作台和数码爱好者的刚需。本文将深入探讨如何基于 VL822-QFN88 这颗 10Gbps HUB 芯片,打造一个支持 Power Delivery 快充协议的 Type-C 四口扩展坞,聚焦从芯片选型到 PCB 布局的全流程实战细节。\n\n一、核心芯片选型与系统架构设计\nVL822-QFN88 的关键特性:作为 USB3.1 Gen2(10Gbps)HUB 控制器,在 10×10mm 封装内集成四个下行端口管理能力。与 QFN76/QFN56 相比,QFN88 提供更完整的 Type-C 接口支持:上行端口支持通过 DFP CC 协议芯片(如 VP246)实现 Type-C 母座输入;下行端口最多搭配两个 UFP CC 协议芯片(如 VP225)实现 Type-C 输出,同时保留两个传统 USB-A 端口;功耗管理支持高/低功耗固件切换。\n\nTypeC 快速充电芯片的选型策略:市场主流方案包括 LDR6282(100W,多协议兼容,高端商用扩展坞)、VL103(60W,高集成度)、VL102(45W,低成本)。实际项目推荐 LDR6282+VL822-QFN88 组合,支持最高 100W PD3.X、完整 CC 通信逻辑、固件升级路径。\n\n二、原理图设计关键要点\n电源树与 PD 协商电路:典型设计包含 5V 基础供电(VL822 核心逻辑和 USB 数据线)、PD 可调输出(LDR6282 控制的 Buck-Boost)、3.3V 辅助电源(CC 逻辑芯片和 Flash)。PDO 配置流程:set_voltage_range(5.0, 20.0)、set_current_limit(5.0)、enable_pps_mode(True)。\n\n复位电路设计:推荐 PD 芯片 GPIO 控制方案,时序精确、响应快(比专用复位引脚快 200-300ms)、故障恢复能力强。典型连接:LDR6282_GPIO3 → VL822_RESET → Ethernet_IC_RESET → CardReader_RESET。\n\n三、PCB 布局实战经验\n高速信号布线:差分对长度匹配 ±5mil,参考平面完整避免跨分割,每对差分线不超过 2 个过孔。关键参数:差分阻抗 90Ω(85-95Ω),单端阻抗 45Ω(42-48Ω),线间距 ≥3×线宽,弯曲半径 ≥1.5×线宽。\n热设计与 EMC:四端口全负载芯片温度可能达 85°C 以上,建议在 VL822 和 PD 芯片下方布置 2oz 铜箔、添加散热过孔阵列;USB 数据线串联共模扼流圈,时钟信号包地处理。\n\n四、固件配置与功能调优\nVL822 支持 SPI 接口烧录低功耗/高功耗固件。烧录步骤:连接 SPI 编程器 → 擦除(约 20 秒)→ 写入镜像(约 256KB)→ 验证校验和。烧录前务必确认固件版本与硬件匹配。(注:本文为技术要点整理/节选,完整内容请访问上方原文链接。)",
+ "cover": null,
+ "author": "weixin_30832405",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "手把手教你用 VL822-QFN88 设计一个支持 TypeC 快速充电的 Type-C 四口 HUB",
+ "seo_description": "基于 VL822-QFN88 的 10Gbps HUB 芯片,打造支持 TypeC 快速充电的 Type-C 四口扩展坞全流程实战。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PD3.1 协议深度解析:从 SPR 到 EPR 的功率跃迁之路",
+ "slug": "csdn-pd31-spr-epr",
+ "summary": "深入 EPR 心跳包、AVS 电压调节等 PD 3.1 高压快充核心机制。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:weixin_30919429),原文链接:https://blog.csdn.net/weixin_30919429/article/details/95589608。内容按本站排版规范整理,版权归原作者所有。\n\nPD3.1 协议最关键的突破,是把电压上限从 21V 直接拉升到 48V,功率上限提升至 240W——相当于用一根 Type-C 线缆带动一台高性能游戏本全速运行。\n\n一、PD3.1 的前世今生:为什么我们需要 EPR?\nSPR(Standard Power Range)即 PD3.X 时代 5V-20V/5A 的经典方案。但给 16 英寸 MacBook Pro 充电时,100W 在满载场景不够用,剪辑 4K 视频瞬时功耗常突破 130W。EPR 新增 28V/36V/48V 三档电压,配合 AVS 可调电压技术,像给快充系统装上了涡轮增压。\n\n容易混淆的概念:EPR 理论峰值 48V5A=240W,但移动电源常见的 140W 方案采用 28V5A 配置(锂电池组放电电压 24V-30V,48V 需复杂升压电路)。某品牌移动电源 EPR 实际输出 28.2V±0.5V,通过 AVS 微调补偿线损。\n\n二、硬件工程师必须掌握的 EPR 核心机制\n心跳包:高功率下的安全绳。当协议切换到 28V 以上高压时,设备每隔 300ms 互相确认状态。若 Source 端连续丢失 3 个心跳包(约 900ms),必须自动降级到 SPR 模式,能在 5μs 内切断 48V 输出,比传统过流保护快 20 倍。\n\nAVS 电压调节的硬件实现:EPR 的 100mV 步进需要特别精准的反馈电路。开发板调试时发现,AVS 调节需配合高精度的电压采样与环路补偿,才能在高电压下稳定工作。(注:本文为技术要点整理/节选,完整内容请访问上方原文链接。)",
+ "cover": null,
+ "author": "weixin_30919429",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "PD3.1 协议深度解析:从 SPR 到 EPR 的功率跃迁之路",
+ "seo_description": "深入 EPR 心跳包、AVS 电压调节等 PD 3.1 高压快充核心机制。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "手把手教你用 FUSB302B 芯片实现 TypeC 快速充电协议(附 STM32 I2C 驱动代码)",
+ "slug": "csdn-fusb302b-pd-protocol",
+ "summary": "从芯片特性、硬件电路到寄存器配置,实战 FUSB302B PD 协议控制器开发。",
+ "body": "【转载声明】本文转载自 CSDN 博客(原作者:CSDN文库),原文链接:https://wenku.csdn.net/column/w2mxn122e08。内容按本站排版规范整理,版权归原作者所有。\n\nType-C 接口的普及让 USB TypeC 快速充电技术成为电子设备供电的主流方案。本文将深入解析 FUSB302B 这颗高集成度 PD 协议芯片的应用方法,从硬件设计到固件开发提供完整项目实现路径。\n\n一、TypeC 快速充电技术基础与芯片选型\n主流快充协议对比:BC1.2(7.5W,固定 5V,D+/D-)、QC3.0(36W,200mV 步进,D+/D-)、PD3.X(100W,动态可调,CC 线,笔记本/手机)。\nPD 协议核心优势:通过 CC 线双向数字通信,支持 3-21V 宽范围电压调节,最高 5A 电流,是跨设备供电理想选择。\n\nFUSB302B 芯片特性:1) Type-C 接口检测(自动识别连接状态和插头方向);2) BMC 编解码(实现 PD 物理层通信);3) 角色控制(Source/Sink/DRP 多模式)。内部结构分 CC 引脚检测电路、BMC 编解码引擎、I2C 接口控制器三模块。选择 FUSB302B 而非分立方案,可减少 BOM 成本约 30%,降低协议栈开发难度。\n\n二、硬件电路设计与实现\n典型应用电路:VBUS → FUSB302B → CC1/CC2;FUSB302B → MCU(I2C);MCU → MOSFET → 负载;MOSFET → 电压反馈。\n关键元件选型:输入电容至少 10μF 低 ESR 陶瓷电容;CC 引脚电阻 56kΩ±1%;I2C 上拉 4.7kΩ 至 VDD。\nPCB 布局:数字地与功率地分开;CC 走线长度 ≤50mm,避免与高频信号平行;芯片底部敷铜加强散热,预留测试点。\n\n三、寄存器配置与协议实现\nFUSB302B 通过 24 个寄存器控制功能,最重要的是 SWITCHES0 寄存器(0x02)配置 CC 线连接与角色。固件需依次完成:上电初始化与 CC 连接检测、接收 Source_Capabilities、解析并选择 PDO、构建并发送 Request、等待 Accept 与 PS_RDY、使能功率路径。通过 I2C 读写寄存器实现完整 PD 协商状态机。(注:本文为技术要点整理/节选,完整内容请访问上方原文链接。)",
+ "cover": null,
+ "author": "CSDN文库",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": "手把手教你用 FUSB302B 芯片实现 TypeC 快速充电协议(附 STM32 I2C 驱动代码)",
+ "seo_description": "从芯片特性、硬件电路到寄存器配置,实战 FUSB302B PD 协议控制器开发。",
+ "published_at": "2026-08-02 10:38:13",
+ "created_at": "2026-08-02 10:38:13",
+ "updated_at": "2026-08-02 10:38:13",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PD 3.1 EPR 线缆与 EMCA 电子标记原理",
+ "slug": "orig-pd-emca-eemark",
+ "summary": "240W 大电流下被动线缆不可用,EMCA 电子标记如何成为 5A/48V 的安全闸门。",
+ "body": "\n【原创声明】本文由云芯技术团队原创撰写,欢迎技术交流,转载请保留出处。\n\n一、为什么 EPR 需要 EMCA\nUSB PD 3.1 把最大功率从 100W 提升到 240W(48V/5A)。5A 大电流对线缆提出硬性要求:普通被动线缆在 5A 下压降和发热不可接受,因此 EPR 线缆必须内置电子标记芯片(EMCA,Electronically Marked Cable Assembly)。\n\n二、EMCA 的供电与通信\nEMCA 连接在 CC 线上的 Ra 电阻(800Ω~1.2kΩ)并联一个 VCONN 供电的小芯片。源端通过 VCONN(典型 1W 以内)给 EMCA 供电,再用 BMC 信令在 CC 线上读取芯片内存储的线缆描述符(Cable VDO):\n- 最大电流能力(3A / 5A)\n- 最高电压(20V / 50V)\n- 是否支持 EPR\n- 线缆长度、厂商信息\n\n三、关键设计要点\n1. VCONN 电容:EMCA 侧需 100nF~10μF 旁路,保证 BMC 通信不掉电。\n2. 浪涌防护:EPR 48V 插拔时 CC 线会承受瞬态高压,EMCA IO 需 60V 以上耐压。\n3. 握手时序:源端先以 SOP 读取线标记,确认支持 5A/50V 才允许进入 EPR AVS 档位;否则锁定在 SPR 100W。\n4. 双头 EMCA:部分 240W 线缆两端都有标记芯片(SOP'/SOP''),需分别寻址。\n\n四、常见坑\n- 用 3A 线缆强上 5A:线缆过热,充电器应直接拒绝。\n- EMCA 耐压不足:48V 插拔浪涌击穿,表现为插上即掉电。\n- 忽略 VDO 版本:PD 3.1 的 Cable VDO 字段扩展了 EPR 位,旧解析逻辑会读错能力。\n\n五、小结\nEPR 240W 不是简单提高电压,而是\"线缆+协议+保护\"三位一体。EMCA 是 5A/48V 的安全闸门,选型与解析都不能省。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "氮化镓 100W 多口 PD 方案设计",
+ "slug": "orig-gan-100w-multi-port",
+ "summary": "2C1A 百瓦充电器拓扑、GaN 选型与多口功率智能分配的量产经验。",
+ "body": "\n【原创声明】本文由云芯技术团队基于量产项目经验原创整理。\n\n一、需求与拓扑\n100W 多口(2C1A)充电器的典型目标:单口 C1 最高 100W,多口同插时智能降至 65W+30W 或 45W+30W+18W。核心是\"一颗初级 PWM + 同步整流 + 多路独立降压(Buck)\"。\n\n二、为什么用 GaN\nGaN HEMT 开关损耗低,可把开关频率提到 300kHz~500kHz,缩小变压器与滤波电感体积,是 100W 做到 65cc 口袋尺寸的关键。\n\n三、协议与功率分配\n1. 协议芯片(如 CH224/CH254 或定制 PD PHY)挂在每个 Type-C 的 CC 上,独立完成 PD 协商。\n2. 总功率由一颗 MCU 或电源管理芯片统筹:依据各口连接设备回报的 RDO(Requested Data Object)动态分配。\n3. 降档策略:C1 插笔记本申请 100W → 满供;再插 C2 手机 → C1 降 65W、C2 给 30W;A 口插旧设备按 18W 上限。\n\n四、热设计\n- GaN 与同步整流共用散热片,温升需 < 60K。\n- 多口满载做温度闭环:壳温超阈值先降电流再降电压,避免触发 OTP 关机。\n\n五、量产注意\n- 共模噪声:多路 Buck 同频干扰,需错相(interleaving)或展频。\n- 安规:100W 属 Class II,初次级间距、Y 电容、绝缘都按 IEC 62368 走。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "CC 通道与 Rp/Rd 电阻配置详解",
+ "slug": "orig-cc-rp-rd",
+ "summary": "从方向检测到供电能力通告,讲清 Type-C 两根 CC 线与 Rp/Rd 电阻的硬件逻辑。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,面向初次接触 Type-C 的硬件工程师。\n\n一、CC 是什么\nType-C 有两根 CC 线(CC1/CC2)。连接时只有一根被用上(另一根做 VCONN 给 EMCA 供电)。CC 承担三大任务:方向检测、供电能力通告、PD 通信载波。\n\n二、Rp 与 Rd\n- 源端(充电器)在 CC 上拉电阻 Rp,阻值代表它能提供的电流档:默认 USB(500mA/900mA)、1.5A、3.0A。\n- sink 端(设备)在 CC 下拉电阻 Rd(5.1kΩ)。两端接上后,sink 通过测量 CC 电压判断源端能力。\n\n三、电压区间\n- 开路 ~0V\n- Rd 到地:根据 Rp 不同,CC 电压落在 ~0.4V(默认)、~0.95V(1.5A)、~1.7V(3A)区间。sink 用比较器或 ADC 区分这三档。\n\n四、方向检测\n插头只连一根 CC。设备看 CC1/CC2 哪根有 Rd 压降,就知道插头正反,从而切换 TX/RX 与 SuperSpeed 走线。\n\n五、设计要点\n1. Rp 用电流源或电阻均可,但需满足 BC1.2 兼容。\n2. EMCA 占用 CC 时,Ra(~1kΩ)替代 Rd,源端据此识别\"线缆已标记\"。\n3. 死区电压:避免 3A 与 1.5A 判定边界附近抖动,建议加迟滞。\n\n六、调试技巧\n用示波器抓 CC 线:插拔瞬间看电压台阶,能快速区分是 Rp 配置错还是 sink 没拉 Rd。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PPS 可编程电源与充电曲线优化",
+ "slug": "orig-pps-curve",
+ "summary": "PPS 让充电器直接贴合电池电压,阶梯跟随 + 温度闭环带来效率与温升双重收益。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,结合手机充电实测。\n\n一、PPS 解决了什么\n传统 PD 固定档(5V/9V/15V/20V)给电池充电时,手机内部还要再做一次 Buck,效率损失大、发热高。PPS(Programmable Power Supply)允许电压以 20mV 步进、电流以 50mA 步进在 3.3V~21V 连续调节,让充电器直接贴合电池电压。\n\n二、电池充电模型\n锂电池充电分恒流(CC)与恒压(CV)两段:\n- 低电量:电池电压低,需要大电流,PPS 给低电压高电流(如 4.0V/5A)。\n- 接近满电:电池电压逼近 4.2V/4.45V,PPS 小幅抬升电压同时收电流,避免过充。\n\n三、曲线优化\n1. 阶梯跟随:手机 PMIC 实时回报目标电压/电流,充电器每 10ms 微调一次。\n2. 温度闭环:电池超 45℃ 时降电流,PPS 立刻响应,比固定档切换更平滑。\n3. 效率拐点:每颗电芯有最优充电区间,PPS 让变换器始终工作在高效点。\n\n四、协议注意\n- PPS RDO 里带最小电压、最大电压、最大电流字段,充电器必须落在窗口内。\n- 步进粒度:申请 20mV 步进,老芯片只支持 100mV 会导致曲线阶梯化、发热上升。\n\n五、实测收益\n对比 9V 固定档 + 手机内 Buck,PPS 直充整机效率提升约 3%~5%,壳温降低 4~8℃。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "PD 诱骗取电模块设计要点",
+ "slug": "orig-decoy-module",
+ "summary": "小家电/DIY 取电场景下,诱骗模块如何向充电器申请目标档位并安全稳压输出。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,面向小家电/DIY 取电场景。\n\n一、什么时候需要诱骗\n很多设备(小风扇、台灯、路由器改供)只想要一个稳定的 9V/12V/20V,但普通 USB-A 只能给 5V。PD 诱骗模块扮演\"假设备\",向充电器申请指定档位后稳压输出。\n\n二、核心元件\n- PD 协议芯片(如 CH224、IP2721):内置 PD 协商状态机,通过电阻或引脚配置目标电压。\n- 后级 LDO/DC-DC:把协商到的电压降到负载需要的电平,并隔离纹波。\n\n三、配置方式\n1. 电阻分压:CH224 的 CFG 脚接不同阻值选 5V/9V/12V/15V/20V。\n2. 多电压切换:用 MCU 改 CFG 或在协议层发不同 RDO,实现档位按钮切换。\n\n四、坑与对策\n- 充电器不支持目标档:诱骗失败回落 5V,负载需容忍 5V 或做欠压保护。\n- 线缆只 3A:申请 20V/5A 实际被限流,按 3A 设计余量。\n- 浪涌:20V 诱骗成功瞬间,后级电容充电电流大,加软启动。\n- 协议超时:部分充电器要求周期性重协商,诱骗芯片需保活。\n\n五、安全边界\n诱骗模块不是充电器,输出功率受源头限制。标称 20V/5A 时必须确认充电器与线缆都支持 5A,否则自焚。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "VBUS 过压保护与滤波电路设计",
+ "slug": "orig-vbus-protection",
+ "summary": "插拔浪涌、误档位、ESD 三重威胁下,OVP 与 π 型滤波如何让 VBUS 可靠。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,聚焦可靠性设计。\n\n一、VBUS 面临的威胁\n- 插拔浪涌:热插拔瞬间电容充电,VBUS 可冲到 24V+(设计 20V 口)。\n- 错误档位:协商失败误上 20V 给只支持 5V 的设备。\n- ESD/EFT:人体放电、快充脉冲群。\n\n二、OVP 方案\n1. 理想二极管 + 分压采样:用运放比较 VBUS 与阈值(如 6.0V for 5V 口),超限切断背靠背 MOS。\n2. 专用 OVP 芯片:集成 28V 耐压、固定阈值、自动恢复,简化设计。\n3. TVS:VBUS 到地放 24V~30V 钳位二极管,吸收瞬态。\n\n三、滤波\n- 输入 π 型滤波(C-L-C)抑制共模与差模噪声,尤其多口同频 Buck 时。\n- 输出加 47μF~220μF 低 ESR 电解 + 100nF 陶瓷,降低负载瞬态跌落。\n\n四、布局要点\n- TVS 尽量靠近 Type-C 座,走线短而粗。\n- OVP MOS 散热焊盘铺铜,过流时温升可控。\n- 地分割:VBUS 功率地与信号地单点连接,避免噪声串入 CC。\n\n五、验证\n- 注入 1kV ESD 看是否复位。\n- 强制误档(手动给 20V 到 5V 口)确认 OVP 在 1ms 内切断。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "tech",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 4,
+ "user_id": 1,
+ "title": "PD 与 QC 共存时协商优先级",
+ "slug": "orig-protocol-priority",
+ "summary": "多协议充电器如何在 CC(PD)与 D+/D-(QC)之间做互斥调度与平滑切换。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,讲清多协议充电器的调度逻辑。\n\n一、为什么要共存\n市场上设备五花八门:iPhone 要 PD、老安卓要 QC2.0/QC3.0、部分笔电要 PD+PPS。单协议充电器会被吐槽\"充得慢\",所以多协议是标配。\n\n二、物理层差异\n- PD 走 CC 线 BMC 编码。\n- QC 走 D+/D- 电压握手(QC2.0 是 D+ 固定电平,QC3.0 是 D- 连续调节)。\n两者信号域不同,可并行检测。\n\n三、优先级策略\n1. 先侦测 CC:有 Rp 且设备拉 Rd → 走 PD,最高效,优先。\n2. 无 CC 响应 → 转 D+ 识别 BC1.2,再尝试 QC。\n3. 都识别不了 → 5V 默认。\n\n四、冲突避免\n- 不可同时 BMC 与 QC 握手,需状态机互斥。\n- 协议切换间隙把 VBUS 拉回 5V 再重协商,防误伤。\n- 设备中途变更(如插上瞬间 QC、稳定后 PD)需支持平滑降级。\n\n五、实战建议\n量产前用协议分析仪抓 CC 与 D+/D- 波形,确认切换无 5V↔20V 毛刺,否则会触发设备端 OVP。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ },
+ {
+ "category_id": 5,
+ "user_id": 1,
+ "title": "多口 PD 动态功率分配",
+ "slug": "orig-dynamic-power-share",
+ "summary": "把总功率当池子,讲透多口充电器如何做基线保障、剩余竞拍与用户无感切换。",
+ "body": "\n【原创声明】本文由云芯技术团队原创,讲透\"总功率池\"调度。\n\n一、功率池模型\n把电源总功率(如 100W)当成一个池子,每个口按需取水。核心约束:Σ(各口实时功率) ≤ 100W。\n\n二、分配算法\n1. 基线分配:每个口先给最低保障(C 口 15W、A 口 10W)。\n2. 剩余竞拍:空闲功率按\"先到先得 + 设备申请上限\"分给已连接设备。\n3. 重协商:新设备插入或旧设备拔掉,触发全局重算,向各口下发新 RDO。\n\n三、用户无感切换\n- 插第二台设备时,第一台从 100W 平滑降到 65W,不应断充。\n- 降档瞬时 VBUS 变化控制在 ±5% 内,避免设备掉线。\n\n四、边界处理\n- 单口封顶:C1 物理上限 100W、C2 上限 65W,防止抢光池子。\n- 过流保护:某口短路,独立限流并隔离,不影响其他口。\n- 冷启动顺序:上电先 5V,协商完再升档,杜绝共启动浪涌叠加。\n\n五、趋势\n新一代用数字电源(PFC+LLC 数字控制)把功率池做成软件可配,OTA 就能调分配策略,是 140W/240W 多口的方向。",
+ "cover": null,
+ "author": "云芯技术团队(原创)",
+ "status": "approved",
+ "template": "standard",
+ "seo_title": null,
+ "seo_description": null,
+ "published_at": "2026-08-02 10:43:30",
+ "created_at": "2026-08-02 10:43:30",
+ "updated_at": "2026-08-02 10:43:30",
+ "deleted_at": null
+ }
+ ],
+ "glossary_terms": [
+ {
+ "term": "PD (Power Delivery)",
+ "slug": "pd-power-delivery",
+ "definition": "USB Power Delivery 协议,通过 USB-C 接口传输最高 240W (48V/5A) 的电力,支持双向供电和智能功率协商。",
+ "aliases": "USB PD, Power Delivery, 快充协议",
+ "category": "协议",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40"
+ },
+ {
+ "term": "PHY (物理层)",
+ "slug": "phy-physical-layer",
+ "definition": "PHY 是物理层(Physical Layer)的缩写,负责数据的物理传输,包括信号编码、调制解调、阻抗匹配等功能。",
+ "aliases": "Physical Layer, 物理层收发器",
+ "category": "硬件",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40"
+ },
+ {
+ "term": "PPS (可编程电源)",
+ "slug": "pps-programmable-power-supply",
+ "definition": "PPS 是 USB PD3.X 中的可编程电源模式,允许在 3.3V 到 21V 范围内以 20mV 步进调整输出电压,实现更高效的直充方案。",
+ "aliases": "Programmable Power Supply",
+ "category": "协议",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40"
+ },
+ {
+ "term": "BC1.2",
+ "slug": "bc1-2",
+ "definition": "Battery Charging Specification 1.2,USB-IF 制定的电池充电规范,定义了 DCP(专用充电口)、CDP(充电下行口)和 SDP(标准下行口)三种充电模式。",
+ "aliases": "Battery Charging 1.2",
+ "category": "协议",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40"
+ },
+ {
+ "term": "同步整流",
+ "slug": "synchronous-rectification",
+ "definition": "同步整流是用 MOSFET 替代肖特基二极管进行整流的技术,可大幅降低整流损耗,提升转换效率至 95% 以上。",
+ "aliases": "Synchronous Rectification, SR",
+ "category": "硬件",
+ "created_at": "2026-08-01 20:54:40",
+ "updated_at": "2026-08-01 20:54:40"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/install.sh b/install.sh
new file mode 100644
index 0000000..9f37823
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,172 @@
+#!/usr/bin/env bash
+# 云创芯 Laravel12 + Vue3(CDN) 一键安装脚本
+# 适用:宝塔 Nginx + PHP 8.3 + MySQL 5.7
+# 设计:零 Node、零前端构建。支持两种分发包:
+# - 完整包(含 vendor/):无需服务器安装 Composer,直接跑
+# - 源码包(不含 vendor/):脚本自动调用 Composer 安装依赖
+set -euo pipefail
+
+export COMPOSER_ALLOW_SUPERUSER=1
+
+# 颜色(ANSI 转义,非 HEX 色值)
+if [ -t 1 ]; then
+ C_OK=$'\033[0;32m'; C_WARN=$'\033[0;33m'; C_ERR=$'\033[0;31m'; C_RESET=$'\033[0m'
+else
+ C_OK=''; C_WARN=''; C_ERR=''; C_RESET=''
+fi
+
+step() { echo; echo "${C_OK}==>${C_RESET} $*"; }
+ok() { echo " ${C_OK}[OK]${C_RESET} $*"; }
+warn() { echo " ${C_WARN}[WARN]${C_RESET} $*"; }
+err() { echo " ${C_ERR}[ERROR]${C_RESET} $*"; exit 1; }
+
+ROOT="$(cd "$(dirname "$0")" && pwd)"
+cd "$ROOT"
+echo "Project root: $ROOT"
+
+# ── PHP 版本检查 ────────────────────────────────────────────────
+PHP_BIN="$(command -v php || true)"
+[ -z "$PHP_BIN" ] && err "未找到 php,请先在宝塔安装 PHP 8.3 并加入命令行。"
+PHP_VER="$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')"
+PHP_MAJOR="$(php -r 'echo PHP_MAJOR_VERSION;')"
+PHP_MINOR="$(php -r 'echo PHP_MINOR_VERSION;')"
+if [ "$PHP_MAJOR" -lt 8 ] || { [ "$PHP_MAJOR" -eq 8 ] && [ "$PHP_MINOR" -lt 2 ]; }; then
+ err "PHP $PHP_VER 过低,本项目要求 PHP >= 8.2(推荐 8.3)。"
+fi
+ok "PHP version: $PHP_VER"
+
+# ── 必要扩展检查(大小写不敏感,兼容 php -m 输出)─────────────
+REQ_EXT=(ctype curl dom fileinfo json mbstring openssl pdo pdo_mysql tokenizer xml)
+missing=()
+for ext in "${REQ_EXT[@]}"; do
+ if ! php -m | grep -iqw "$ext"; then
+ missing+=("$ext")
+ fi
+done
+if [ ${#missing[@]} -gt 0 ]; then
+ err "缺少 PHP 扩展: ${missing[*]}。请在宝塔 PHP-8.3 设置中安装后重试。"
+fi
+ok "All required PHP extensions present"
+
+# ── 依赖安装 ───────────────────────────────────────────────────
+# 完整包已内置 vendor/,直接跳过;源码包才需要 Composer。
+step "Checking PHP dependencies"
+if [ -f vendor/autoload.php ]; then
+ ok "vendor/ 已内置,跳过 Composer 安装"
+else
+ if ! command -v composer >/dev/null 2>&1; then
+ warn "Composer 未安装,正在尝试自动安装..."
+ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ php composer-setup.php --quiet
+ rm -f composer-setup.php
+ mv composer.phar /usr/local/bin/composer
+ fi
+ if ! composer --version >/dev/null 2>&1; then
+ err "Composer 不可用。宝塔 PHP 可能禁用了 putenv/proc_open/pcntl_signal,请在【软件商店 → PHP8.3 → 设置 → 禁用函数】中删除这些函数并重启 PHP-FPM;或改用内置 vendor 的完整包。"
+ fi
+ ok "Composer: $(composer --version | head -n1)"
+ composer install --no-dev --optimize-autoloader --no-interaction
+ ok "Dependencies installed"
+fi
+
+# ── 运行时目录(Laravel 启动即需 bootstrap/cache 可写)──────────
+step "Ensuring runtime directories"
+mkdir -p bootstrap/cache \
+ storage/app/public \
+ storage/framework/cache/data \
+ storage/framework/sessions \
+ storage/framework/views \
+ storage/framework/testing \
+ storage/logs
+ok "Runtime directories ready"
+
+# ── 环境配置 ────────────────────────────────────────────────────
+if [ ! -f .env ]; then
+ step "Creating .env from .env.example"
+ cp .env.example .env
+ warn ".env 已从模板创建,但数据库配置仍是默认值。"
+ warn "请先编辑 .env,填写 DB_DATABASE / DB_USERNAME / DB_PASSWORD / APP_URL,"
+ warn "保存后重新运行 ./install.sh 继续安装。"
+ exit 0
+else
+ ok ".env already exists — 跳过覆盖(如需重置请手动删除 .env)"
+fi
+
+if grep -q '^DB_PASSWORD=$' .env; then
+ err ".env 中 DB_PASSWORD 为空,请先填写数据库密码后重新运行。"
+fi
+
+# APP_KEY 为空才生成,避免覆盖已加密数据的密钥
+if grep -q '^APP_KEY=$' .env; then
+ php artisan key:generate --force
+ ok "APP_KEY generated"
+else
+ ok "APP_KEY already set — 保留现有密钥(覆盖会导致已加密数据无法解密)"
+fi
+
+# ── 存储权限 + 软链 ────────────────────────────────────────────
+step "Preparing storage"
+chmod -R 775 storage bootstrap/cache 2>/dev/null || true
+# 宝塔默认运行用户为 www
+if id www >/dev/null 2>&1; then
+ chown -R www:www storage bootstrap/cache 2>/dev/null || true
+ ok "storage / bootstrap/cache 属主已设为 www"
+fi
+php artisan storage:link --force >/dev/null 2>&1 || true
+ok "Storage ready"
+
+# ── 数据库连通性 ───────────────────────────────────────────────
+step "Testing database connection"
+if ! php -r '
+require "vendor/autoload.php";
+$app = require "bootstrap/app.php";
+$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
+try { Illuminate\Support\Facades\DB::connection()->getPdo(); echo "OK"; }
+catch (Throwable $e) { fwrite(STDERR, $e->getMessage()); exit(1); }
+' >/dev/null 2>&1; then
+ err "数据库连接失败。请检查 .env 中 DB_HOST / DB_DATABASE / DB_USERNAME / DB_PASSWORD,并确认 MySQL 已启动。"
+fi
+ok "Database reachable"
+
+# ── 数据库迁移 ─────────────────────────────────────────────────
+step "Running migrations"
+php artisan migrate --force
+ok "Migrations done"
+
+# ── 填充种子数据(全部使用 firstOrCreate,重复执行安全)────────
+step "Seeding database"
+php artisan db:seed --force
+ok "Seed data inserted (admin: admin@cloud-chip.cn / cloudchip2026)"
+
+# ── 缓存优化(生产)────────────────────────────────────────────
+step "Optimizing cache"
+php artisan config:cache
+php artisan route:cache
+php artisan view:cache
+ok "Config / route / view cache built"
+
+# ── 健康检查 ───────────────────────────────────────────────────
+step "Health check"
+APP_URL_VAL="$(grep -E '^APP_URL=' .env | head -n1 | cut -d= -f2- | tr -d '"' | tr -d "'" || true)"
+APP_URL_VAL="${APP_URL_VAL:-http://127.0.0.1}"
+if command -v curl >/dev/null 2>&1; then
+ if curl -fsS --max-time 10 "${APP_URL_VAL%/}/api/health" >/dev/null 2>&1; then
+ ok "API health: OK ($APP_URL_VAL)"
+ else
+ warn "健康检查未通过——若站点尚未在宝塔绑定域名/配置伪静态,这是正常的。"
+ warn "完成站点配置后手动验证:curl -I ${APP_URL_VAL%/}/"
+ fi
+else
+ warn "未找到 curl,跳过健康检查。"
+fi
+
+echo
+echo "${C_OK}安装完成。${C_RESET}"
+echo
+echo "下一步:"
+echo " 1. 宝塔添加站点,网站目录指向本目录下的 ${C_OK}public/${C_RESET}"
+echo " 2. 站点设置 → 伪静态 → 选择 ${C_OK}laravel5${C_RESET}(或粘贴 DEPLOY.md 中的规则)"
+echo " 3. 申请 SSL 证书并开启强制 HTTPS(.env 中 SESSION_SECURE_COOKIE=true 依赖 HTTPS)"
+echo " 4. 访问 /admin 登录后台,先到【站点设置】核对 ICP 备案号与 SEO 信息"
+echo
+echo " 管理员账号:admin@cloud-chip.cn / cloudchip2026 ${C_WARN}(务必立即在 /settings 修改密码)${C_RESET}"
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..39429f1
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,21 @@
+
+
+ Options -MultiViews -Indexes
+
+
+ RewriteEngine On
+
+ RewriteCond %{HTTP:Authorization} .
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+ RewriteCond %{REQUEST_FILENAME} -d [OR]
+ RewriteCond %{REQUEST_FILENAME} -f
+ RewriteRule ^ ^$1 [N]
+
+ RewriteCond %{REQUEST_URI} (.+)/$
+ RewriteRule ^ %1 [L,R=301]
+
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^ index.php [L]
+
diff --git a/public/css/app.css b/public/css/app.css
new file mode 100644
index 0000000..8746b9d
--- /dev/null
+++ b/public/css/app.css
@@ -0,0 +1,1096 @@
+:root {
+ /* ── 深色科技风(PD技术汇):深蓝近黑底 + 蓝/青色光效 ── */
+ --color-bg: #060b16;
+ --color-surface: #0c1525;
+ --color-surface-2: #121d33;
+ --color-border: rgba(122, 162, 214, 0.16);
+ --color-text: #e8eefb;
+ --color-text-muted: #93a6c4;
+ --color-primary: #4a8af4;
+ --color-primary-hover: #76a9f7;
+ --color-primary-soft: rgba(var(--glow-color), 0.14);
+ --color-accent: #6fa8f7;
+ --color-danger: #f87171;
+ --color-danger-hover: #ef4444;
+ --color-danger-soft: rgba(248, 113, 113, 0.14);
+ --color-success: #34d399;
+ --color-success-soft: rgba(52, 211, 153, 0.14);
+ --color-warning: #fbbf24;
+ --color-warning-strong: #d97706;
+ --color-warning-soft: rgba(251, 191, 36, 0.12);
+ --color-info: #4a8af4;
+ --color-info-soft: rgba(var(--glow-color), 0.14);
+ --color-logo-bg: #ffffff;
+
+ /* 光效(--glow-color 由主题动态注入覆盖,所有辉光跟随主题切换) */
+ --glow-color: 74, 138, 244;
+ --glow-cyan: 0 0 26px -4px rgba(var(--glow-color), 0.55);
+ --glow-soft: 0 10px 34px -12px rgba(var(--glow-color), 0.38);
+ --ring-cyan: 0 0 0 1px rgba(var(--glow-color), 0.45), 0 8px 30px -8px rgba(var(--glow-color), 0.45);
+
+ --radius-sm: 6px;
+ --radius: 10px;
+ --radius-lg: 16px;
+
+ --space-1: 4px;
+ --space-2: 8px;
+ --space-3: 12px;
+ --space-4: 16px;
+ --space-5: 24px;
+ --space-6: 32px;
+ --space-8: 48px;
+
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
+ --shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
+
+ --font-sans: "Inter", "Noto Sans SC", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
+ --transition: 0.15s ease;
+}
+
+* { box-sizing: border-box; }
+
+html, body {
+ margin: 0;
+ padding: 0;
+ background: var(--color-bg);
+ color: var(--color-text);
+ font-family: var(--font-sans);
+ font-size: 15px;
+ line-height: 1.6;
+ -webkit-font-smoothing: antialiased;
+}
+
+a { color: var(--color-primary); text-decoration: none; }
+a:hover { color: var(--color-primary-hover); }
+
+.logo-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--color-logo-bg);
+ border-radius: var(--radius-lg);
+ padding: var(--space-2);
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.04), var(--shadow-sm);
+}
+
+@media (min-width: 1024px) {
+ .logo-badge {
+ padding: var(--space-3);
+ }
+}
+
+.container {
+ width: 100%;
+ max-width: 1080px;
+ margin: 0 auto;
+ padding: 0 var(--space-4);
+}
+
+/* Header */
+.site-header {
+ border-bottom: 1px solid var(--color-border);
+ background: var(--color-bg);
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+.nav {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 60px;
+}
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-weight: 700;
+ font-size: 17px;
+ color: var(--color-text);
+}
+.brand-icon { color: var(--color-primary); }
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: var(--space-4);
+}
+.nav-links a { color: var(--color-text-muted); font-weight: 500; }
+.nav-links a:hover { color: var(--color-text); }
+.nav-user { color: var(--color-text-muted); font-size: 14px; }
+
+.inline-form { display: inline; margin: 0; }
+
+/* Main */
+.main { padding: var(--space-6) var(--space-4); min-height: 60vh; }
+
+h1 { font-size: 30px; line-height: 1.25; margin: 0 0 var(--space-4); }
+h2 { font-size: 22px; margin: var(--space-6) 0 var(--space-4); }
+h3 { font-size: 17px; margin: 0 0 var(--space-2); }
+p { margin: 0 0 var(--space-3); }
+.muted { color: var(--color-text-muted); }
+
+/* Cards */
+.card {
+ background: var(--color-surface);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ padding: var(--space-5);
+ box-shadow: var(--shadow-sm);
+}
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: var(--space-4);
+}
+.code {
+ display: inline-block;
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ font-size: 13px;
+ color: var(--color-primary);
+ background: var(--color-primary-soft);
+ padding: 2px var(--space-2);
+ border-radius: var(--radius-sm);
+}
+
+/* Buttons */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ border: 1px solid transparent;
+ border-radius: var(--radius-sm);
+ padding: var(--space-2) var(--space-4);
+ font-size: 15px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background var(--transition), border-color var(--transition), color var(--transition);
+ background: var(--color-surface-2);
+ color: var(--color-text);
+}
+.btn:hover { background: var(--color-border); }
+.btn-primary { background: var(--color-primary); color: #fff; }
+.btn-primary:hover { background: var(--color-primary-hover); color: #fff; }
+.btn-sm { padding: var(--space-1) var(--space-3); font-size: 14px; }
+.btn:disabled { opacity: 0.6; cursor: not-allowed; }
+.btn-link {
+ background: none;
+ border: none;
+ color: var(--color-text-muted);
+ cursor: pointer;
+ font: inherit;
+ font-weight: 500;
+ padding: 0;
+}
+.btn-link:hover { color: var(--color-text); }
+
+/* Forms */
+.field { margin-bottom: var(--space-4); }
+.field label {
+ display: block;
+ font-weight: 600;
+ font-size: 14px;
+ margin-bottom: var(--space-2);
+}
+.input, .textarea, .select {
+ width: 100%;
+ padding: var(--space-3);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ background: var(--color-bg);
+ color: var(--color-text);
+ font: inherit;
+ transition: border-color var(--transition), box-shadow var(--transition);
+}
+.input:focus, .textarea:focus, .select:focus {
+ outline: none;
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 3px var(--color-primary-soft);
+}
+.textarea { min-height: 120px; resize: vertical; }
+.form-hint { font-size: 13px; color: var(--color-text-muted); margin-top: var(--space-1); }
+.char-count { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px; background: var(--color-surface-2); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
+.char-count.over { background: var(--color-danger-soft); color: var(--color-danger); font-weight: 600; }
+.field-error { color: var(--color-danger); font-size: 13px; margin-top: var(--space-1); }
+
+/* Alerts */
+.alert {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-3) var(--space-4);
+ border-radius: var(--radius-sm);
+ margin-bottom: var(--space-4);
+ font-size: 14px;
+}
+.alert-success { background: var(--color-success-soft); color: var(--color-success); border: 1px solid var(--color-success); }
+.alert-danger { background: var(--color-danger-soft); color: var(--color-danger); border: 1px solid var(--color-danger); }
+
+/* Footer */
+.site-footer {
+ border-top: 1px solid var(--color-border);
+ background: var(--color-surface);
+ padding: var(--space-6) 0;
+ margin-top: var(--space-8);
+ font-size: 14px;
+}
+.site-footer p { margin: 0 0 var(--space-1); }
+
+/* Hero */
+.hero {
+ background: var(--color-primary-soft);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-8);
+ margin-bottom: var(--space-6);
+}
+.hero h1 { margin-bottom: var(--space-3); }
+.hero .lead { font-size: 17px; color: var(--color-text-muted); max-width: 640px; }
+
+/* Search box */
+.search-box {
+ width: 100%;
+ max-width: 360px;
+ padding: var(--space-3);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ font: inherit;
+ margin-bottom: var(--space-4);
+}
+.search-box:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary-soft); }
+
+/* ===== Link cards ===== */
+.link-card {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+ color: var(--color-text);
+ transition: border-color var(--transition), box-shadow var(--transition);
+}
+.link-card:hover {
+ border-color: var(--color-primary);
+ box-shadow: var(--shadow);
+ color: var(--color-text);
+}
+.link-card h3 { color: var(--color-primary); }
+
+/* ===== Filter bar ===== */
+.filter-bar {
+ display: flex;
+ gap: var(--space-3);
+ align-items: center;
+ flex-wrap: wrap;
+}
+.filter-bar .input { max-width: 280px; }
+.filter-bar .select { max-width: 200px; }
+
+/* ===== Nav admin badge ===== */
+.nav-admin {
+ color: var(--color-primary) !important;
+ font-weight: 700 !important;
+}
+
+/* ===== Meta text ===== */
+.meta {
+ font-size: 13px;
+ color: var(--color-text-muted);
+ margin: 0 0 var(--space-2);
+}
+
+/* ===== Prose content ===== */
+.prose { line-height: 1.7; word-break: break-word; }
+.prose p { margin: 0 0 var(--space-3); }
+
+/* ===== List container ===== */
+.list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+/* ===== Reply card ===== */
+.reply { padding: var(--space-4); }
+
+/* ===== Danger button ===== */
+.btn-danger { background: var(--color-danger); color: #fff; }
+.btn-danger:hover { background: var(--color-danger-hover); color: #fff; }
+
+/* ===== Pagination (bootstrap-5 compatible) ===== */
+.pagination {
+ display: flex;
+ gap: var(--space-1);
+ list-style: none;
+ padding: 0;
+ margin: var(--space-5) 0 0;
+}
+.page-link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 36px;
+ height: 36px;
+ padding: 0 var(--space-2);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ color: var(--color-text);
+ font-size: 14px;
+ text-decoration: none;
+ transition: background var(--transition), border-color var(--transition);
+}
+.page-link:hover { background: var(--color-surface-2); border-color: var(--color-primary); color: var(--color-primary); }
+.page-item.active .page-link { background: var(--color-primary); border-color: var(--color-primary); color: #fff; }
+.page-item.disabled .page-link { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
+
+/* ===== Admin layout ===== */
+.admin-body { background: var(--color-surface); }
+.admin-shell {
+ display: flex;
+ min-height: 100vh;
+}
+.admin-side {
+ width: 240px;
+ background: var(--color-bg);
+ border-right: 1px solid var(--color-border);
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ position: sticky;
+ top: 0;
+ height: 100vh;
+ overflow-y: auto;
+}
+.admin-brand {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-weight: 700;
+ font-size: 16px;
+ padding: var(--space-5) var(--space-4);
+ border-bottom: 1px solid var(--color-border);
+ color: var(--color-text);
+}
+.admin-brand svg { color: var(--color-primary); }
+.admin-nav {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ padding: var(--space-3) 0;
+}
+.admin-nav a {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-3) var(--space-4);
+ color: var(--color-text-muted);
+ font-weight: 500;
+ font-size: 14px;
+ border-left: 3px solid transparent;
+ transition: background var(--transition), color var(--transition), border-color var(--transition);
+}
+.admin-nav a:hover { background: var(--color-surface); color: var(--color-text); }
+.admin-nav a.active { background: var(--color-primary-soft); color: var(--color-primary); border-left-color: var(--color-primary); font-weight: 600; }
+.admin-foot {
+ padding: var(--space-4);
+ border-top: 1px solid var(--color-border);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+.admin-foot a { display: flex; align-items: center; gap: var(--space-2); font-size: 14px; color: var(--color-text-muted); }
+.admin-foot a:hover { color: var(--color-text); }
+
+.admin-main {
+ flex: 1;
+ padding: var(--space-6);
+ overflow-x: auto;
+}
+.admin-topbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--space-5);
+ flex-wrap: wrap;
+ gap: var(--space-3);
+}
+.admin-topbar h1 { margin: 0; }
+.admin-actions { display: flex; gap: var(--space-2); }
+
+/* ===== Admin stat grid ===== */
+.stat-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+ gap: var(--space-4);
+ margin-bottom: var(--space-6);
+}
+.stat-card {
+ background: var(--color-bg);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ padding: var(--space-5);
+ text-align: center;
+ box-shadow: var(--shadow-sm);
+}
+.stat-num {
+ display: block;
+ font-size: 32px;
+ font-weight: 800;
+ color: var(--color-primary);
+ line-height: 1.2;
+}
+.stat-label {
+ display: block;
+ font-size: 14px;
+ color: var(--color-text-muted);
+ margin-top: var(--space-1);
+}
+
+/* ===== Admin two-column ===== */
+.admin-cols {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-4);
+}
+
+/* ===== Mini row ===== */
+.mini-row {
+ padding: var(--space-3) 0;
+ border-bottom: 1px solid var(--color-border);
+}
+.mini-row:last-child { border-bottom: none; }
+
+/* ===== Data table ===== */
+.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ background: var(--color-bg);
+ border-radius: var(--radius);
+ overflow: hidden;
+ box-shadow: var(--shadow-sm);
+}
+.data-table th,
+.data-table td {
+ padding: var(--space-3) var(--space-4);
+ text-align: left;
+ border-bottom: 1px solid var(--color-border);
+ font-size: 14px;
+}
+.data-table th {
+ background: var(--color-surface-2);
+ font-weight: 600;
+ color: var(--color-text-muted);
+ font-size: 13px;
+ white-space: nowrap;
+}
+.data-table tbody tr:hover { background: var(--color-surface); }
+.data-table td.col-op,
+.data-table th.col-op { text-align: right; white-space: nowrap; }
+.row-actions {
+ display: flex;
+ gap: var(--space-1);
+ justify-content: flex-end;
+ align-items: center;
+}
+.row-actions form { display: inline; margin: 0; }
+
+/* ===== Form card & actions ===== */
+.form-card {
+ max-width: 720px;
+ background: var(--color-bg);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ padding: var(--space-6);
+ box-shadow: var(--shadow-sm);
+}
+.form-actions {
+ display: flex;
+ gap: var(--space-3);
+ margin-top: var(--space-5);
+ padding-top: var(--space-5);
+ border-top: 1px solid var(--color-border);
+}
+
+/* ===== Row card (horizontal link card with arrow) ===== */
+.row-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-4);
+ padding: var(--space-4) var(--space-5);
+ background: var(--color-bg);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ color: var(--color-text);
+ box-shadow: var(--shadow-sm);
+ transition: border-color var(--transition), box-shadow var(--transition);
+}
+.row-card:hover {
+ border-color: var(--color-primary);
+ box-shadow: var(--shadow);
+ color: var(--color-text);
+}
+.row-card h3 { color: var(--color-primary); }
+.row-card svg { color: var(--color-text-muted); flex-shrink: 0; }
+.row-card:hover svg { color: var(--color-primary); }
+
+/* ===== Spec block ===== */
+.spec-block {
+ background: var(--color-surface-2);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-sm);
+ padding: var(--space-4);
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ font-size: 13px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ overflow-x: auto;
+}
+
+/* ===== Badge ===== */
+.badge {
+ display: inline-block;
+ padding: 2px var(--space-2);
+ font-size: 12px;
+ font-weight: 600;
+ border-radius: var(--radius-sm);
+ background: var(--color-surface-2);
+ color: var(--color-text-muted);
+}
+.badge-primary { background: var(--color-primary-soft); color: var(--color-primary); }
+
+/* 角色徽章(与 RBAC 五角色对应) */
+.badge-role-super_admin { background: var(--color-warning-soft); color: var(--color-warning); }
+.badge-role-admin { background: var(--color-primary-soft); color: var(--color-primary); }
+.badge-role-forum_admin{ background: var(--color-success-soft); color: var(--color-success); }
+.badge-role-staff { background: var(--color-info-soft); color: var(--color-info); }
+.badge-role-user { background: var(--color-surface-2); color: var(--color-text-muted); }
+
+/* 用户权限勾选组 */
+.perm-group { border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-3) var(--space-4); margin-bottom: var(--space-3); }
+.perm-group-title { font-weight: 600; margin-bottom: var(--space-2); color: var(--color-text); }
+.perm-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--space-2) var(--space-4); }
+.perm-item { display: flex; align-items: center; gap: var(--space-2); font-size: 13px; }
+.perm-item input { width: 16px; height: 16px; accent-color: var(--color-primary); }
+.role-hint { font-size: 13px; color: var(--color-text-muted); margin: var(--space-2) 0 var(--space-4); }
+
+/* ===== HR inside cards ===== */
+.card hr { border: none; border-top: 1px solid var(--color-border); margin: var(--space-4) 0; }
+
+/* ===== Responsive ===== */
+@media (max-width: 768px) {
+ .nav-links { gap: var(--space-2); flex-wrap: wrap; }
+ .admin-shell { flex-direction: column; }
+ .admin-side {
+ width: 100%;
+ height: auto;
+ position: relative;
+ border-right: none;
+ border-bottom: 1px solid var(--color-border);
+ }
+ .admin-nav { flex-direction: row; flex-wrap: wrap; padding: var(--space-2); }
+ .admin-nav a { border-left: none; border-bottom: 3px solid transparent; padding: var(--space-2) var(--space-3); }
+ .admin-nav a.active { border-left: none; border-bottom-color: var(--color-primary); }
+ .admin-foot { flex-direction: row; }
+ .admin-cols { grid-template-columns: 1fr; }
+ .filter-bar { flex-direction: column; align-items: stretch; }
+ .filter-bar .input, .filter-bar .select { max-width: 100%; }
+ .data-table { font-size: 13px; }
+ .data-table th, .data-table td { padding: var(--space-2) var(--space-3); }
+}
+
+/* ── 品牌 logo / 等级 / 标签 增强 ── */
+.brand-logo { height: 28px; width: auto; display: block; }
+.level-badge {
+ display: inline-block;
+ padding: 1px 8px;
+ border-radius: 999px;
+ background: var(--color-primary);
+ color: #fff;
+ font-size: 12px;
+ font-weight: 600;
+ margin-right: 6px;
+}
+.tag {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: var(--radius-sm);
+ background: var(--color-primary-soft);
+ color: var(--color-primary);
+ font-size: 12px;
+ margin-left: 8px;
+}
+.lead { font-size: 16px; color: var(--color-text-muted); line-height: 1.7; }
+.icp-link { color: var(--color-text-muted); margin-left: 8px; text-decoration: none; }
+.icp-link:hover { text-decoration: underline; }
+.level-bar {
+ height: 8px;
+ border-radius: 999px;
+ background: var(--color-surface-2);
+ overflow: hidden;
+ margin: 8px 0;
+}
+.level-bar-fill {
+ height: 100%;
+ background: var(--color-primary);
+ border-radius: 999px;
+ transition: width .3s ease;
+}
+
+/* 状态文字色(审核队列/文章列表用) */
+.text-warning { color: var(--color-warning-strong); }
+.text-danger { color: var(--color-danger); }
+.text-success { color: var(--color-success); }
+
+/* ── 数据库管理页 ── */
+.mb-6 { margin-bottom: var(--space-6); }
+.mt-4 { margin-top: var(--space-4); }
+.mt-6 { margin-top: var(--space-6); }
+.kv { display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-6); }
+.kv > div { display: flex; flex-direction: column; gap: 2px; }
+.kv .muted { font-size: 12px; }
+.kv strong { font-size: 16px; font-weight: 600; }
+.mig-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-2); max-height: 320px; overflow: auto; }
+.mig-list li { display: flex; align-items: center; gap: var(--space-2); font-size: 14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
+.mig-list.done li { color: var(--color-text-muted); }
+.mig-output { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-3); font-size: 13px; white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow: auto; max-height: 360px; }
+
+/* ── Quill 富文本编辑器 ── */
+.rich-editor { margin-bottom: var(--space-4); }
+.ql-toolbar.ql-snow { border-color: var(--color-border); border-top-left-radius: var(--radius-sm); border-top-right-radius: var(--radius-sm); }
+.ql-container.ql-snow { border-color: var(--color-border); border-bottom-left-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); font-family: inherit; font-size: 15px; }
+.ql-editor { min-height: 300px; }
+.ql-editor.ql-blank::before { color: var(--color-text-muted); font-style: normal; }
+
+/* ════════════ 深色科技风 · 光效工具类(PD技术汇) ════════════ */
+
+/* 文字辉光 */
+.glow-text { text-shadow: 0 0 22px rgba(var(--glow-color), 0.55); }
+
+/* 辉光卡片:边框微亮 + 柔和投影,hover 增强 */
+.glow-card {
+ border: 1px solid var(--color-border);
+ background: var(--color-surface);
+ box-shadow: var(--shadow);
+ transition: transform var(--transition), border-color var(--transition), box-shadow var(--transition);
+}
+.glow-card:hover {
+ border-color: rgba(var(--glow-color), 0.55);
+ box-shadow: var(--ring-cyan);
+ transform: translateY(-4px);
+}
+
+/* Hero 极光背景:深底 + 蓝/青径向辉光 */
+.hero-aurora {
+ position: relative;
+ background:
+ radial-gradient(60% 70% at 18% 12%, rgba(var(--glow-color), 0.20), transparent 60%),
+ radial-gradient(55% 65% at 85% 20%, rgba(var(--glow-color), 0.16), transparent 60%),
+ radial-gradient(70% 90% at 50% 110%, rgba(var(--glow-color), 0.14), transparent 60%),
+ linear-gradient(180deg, #070d1c 0%, #060b16 100%);
+ overflow: hidden;
+}
+/* Hero 网格纹理 */
+.hero-grid::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background-image:
+ linear-gradient(rgba(122, 162, 214, 0.07) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(122, 162, 214, 0.07) 1px, transparent 1px);
+ background-size: 46px 46px;
+ mask-image: radial-gradient(70% 70% at 50% 35%, #000 40%, transparent 100%);
+ -webkit-mask-image: radial-gradient(70% 70% at 50% 35%, #000 40%, transparent 100%);
+ pointer-events: none;
+}
+
+/* 区块标题小标 */
+.eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--color-primary);
+}
+.eyebrow::before {
+ content: "";
+ width: 22px;
+ height: 2px;
+ border-radius: 2px;
+ background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
+}
+
+/* 导航链接:hover 下划线高亮 */
+.nav-link {
+ position: relative;
+ color: var(--color-text-muted);
+ font-weight: 500;
+ transition: color var(--transition);
+}
+.nav-link::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ bottom: -6px;
+ height: 2px;
+ width: 0;
+ background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
+ transition: width var(--transition);
+}
+.nav-link:hover, .nav-link.active { color: var(--color-text); }
+.nav-link:hover::after, .nav-link.active::after { width: 100%; }
+
+/* 主 CTA 按钮辉光 */
+.cta-glow {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
+ color: #04121f;
+ font-weight: 700;
+ box-shadow: var(--glow-cyan);
+ transition: transform var(--transition), box-shadow var(--transition), filter var(--transition);
+}
+.cta-glow:hover {
+ transform: translateY(-2px);
+ filter: brightness(1.08);
+ box-shadow: 0 0 34px -2px rgba(var(--glow-color), 0.75);
+ color: #04121f;
+}
+
+/* 合作品牌墙:灰度 -> hover 彩色 + 放大 */
+.brand-logo-cell {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 84px;
+ padding: 12px;
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ background: var(--color-surface);
+ transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
+}
+.brand-logo-cell img,
+.brand-logo-cell .brand-fallback {
+ max-height: 44px;
+ width: auto;
+ filter: grayscale(1) brightness(1.6);
+ opacity: 0.7;
+ transition: filter var(--transition), opacity var(--transition), transform var(--transition);
+}
+.brand-logo-cell:hover {
+ border-color: rgba(var(--glow-color), 0.5);
+ box-shadow: var(--ring-cyan);
+ transform: translateY(-3px);
+}
+.brand-logo-cell:hover img,
+.brand-logo-cell:hover .brand-fallback {
+ filter: grayscale(0) brightness(1);
+ opacity: 1;
+ transform: scale(1.06);
+}
+
+/* 数据滚动数字 */
+.stat-num {
+ font-size: clamp(34px, 5vw, 52px);
+ font-weight: 800;
+ line-height: 1.1;
+ color: var(--color-primary);
+ text-shadow: 0 0 22px rgba(var(--glow-color), 0.45);
+ font-variant-numeric: tabular-nums;
+}
+
+/* 后台主题配色选择器 */
+.theme-options { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
+.theme-opt {
+ display: inline-flex; align-items: center; gap: 8px; cursor: pointer; position: relative;
+ border: 1px solid var(--color-border); border-radius: 10px;
+ padding: 8px 12px; font-size: 13px; font-weight: 600; color: var(--color-text-muted);
+ transition: border-color .15s, background .15s, color .15s;
+}
+.theme-opt:hover { border-color: var(--color-primary); }
+.theme-opt.active { border-color: var(--color-primary); background: var(--color-primary-soft); color: var(--color-text); }
+.theme-opt input { position: absolute; opacity: 0; pointer-events: none; }
+.theme-swatch { width: 18px; height: 18px; border-radius: 5px; display: inline-block; box-shadow: 0 0 0 2px rgba(255,255,255,.12); }
+
+/* 卡片图片容器(产品/文章封面) */
+.media-cover {
+ aspect-ratio: 16 / 10;
+ width: 100%;
+ object-fit: cover;
+ background: linear-gradient(135deg, #0e1830, #0a1326);
+}
+
+/* 顶部滚动后加深的背景(由 JS 切换 .scrolled) */
+.site-header.scrolled {
+ background: rgba(6, 11, 22, 0.92);
+ border-bottom-color: rgba(122, 162, 214, 0.22);
+}
+
+/* 暗色下输入框/文本域底色 */
+.input, .textarea, .select { background: var(--color-surface-2); color: var(--color-text); }
+.input:focus, .textarea:focus, .select:focus { box-shadow: 0 0 0 3px var(--color-primary-soft); }
+
+/* 分页/标签在暗色下的微调 */
+.page-link { background: var(--color-surface); color: var(--color-text); }
+.page-link:hover { background: var(--color-surface-2); }
+.page-item.active .page-link { background: var(--color-primary); border-color: var(--color-primary); color: #04121f; }
+
+/* 暗色下的 hero/卡片边框对比增强 */
+.hero, .card, .link-card, .row-card, .data-table, .form-card, .stat-card {
+ background: var(--color-surface);
+ border-color: var(--color-border);
+}
+
+/* 滚动揭示动画(仅当 JS 可用时隐藏初始态,避免无脚本时内容不可见) */
+.js .reveal {
+ opacity: 0;
+ transform: translateY(18px);
+ transition: opacity 0.6s ease, transform 0.6s ease;
+}
+.js .reveal.in {
+ opacity: 1;
+ transform: none;
+}
+@media (prefers-reduced-motion: reduce) {
+ .js .reveal { opacity: 1; transform: none; transition: none; }
+}
+
+/* 区块通用间距 */
+.section { padding-top: 64px; padding-bottom: 64px; }
+@media (min-width: 1024px) { .section { padding-top: 88px; padding-bottom: 88px; } }
+
+/* 区块标题 */
+.section-head { max-width: 720px; }
+.section-title { font-size: 28px; font-weight: 800; letter-spacing: -0.01em; color: var(--color-text); }
+@media (min-width: 1024px) { .section-title { font-size: 34px; } }
+.section-sub { margin-top: 10px; color: var(--color-text-muted); font-size: 15px; }
+
+/* 玻璃卡片(深底微透) */
+.glass {
+ background: linear-gradient(180deg, rgba(18,29,51,0.9), rgba(12,21,37,0.9));
+ border: 1px solid var(--color-border);
+ backdrop-filter: blur(6px);
+}
+
+/* Hero 标题渐变文字 */
+.gradient-text {
+ background: linear-gradient(120deg, #e8eefb 0%, #7dd3fc 55%, #22d3ee 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ color: transparent;
+}
+
+/* 暗色下主色按钮/底色文字改为深色以保证对比度 */
+.btn-primary { color: #04121f; }
+.btn-primary:hover { color: #04121f; }
+.bg-primary { color: #04121f; }
+a.bg-primary:hover, button.bg-primary:hover { color: #04121f; }
+
+/* ── 首页 Hero 轮播 ─────────────────────────────── */
+.hero-carousel { position: relative; }
+.hero-slide {
+ position: absolute;
+ inset: 0;
+ opacity: 0;
+ transition: opacity .6s ease;
+ pointer-events: none;
+}
+.hero-slide.is-active {
+ position: relative;
+ opacity: 1;
+ pointer-events: auto;
+}
+.hero-arrow {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 5;
+ width: 44px;
+ height: 44px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 28px;
+ line-height: 1;
+ color: #fff;
+ background: rgba(255, 255, 255, 0.15);
+ border: none;
+ border-radius: 9999px;
+ cursor: pointer;
+ transition: background .2s ease;
+}
+.hero-arrow:hover { background: rgba(255, 255, 255, 0.32); }
+.hero-prev { left: 0; }
+.hero-next { right: 0; }
+.hero-dots {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: -1.5rem;
+ z-index: 5;
+ display: flex;
+ gap: .5rem;
+ justify-content: center;
+}
+.hero-dot {
+ width: 10px;
+ height: 10px;
+ padding: 0;
+ border: none;
+ border-radius: 9999px;
+ background: rgba(255, 255, 255, 0.5);
+ cursor: pointer;
+ transition: width .2s ease, background .2s ease;
+}
+.hero-dot.is-active {
+ width: 24px;
+ background: #fff;
+}
+
+/* 页脚富文本区块(后台「站点设置 → 页脚自定义内容」维护) */
+.footer-rich {
+ padding-top: 1.5rem;
+ border-top: 1px solid var(--color-border);
+ font-size: 14px;
+ line-height: 1.7;
+ color: var(--color-text-muted);
+}
+.footer-rich > *:first-child { margin-top: 0; }
+.footer-rich > *:last-child { margin-bottom: 0; }
+.footer-rich h1, .footer-rich h2, .footer-rich h3, .footer-rich h4 {
+ color: var(--color-text);
+ margin: 0.6em 0 0.4em;
+ font-weight: 600;
+}
+.footer-rich p { margin: 0 0 0.6em; }
+.footer-rich a { color: var(--color-primary); text-decoration: none; }
+.footer-rich a:hover { text-decoration: underline; }
+.footer-rich ul, .footer-rich ol { padding-left: 1.25rem; margin: 0 0 0.6em; }
+.footer-rich img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 8px;
+ margin: 0.4em 0;
+}
+.footer-rich blockquote {
+ border-left: 3px solid var(--color-primary);
+ padding-left: 0.8em;
+ margin: 0.6em 0;
+ color: var(--color-text-muted);
+}
+.footer-rich table { width: 100%; border-collapse: collapse; margin: 0.6em 0; }
+.footer-rich th, .footer-rich td { border: 1px solid var(--color-border); padding: 0.4em 0.6em; }
+
+/* ════════════ 富文本渲染(Hero / 页脚 / 文章正文 复用) ════════════
+ 后台富文本由 Quill 产出语义化 HTML(h1-h3 / p / ul / 行内 color 等)
+ + 排版类(ql-align-* / ql-size-* / ql-indent-*)。前台用本块统一渲染,
+ 不再依赖把 Tailwind 工具类塞进正文。 */
+
+.rich-content { line-height: 1.7; word-break: break-word; }
+.rich-content > *:first-child { margin-top: 0; }
+.rich-content > *:last-child { margin-bottom: 0; }
+.rich-content h1 { font-size: clamp(28px, 4vw, 44px); font-weight: 800; line-height: 1.15; letter-spacing: -0.01em; margin: 0 0 0.4em; }
+.rich-content h2 { font-size: clamp(22px, 3vw, 32px); font-weight: 700; margin: 0.6em 0 0.35em; }
+.rich-content h3 { font-size: clamp(18px, 2.2vw, 24px); font-weight: 600; margin: 0.6em 0 0.35em; }
+.rich-content h4 { font-size: clamp(16px, 1.8vw, 19px); font-weight: 600; margin: 0.6em 0 0.35em; }
+.rich-content h5, .rich-content h6 { font-weight: 600; margin: 0.5em 0 0.3em; }
+.rich-content p { margin: 0 0 0.8em; }
+.rich-content ul, .rich-content ol { padding-left: 1.4em; margin: 0 0 0.8em; }
+.rich-content li { margin: 0.25em 0; }
+.rich-content a { text-decoration: underline; }
+.rich-content img { max-width: 100%; height: auto; border-radius: 10px; margin: 0.5em 0; }
+.rich-content blockquote { border-left: 3px solid currentColor; padding-left: 0.9em; margin: 0.7em 0; opacity: 0.85; }
+.rich-content code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: rgba(127,127,127,0.18); padding: 2px 6px; border-radius: 6px; font-size: 0.9em; }
+.rich-content pre { background: rgba(127,127,127,0.14); padding: 1em; border-radius: 10px; overflow: auto; margin: 0.7em 0; }
+.rich-content pre code { background: none; padding: 0; }
+.rich-content table { width: 100%; border-collapse: collapse; margin: 0.7em 0; }
+.rich-content th, .rich-content td { border: 1px solid; padding: 0.45em 0.7em; }
+.rich-content hr { border: none; border-top: 1px solid; margin: 1em 0; opacity: 0.3; }
+.rich-content strong, .rich-content b { font-weight: 700; }
+
+/* Quill 语义类(编辑器产出,Purifier 现已放行 class) */
+.rich-content .ql-align-center { text-align: center; }
+.rich-content .ql-align-right { text-align: right; }
+.rich-content .ql-align-justify { text-align: justify; }
+.rich-content .ql-size-small { font-size: 0.75em; }
+.rich-content .ql-size-large { font-size: 1.5em; }
+.rich-content .ql-size-huge { font-size: 2.25em; line-height: 1.1; }
+.rich-content .ql-indent-1 { padding-left: 2em; }
+.rich-content .ql-indent-2 { padding-left: 4em; }
+.rich-content .ql-indent-3 { padding-left: 6em; }
+.rich-content .ql-indent-4 { padding-left: 8em; }
+.rich-content .ql-indent-5 { padding-left: 10em; }
+.rich-content .ql-indent-6 { padding-left: 12em; }
+.rich-content .ql-indent-7 { padding-left: 14em; }
+.rich-content .ql-indent-8 { padding-left: 16em; }
+.rich-content .ql-direction-rtl { direction: rtl; text-align: right; }
+.rich-content .ql-syntax {
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ white-space: pre-wrap;
+ background: rgba(127,127,127,0.14);
+ padding: 1em;
+ border-radius: 10px;
+ display: block;
+ margin: 0.7em 0;
+}
+
+/* 暗底变体:用于首页 Hero 渐变背景(文字默认白) */
+.rich-content--on-dark { color: #fff; }
+.rich-content--on-dark h1, .rich-content--on-dark h2, .rich-content--on-dark h3,
+.rich-content--on-dark h4, .rich-content--on-dark h5, .rich-content--on-dark h6,
+.rich-content--on-dark strong, .rich-content--on-dark b { color: #fff; }
+.rich-content--on-dark a { color: #7dd3fc; }
+.rich-content--on-dark blockquote { border-left-color: rgba(255,255,255,0.5); }
+.rich-content--on-dark th, .rich-content--on-dark td { border-color: rgba(255,255,255,0.3); }
+.rich-content--on-dark hr { border-top-color: rgba(255,255,255,0.3); }
+
+/* Hero 正文容器:限定可读宽度,避免大屏铺满 */
+/* Hero 正文容器:居中排版,限定可读宽度并水平居中 */
+.hero-slide-content { max-width: 820px; margin: 0 auto; text-align: center; }
+
+/* Hero 小标签(富文本内可调用) */
+.hero-eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: #7dd3fc;
+ margin: 0 0 0.6em;
+}
+
+/* Hero 富文本内可复用的排版组件(不依赖 Tailwind 工具类) */
+.hero-feature-grid {
+ display: grid;
+ grid-template-columns: repeat(1, minmax(0, 1fr));
+ gap: 16px;
+ margin: 1.6em 0 0;
+}
+@media (min-width: 640px) { .hero-feature-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
+.hero-feature-card {
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ background: rgba(255, 255, 255, 0.08);
+ border-radius: 12px;
+ padding: 18px 20px;
+ backdrop-filter: blur(4px);
+}
+.hero-feature-card .hf-title { font-size: 18px; font-weight: 700; color: #fff; margin: 0 0 4px; }
+.hero-feature-card .hf-desc { font-size: 14px; color: rgba(255, 255, 255, 0.82); margin: 0; }
+.hero-callout {
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ background: rgba(255, 255, 255, 0.08);
+ border-radius: 12px;
+ padding: 16px 20px;
+ margin: 1em 0;
+}
+
+/* 页脚 / 文章正文同样渲染 Quill 排版类 */
+.footer-rich .ql-align-center, .prose .ql-align-center { text-align: center; }
+.footer-rich .ql-align-right, .prose .ql-align-right { text-align: right; }
+.footer-rich .ql-align-justify, .prose .ql-align-justify { text-align: justify; }
+.footer-rich .ql-size-small, .prose .ql-size-small { font-size: 0.75em; }
+.footer-rich .ql-size-large, .prose .ql-size-large { font-size: 1.5em; }
+.footer-rich .ql-size-huge, .prose .ql-size-huge { font-size: 2.25em; line-height: 1.1; }
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..9bc55c5746a1030da561c1533480a55c1a84b5f8
GIT binary patch
literal 27886
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
literal 0
HcmV?d00001
diff --git a/public/images/logo-56.png b/public/images/logo-56.png
new file mode 100644
index 0000000000000000000000000000000000000000..aaf0b5829ec5f27cc4d8148a85305bd99ae91d8c
GIT binary patch
literal 5128
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
literal 0
HcmV?d00001
diff --git a/public/images/logo.ico b/public/images/logo.ico
new file mode 100644
index 0000000000000000000000000000000000000000..4074af46182bf6706e0a2c2a70a94e09c7727f7e
GIT binary patch
literal 123262
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