mirror of
https://github.com/ProxyPanel/ProxyPanel.git
synced 2026-04-11 15:10:54 +00:00
Clean code
This commit is contained in:
@@ -55,10 +55,8 @@ class QuantumultX
|
||||
} else {
|
||||
$config[] = 'obfs=wss';
|
||||
}
|
||||
} else {
|
||||
if ($server['v2_net'] === 'ws') {
|
||||
$config[] = 'obfs=ws';
|
||||
}
|
||||
} elseif ($server['v2_net'] === 'ws') {
|
||||
$config[] = 'obfs=ws';
|
||||
}
|
||||
|
||||
if ($server['v2_tls']) {
|
||||
|
||||
@@ -8,10 +8,10 @@ class Surfboard
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=custom",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
"{$server['method']}",
|
||||
"{$server['passwd']}",
|
||||
$server['host'],
|
||||
$server['port'],
|
||||
$server['method'],
|
||||
$server['passwd'],
|
||||
sysConfig('website_url').'/clients/SSEncrypt.module',
|
||||
'tfo=true',
|
||||
"udp-relay={$server['udp']}",
|
||||
@@ -25,21 +25,18 @@ class Surfboard
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=vmess",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
$server['host'],
|
||||
$server['port'],
|
||||
"username={$server['uuid']}",
|
||||
'tfo=true',
|
||||
"udp-relay={$server['udp']}",
|
||||
];
|
||||
|
||||
if ($server['v2_tls']) {
|
||||
$config[] = 'tls=true';
|
||||
$config[] = "sni={$server['v2_host']}";
|
||||
$config = array_merge($config, ['tls=true', "sni={$server['v2_host']}"]);
|
||||
}
|
||||
if ($server['v2_net'] === 'ws') {
|
||||
$config[] = 'ws=true';
|
||||
$config[] = "ws-path={$server['v2_path']}";
|
||||
$config[] = "ws-headers=Host:{$server['v2_host']}";
|
||||
$config = array_merge($config, ['ws=true', "ws-path={$server['v2_path']}", "ws-headers=Host:{$server['v2_host']}"]);
|
||||
}
|
||||
|
||||
return implode(',', $config).PHP_EOL;
|
||||
|
||||
@@ -8,8 +8,8 @@ class Surge
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=ss",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
$server['host'],
|
||||
$server['port'],
|
||||
"encrypt-method={$server['method']}",
|
||||
"password={$server['passwd']}",
|
||||
'tfo=true',
|
||||
@@ -24,21 +24,18 @@ class Surge
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=vmess",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
$server['host'],
|
||||
$server['port'],
|
||||
"username={$server['uuid']}",
|
||||
'tfo=true',
|
||||
"udp-relay={$server['udp']}",
|
||||
];
|
||||
|
||||
if ($server['v2_tls']) {
|
||||
$config[] = 'tls=true';
|
||||
$config[] = "sni={$server['v2_host']}";
|
||||
$config = array_merge($config, ['tls=true', "sni={$server['v2_host']}"]);
|
||||
}
|
||||
if ($server['v2_net'] === 'ws') {
|
||||
$config[] = 'ws=true';
|
||||
$config[] = "ws-path={$server['v2_path']}";
|
||||
$config[] = "ws-headers=Host:{$server['v2_host']}";
|
||||
$config = array_merge($config, ['ws=true', "ws-path={$server['v2_path']}", "ws-headers=Host:{$server['v2_host']}"]);
|
||||
}
|
||||
|
||||
return implode(',', $config).PHP_EOL;
|
||||
@@ -48,8 +45,8 @@ class Surge
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=trojan",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
$server['host'],
|
||||
$server['port'],
|
||||
"password={$server['passwd']}",
|
||||
$server['sni'] ? "sni={$server['sni']}" : '',
|
||||
'tfo=true',
|
||||
|
||||
@@ -24,7 +24,7 @@ class URLSchemes
|
||||
// TODO: More study required about id usage https://shadowsocks.org/en/wiki/SIP008-Online-Configuration-Delivery.html
|
||||
public static function buildShadowsocksSIP008($server)
|
||||
{
|
||||
$config = [
|
||||
return [
|
||||
'id' => $server['id'],
|
||||
'remark' => $server['name'],
|
||||
'server' => $server['host'],
|
||||
@@ -32,8 +32,6 @@ class URLSchemes
|
||||
'password' => $server['passwd'],
|
||||
'method' => $server['method'],
|
||||
];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function buildVmess($server)
|
||||
|
||||
@@ -32,7 +32,7 @@ class CloudFlare
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return [];
|
||||
}
|
||||
|
||||
public function store($ip, $type)
|
||||
|
||||
@@ -44,7 +44,7 @@ class DNSPod
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return [];
|
||||
}
|
||||
|
||||
public function domainList()
|
||||
@@ -57,13 +57,16 @@ class DNSPod
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send($action, $data = [])
|
||||
private function send($action, $data = null)
|
||||
{
|
||||
$public = [
|
||||
$parameters = [
|
||||
'login_token' => sysConfig('ddns_key').','.sysConfig('ddns_secret'),
|
||||
'format' => 'json',
|
||||
];
|
||||
$parameters = array_merge($data, $public);
|
||||
|
||||
if ($data) {
|
||||
$parameters = array_merge($data, $parameters);
|
||||
}
|
||||
|
||||
$response = Http::timeout(15)->asForm()->post(self::$apiHost.$action, $parameters);
|
||||
$message = $response->json();
|
||||
|
||||
@@ -20,11 +20,11 @@ class Namesilo
|
||||
$domainInfo = $this->analysisDomain();
|
||||
if ($domainInfo) {
|
||||
return $this->send('dnsAddRecord', [
|
||||
'domain' => $domainInfo[0],
|
||||
'rrtype' => $type,
|
||||
'rrhost' => $domainInfo[1],
|
||||
'domain' => $domainInfo[0],
|
||||
'rrtype' => $type,
|
||||
'rrhost' => $domainInfo[1],
|
||||
'rrvalue' => $ip,
|
||||
'rrttl' => 3600,
|
||||
'rrttl' => 3600,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class Namesilo
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return [];
|
||||
}
|
||||
|
||||
public function domainList()
|
||||
@@ -63,8 +63,8 @@ class Namesilo
|
||||
{
|
||||
$params = [
|
||||
'version' => 1,
|
||||
'type' => 'xml',
|
||||
'key' => sysConfig('ddns_key'),
|
||||
'type' => 'xml',
|
||||
'key' => sysConfig('ddns_key'),
|
||||
];
|
||||
$query = array_merge($params, $data);
|
||||
|
||||
@@ -74,7 +74,6 @@ class Namesilo
|
||||
if ($result && $result['reply']['code'] === '300' && $result['reply']['detail'] === 'success') {
|
||||
return $result['reply'];
|
||||
}
|
||||
|
||||
Log::error('[Namesilo API] - ['.$action.'] 请求失败:'.var_export($result, true));
|
||||
|
||||
return false;
|
||||
@@ -85,13 +84,18 @@ class Namesilo
|
||||
$recordId = $this->getRecordId($type);
|
||||
$domainInfo = $this->analysisDomain();
|
||||
|
||||
return $this->send('dnsUpdateRecord', [
|
||||
'domain' => $domainInfo[0],
|
||||
'rrid' => $recordId[0],
|
||||
'rrhost' => $domainInfo[1],
|
||||
'rrvalue' => $ip,
|
||||
'rrttl' => 3600,
|
||||
]);
|
||||
if ($domainInfo && $recordId) {
|
||||
return $this->send('dnsUpdateRecord', [
|
||||
'domain' => $domainInfo[0],
|
||||
'rrid' => $recordId[0],
|
||||
'rrhost' => $domainInfo[1],
|
||||
'rrvalue' => $ip,
|
||||
'rrttl' => 3600,
|
||||
]);
|
||||
}
|
||||
Log::error('[Namesilo API] - [更新] 处理失败:'.var_export($recordId, true).var_export($domainInfo, true));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getRecordId($type = null)
|
||||
|
||||
@@ -2,22 +2,15 @@
|
||||
|
||||
namespace App\Components;
|
||||
|
||||
use App\Channels\BarkChannel;
|
||||
use App\Channels\ServerChanChannel;
|
||||
use App\Models\Config;
|
||||
use App\Models\CouponLog;
|
||||
use App\Models\Marketing;
|
||||
use App\Models\NotificationLog;
|
||||
use App\Models\SsConfig;
|
||||
use App\Models\User;
|
||||
use App\Models\UserBanedLog;
|
||||
use App\Models\UserCreditLog;
|
||||
use App\Models\UserDataModifyLog;
|
||||
use App\Models\UserSubscribe;
|
||||
use Cache;
|
||||
use DateTime;
|
||||
use NotificationChannels\BearyChat\BearyChatChannel;
|
||||
use NotificationChannels\Telegram\TelegramChannel;
|
||||
use Str;
|
||||
|
||||
class Helpers
|
||||
|
||||
@@ -58,14 +58,15 @@ class IP
|
||||
|
||||
if ($ipInfo) {
|
||||
$location = explode('|', $ipInfo['region']);
|
||||
|
||||
return [
|
||||
'country' => $location[0] ?: '',
|
||||
'province' => $location[2] ?: '',
|
||||
'city' => $location[3] ?: '',
|
||||
'isp' => $location[4] ?: '',
|
||||
'area' => $location[1] ?: '',
|
||||
];
|
||||
if ($location) {
|
||||
return [
|
||||
'country' => $location[0] ?: '',
|
||||
'province' => $location[2] ?: '',
|
||||
'city' => $location[3] ?: '',
|
||||
'isp' => $location[4] ?: '',
|
||||
'area' => $location[1] ?: '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $ipInfo;
|
||||
|
||||
@@ -74,11 +74,12 @@ class NodeStatusDetection extends Command
|
||||
private function checkNodeNetwork(): void
|
||||
{
|
||||
$detectionCheckTimes = sysConfig('detection_check_times');
|
||||
$data = [];
|
||||
|
||||
foreach (Node::whereIsRelay(0)->whereStatus(1)->where('detection_type', '>', 0)->get() as $node) {
|
||||
if ($node->detection_type === 0) {
|
||||
continue;
|
||||
}
|
||||
$node_id = (int) $node->id;
|
||||
// 使用DDNS的node先通过gethostbyname获取ipv4地址
|
||||
if ($node->is_ddns) {
|
||||
$ip = gethostbyname($node->server);
|
||||
@@ -91,20 +92,20 @@ class NodeStatusDetection extends Command
|
||||
if ($node->detection_type !== 1) {
|
||||
$icmpCheck = (new NetworkDetection)->networkCheck($node->ip, true);
|
||||
if ($icmpCheck !== false && $icmpCheck !== '通讯正常') {
|
||||
$data[$node->id]['icmp'] = $icmpCheck;
|
||||
$data[$node_id]['icmp'] = $icmpCheck;
|
||||
}
|
||||
}
|
||||
if ($node->detection_type !== 2) {
|
||||
$tcpCheck = (new NetworkDetection)->networkCheck($node->ip, false, $node->single ? $node->port : 22);
|
||||
if ($tcpCheck !== false && $tcpCheck !== '通讯正常') {
|
||||
$data[$node->id]['tcp'] = $tcpCheck;
|
||||
$data[$node_id]['tcp'] = $tcpCheck;
|
||||
}
|
||||
}
|
||||
|
||||
// 节点检测次数
|
||||
if (isset($data[$node->id]) && $detectionCheckTimes) {
|
||||
if (isset($data[$node_id]) && $detectionCheckTimes) {
|
||||
// 已通知次数
|
||||
$cacheKey = 'detection_check_times'.$node->id;
|
||||
$cacheKey = 'detection_check_times'.$node_id;
|
||||
if (Cache::has($cacheKey)) {
|
||||
$times = Cache::get($cacheKey);
|
||||
} else {
|
||||
@@ -118,18 +119,18 @@ class NodeStatusDetection extends Command
|
||||
} else {
|
||||
Cache::forget($cacheKey);
|
||||
$node->update(['status' => 0]);
|
||||
$data[$node->id]['message'] = '自动进入维护状态';
|
||||
$data[$node_id]['message'] = '自动进入维护状态';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data[$node->id])) {
|
||||
$data[$node->id]['name'] = $node->name;
|
||||
if (isset($data[$node_id])) {
|
||||
$data[$node_id]['name'] = $node->name;
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
if ($data) { //只有在出现阻断线路时,才会发出警报
|
||||
if (isset($data)) { //只有在出现阻断线路时,才会发出警报
|
||||
Notification::send(User::find(1), new NodeBlocked($data));
|
||||
|
||||
Log::info("节点状态日志: \r\n".var_export($data, true));
|
||||
|
||||
@@ -57,8 +57,8 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command('dailyNodeReport')->dailyAt('09:00');
|
||||
$schedule->command('userTrafficWarning')->dailyAt('10:30');
|
||||
$schedule->command('userExpireWarning')->dailyAt('20:00');
|
||||
$schedule->command('userDailyTrafficStatistics')->dailyAt('23:55');
|
||||
$schedule->command('nodeDailyTrafficStatistics')->dailyAt('23:57');
|
||||
$schedule->command('userDailyTrafficStatistics')->dailyAt('23:58');
|
||||
$schedule->command('nodeDailyTrafficStatistics')->dailyAt('23:59');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@ class ArticleController extends Controller
|
||||
// 文章列表
|
||||
public function index()
|
||||
{
|
||||
return view('admin.article.index', ['articles' => Article::orderByDesc('sort')->paginate(15)->appends(request('page'))]);
|
||||
return view('admin.article.index', ['articles' => Article::orderByDesc('sort')->paginate()->appends(request('page'))]);
|
||||
}
|
||||
|
||||
// 添加文章页面
|
||||
|
||||
@@ -115,7 +115,7 @@ class CouponController extends Controller
|
||||
$spreadsheet->setActiveSheetIndex(0);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('抵用券');
|
||||
$sheet->fromArray(['名称', '使用次数', '有效期', '券码', '金额(元)', '使用限制(元)'], null);
|
||||
$sheet->fromArray(['名称', '使用次数', '有效期', '券码', '金额(元)', '使用限制(元)']);
|
||||
foreach ($voucherList as $k => $vo) {
|
||||
$dateRange = $vo->start_time.' ~ '.$vo->end_time;
|
||||
$sheet->fromArray([$vo->name, $vo->usable_times ?? '无限制', $dateRange, $vo->sn, $vo->value, $vo->rule], null, 'A'.($k + 2));
|
||||
@@ -126,7 +126,7 @@ class CouponController extends Controller
|
||||
$spreadsheet->setActiveSheetIndex(1);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('折扣券');
|
||||
$sheet->fromArray(['名称', '使用次数', '有效期', '券码', '折扣(折)', '使用限制(元)'], null);
|
||||
$sheet->fromArray(['名称', '使用次数', '有效期', '券码', '折扣(折)', '使用限制(元)']);
|
||||
foreach ($discountCouponList as $k => $vo) {
|
||||
$dateRange = $vo->start_time.' ~ '.$vo->end_time;
|
||||
$sheet->fromArray([$vo->name, $vo->usable_times ?? '无限制', $dateRange, $vo->sn, $vo->value, $vo->rule], null, 'A'.($k + 2));
|
||||
@@ -137,7 +137,7 @@ class CouponController extends Controller
|
||||
$spreadsheet->setActiveSheetIndex(2);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('充值券');
|
||||
$sheet->fromArray(['名称', '有效期', '券码', '金额(元)'], null);
|
||||
$sheet->fromArray(['名称', '有效期', '券码', '金额(元)']);
|
||||
foreach ($refillList as $k => $vo) {
|
||||
$dateRange = $vo->start_time.' ~ '.$vo->end_time;
|
||||
$sheet->fromArray([$vo->name, $dateRange, $vo->sn, $vo->value], null, 'A'.($k + 2));
|
||||
|
||||
@@ -44,7 +44,7 @@ class LogsController extends Controller
|
||||
|
||||
if (isset($is_coupon)) {
|
||||
if ($is_coupon) {
|
||||
$query->where('coupon_id', '<>', null);
|
||||
$query->where('coupon_id', '<>');
|
||||
} else {
|
||||
$query->whereCouponId(null);
|
||||
}
|
||||
@@ -64,7 +64,9 @@ class LogsController extends Controller
|
||||
|
||||
if (isset($range_time) && $range_time !== ',') {
|
||||
$range_time = explode(',', $range_time);
|
||||
$query->where('created_at', '>=', $range_time[0])->where('created_at', '<=', $range_time[1]);
|
||||
if ($range_time) {
|
||||
$query->where('created_at', '>=', $range_time[0])->where('created_at', '<=', $range_time[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($order_id)) {
|
||||
|
||||
@@ -197,7 +197,7 @@ class ToolsController extends Controller
|
||||
}
|
||||
|
||||
$logs = $this->tail($file, 10000);
|
||||
if (false !== $logs) {
|
||||
if ($logs) {
|
||||
foreach ($logs as $log) {
|
||||
if (str_contains($log, 'TCP connecting')) {
|
||||
continue;
|
||||
|
||||
@@ -96,7 +96,7 @@ class AdminController extends Controller
|
||||
$spreadsheet->setActiveSheetIndex(0);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('邀请码');
|
||||
$sheet->fromArray(['邀请码', '有效期'], null);
|
||||
$sheet->fromArray(['邀请码', '有效期']);
|
||||
|
||||
foreach ($inviteList as $k => $vo) {
|
||||
$sheet->fromArray([$vo->code, $vo->dateline], null, 'A'.($k + 2));
|
||||
|
||||
@@ -37,7 +37,7 @@ class BaseController
|
||||
public function returnData(string $message, string $status = 'fail', int $code = 400, array $data = [], array $addition = null): JsonResponse
|
||||
{
|
||||
$etag = self::abortIfNotModified($data);
|
||||
$data = ['status' => $status, 'code' => $code, 'data' => $data, 'message' => $message];
|
||||
$data = compact('status', 'code', 'data', 'message');
|
||||
|
||||
if (isset($addition)) {
|
||||
$data = array_merge($data, $addition);
|
||||
|
||||
@@ -339,33 +339,36 @@ class AuthController extends Controller
|
||||
{
|
||||
$emailFilterList = EmailFilter::whereType(sysConfig('is_email_filtering'))->pluck('words')->toArray();
|
||||
$emailSuffix = explode('@', $email); // 提取邮箱后缀
|
||||
switch (sysConfig('is_email_filtering')) {
|
||||
// 黑名单
|
||||
case 1:
|
||||
if (in_array(strtolower($emailSuffix[1]), $emailFilterList, true)) {
|
||||
if ($returnType) {
|
||||
return Redirect::back()->withErrors(trans('auth.email.error.banned'));
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'fail', 'message' => trans('auth.email.error.banned')]);
|
||||
}
|
||||
break;
|
||||
//白名单
|
||||
case 2:
|
||||
if (! in_array(strtolower($emailSuffix[1]), $emailFilterList, true)) {
|
||||
if ($emailSuffix) {
|
||||
switch (sysConfig('is_email_filtering')) {
|
||||
// 黑名单
|
||||
case 1:
|
||||
if (in_array(strtolower($emailSuffix[1]), $emailFilterList, true)) {
|
||||
if ($returnType) {
|
||||
return Redirect::back()->withErrors(trans('auth.email.error.banned'));
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'fail', 'message' => trans('auth.email.error.banned')]);
|
||||
}
|
||||
break;
|
||||
//白名单
|
||||
case 2:
|
||||
if (! in_array(strtolower($emailSuffix[1]), $emailFilterList, true)) {
|
||||
if ($returnType) {
|
||||
return Redirect::back()->withErrors(trans('auth.email.error.invalid'));
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'fail', 'message' => trans('auth.email.error.invalid')]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($returnType) {
|
||||
return Redirect::back()->withErrors(trans('auth.email.error.invalid'));
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'fail', 'message' => trans('auth.email.error.invalid')]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($returnType) {
|
||||
return Redirect::back()->withErrors(trans('auth.email.error.invalid'));
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'fail', 'message' => trans('auth.email.error.invalid')]);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -146,7 +146,10 @@ class Controller extends BaseController
|
||||
// 节点一天内的流量
|
||||
$hourlyData = array_fill(0, date('G') + 1, 0);
|
||||
foreach ($hourlyFlow as $date => $dataFlow) {
|
||||
$hourlyData[date('G', strtotime($date))] = round($dataFlow / GB, 3);
|
||||
$date = date('G', strtotime($date));
|
||||
if ($date) {
|
||||
$hourlyData[$date] = round($dataFlow / GB, 3);
|
||||
}
|
||||
}
|
||||
$hourlyData[date('G') + 1] = round($currentFlow / GB, 3);
|
||||
|
||||
@@ -160,8 +163,8 @@ class Controller extends BaseController
|
||||
return [
|
||||
'trafficDaily' => json_encode($dailyData),
|
||||
'trafficHourly' => json_encode($hourlyData),
|
||||
'monthDays' => json_encode(range(1, date('j'), 1)), // 本月天数
|
||||
'dayHours' => json_encode(range(0, date('G') + 1, 1)), // 本日小时
|
||||
'monthDays' => json_encode(range(1, date('j'))), // 本月天数
|
||||
'dayHours' => json_encode(range(0, date('G') + 1)), // 本日小时
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class isForbidden
|
||||
}
|
||||
|
||||
// 拒绝通过订阅链接域名访问网站,防止网站被探测
|
||||
if (false !== strpos(sysConfig('subscribe_domain'), $request->getHost())
|
||||
if (strpos(sysConfig('subscribe_domain'), $request->getHost()) !== false
|
||||
&& ! str_contains(sysConfig('subscribe_domain'), sysConfig('website_url'))) {
|
||||
Log::info('识别到通过订阅链接访问,强制跳转至百度('.IP::getClientIp().')');
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* 标签.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@component('mail::message')
|
||||
# {{$title}}
|
||||
|
||||
{!! $content !!}
|
||||
|
||||
@component('mail::button', ['url' => $url])
|
||||
|
||||
Reference in New Issue
Block a user