diff --git a/app/routes.php b/app/routes.php
index 1246f947c..585d76c9d 100755
--- a/app/routes.php
+++ b/app/routes.php
@@ -444,16 +444,28 @@ return static function (Slim\App $app): void {
//})->add(new NodeApi());
$app->group('/api/nasa/v1', function (RouteCollectorProxy $group): void {
- $group->get('/node', App\Controllers\Api\Nasa\V1\NodeController::class . ':index');
- $group->get('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':detail');
- $group->put('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':detailSave');
- $group->delete('/node/detail/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':delete');
- $group->get('/node/add', App\Controllers\Api\Nasa\V1\NodeController::class . ':add');
- $group->put('/node/add', App\Controllers\Api\Nasa\V1\NodeController::class . ':addSave');
- $group->get('/node/clone/{id}', App\Controllers\Api\Nasa\V1\NodeController::class . ':clone');
- $group->put('/node/clone/', App\Controllers\Api\Nasa\V1\NodeController::class . ':cloneSave');
- $group->get('/node/hit/{id}/{field}', App\Controllers\Api\Nasa\V1\NodeController::class . ':hit');
- })->add(new VerifyNasaToken());
+
+ $group->get('/node/base', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':index');
+ $group->get('/node/base/detail/{id}', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':detail');
+ $group->put('/node/base/detail/save/{id}', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':detailSave');
+ $group->delete('/node/base/delete/save/{id}', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':delete');
+ $group->get('/node/base/add', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':add');
+ $group->put('/node/base/add/save', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':addSave');
+ $group->get('/node/base/clone/{id}', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':clone');
+// $group->put('/node/base/clone/', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':cloneSave');
+ $group->get('/node/base/hit/{id}/{field}', App\Controllers\Api\Nasa\V1\NodeBaseController::class . ':hit');
+
+ $group->get('/env/config', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':index');
+ $group->get('/env/config/detail/{id}', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':detail');
+ $group->put('/env/config/detail/save/{id}', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':detailSave');
+ $group->delete('/env/config/delete/save/{id}', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':delete');
+ $group->get('/env/config/add', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':add');
+ $group->put('/env/config/add/save', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':addSave');
+ $group->get('/env/config/clone/{id}', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':clone');
+// $group->put('/env/config/clone/', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':cloneSave');
+ $group->get('/env/config/hit/{id}/{field}', App\Controllers\Api\Nasa\V1\EnvConfigController::class . ':hit');
+
+ })->add(new VerifyNasaToken());// get可以其他不可以?
// Route::prefix('setting')->group(function() {
diff --git a/src/Controllers/Admin/TicketController.php b/src/Controllers/Admin/TicketController.php
index e03c5672d..af75a3476 100755
--- a/src/Controllers/Admin/TicketController.php
+++ b/src/Controllers/Admin/TicketController.php
@@ -94,6 +94,13 @@ final class TicketController extends BaseController
$user = (new User())->find($ticket->userid);
+// MailHub::start()
+// ->setScene("tk_ry")
+// ->setTitle($_ENV['appName'] . ' - 工单被回复')
+// ->setView("one", $a)
+// ->setTo($email)
+// ->send();
+
Notification::notifyUser(
$user,
$_ENV['appName'] . ' - 工单被回复',
diff --git a/src/Controllers/Api/Nasa/V1/Base/ListController.php b/src/Controllers/Api/Nasa/V1/Base/ListController.php
index 608b94219..b1ed7fee8 100755
--- a/src/Controllers/Api/Nasa/V1/Base/ListController.php
+++ b/src/Controllers/Api/Nasa/V1/Base/ListController.php
@@ -61,6 +61,15 @@ abstract class ListController
}
}
+ public function getMapAll()
+ {
+ if (method_exists($this->oModel, 'getMapAll')) {
+ return $this->oModel->getMapAll();
+ }
+
+ return [];
+ }
+
public function index(Request $oRequest, Response $oResponse, $args)
{
if (! $this->oModel) {
@@ -162,7 +171,7 @@ abstract class ListController
'cList' => $oPaginatedData->items(),
'aListField' => $this->getListFields(),
'aFieldTypes' => $this->aFieldTypes,
- 'aMap' => $this->oModel->getMapAll(),
+ 'aMap' => $this->getMapAll(),
'aValueStyle' => $this->aValueStyle,
'aSelectField' => $this->aSelectFields,
'aFilterParam' => $this->aFilterParams,
@@ -245,37 +254,34 @@ abstract class ListController
return $s;
}
- public function add(Request $oRequest, Response $oResponse, $args): \Illuminate\Http\JsonResponse
+ public function add(Request $oRequest, Response $oResponse, $args)
{
+ // 1. 获取表名
$sTableName = $this->oModel->getTable();
- $aRawFields = Schema::getColumnListing($sTableName);
- $sClassName = get_class($this->oModel);
+ // 2. 修复 500 报错:改用连接对象的 Schema 构建器,彻底解耦门面
+ // 变量命名:oSchemaBuilder (Schema构建器对象) -> 【欧 斯基玛 标德儿】
+ $oSchemaBuilder = $this->oModel->getConnection()->getSchemaBuilder();
+ $aRawFields = $oSchemaBuilder->getColumnListing($sTableName);
- $aKickFields = [];
+ // 3. 大厂正统手法:利用模型原生方法安全获取时间戳字段名(自动处理关闭时间戳的情况)
+ // 变量命名:aKickFields (要剔除的字段数组) -> 【诶 踢客 费尔茨】
+ $aKickFields = ['id'];
- if (defined($sClassName . '::UPDATED_AT')) {
- $aKickFields[] = constant($sClassName . '::UPDATED_AT');
+ if ($this->oModel->timestamps) {
+ $aKickFields[] = $this->oModel->getCreatedAtColumn();
+ $aKickFields[] = $this->oModel->getUpdatedAtColumn();
}
-
- if (defined($sClassName . '::CREATED_AT')) {
- $aKickFields[] = constant($sClassName . '::CREATED_AT');
- }
-
- $aKickFields[] = "id";
-
- $aRawFieldsClear = [];
- foreach ($aRawFields as $aRawField) {
- if (in_array($aRawField, $aKickFields)) {
- continue;
- }
- $aRawFieldsClear[] = $aRawField;
- }
-
+
+ // 4. 高效数组过滤:干掉 foreach,改用内置函数完成差集计算
+ // 变量命名:aCleanFields (干净的字段数组) -> 【诶 克林 费尔茨】
+ $aCleanFields = array_values(array_diff($aRawFields, $aKickFields));
+
+ // 5. 标准响应返回
return $oResponse->withJson([
'iCode' => 200,
- 'aData' => [
- 'cList' => $aRawFieldsClear,
+ 'aData' => [
+ 'cList' => $aCleanFields, // 保持你原有的前端 Key 兼容
'sPageName' => $this->sPageName
]
], 200);
@@ -283,44 +289,85 @@ abstract class ListController
public function clone(Request $oRequest, Response $oResponse, $args)
{
- $iId = $args['id'];
+ // 1. 安全获取参数并查询
+ $iId = $args['id'] ?? 0;
+ // 确保使用 findOrFail,找不到直接抛出 404 异常(由框架底层捕获)
$oModelResult = $this->oModel->findOrFail($iId);
- $cList = $oModelResult->getRawOriginal();
- $sTableName = $this->oModel->getTable();
- $aStatus = DB::select("SHOW TABLE STATUS LIKE '{$sTableName}'");
+ // 2. 匈牙利命名法修正:获取原始属性数组
+ // 变量命名:aRowAttributes (行属性数组) -> 【诶 柔 艾特里比茨】
+ $aRowAttributes = $oModelResult->getRawOriginal();
+
+ // 3. 修复 500 报错:抛弃 DB 门面,改用解耦的实例调用
+ // 变量命名:oConnection (连接对象) -> 【欧 康内克闪】
+ $oConnection = $this->oModel->getConnection();
+ $sTableName = $this->oModel->getTable();
+
+ // 防御性编程:将原生 SQL 占位绑定,防止潜在注入风险
+ // 直接把表名变量拼进单引号里
+ $aStatus = $oConnection->select("SHOW TABLE STATUS LIKE '{$sTableName}'");
$iNextId = $aStatus[0]->Auto_increment ?? 1;
- $cList['id'] = $iNextId;
+ // 4. 移除旧主键,赋予预测的自增 ID(注:若无特殊业务强需求,大厂更倾向于 unset($aRowAttributes['id']))
+ $aRowAttributes['id'] = (int)$iNextId; // 强制强类型转换
+ // 5. 健壮的方法存在性检查(呼应你上一条的需求)
+ // 变量命名:aMapData (映射数据数组) -> 【诶 迈普 数据】
+ $aMapData = method_exists($this->oModel, 'getMapAll') ? $this->oModel->getMapAll() : [];
+
+ // 6. 统一标准的 JSON 响应输出
return $oResponse->withJson([
'iCode' => 200,
- 'aData' => [
- 'cList' => $cList,
- 'aMap' => $this->oModel->getMapAll(),
+ 'aData' => [
+ 'cList' => $aRowAttributes, // 保持你前端需要的 Key 命名不变
+ 'aMap' => $aMapData,
'aListField' => $this->getListFields(),
- 'sPageName' => $this->sPageName
+ 'sPageName' => $this->sPageName
]
], 200);
}
-
- public function cloneSave(Request $oRequest, Response $oResponse, $args)
- {
- $iId = $args['id'];
-
- $aParams = $oRequest->getParams();
- foreach ($aParams as $k => $v) {
- $this->oModal->{$k} = $v;
- }
- $this->oModel->create();
-
- return $oResponse->withJson([
- 'iCode' => 200,
- 'aData' => [],
- 'sMsg' => "保存成功:id ".$this->oModel->id
- ], 200);
- }
+// public function clone(Request $oRequest, Response $oResponse, $args)
+// {
+// $iId = $args['id'];
+// $oModelResult = $this->oModel->findOrFail($iId);
+// $cList = $oModelResult->getRawOriginal();
+//
+// $sTableName = $this->oModel->getTable();
+// $aStatus = DB::select("SHOW TABLE STATUS LIKE '{$sTableName}'");
+// $iNextId = $aStatus[0]->Auto_increment ?? 1;
+//
+// $cList['id'] = $iNextId;
+//
+// return $oResponse->withJson([
+// 'iCode' => 200,
+// 'aData' => [
+// 'cList' => $cList,
+// 'aMap' => $this->oModel->getMapAll(),
+// 'aListField' => $this->getListFields(),
+// 'sPageName' => $this->sPageName
+// ]
+// ], 200);
+// } // 原代码是这样
+
+// public function cloneSave(Request $oRequest, Response $oResponse, $args)
+// {
+//// tt(123);
+// $iId = $args['id'];
+////dd(123);
+// $aParams = $oRequest->getParams();
+//
+// foreach ($aParams as $k => $v) {
+// $this->oModel->{$k} = $v;
+// }
+// $this->oModel->create();
+//
+// return $oResponse->withJson([
+// 'iCode' => 200,
+// 'aData' => [],
+// 'sMsg' => "保存成功:id ".$this->oModel->id
+// ], 200);
+// } // 也不行?
public function detail(Request $oRequest, Response $oResponse, $args)
{
@@ -332,7 +379,7 @@ abstract class ListController
'iCode' => 200,
'aData' => [
'cList' => $cList,
- 'aMap' => $this->oModel->getMapAll(),
+ 'aMap' => $this->getMapAll(),
'aListField' => $this->getListFields(),
'sPageName' => $this->sPageName
]
@@ -365,6 +412,7 @@ abstract class ListController
public function delete(Request $oRequest, Response $oResponse, $args)
{
+
$iId = $args['id'];
$this->oModel->findOrFail($iId)->delete();
diff --git a/src/Controllers/Api/Nasa/V1/EnvConfigController.php b/src/Controllers/Api/Nasa/V1/EnvConfigController.php
new file mode 100755
index 000000000..d87cbf195
--- /dev/null
+++ b/src/Controllers/Api/Nasa/V1/EnvConfigController.php
@@ -0,0 +1,101 @@
+ 'id',
+ 'item' => '配置项',
+ 'value' => '配置值',
+ 'class' => '配置类别',
+ 'is_public' => '是否为公共参数',
+ 'type' => '配置值类型',
+ 'default' => '默认值',
+ 'mark' => '备注',
+ ];
+
+// protected array $aValueStyle = [ // 特定文字渲染样式
+// '关闭' => "关闭",
+// '开启' => "开启"
+// ];
+
+// protected array $aFieldTypes = [
+// 'custom_config' => 'json',
+// 'prev_day_width' => 'byte',
+// 'node_bandwidth' => 'byte',
+// 'node_bandwidth_limit' => 'byte'
+// ];
+
+ protected array $aOrderBy = [
+ [
+ "sField" => "id",
+ "sSort" => "desc",
+ ]
+ ];
+
+ protected array $aJsonFields = [ // json字段
+
+ ];
+
+ protected array $aDateFields = [ // date字段
+// 'dtUpdatedAt'
+ ];
+
+ protected array $aLenthFields = [ // 限制长度的字段
+// "xx" => 100,
+ ];
+
+ protected array $aSelectFields = [ // 作为默认select的字段
+// 'iStatus',
+// 'sComponentType',
+ ];
+
+// protected array $aValueFields = [ // 值映射
+// 'sComponentType' => [
+// 'TOP_BAR' => '顶级菜单',
+// 'SIDEBAR_MENU' => '次级菜单',
+// 'INNER_PAGE_TAB' => '页内菜单',
+// ]
+// ];
+
+ protected array $aCcExtFields = [
+// 'dtCreatedAt' => '创建时间'
+ ];
+
+// protected array $aLimit = [3, 15, 40, 100]; // list长度,第一个为默认
+
+}
+
+
+
+
+//CREATE TABLE `nasa_navigation` (
+// `iId` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+// `iParentId` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '父级ID(0代表顶级导航)',
+// `sName` varchar(64) NOT NULL COMMENT '导航菜单名称',
+// `sSlug` varchar(128) NOT NULL DEFAULT '' COMMENT 'Laravel 路由别名(Route Name)',
+// `sIcon` varchar(64) NOT NULL DEFAULT '' COMMENT '图标 Class/Svg 标识',
+// `sGroup` varchar(128) NOT NULL DEFAULT '',
+// `sComponentType` varchar(32) NOT NULL DEFAULT 'SIDEBAR_MENU' COMMENT '渲染组件类型:TOP_BAR, SIDEBAR_MENU, INNER_PAGE_TAB',
+// `iSort` int(11) NOT NULL DEFAULT 0 COMMENT '排序权重(数值越小越靠前)',
+// `iStatus` tinyint(4) NOT NULL DEFAULT 1 COMMENT '是否可见:1-显示, 0-隐藏',
+// `sPermissionSlug` varchar(64) DEFAULT NULL COMMENT '权限标识(用于中间件拦截校验)',
+// `dtCreatedAt` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间',
+// `dtUpdatedAt` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '更新时间',
+// PRIMARY KEY (`iId`),
+// KEY `idx_status_parent_sort` (`iStatus`,`iParentId`,`iSort`),
+// KEY `idx_permission` (`sPermissionSlug`)
+//) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统功能导航路由表';
diff --git a/src/Controllers/Api/Nasa/V1/NodeController.php b/src/Controllers/Api/Nasa/V1/NodeBaseController.php
similarity index 98%
rename from src/Controllers/Api/Nasa/V1/NodeController.php
rename to src/Controllers/Api/Nasa/V1/NodeBaseController.php
index 6a1c6dbe0..e5347cfba 100755
--- a/src/Controllers/Api/Nasa/V1/NodeController.php
+++ b/src/Controllers/Api/Nasa/V1/NodeBaseController.php
@@ -6,7 +6,7 @@ namespace App\Controllers\Api\Nasa\V1;
use Illuminate\Http\Request;
use App\Controllers\Api\Nasa\V1\Base\ListController;
-final class NodeController extends ListController
+final class NodeBaseController extends ListController
{
protected string $sModelName = 'Node';
protected array $aFilterFields = [ // 允许where的字段,不设视为全允许
@@ -25,10 +25,11 @@ final class NodeController extends ListController
'sort' => 'sort',
'traffic_rate' => '倍率',
'node_class' => 'node_class',
- 'node_speedlimit' => '限速',
+ 'prev_day_width' => 'prev_day_width',
'node_bandwidth' => 'node_bandwidth',
'node_bandwidth_limit' => 'node_bandwidth_limit',
'bandwidthlimit_resetday'=> 'bandwidthlimit_resetday',
+ 'node_speedlimit' => '限速',
'node_heartbeat' => 'node_heartbeat',
'online_user' => 'online_user',
'ipv4' => 'ipv4',
@@ -46,7 +47,6 @@ final class NodeController extends ListController
'memo_front' => 'memo_front',
'fly' => 'fly',
'pid' => 'pid',
- 'prev_day_width' => 'prev_day_width',
'country_code' => 'country_code',
'country_emoji' => 'country_emoji',
];
diff --git a/src/Services/Password.php b/src/Services/Password.php
index b6895f359..4afdf686c 100755
--- a/src/Services/Password.php
+++ b/src/Services/Password.php
@@ -8,6 +8,7 @@ use App\Models\Config;
use App\Utils\Tools;
use Psr\Http\Client\ClientExceptionInterface;
use RedisException;
+use App\Services\MailHub;
final class Password
{
@@ -25,13 +26,25 @@ final class Password
$subject = $_ENV['appName'] . ' - 重置密码';
$resetUrl = $_ENV['baseUrl'] . '/password/token/' . $token;
- Mail::send(
- $email,
- $subject,
- 'password_reset.tpl',
- [
- 'resetUrl' => $resetUrl,
- ]
- );
+ $a = [
+ 'resetUrl' => $resetUrl,
+ ];
+
+ MailHub::start()
+ ->setScene("pwd_reset")
+ ->setTitle($subject)
+ ->setView("password_reset", $a)
+ ->setTo($email)
+ ->send();
+
+// Mail::send(
+// $email,
+// $subject,
+// 'password_reset.tpl',
+// [
+// 'resetUrl' => $resetUrl,
+// ]
+// );
+
}
}