seiger/sapi
Composer 安装命令:
composer require seiger/sapi
包简介
API tools package for Evolution CMS.
README 文档
README
sApi is a lightweight, framework-agnostic API core for Evolution CMS, designed to build modern, versioned JSON APIs with Laravel-style routing and JWT authentication.
The package provides only the essential infrastructure:
- routing
- middleware
- authentication
- extensibility for other packages
Business logic, CRUD scaffolding, GraphQL, and API documentation are intentionally out of scope of the core and should be implemented via separate modules.
Key Features
- ✅ Single front controller (
/api/index.php,/rest/index.php, etc.) - ✅ Laravel-style routing (based on
illuminate/routing) - ✅ API versioning via URL (
/v1/...) - ✅ JWT authentication (Bearer tokens)
- ✅ Middleware with scope-based authorization
- ✅ JSON-only responses (no XML, no GraphQL)
- ✅ Automatic discovery of API routes from other packages
- ✅ OpenAPI-friendly response conventions
- ✅ Designed for Evolution CMS ecosystem
Requirements
- PHP 8.4+
- Evolution CMS 3.5+
- Composer
Installation
composer require seiger/sapi
Environment Variables
Recommended location: core/custom/.env (EvolutionCMS loads it in core/bootstrap.php).
| Variable | Default | Description |
|---|---|---|
SAPI_BASE_PATH |
api |
Base prefix for all sApi routes (e.g. rest → /rest/token). |
SAPI_VERSION |
v1 |
Optional API version prefix (e.g. v1 → /api/v1/token). Leave empty to use unversioned routes. |
SAPI_JWT_SECRET |
(empty) | HS256 secret used to sign/verify JWTs. Required to issue tokens. |
SAPI_JWT_TTL |
3600 |
Token TTL in seconds. |
SAPI_JWT_SCOPES |
* |
Default token scopes (comma-separated). |
SAPI_JWT_ISS |
(empty) | Optional iss claim. |
SAPI_LOGGING_ENABLED |
1 |
Enable/disable all sApi logging. |
SAPI_LOG_ACCESS_ENABLED |
1 |
Enable/disable access log entries. |
SAPI_LOG_EXCLUDE_PATHS |
(empty) | Comma-separated paths to skip from access logging (e.g. /rest/health). |
SAPI_LOG_BODY_ON_ERROR |
1 |
Log request body for 4xx/5xx. |
SAPI_LOG_MAX_BODY_BYTES |
4096 |
Max request body bytes to log. |
SAPI_LOG_AUDIT_ENABLED |
1 |
Enable/disable audit events logging. |
SAPI_AUDIT_EXCLUDE_EVENTS |
(empty) | Comma-separated event patterns to skip (supports * via fnmatch). |
SAPI_AUDIT_MAX_CONTEXT_BYTES |
8192 |
Max audit context size. |
SAPI_REDACT_BODY_KEYS |
password,token,refresh_token,jwt,secret |
Keys to redact in logged bodies/contexts. |
APP_NAME |
evo |
Used as Monolog channel name for the sapi logger. |
LOG_LEVEL |
debug |
Log level for the sapi logger. |
LOG_DAILY_DAYS |
14 |
Retention days for daily logs. |
Basic Concept
sApi acts as an API kernel, not a full framework.
You create a single front controller file (for example /api/index.php), bootstrap Evolution CMS, and let sApi handle:
- routing
- authentication
- middleware execution
- request dispatching
All API endpoints are defined via route providers.
Front Controller Example
<?php declare(strict_types=1); define('EVO_API_MODE', true); define('IN_MANAGER_MODE', false); require_once dirname(__DIR__) . '/index.php'; require_once EVO_BASE_PATH . 'vendor/autoload.php'; use Seiger\sApi\Kernel; $kernel = new Kernel($modx); $kernel->handle();
The base API path is configured via SAPI_BASE_PATH (e.g. api, rest).
Routing
sApi uses Laravel-style routing internally.
Example (core routes)
$router->group(['prefix' => 'v1'], function () use ($router) { $router->get('health', function () { return new JsonResponse(['ok' => true]); }); });
JWT Authentication
sApi supports Bearer JWT authentication.
Authorization: Bearer <jwt-token>
Issuing a Token
To get a JWT, call the built-in token endpoint:
POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/token(ifSAPI_VERSIONis empty →/{SAPI_BASE_PATH}/token)- Body (JSON):
{ "username": "manager_user", "password": "..." } - Access is restricted by the
sapi_accessrole permission. The package migration creates theApiUserrole and grants this permission to it by default.
Success response:
{
"data": {
"access_token": "....",
"token_type": "Bearer",
"expires_in": 3600
}
}
Middleware
$router->group(['middleware' => ['jwt:orders:read']], function () use ($router) { $router->get('orders', OrdersController::class . '@index'); });
JWT payload example:
{
"sub": 12,
"scopes": ["orders:read", "orders:write"],
"exp": 1710000000
}
Access Logging (JSON lines)
sApi writes access logs for every API request under SAPI_BASE_PATH (except exclude_paths) in JSON lines format using the standard Evolution/Laravel logger (Monolog daily).
- Daily file:
core/storage/logs/sapi-YYYY-MM-DD.log - Retention:
LOG_DAILY_DAYS(default14)
Configuration is via .env variables (see SAPI_LOG_* and SAPI_REDACT_BODY_KEYS above).
Notes:
- Response always includes
X-Request-Id. Authorization/ cookies are never written to access logs.- Request body is logged only for
4xx/5xx(with redaction + size limit).
Audit Logging (Business Events)
Audit logs are explicit business events (e.g. token.issued, orders.updated) written as JSON lines into the same daily sapi log channel (timeline-friendly).
- Daily file:
core/storage/logs/sapi-YYYY-MM-DD.log - Retention:
LOG_DAILY_DAYS(default14)
Example:
app(\Seiger\sApi\Logging\AuditLogger::class)->event('orders.updated', [ 'order_id' => 123, 'status' => 'paid', ], 'notice');
Notes:
request_id,route,subcan be auto-filled from the current request context (when available).- Never log JWT tokens or secrets in audit context (redaction is applied by key name).
Route Providers (Extensibility)
Other packages (e.g. sCommerce, sSeo) can automatically register their API endpoints.
Interface
use Illuminate\Routing\Router; interface RouteProviderInterface { public function register(Router $router): void; }
Example Provider (in another package)
final class OrdersRouteProvider implements RouteProviderInterface { public function register(Router $router): void { $router->group(['prefix' => 'orders', 'middleware' => ['jwt:orders:read']], function () use ($router) { $router->get('', [OrdersController::class, 'index'])->name('index'); }); } }
Composer Auto-Discovery
{
"extra": {
"sapi": {
"route_providers": [
{ "class": "Seiger\\sCommerce\\Api\\Routes\\OrdersRouteProvider", "endpoint": "orders" },
{ "class": "Seiger\\sCommerce\\Api\\Routes\\LegacyOrdersRouteProvider", "endpoint": "orders", "version": "v0" }
]
}
}
}
Rules:
endpointis required.versionis optional; if omitted,SAPI_VERSIONis used (can be empty for unversioned routes).- Cache key is
{version}/{endpoint}or{endpoint}when version is empty. - Priority:
core/custom/composer.jsonoverrides vendor entries. - Route name prefix:
sApi.{endpoint}.{version}.…(version is omitted when empty). - Providers must register routes without
SAPI_BASE_PATHand without version in the URI (sApi applies them).
Cache
Discovery is cached in core/storage/cache/sapi_routes_map.php and is rebuilt only when:
core/composer.lockchanges (mtime/size)core/custom/composer.jsonchanges (mtime/size), if the file existsSAPI_VERSIONchangesSAPI_BASE_PATHchanges
Response Conventions
sApi keeps responses intentionally simple and OpenAPI-friendly.
Success responses
Successful responses use a top-level data member.
{
"data": {}
}
For named collections:
{
"data": {
"categories": [
{
"id": 1,
"title": "News",
"alias": "news"
}
]
}
}
An optional message field may be included when it adds useful human context.
Error responses
Error responses use application/problem+json and follow RFC 9457:
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid token."
}
Validation-style extensions may be added as extra members, for example errors.
Design rules
- use
snake_caseJSON keys - prefer
dataover generic wrappers such asobject - do not duplicate the HTTP status code in the body by default
- prefer standard fields like
access_token,token_type,expires_in - keep business payloads inside
data
See also: docs/response-conventions.md
OpenAPI / Swagger
sApi is designed to be compatible with OpenAPI 3.1.
Recommended standards around the core:
- OpenAPI 3.1 for contract description
- RFC 9457 for error responses (
application/problem+json) - familiar OAuth 2 token response fields for access tokens
Documentation generation and Swagger UI are not included in the core
and will be provided via a separate module.
License
This package is licensed under the GNU General Public License v3.0.
You are free to use it in commercial and non-commercial projects.
If you distribute modified versions or derivative works, they must also be licensed under GPLv3.
See the LICENSE file for details.
Roadmap (High-Level)
- JWT refresh tokens
- Rate limiting middleware
- OpenAPI documentation module
- CLI helpers for API debugging
- Request validation helpers
Philosophy
Explicit over magic.
Infrastructure, not business logic.
Composable, not monolithic.
sApi is intentionally minimal.
If you need full-stack API automation, use a framework.
If you need a solid API core for Evolution CMS — this is it.
seiger/sapi 适用场景与选型建议
seiger/sapi 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 85 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 01 月 05 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「shop」 「api」 「catalog」 「e-commerce」 「api-tools」 「evocms」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 seiger/sapi 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 seiger/sapi 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 seiger/sapi 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Admin Hub for GetCandy. A modern headless e-commerce solution for Laravel PHP framework.
A PSR-7 compatible library for making CRUD API endpoints
Submodule for SilverShop which allows you to compare products in a table.
Product catalog for your Symfony2 applications.
A shopping system for APPUI
Extension for Catalog Manager.
统计信息
- 总下载量: 85
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 41
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: GPL-3.0
- 更新时间: 2026-01-05