mirror of
https://github.com/acepanel/panel.git
synced 2026-02-05 12:23:35 +08:00
特性(网站管理):新增免费SSL证书申请
This commit is contained in:
@@ -57,6 +57,7 @@ class SettingsController extends Controller
|
||||
$data = [
|
||||
'name' => $settingArr['name'],
|
||||
'username' => $request->user()->username,
|
||||
'email' => $request->user()->email,
|
||||
'password' => '',
|
||||
'port' => $matches[1],
|
||||
'api' => $api,
|
||||
@@ -80,19 +81,22 @@ class SettingsController extends Controller
|
||||
$settings = $request->all();
|
||||
// 将数据入库
|
||||
foreach ($settings as $key => $value) {
|
||||
if ($key == 'access_token' || $key == 'username' || $key == 'password' || $key == 'api_token' || $key == 'api' || $key == 'port') {
|
||||
if ($key == 'access_token' || $key == 'username' || $key == 'email' || $key == 'password' || $key == 'api_token' || $key == 'api' || $key == 'port') {
|
||||
continue;
|
||||
}
|
||||
// 创建或更新
|
||||
Setting::query()->updateOrCreate(['name' => $key], ['value' => $value]);
|
||||
}
|
||||
// 单独处理用户名和密码
|
||||
// 单独处理用户名、密码、邮箱
|
||||
if ($request->input('username') != $request->user()->username) {
|
||||
$request->user()->update(['username' => $request->input('username')]);
|
||||
}
|
||||
if ($request->input('password') != '') {
|
||||
$request->user()->update(['password' => Hash::make($request->input('password'))]);
|
||||
}
|
||||
if ($request->input('email') != $request->user()->email) {
|
||||
$request->user()->update(['email' => $request->input('email')]);
|
||||
}
|
||||
// 处理面板端口
|
||||
$port = $request->input('port');
|
||||
$nginxConf = file_get_contents('/www/server/nginx/conf/nginx.conf');
|
||||
|
||||
@@ -9,9 +9,12 @@ namespace App\Http\Controllers\Api;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Website;
|
||||
use App\Models\Setting;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use skoerfgen\ACMECert\ACME_Exception;
|
||||
use skoerfgen\ACMECert\ACMECert;
|
||||
|
||||
class WebsitesController extends Controller
|
||||
{
|
||||
@@ -372,6 +375,13 @@ EOF;
|
||||
$website['ssl_certificate_key'] = file_get_contents($matches5[1][0]);
|
||||
$website['http_redirect'] = str_contains($nginx_config, '# http重定向标记位');
|
||||
$website['hsts'] = str_contains($nginx_config, '# hsts标记位');
|
||||
try {
|
||||
$sslDate = (new ACMECert())->getRemainingDays($website['ssl_certificate']);
|
||||
$sslDate = round($sslDate, 2);
|
||||
} catch (Exception $e) {
|
||||
$sslDate = '未知';
|
||||
}
|
||||
$website['ssl_date'] = $sslDate;
|
||||
} else {
|
||||
$website['ssl_certificate'] = @file_get_contents('/www/server/vhost/ssl/'.$name.'.pem');
|
||||
$website['ssl_certificate_key'] = @file_get_contents('/www/server/vhost/ssl/'.$name.'.key');
|
||||
@@ -1027,6 +1037,152 @@ EOF;
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签发SSL证书
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws ACME_Exception|Exception
|
||||
*/
|
||||
public function issueSsl(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$input = $this->validate($request, [
|
||||
'type' => 'required|in:lets,buypass,google,sslcom,zerossl',
|
||||
'name' => 'required',
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '参数错误:'.$e->getMessage(),
|
||||
'errors' => $e->errors()
|
||||
], 200);
|
||||
}
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
// 检查网站是否存在
|
||||
$website = Website::query()->where('name', $input['name'])->first();
|
||||
if (!$website) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '网站不存在',
|
||||
], 200);
|
||||
}
|
||||
// 从配置文件中获取网站域名
|
||||
$nginxConfig = file_get_contents('/www/server/vhost/'.$website['name'].'.conf');
|
||||
$domainConfig = $this->cut('# server_name标记位开始', '# server_name标记位结束', $nginxConfig);
|
||||
preg_match_all('/server_name\s+(.+);/', $domainConfig, $matches1);
|
||||
$domains = explode(" ", $matches1[1][0]);
|
||||
// 从配置文件中获取网站目录
|
||||
$pathConfig = $this->cut('# root标记位开始', '# root标记位结束', $nginxConfig);
|
||||
preg_match_all('/root\s+(.+);/', $pathConfig, $matches2);
|
||||
$path = $matches2[1][0];
|
||||
|
||||
/**
|
||||
* 对域名需要进行一下处理,如果域名是泛域名,返回暂不支持泛域名
|
||||
*/
|
||||
foreach ($domains as $domain) {
|
||||
if (str_contains($domain, '*')) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '暂不支持泛域名',
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($input['type']) {
|
||||
case 'lets':
|
||||
$ac = new ACMECert('https://acme-v02.api.letsencrypt.org/directory');
|
||||
break;
|
||||
case 'buypass':
|
||||
$ac = new ACMECert('https://api.buypass.com/acme/directory');
|
||||
break;
|
||||
case 'google':
|
||||
$ac = new ACMECert('https://dv.acme-v02.api.pki.goog/directory');
|
||||
break;
|
||||
case 'sslcom':
|
||||
$ac = new ACMECert('https://acme.ssl.com/sslcom-dv-rsa');
|
||||
break;
|
||||
case 'zerossl':
|
||||
$ac = new ACMECert('https://acme.zerossl.com/v2/DV90');
|
||||
break;
|
||||
default:
|
||||
$res = [
|
||||
'code' => 1,
|
||||
'msg' => '参数错误:type',
|
||||
];
|
||||
return response()->json($res);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$accountKey = $ac->generateECKey('P-384');
|
||||
$certKey = $ac->generateECKey('P-384');
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '生成密钥失败:'.$e->getMessage(),
|
||||
], 200);
|
||||
}
|
||||
try {
|
||||
$ac->loadAccountKey($accountKey);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '加载密钥失败:'.$e->getMessage(),
|
||||
], 200);
|
||||
}
|
||||
try {
|
||||
$ac->register(true, $user->email);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '注册CA账户失败:'.$e->getMessage(),
|
||||
], 200);
|
||||
}
|
||||
|
||||
// 初始化域名数组
|
||||
$domainConfig = [];
|
||||
foreach ($domains as $domain) {
|
||||
$domainConfig[$domain] = [
|
||||
'challenge' => 'http-01',
|
||||
'docroot' => $path
|
||||
];
|
||||
}
|
||||
|
||||
$handler = function ($opts) {
|
||||
$fn = $opts['config']['docroot'].$opts['key'];
|
||||
@mkdir(dirname($fn), 0777, true);
|
||||
file_put_contents($fn, $opts['value']);
|
||||
return function ($opts) {
|
||||
unlink($opts['config']['docroot'].$opts['key']);
|
||||
};
|
||||
};
|
||||
|
||||
// 申请证书
|
||||
try {
|
||||
$fullchain = $ac->getCertificateChain($certKey, $domainConfig, $handler);
|
||||
} catch (ACME_Exception $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '申请证书失败:'.$e->getMessage(),
|
||||
], 200);
|
||||
}
|
||||
|
||||
// 写入证书
|
||||
$sslDir = '/www/server/vhost/ssl/';
|
||||
file_put_contents($sslDir.$website['name'].'.key', $certKey);
|
||||
file_put_contents($sslDir.$website['name'].'.pem', $fullchain);
|
||||
|
||||
// 返回
|
||||
$res = [
|
||||
'code' => 0,
|
||||
'msg' => 'success',
|
||||
];
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 裁剪字符串
|
||||
* @param $begin
|
||||
|
||||
Reference in New Issue
Block a user