no message
This commit is contained in:
parent
d3a3651805
commit
6215424ced
112
app/Http/Controllers/Api/Nasa/V1/ArticleBaseController.php
Executable file
112
app/Http/Controllers/Api/Nasa/V1/ArticleBaseController.php
Executable file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\Nasa\V1;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Api\Nasa\V1\Base\ListController;
|
||||
use App\Services\Tg\Hook\Mod\Arma;
|
||||
|
||||
final class ArticleBaseController extends ListController
|
||||
{
|
||||
protected string $sModelPath = 'App\Models\Article';
|
||||
protected array $aFilterFields = [ // 允许where的字段,不设视为全允许
|
||||
// 's_name',
|
||||
// 'i_status'
|
||||
];
|
||||
|
||||
protected string $sPageName = "article";
|
||||
|
||||
protected array $aListFields = [ // list展示的字段和名字映射
|
||||
'id' => 'id',
|
||||
'user_name' => 'user_name',
|
||||
'project_name'=> 'project_name',
|
||||
'mail_name' => 'mail_name',
|
||||
'mail_tail' => 'mail_tail',
|
||||
'sale_code' => 'sale_code',
|
||||
'sale_rate' => 'sale_rate',
|
||||
'sale_price' => 'sale_price',
|
||||
'i_in10' => 'i_in10',
|
||||
'inday_count' => 'inday_count',
|
||||
'cron_condition' => 'cron_condition',
|
||||
'mod' => 'mod',
|
||||
'mod_type' => 'mod_type',
|
||||
'status' => 'status',
|
||||
'memo' => 'memo',
|
||||
'ship_code' => 'ship_code',
|
||||
'fn_www' => 'fn_www',
|
||||
'show_huodong' => 'show_huodong',
|
||||
'show_error' => 'show_error',
|
||||
// 'updated_at' => 'updated_at',
|
||||
// 'created_at' => 'created_at'
|
||||
];
|
||||
|
||||
protected array $aValueStyle = [ // 特定文字渲染样式
|
||||
// '关闭' => "<span class='text-danger'>关闭</span>",
|
||||
// '开启' => "<span class='text-success'>开启</span>"
|
||||
];
|
||||
|
||||
protected array $aFieldTypes = [
|
||||
// 'schema' => 'text',
|
||||
// 'schema_pro' => 'text',
|
||||
// 'cmd' => 'text',
|
||||
// 'cmd_log' => 'text',
|
||||
// 'opt' => 'text'
|
||||
];
|
||||
|
||||
protected array $aOrderBy = [
|
||||
[
|
||||
"sField" => "id",
|
||||
"sSort" => "desc",
|
||||
]
|
||||
];
|
||||
|
||||
protected array $aJsonFields = [ // json字段
|
||||
// 'schema'
|
||||
];
|
||||
|
||||
protected array $aDateFields = [ // date字段
|
||||
// 'dtUpdatedAt'
|
||||
];
|
||||
|
||||
protected array $aLenthFields = [ // 限制长度的字段
|
||||
// "xx" => 100,
|
||||
];
|
||||
|
||||
protected array $aSelectFields = [ // 作为默认select的字段
|
||||
// 'status',
|
||||
];
|
||||
|
||||
protected array $aValueFields = [ // 值映射
|
||||
// 'status' => [
|
||||
// '1' => '开启',
|
||||
// '0' => '关闭',
|
||||
// ]
|
||||
];
|
||||
|
||||
protected array $aCcExtFields = [
|
||||
// 'dtCreatedAt' => '创建时间'
|
||||
];
|
||||
|
||||
// protected array $aLimit = [3, 15, 40, 100]; // list长度,第一个为默认
|
||||
|
||||
// public function syncSave(Request $oRequest)
|
||||
// {
|
||||
// $id = request('id');
|
||||
// $siteGroupCode = request('siteGroupCode');
|
||||
// $cmd = request('type');
|
||||
//
|
||||
// $aParam['aOption'][1] = $id;
|
||||
// $aParam['aArg'][1] = $siteGroupCode;
|
||||
// $aParam['aArg'][2] = $cmd;
|
||||
// $oArma = new Arma([]);
|
||||
// $r = $oArma->cmd($aParam, []);
|
||||
//
|
||||
// return response()->json([
|
||||
// 'iCode' => 200,
|
||||
// 'aData' => [],
|
||||
// 'sMsg' => $r
|
||||
// ], 200);
|
||||
//
|
||||
// }
|
||||
}
|
||||
500
app/Http/Controllers/Api/Nasa/V1/Base/ListController.php
Executable file
500
app/Http/Controllers/Api/Nasa/V1/Base/ListController.php
Executable file
@ -0,0 +1,500 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\Nasa\V1\Base;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
abstract class ListController
|
||||
{
|
||||
private string $sOperatorDefault = 'eq';
|
||||
private string $sSortDefault = 'asc';
|
||||
|
||||
private array $aFilterParams = [
|
||||
'c' => [],
|
||||
'cc' => [],
|
||||
'ob' => [],
|
||||
];
|
||||
|
||||
protected $oModel;
|
||||
protected array $aValueFields = [];
|
||||
protected string $sPageName = '';
|
||||
// protected string $sModelName = '';
|
||||
protected string $sModelPath = '';
|
||||
protected array $aFilterFields = []; // 允许where的字段
|
||||
// protected array $aArticleFields = []; // 文章字段
|
||||
protected array $aFieldTypes = [];
|
||||
protected array $aLenthFields = []; // 限制长度的字段
|
||||
protected array $aSelectFields = []; // 默认作为select的字段
|
||||
protected array $aListFields = []; // list展示的字段和名字映射
|
||||
protected array $aDateFields = []; // date字段
|
||||
protected array $aJsonFields = []; // json字段
|
||||
// protected array $aValueStyle = []; // 特定文字渲染样式
|
||||
protected int $iLimitMax = 100; // 默认limit限制
|
||||
protected int $iLimit = 20; // 默认显示行数
|
||||
protected array $aCcExtFields = []; // 自定义条件字段追加在listFields后
|
||||
protected array $aCcFields = [];
|
||||
protected int $iOrderByMax = 3;
|
||||
protected array $aOrderBy = [
|
||||
[
|
||||
"sField" => "id",
|
||||
"sSort" => "desc"
|
||||
]
|
||||
];
|
||||
protected array $aValueStyle = [ // 特定文字渲染样式
|
||||
'关闭' => "<span class='text-danger'>关闭</span>",
|
||||
'开启' => "<span class='text-success'>开启</span>"
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if ($this->sModelPath) {
|
||||
if (! class_exists($this->sModelPath)) {
|
||||
throw new InvalidArgumentException("【系统架构错误】: 找不到领域模型 {$this->sModelPath}");
|
||||
}
|
||||
|
||||
$this->oModel = new $this->sModelPath();
|
||||
$this->aCcFields = array_merge($this->aListFields, $this->aCcExtFields);
|
||||
$this->aCcFields = $this->getCcFields();
|
||||
}
|
||||
}
|
||||
|
||||
private function getCcFields()
|
||||
{
|
||||
$a = [];
|
||||
foreach ($this->aCcFields as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
foreach ($v as $kk => $vv) {
|
||||
$a[$kk] = $vv;
|
||||
}
|
||||
} else {
|
||||
$a[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
private function mapOperator($sOperator)
|
||||
{
|
||||
$sSqlOperator = match ($sOperator) {
|
||||
'like' => 'like',
|
||||
'eq' => '=',
|
||||
'ne' => '!=',
|
||||
'gt' => '>',
|
||||
'gte' => '>=',
|
||||
'lt' => '<',
|
||||
'lte' => '<=',
|
||||
default => '',
|
||||
};
|
||||
|
||||
return $sSqlOperator;
|
||||
}
|
||||
|
||||
private function getOrderBySchema()
|
||||
{
|
||||
$iOrderByCount = count($this->aOrderBy);
|
||||
$iDiff = $this->iOrderByMax - $iOrderByCount;
|
||||
$a = $this->aOrderBy;
|
||||
for ($i=0; $i<$iDiff; $i++) {
|
||||
$a[] = [];
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
|
||||
private function getOrderBy()
|
||||
{
|
||||
return $this->aOrderBy;
|
||||
}
|
||||
|
||||
public function getListFields()
|
||||
{
|
||||
$a = [];
|
||||
|
||||
foreach ($this->aListFields as $k => $v) {
|
||||
if ($v) {
|
||||
$a[$k] = $v;
|
||||
} else {
|
||||
$a[$k] = $k;
|
||||
}
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
private function getCcLast()
|
||||
{
|
||||
$aCcFieldsTmp = $this->aCcFields;
|
||||
|
||||
foreach ($this->aFilterParams['cc'] as $ACC) {
|
||||
unset($aCcFieldsTmp[$ACC['sField']]);
|
||||
}
|
||||
|
||||
$s = head(array_keys($aCcFieldsTmp));
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
public function add(Request $oRequest): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$sTableName = $this->oModel->getTable();
|
||||
$aRawFields = Schema::getColumnListing($sTableName);
|
||||
|
||||
$sClassName = get_class($this->oModel);
|
||||
|
||||
$aKickFields = [];
|
||||
|
||||
if (defined($sClassName . '::UPDATED_AT')) {
|
||||
$aKickFields[] = constant($sClassName . '::UPDATED_AT');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [
|
||||
'cList' => $aRawFieldsClear,
|
||||
'sPageName' => $this->sPageName
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function clone(Request $oRequest)
|
||||
{
|
||||
$iId = request('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 response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [
|
||||
'cList' => $cList,
|
||||
'aMap' => $this->getMapAll(),
|
||||
'aListField' => $this->getListFields(),
|
||||
'sPageName' => $this->sPageName
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
// public function cloneSave(Request $oRequest)
|
||||
// {
|
||||
// $iId = request('id');
|
||||
//
|
||||
// $aParams = $oRequest->all();
|
||||
//
|
||||
// foreach ($aParams as $k => $v) {
|
||||
// $this->oModal->{$k} = $v;
|
||||
// }
|
||||
// $this->oModel->create();
|
||||
//
|
||||
// return response()->json([
|
||||
// 'iCode' => 200,
|
||||
// 'aData' => [],
|
||||
// 'sMsg' => "保存成功:id ".$this->oModel->id
|
||||
// ], 200);
|
||||
// }
|
||||
|
||||
public function detail(Request $oRequest)
|
||||
{
|
||||
$iId = request('id');
|
||||
$oModelResult = $this->oModel->findOrFail($iId);
|
||||
$cList = $oModelResult->getRawOriginal();
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [
|
||||
'cList' => $cList,
|
||||
'aMap' => $this->getMapAll(),
|
||||
'aListField' => $this->getListFields(),
|
||||
'sPageName' => $this->sPageName
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function hit(Request $oRequest)
|
||||
{
|
||||
$iId = request('id');
|
||||
$sField = request('field');
|
||||
|
||||
$sFieldType = $this->aFieldTypes[$sField] ?? '';
|
||||
|
||||
$sHit = $this->oModel->find($iId)?->{$sField};
|
||||
if (is_array($sHit)) {
|
||||
$sHit = json_encode($sHit);
|
||||
}
|
||||
|
||||
if ($sFieldType == 'json') {
|
||||
$sHit = json_decode($sHit, true);
|
||||
$sHit = json_encode($sHit, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [
|
||||
'sHit' => $sHit
|
||||
]
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
public function delete(Request $oRequest)
|
||||
{
|
||||
$iId = request('id');
|
||||
|
||||
$this->oModel->findOrFail($iId)->delete();
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [],
|
||||
'sMsg' => '删除成功'
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function addSave(Request $oRequest)
|
||||
{
|
||||
$aParams = $oRequest->all();
|
||||
|
||||
foreach ($aParams as $k => $v) {
|
||||
$this->oModel->{$k} = $v;
|
||||
}
|
||||
|
||||
if (!$this->oModel->save()) {
|
||||
return response()->json(['code' => 500, 'msg' => '保存失败?'], 500);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [],
|
||||
'sMsg' => '保存成功'
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function detailSave(Request $oRequest)
|
||||
{
|
||||
$iId = request('id');
|
||||
|
||||
$oThisModel = $this->oModel->find($iId);
|
||||
|
||||
$aParams = $oRequest->all();
|
||||
|
||||
unset($aParams['id'], $aParams['_method']);
|
||||
|
||||
foreach ($aParams as $k => &$v) {
|
||||
if (empty($v) && empty($oThisModel->{$k})) {
|
||||
continue;
|
||||
}
|
||||
if (empty($v) && $v !== 0 && $v !== '0') {
|
||||
$v = '';
|
||||
}
|
||||
if ($oThisModel->{$k} != $v) {
|
||||
$oThisModel->{$k} = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$oThisModel->save();
|
||||
|
||||
$aChangeField = $oThisModel->getChanges();
|
||||
|
||||
/// 排除update_at
|
||||
$sClassName = get_class($this->oModel);
|
||||
if (
|
||||
defined($sClassName . '::UPDATED_AT')
|
||||
&& ($sUpdateColumn = constant($sClassName . '::UPDATED_AT'))
|
||||
&& !isset($aParams[$sUpdateColumn])
|
||||
&& isset($aChangeField[$sUpdateColumn])
|
||||
) {
|
||||
unset($aChangeField[$sUpdateColumn]);
|
||||
}
|
||||
|
||||
/// 构造msg
|
||||
$sMsg = "";
|
||||
$sJiantou = " -> ";
|
||||
foreach ($aChangeField as $k => $v) {
|
||||
|
||||
if (mb_strlen($v, 'UTF-8') > 60 ) {
|
||||
$sJiantou = '';
|
||||
// $v = mb_substr($v, 0, 60, 'UTF-8');
|
||||
$v = '';
|
||||
}
|
||||
|
||||
if (!isset($aParams[$k])) {
|
||||
$sMsg .= "⚠️ ".$k . $sJiantou . $v . "<br><br>";
|
||||
} else {
|
||||
$sMsg .= "✅ ".$k . $sJiantou . $v . "<br><br>";
|
||||
unset($aParams[$k]);
|
||||
}
|
||||
}
|
||||
foreach ($aParams as $k => $v) {
|
||||
|
||||
if (mb_strlen($v, 'UTF-8') > 60 ) {
|
||||
$sJiantou = '';
|
||||
// $v = mb_substr($v, 0, 60, 'UTF-8');
|
||||
$v = '';
|
||||
}
|
||||
|
||||
$sMsg .= "❌ ".$k . $sJiantou . $v . "<br><br>";
|
||||
}
|
||||
$sMsg = rtrim($sMsg, "<br><br>");
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => $aChangeField,
|
||||
'sMsg' => $sMsg
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function getMapAll()
|
||||
{
|
||||
$a = [];
|
||||
|
||||
if (method_exists($this->oModel, 'getMapAll')) {
|
||||
$a = $this->oModel->getMapAll();
|
||||
}
|
||||
|
||||
$b = array_merge($a, $this->aValueFields);
|
||||
|
||||
return $b;
|
||||
}
|
||||
|
||||
public function index(Request $oRequest)
|
||||
{
|
||||
if (! $this->oModel) {
|
||||
return response()->json(['code' => 500, 'msg' => '未绑定核心领域模型'], 500);
|
||||
}
|
||||
|
||||
$oQuery = $this->oModel->newQuery();
|
||||
|
||||
$aParams = $oRequest->all();
|
||||
|
||||
foreach ($aParams as $k => $v) {
|
||||
|
||||
if (!empty($this->aFilterFields)) {
|
||||
if (!in_array($k, $this->aFilterFields)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// w__name__eq=1
|
||||
$aK = explode("__", $k);
|
||||
$sType = $aK[0] ?? '';
|
||||
$sField = $aK[1] ?? '';
|
||||
$sOperator = $aK[2] ?? $this->sOperatorDefault;
|
||||
$sSqlOperator = $this->mapOperator($sOperator);
|
||||
|
||||
if (!isset($this->aFilterParams[$sType])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$sType || !$sField) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aFilterParams[$k] = $v;
|
||||
|
||||
if ($sType === 'ob') {
|
||||
$this->aFilterParams[$sType][] = [
|
||||
'sField' => $sField,
|
||||
'sSort' => $v ?? $this->sSortDefault
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sType === 'c') { // 预制条件,字典式
|
||||
$this->aFilterParams[$sType][$sField] = [
|
||||
'sOperator' => $sOperator,
|
||||
'sValue' => $v
|
||||
];
|
||||
}
|
||||
|
||||
if ($sType === 'cc') { // 自定义条件,集合式
|
||||
$this->aFilterParams[$sType][] = [
|
||||
'sField' => $sField,
|
||||
'sOperator' => $sOperator,
|
||||
'sValue' => $v
|
||||
];
|
||||
}
|
||||
|
||||
if (blank($v)) continue; // 空v可以back但不加入sql查询
|
||||
|
||||
$sSqlValue = $v;
|
||||
if ($sSqlOperator === 'like') {
|
||||
$sSqlValue = "%{$v}%";
|
||||
}
|
||||
|
||||
if (in_array($sField, $this->aDateFields)) {
|
||||
$oQuery->whereDate($sField, $sSqlOperator, $sSqlValue);
|
||||
} else {
|
||||
$oQuery->where($sField, $sSqlOperator, $sSqlValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$iLimit = (int) $oRequest->input('limit', $this->iLimit);
|
||||
$iLimit = max(1, $iLimit);
|
||||
$iLimit = min($this->iLimitMax, $iLimit);
|
||||
|
||||
if (!empty($this->aFilterParams['ob'])) {
|
||||
$this->aOrderBy = $this->aFilterParams['ob'];
|
||||
}
|
||||
|
||||
foreach ($this->aOrderBy as $aOrderBy) {
|
||||
$oQuery->orderBy($aOrderBy['sField'], $aOrderBy['sSort']);
|
||||
}
|
||||
|
||||
$oPaginatedData = $oQuery->paginate($iLimit);
|
||||
// tt($oPaginatedData);
|
||||
// $oPaginatedData = $oQuery->paginate($iLimit, array_keys($this->aListFields));
|
||||
|
||||
return response()->json([
|
||||
'iCode' => 200,
|
||||
'aData' => [
|
||||
'cList' => $oPaginatedData->items(),
|
||||
'aListField' => $this->getListFields(),
|
||||
'aFieldTypes' => $this->aFieldTypes,
|
||||
'aMap' => $this->getMapAll(),
|
||||
'aValueStyle' => $this->aValueStyle,
|
||||
'aSelectField' => $this->aSelectFields,
|
||||
'aFilterParam' => $this->aFilterParams,
|
||||
'aCcField' => $this->aCcFields,
|
||||
'sCcLast' => $this->getCcLast(),
|
||||
'sPageName' => $this->sPageName,
|
||||
'aPager' => [
|
||||
'aOrderBySchema'=> $this->getOrderBySchema(),
|
||||
'aOrderBy' => $this->getOrderBy(),
|
||||
'iTotal' => $oPaginatedData->total(),
|
||||
'iLimit' => min($this->iLimitMax, $iLimit),
|
||||
'iCurrentPage' => $oPaginatedData->currentPage(),
|
||||
'iLastPage' => $oPaginatedData->lastPage(),
|
||||
'iPerPage' => $oPaginatedData->perPage(),
|
||||
'iPrevPage' => max(1, $oPaginatedData->currentPage() - 1),
|
||||
'iNextPage' => min($oPaginatedData->lastPage(), $oPaginatedData->currentPage() + 1)
|
||||
]
|
||||
]
|
||||
], 200);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -246,6 +246,13 @@ class CronController
|
||||
|
||||
}
|
||||
|
||||
// 没有文件夹,创建一个
|
||||
if (!is_dir($this->sProjectPath."/feed")) {
|
||||
|
||||
mkdir($this->sProjectPath."/feed", 0755, true);
|
||||
|
||||
}
|
||||
|
||||
// 没有仓库,创建一个
|
||||
if (!is_dir($this->sProjectPath . "/.git")) {
|
||||
|
||||
|
||||
49
app/Http/Middleware/VerifyNasaToken.php
Normal file
49
app/Http/Middleware/VerifyNasaToken.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* 🚀 NASA 核心网关:Header 签名安全卫士
|
||||
* 🗣️ Verify 发音:/ˈverɪfaɪ/ ( 维瑞发一 ,意为:校验、证实 )
|
||||
*/
|
||||
final class VerifyNasaToken
|
||||
{
|
||||
public function handle(Request $oRequest, Closure $fNext): Response
|
||||
{
|
||||
// 👑 大厂全线对接:丢掉 $oRequest->input(),全面改用 ->header() 降维打击!
|
||||
// 🎯 核心微操:Laravel 的 header() 方法天生自带防抖,它对大小写不敏感,
|
||||
// 你传 'X-Nasa-Token' 或者 'x-nasa-token',底层都能稳稳咬住!
|
||||
$iRequestTime = (int)$oRequest->header('X-Nasa-Timestamp');
|
||||
$sClientToken = $oRequest->header('X-Nasa-Token');
|
||||
|
||||
// 1. 钢铁防御线一:前置空值熔断,连参数都没有的盲流直接当场轰杀
|
||||
if (!$iRequestTime || !$sClientToken) {
|
||||
return response()->json(['iStatus' => 0, 'sMsg' => 'Security Headers Missing'], 403);
|
||||
}
|
||||
|
||||
// 2. 时效性防御:前后端时间差超过 60 秒判定为重放攻击或过期
|
||||
if (abs(time() - $iRequestTime) > 60) {
|
||||
Log::warning("🛰️ NASA 网关拦截:请求超时已过期!当前服务器时间: " . time() . ",请求时间: {$iRequestTime}");
|
||||
return response()->json(['iStatus' => 0, 'sMsg' => 'Request Expired'], 403);
|
||||
}
|
||||
|
||||
// 3. 验签防御:从配置核心仓掏出密钥,用相同算法重算 Token 对比
|
||||
$sLocalSecret = config('nasa.secret');
|
||||
$sExpectedToken = md5("timestamp={$iRequestTime}&secret={$sLocalSecret}");
|
||||
|
||||
// 4. 钢铁防御线二:像素级对撞签名
|
||||
if ($sClientToken !== $sExpectedToken) {
|
||||
$s = "🚨 NASA 网关警报:发现非法伪造签名!客户端传值: {$sClientToken},期望值: {$sExpectedToken}";
|
||||
Log::error($s);
|
||||
return response()->json(['iStatus' => 0, 'sMsg' => $s], 403);
|
||||
}
|
||||
|
||||
// 5. 安全放行,通电!
|
||||
return $fNext($oRequest);
|
||||
}
|
||||
}
|
||||
@ -11,10 +11,18 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
// $middleware->alias([
|
||||
// 'test' => \App\Http\Middleware\Test::class,
|
||||
// ->withMiddleware(function (Middleware $middleware) {
|
||||
//// $middleware->alias([
|
||||
//// 'test' => \App\Http\Middleware\Test::class,
|
||||
//// ]);
|
||||
// $middleware->validateCsrfTokens(except: [
|
||||
// 'api/*',
|
||||
// ]);
|
||||
// })
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->alias([
|
||||
'nasa.auth' => \App\Http\Middleware\VerifyNasaToken::class,
|
||||
]);
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
'api/*',
|
||||
]);
|
||||
|
||||
6
config/nasa.php
Executable file
6
config/nasa.php
Executable file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'secret' => env('NASA_API_SECRET'),
|
||||
'verify_ssl' => env('NASA_API_VERIFY_SSL', true),
|
||||
];
|
||||
@ -100,7 +100,24 @@ sudo ln -s /snap/bin/certbot /usr/bin/certbot
|
||||
|
||||
sudo certbot --nginx
|
||||
|
||||
========nginx配置================
|
||||
=============添加拉取仓库流程==================
|
||||
|
||||
先走通标准流程,会自动init,生成fn和vertion那些传上去。
|
||||
|
||||
然后:
|
||||
git remote add tom https://gitea.diaodu.us/tom/github-pachong.git - 添加拉取地址,tom是自定义名字
|
||||
git config --global --add safe.directory /www/wwwroot/github-6.diaodu.us/github/xibanyahu-phppachong - 附加
|
||||
git pull tom master
|
||||
git reset --hard tom/master - 覆盖切换
|
||||
chown www-data:www-data * - 老活
|
||||
然后/test#cron__id 1,不出意外就通了
|
||||
|
||||
之后每次拉取都:
|
||||
git pull tom master
|
||||
git reset --hard tom/master
|
||||
|
||||
|
||||
=============nginx配置========================
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
@ -137,6 +154,8 @@ iphonevpn clash telegram nefilx vpn vless clashmeta vmess surge ssr
|
||||
|
||||
======
|
||||
|
||||
sudo -u www-data git push -u origin main --force
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"user_name": "jichangtuijian99", // github的username
|
||||
@ -160,22 +179,23 @@ iphonevpn clash telegram nefilx vpn vless clashmeta vmess surge ssr
|
||||
}
|
||||
|
||||
{
|
||||
"user_name": "xibanyahu",
|
||||
"project_name": "xibanyahu.github.io",
|
||||
"mail_name": "xibanyahu",
|
||||
"id": 1,
|
||||
"user_name": "danmaifu",
|
||||
"project_name": "mianfeijiedian",
|
||||
"mail_name": "erdama123",
|
||||
"mail_tail": "outlook.com",
|
||||
"sale_code": "clashfreenode.com",
|
||||
"sale_code": "clashv2rayfree.com",
|
||||
"sale_rate": 7,
|
||||
"sale_price": 8,
|
||||
"i_in10": "false",
|
||||
"inday_count": 3,
|
||||
"inday_count": 4,
|
||||
"cron_condition": "false",
|
||||
"mod": "test",
|
||||
"mod": "fn_c",
|
||||
"mod_type": "blade",
|
||||
"status": 1,
|
||||
"memo": "",
|
||||
"ship_code": "fuuuu",
|
||||
"ship_code": "fuuu",
|
||||
"fn_www": "clashfreenode.com",
|
||||
"show_huodong": 0,
|
||||
"show_huodong": 1,
|
||||
"show_error": 1
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Route;
|
||||
//use App\Http\Controllers\CronController;
|
||||
//use App\Http\Controllers\TestsController;
|
||||
use App\Http\Controllers\Api\Tg\HookController;
|
||||
|
||||
use App\Http\Controllers\Api\Nasa\V1\ArticleBaseController as NasaV1ArticleBaseController;
|
||||
|
||||
//Route::post('/api/tg/hook', [HookController::class, 'cmd']);
|
||||
|
||||
@ -15,3 +15,20 @@ use App\Http\Controllers\Api\Tg\HookController;
|
||||
// Route::post('/api/tg/hook', [HookController::class, 'cmd']);
|
||||
|
||||
//});
|
||||
|
||||
Route::prefix('nasa/v1')->middleware('nasa.auth')->group(function () {
|
||||
|
||||
Route::prefix('article/base')->group(function() {
|
||||
Route::get('', [NasaV1ArticleBaseController::class, 'index']);
|
||||
Route::get('detail/{id}', [NasaV1ArticleBaseController::class, 'detail']);
|
||||
Route::put('detail/save/{id}', [NasaV1ArticleBaseController::class, 'detailSave']);
|
||||
Route::delete('delete/{id}', [NasaV1ArticleBaseController::class, 'delete']);
|
||||
Route::get('add', [NasaV1ArticleBaseController::class, 'add']);
|
||||
Route::put('add/save', [NasaV1ArticleBaseController::class, 'addSave']);
|
||||
Route::get('clone/{id}', [NasaV1ArticleBaseController::class, 'clone']);
|
||||
Route::put('clone/save', [NasaV1ArticleBaseController::class, 'cloneSave']);
|
||||
Route::get('hit/{id}/{field}', [NasaV1ArticleBaseController::class, 'hit']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user