ServerController.php 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. <?php
  2. namespace App\Http\Controllers;
  3. use Carbon\Carbon;
  4. use App\Models\Site;
  5. use App\Jobs\CronSSH;
  6. use App\Models\Server;
  7. use App\Jobs\PhpCliSSH;
  8. use phpseclib3\Net\SSH2;
  9. use App\Jobs\RootResetSSH;
  10. use Illuminate\Support\Str;
  11. use Illuminate\Http\Request;
  12. use App\Jobs\PanelDomainAddSSH;
  13. use App\Jobs\PanelDomainSslSSH;
  14. use App\Jobs\PanelDomainRemoveSSH;
  15. use Illuminate\Support\Facades\URL;
  16. use Illuminate\Support\Facades\Http;
  17. use Illuminate\Support\Facades\Validator;
  18. class ServerController extends Controller
  19. {
  20. /**
  21. * List all servers
  22. *
  23. * @OA\Get(
  24. * path="/api/servers",
  25. * summary="List all servers",
  26. * tags={"Servers"},
  27. * description="List all servers managed by panel.",
  28. * @OA\Parameter(
  29. * name="Authorization",
  30. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  31. * required=true,
  32. * in="header",
  33. * @OA\Schema(type="string")
  34. * ),
  35. * @OA\Response(
  36. * response=200,
  37. * description="Successful request",
  38. * @OA\JsonContent(
  39. * type="array",
  40. * @OA\Items(
  41. * @OA\Property(
  42. * property="server_id",
  43. * description="Server unique ID",
  44. * type="string",
  45. * example="abc-123-def-456"
  46. * ),
  47. * @OA\Property(
  48. * property="name",
  49. * description="Server name",
  50. * type="string",
  51. * example="Staging Server",
  52. * ),
  53. * @OA\Property(
  54. * property="ip",
  55. * description="Server IP",
  56. * type="string",
  57. * example="123.123.123.123",
  58. * ),
  59. * @OA\Property(
  60. * property="provider",
  61. * description="Server provider",
  62. * type="string",
  63. * example="AWS",
  64. * ),
  65. * @OA\Property(
  66. * property="location",
  67. * description="Server location",
  68. * type="string",
  69. * example="Frankfurt"
  70. * ),
  71. * @OA\Property(
  72. * property="php",
  73. * description="Server PHP CLI version",
  74. * type="string",
  75. * example="7.4"
  76. * ),
  77. * @OA\Property(
  78. * property="default",
  79. * description="Server default status (panel server)",
  80. * type="boolean",
  81. * example="false"
  82. * ),
  83. * @OA\Property(
  84. * property="status",
  85. * description="Server installation status (0 not installed, 1 installed)",
  86. * type="integer",
  87. * example="1"
  88. * ),
  89. * @OA\Property(
  90. * property="sites",
  91. * description="The number of sites on this server",
  92. * type="integer",
  93. * example="12"
  94. * ),
  95. * )
  96. * )
  97. * ),
  98. * @OA\Response(
  99. * response=401,
  100. * description="Unauthorized access error"
  101. * )
  102. * )
  103. */
  104. public function index()
  105. {
  106. $servers = Server::all();
  107. $response = [];
  108. foreach ($servers as $server) {
  109. $data = [
  110. 'server_id' => $server->server_id,
  111. 'name' => $server->name,
  112. 'ip' => $server->ip,
  113. 'provider' => $server->provider,
  114. 'location' => $server->location,
  115. 'default' => $server->default,
  116. 'status' => $server->status,
  117. 'sites' => count($server->sites)
  118. ];
  119. array_push($response, $data);
  120. }
  121. return response()->json($response);
  122. }
  123. /**
  124. * Add a new server
  125. *
  126. * @OA\Post(
  127. * path="/api/servers",
  128. * summary="Add a new Server",
  129. * tags={"Servers"},
  130. * description="Add a new server to manage with panel.",
  131. * @OA\Parameter(
  132. * name="Authorization",
  133. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  134. * required=true,
  135. * in="header",
  136. * @OA\Schema(type="string")
  137. * ),
  138. * @OA\RequestBody(
  139. * required = true,
  140. * description = "Server creation payload",
  141. * @OA\JsonContent(
  142. * type="object",
  143. * @OA\Property(
  144. * property="ip",
  145. * description="Server IP",
  146. * type="string",
  147. * example="123.123.123.123",
  148. * ),
  149. * @OA\Property(
  150. * property="name",
  151. * description="Server name",
  152. * type="string",
  153. * example="Production Server",
  154. * minLength=3
  155. * ),
  156. * @OA\Property(
  157. * property="provider",
  158. * description="Server provider",
  159. * type="string",
  160. * example="Digital Ocean",
  161. * ),
  162. * @OA\Property(
  163. * property="location",
  164. * description="Server location",
  165. * type="string",
  166. * example="Amsterdam",
  167. * ),
  168. * required={"ip","name"}
  169. * )
  170. * ),
  171. * @OA\Response(
  172. * response=200,
  173. * description="Successful server creation",
  174. * @OA\JsonContent(
  175. * @OA\Property(
  176. * property="server_id",
  177. * description="Server unique ID",
  178. * type="string",
  179. * example="abc-123-def-456"
  180. * ),
  181. * @OA\Property(
  182. * property="name",
  183. * description="Server name",
  184. * type="string",
  185. * example="Staging Server",
  186. * ),
  187. * @OA\Property(
  188. * property="ip",
  189. * description="Server IP",
  190. * type="string",
  191. * example="123.123.123.123",
  192. * ),
  193. * @OA\Property(
  194. * property="provider",
  195. * description="Server provider",
  196. * type="string",
  197. * example="AWS",
  198. * ),
  199. * @OA\Property(
  200. * property="location",
  201. * description="Server location",
  202. * type="string",
  203. * example="Frankfurt"
  204. * ),
  205. * @OA\Property(
  206. * property="setup",
  207. * description="Server setup script",
  208. * type="string",
  209. * example="https://panel.domain.ltd/sh/setup/123456"
  210. * ),
  211. * )
  212. * ),
  213. * @OA\Response(
  214. * response=409,
  215. * description="Server conflict"
  216. * ),
  217. * @OA\Response(
  218. * response=400,
  219. * description="Bad Request"
  220. * ),
  221. * @OA\Response(
  222. * response=401,
  223. * description="Unauthorized access error"
  224. * )
  225. * )
  226. */
  227. public function create(Request $request)
  228. {
  229. $validator = Validator::make($request->all(), [
  230. 'ip' => 'required|ip',
  231. 'name' => 'required|min:3',
  232. ]);
  233. if ($validator->fails()) {
  234. return response()->json([
  235. 'message' => __('cipi.bad_request'),
  236. 'errors' => $validator->errors()->getMessages()
  237. ], 400);
  238. }
  239. if ($request->ip == $request->server('SERVER_ADDR')) {
  240. return response()->json([
  241. 'message' => __('cipi.server_conflict_ip_current_message'),
  242. 'errors' => __('cipi.server_conflict')
  243. ], 409);
  244. }
  245. if (Server::where('ip', $request->ip)->first()) {
  246. return response()->json([
  247. 'message' => __('cipi.server_conflict_ip_duplicate_message'),
  248. 'errors' => __('cipi.server_conflict')
  249. ], 409);
  250. }
  251. $server = new Server();
  252. $server->ip = $request->ip;
  253. $server->name = $request->name;
  254. $server->provider = $request->provider;
  255. $server->location = $request->location;
  256. $server->password = Str::random(24);
  257. $server->database = Str::random(24);
  258. $server->server_id = Str::uuid();
  259. $server->cron = ' ';
  260. $server->save();
  261. return response()->json([
  262. 'server_id' => $server->server_id,
  263. 'name' => $request->name,
  264. 'provider' => $request->provider,
  265. 'location' => $request->location,
  266. 'ip' => $request->ip,
  267. 'setup' => URL::to('/sh/setup/'.$server->server_id)
  268. ]);
  269. }
  270. /**
  271. * Delete a server
  272. *
  273. * @OA\Delete(
  274. * path="/api/servers/{server_id}",
  275. * summary="Delete a Server",
  276. * tags={"Servers"},
  277. * description="Delete a server from panel.",
  278. * @OA\Parameter(
  279. * name="Authorization",
  280. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  281. * required=true,
  282. * in="header",
  283. * @OA\Schema(type="string")
  284. * ),
  285. * @OA\Parameter(
  286. * name="server_id",
  287. * description="The id of the server to delete.",
  288. * required=true,
  289. * in="path",
  290. * @OA\Schema(type="string")
  291. * ),
  292. * @OA\Response(
  293. * response=200,
  294. * description="Successful server deleted",
  295. * ),
  296. * @OA\Response(
  297. * response=404,
  298. * description="Server not found"
  299. * ),
  300. * @OA\Response(
  301. * response=400,
  302. * description="Bad Request"
  303. * ),
  304. * @OA\Response(
  305. * response=401,
  306. * description="Unauthorized access error"
  307. * )
  308. * )
  309. */
  310. public function destroy(string $server_id)
  311. {
  312. $server = Server::where('server_id', $server_id)->first();
  313. if (!$server) {
  314. return response()->json([
  315. 'message' => __('cipi.server_not_found_message_default'),
  316. 'errors' => __('cipi.server_not_found')
  317. ], 404);
  318. }
  319. if ($server->default) {
  320. return response()->json([
  321. 'message' => __('cipi.delete_default_server_message'),
  322. 'errors' => __('cipi.bad_request')
  323. ], 400);
  324. }
  325. $server->delete();
  326. return response()->json([]);
  327. }
  328. /**
  329. * Server information
  330. *
  331. * @OA\Get(
  332. * path="/api/servers/{server_id}",
  333. * summary="Server information",
  334. * tags={"Servers"},
  335. * description="Get server information.",
  336. * @OA\Parameter(
  337. * name="Authorization",
  338. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  339. * required=true,
  340. * in="header",
  341. * @OA\Schema(type="string")
  342. * ),
  343. * @OA\Parameter(
  344. * name="server_id",
  345. * description="The id of the server.",
  346. * required=true,
  347. * in="path",
  348. * @OA\Schema(type="string")
  349. * ),
  350. * @OA\Response(
  351. * response=200,
  352. * description="Successful server information",
  353. * @OA\JsonContent(
  354. * @OA\Property(
  355. * property="server_id",
  356. * description="Server unique ID",
  357. * type="string",
  358. * example="abc-123-def-456"
  359. * ),
  360. * @OA\Property(
  361. * property="name",
  362. * description="Server name",
  363. * type="string",
  364. * example="Staging Server",
  365. * ),
  366. * @OA\Property(
  367. * property="ip",
  368. * description="Server IP",
  369. * type="string",
  370. * example="123.123.123.123",
  371. * ),
  372. * @OA\Property(
  373. * property="provider",
  374. * description="Server provider",
  375. * type="string",
  376. * example="AWS",
  377. * ),
  378. * @OA\Property(
  379. * property="default",
  380. * description="Server default status (panel server)",
  381. * type="boolean",
  382. * example="false"
  383. * ),
  384. * @OA\Property(
  385. * property="php",
  386. * description="Server PHP CLI version",
  387. * type="string",
  388. * example="7.4"
  389. * ),
  390. * @OA\Property(
  391. * property="github_key",
  392. * description="Server Github deploy key",
  393. * type="string"
  394. * ),
  395. * @OA\Property(
  396. * property="build",
  397. * description="Server build version",
  398. * type="integer",
  399. * example="20210317001"
  400. * ),
  401. * @OA\Property(
  402. * property="cron",
  403. * description="Server cron",
  404. * type="text",
  405. * ),
  406. * @OA\Property(
  407. * property="sites",
  408. * description="The number of sites on this server",
  409. * type="integer",
  410. * example="12"
  411. * ),
  412. * )
  413. * ),
  414. * @OA\Response(
  415. * response=404,
  416. * description="Server not found or not installed"
  417. * ),
  418. * @OA\Response(
  419. * response=400,
  420. * description="Bad Request"
  421. * ),
  422. * @OA\Response(
  423. * response=401,
  424. * description="Unauthorized access error"
  425. * )
  426. * )
  427. */
  428. public function show(string $server_id)
  429. {
  430. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  431. if (!$server) {
  432. return response()->json([
  433. 'message' => __('cipi.server_not_found_message'),
  434. 'errors' => __('cipi.server_not_found')
  435. ], 404);
  436. }
  437. return response()->json([
  438. 'sever_id' => $server->server_id,
  439. 'name' => $server->name,
  440. 'ip' => $server->ip,
  441. 'location' => $server->location,
  442. 'provider' => $server->provider,
  443. 'default' => $server->default,
  444. 'php' => $server->php,
  445. 'github_key'=> $server->github_key,
  446. 'build' => $server->build,
  447. 'cron' => $server->cron,
  448. 'sites' => count($server->sites)
  449. ]);
  450. }
  451. /**
  452. * Panel server information
  453. *
  454. * @OA\Get(
  455. * path="/api/servers/panel",
  456. * summary="Panel server information",
  457. * tags={"Servers"},
  458. * description="Get panel server information.",
  459. * @OA\Parameter(
  460. * name="Authorization",
  461. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  462. * required=true,
  463. * in="header",
  464. * @OA\Schema(type="string")
  465. * ),
  466. * @OA\Response(
  467. * response=200,
  468. * description="Successful server information",
  469. * @OA\JsonContent(
  470. * @OA\Property(
  471. * property="server_id",
  472. * description="Server unique ID",
  473. * type="string",
  474. * example="abc-123-def-456"
  475. * ),
  476. * @OA\Property(
  477. * property="name",
  478. * description="Server name",
  479. * type="string",
  480. * example="Staging Server",
  481. * ),
  482. * @OA\Property(
  483. * property="ip",
  484. * description="Server IP",
  485. * type="string",
  486. * example="123.123.123.123",
  487. * ),
  488. * @OA\Property(
  489. * property="provider",
  490. * description="Server provider",
  491. * type="string",
  492. * example="AWS",
  493. * ),
  494. * @OA\Property(
  495. * property="domain",
  496. * description="Server default domain for panel",
  497. * type="string",
  498. * example="panel.domain.ltd"
  499. * ),
  500. * @OA\Property(
  501. * property="php",
  502. * description="Server PHP CLI version",
  503. * type="string",
  504. * example="7.4"
  505. * ),
  506. * @OA\Property(
  507. * property="github_key",
  508. * description="Server Github deploy key",
  509. * type="string"
  510. * ),
  511. * @OA\Property(
  512. * property="build",
  513. * description="Server build version",
  514. * type="integer",
  515. * example="20210317001"
  516. * ),
  517. * @OA\Property(
  518. * property="cron",
  519. * description="Server cron",
  520. * type="text",
  521. * ),
  522. * @OA\Property(
  523. * property="sites",
  524. * description="The number of sites on this server",
  525. * type="integer",
  526. * example="12"
  527. * ),
  528. * )
  529. * ),
  530. * @OA\Response(
  531. * response=401,
  532. * description="Unauthorized access error"
  533. * ),
  534. * @OA\Response(
  535. * response=404,
  536. * description="Server not found"
  537. * ),
  538. * )
  539. */
  540. public function panel()
  541. {
  542. $server = Server::where('default', 1)->first();
  543. if (!$server) {
  544. return response()->json([
  545. 'message' => __('cipi.server_not_found_native_message'),
  546. 'errors' => __('cipi.server_not_found')
  547. ], 404);
  548. }
  549. $site = Site::where('server_id', $server->id)->where('panel', 1)->first();
  550. if (!$site) {
  551. $domain = '';
  552. } else {
  553. $domain = $site->domain;
  554. }
  555. return response()->json([
  556. 'sever_id' => $server->server_id,
  557. 'name' => $server->name,
  558. 'ip' => $server->ip,
  559. 'location' => $server->location,
  560. 'provider' => $server->provider,
  561. 'domain' => $domain,
  562. 'php' => $server->php,
  563. 'github_key'=> $server->github_key,
  564. 'build' => $server->build,
  565. 'cron' => $server->cron,
  566. 'sites' => count($server->sites)
  567. ]);
  568. }
  569. /**
  570. * Add a domain / subdomain to panel
  571. *
  572. * @OA\Patch(
  573. * path="/api/servers/panel/domain",
  574. * summary="Add a domain / subdomain to panel",
  575. * tags={"Servers"},
  576. * description="Add a domain / subdomain to panel.",
  577. * @OA\Parameter(
  578. * name="Authorization",
  579. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  580. * required=true,
  581. * in="header",
  582. * @OA\Schema(type="string")
  583. * ),
  584. * @OA\RequestBody(
  585. * required = true,
  586. * description = "Panel domain payload",
  587. * @OA\JsonContent(
  588. * type="object",
  589. * @OA\Property(
  590. * property="domain",
  591. * description="Panel domain",
  592. * type="string",
  593. * example="panel.domain.ltd",
  594. * ),
  595. * )
  596. * ),
  597. * @OA\Response(
  598. * response=200,
  599. * description="Successful panel domain update",
  600. * ),
  601. * @OA\Response(
  602. * response=401,
  603. * description="Unauthorized access error"
  604. * ),
  605. * @OA\Response(
  606. * response=400,
  607. * description="Bad Request"
  608. * ),
  609. * @OA\Response(
  610. * response=404,
  611. * description="Server not found"
  612. * ),
  613. * )
  614. */
  615. public function paneldomain(Request $request)
  616. {
  617. $server = Server::where('default', 1)->first();
  618. if (!$server) {
  619. return response()->json([
  620. 'message' => __('cipi.server_not_found_native_message'),
  621. 'errors' => __('cipi.server_not_found')
  622. ], 404);
  623. }
  624. $site = Site::where('server_id', $server->id)->where('panel', true)->first();
  625. if ($site) {
  626. $site->delete();
  627. PanelDomainRemoveSSH::dispatch($server)->delay(Carbon::now()->addSeconds(3));
  628. }
  629. if ($request->domain && $request->domain != '') {
  630. $validator = Validator::make($request->all(), [
  631. 'domain' => 'required'
  632. ]);
  633. if ($validator->fails()) {
  634. return response()->json([
  635. 'message' => __('cipi.bad_request'),
  636. 'errors' => $validator->errors()->getMessages()
  637. ], 400);
  638. }
  639. $newsite = new Site;
  640. $newsite->server_id = $server->id;
  641. $newsite->domain = $request->domain;
  642. $newsite->site_id = sha1(microtime());
  643. $newsite->username = md5(microtime());
  644. $newsite->password = 'Secret_123';
  645. $newsite->database = 'Secret_123';
  646. $newsite->panel = true;
  647. $newsite->save();
  648. PanelDomainAddSSH::dispatch($server)->delay(Carbon::now()->addSeconds(3));
  649. }
  650. return response()->json([]);
  651. }
  652. /**
  653. * Require SSL for panel
  654. *
  655. * @OA\Post(
  656. * path="/api/servers/panel/ssl",
  657. * summary="Require SSL for panel",
  658. * tags={"Servers"},
  659. * description="Require SSL for panel domain / subdomain.",
  660. * @OA\Parameter(
  661. * name="Authorization",
  662. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  663. * required=true,
  664. * in="header",
  665. * @OA\Schema(type="string")
  666. * ),
  667. * @OA\Response(
  668. * response=200,
  669. * description="Successful SSL generation"
  670. * ),
  671. * @OA\Response(
  672. * response=401,
  673. * description="Unauthorized access error"
  674. * ),
  675. * @OA\Response(
  676. * response=400,
  677. * description="Bad request"
  678. * ),
  679. * @OA\Response(
  680. * response=404,
  681. * description="Server not found"
  682. * ),
  683. * )
  684. */
  685. public function panelssl()
  686. {
  687. $server = Server::where('default', 1)->first();
  688. if (!$server) {
  689. return response()->json([
  690. 'message' => __('cipi.server_not_found_native_message'),
  691. 'errors' => __('cipi.server_not_found')
  692. ], 404);
  693. }
  694. $site = Site::where('server_id', $server->id)->where('panel', true)->first();
  695. if ($site) {
  696. PanelDomainSslSSH::dispatch($server, $site)->delay(Carbon::now()->addSeconds(3));
  697. } else {
  698. return response()->json([
  699. 'message' => __('cipi.ssl_request_error_message'),
  700. 'errors' => __('cipi.bad_request')
  701. ], 400);
  702. }
  703. return response()->json([]);
  704. }
  705. /**
  706. * Server edit
  707. *
  708. * @OA\Patch(
  709. * path="/api/servers/{server_id}",
  710. * summary="Server edit",
  711. * tags={"Servers"},
  712. * description="Edit server information.",
  713. * @OA\Parameter(
  714. * name="Authorization",
  715. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  716. * required=true,
  717. * in="header",
  718. * @OA\Schema(type="string")
  719. * ),
  720. * @OA\Parameter(
  721. * name="server_id",
  722. * description="The id of the server to edit.",
  723. * required=true,
  724. * in="path",
  725. * @OA\Schema(type="string")
  726. * ),
  727. * @OA\RequestBody(
  728. * required = true,
  729. * description = "Server creation payload",
  730. * @OA\JsonContent(
  731. * type="object",
  732. * @OA\Property(
  733. * property="ip",
  734. * description="Server IP",
  735. * type="string",
  736. * example="123.123.123.123",
  737. * ),
  738. * @OA\Property(
  739. * property="name",
  740. * description="Server name",
  741. * type="string",
  742. * example="Production Server",
  743. * minLength=3
  744. * ),
  745. * @OA\Property(
  746. * property="provider",
  747. * description="Server provider",
  748. * type="string",
  749. * example="Digital Ocean",
  750. * ),
  751. * @OA\Property(
  752. * property="location",
  753. * description="Server location",
  754. * type="string",
  755. * example="Amsterdam",
  756. * ),
  757. * @OA\Property(
  758. * property="php",
  759. * description="Server PHP CLI version",
  760. * type="string",
  761. * example="7.4",
  762. * ),
  763. * @OA\Property(
  764. * property="cron",
  765. * description="Server crontab",
  766. * type="text",
  767. * ),
  768. * )
  769. * ),
  770. * @OA\Response(
  771. * response=200,
  772. * description="Successful server editing",
  773. * @OA\JsonContent(
  774. * @OA\Property(
  775. * property="server_id",
  776. * description="Server unique ID",
  777. * type="string",
  778. * example="abc-123-def-456"
  779. * ),
  780. * @OA\Property(
  781. * property="name",
  782. * description="Server name",
  783. * type="string",
  784. * example="Staging Server",
  785. * ),
  786. * @OA\Property(
  787. * property="ip",
  788. * description="Server IP",
  789. * type="string",
  790. * example="123.123.123.123",
  791. * ),
  792. * @OA\Property(
  793. * property="provider",
  794. * description="Server provider",
  795. * type="string",
  796. * example="AWS",
  797. * ),
  798. * @OA\Property(
  799. * property="default",
  800. * description="Server default status (panel server)",
  801. * type="boolean",
  802. * example="false"
  803. * ),
  804. * @OA\Property(
  805. * property="status",
  806. * description="Server status",
  807. * type="integer",
  808. * example="1"
  809. * ),
  810. * @OA\Property(
  811. * property="php",
  812. * description="Server PHP CLI version",
  813. * type="string",
  814. * example="7.4"
  815. * ),
  816. * @OA\Property(
  817. * property="github_key",
  818. * description="Server Github deploy key",
  819. * type="string"
  820. * ),
  821. * @OA\Property(
  822. * property="build",
  823. * description="Server build version",
  824. * type="integer",
  825. * example="20210317001"
  826. * ),
  827. * @OA\Property(
  828. * property="cron",
  829. * description="Server cron",
  830. * type="text",
  831. * ),
  832. * )
  833. * ),
  834. * @OA\Response(
  835. * response=404,
  836. * description="Server not found or not installed"
  837. * ),
  838. * @OA\Response(
  839. * response=400,
  840. * description="Bad Request"
  841. * ),
  842. * @OA\Response(
  843. * response=401,
  844. * description="Unauthorized access error"
  845. * ),
  846. * @OA\Response(
  847. * response=409,
  848. * description="Server conflict"
  849. * ),
  850. * )
  851. */
  852. public function edit(Request $request, string $server_id)
  853. {
  854. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  855. if (!$server) {
  856. return response()->json([
  857. 'message' => __('cipi.server_not_found_message'),
  858. 'errors' => __('cipi.server_not_found')
  859. ], 404);
  860. }
  861. if ($request->ip) {
  862. $validator = Validator::make($request->all(), [
  863. 'ip' => 'required|ip'
  864. ]);
  865. if ($validator->fails()) {
  866. return response()->json([
  867. 'message' => __('cipi.bad_request'),
  868. 'errors' => $validator->errors()->getMessages()
  869. ], 400);
  870. }
  871. if (!$server->default && $request->ip == str_replace("\n", '', file_get_contents('https://checkip.amazonaws.com'))) {
  872. return response()->json([
  873. 'message' => __('cipi.edit_server_current_ip_error_message'),
  874. 'errors' => __('cipi.server_conflict')
  875. ], 409);
  876. }
  877. if (Server::where('ip', $request->ip)->where('server_id', '<>', $server_id)->first()) {
  878. return response()->json([
  879. 'message' => __('cipi.server_conflict_ip_duplicate_message'),
  880. 'errors' => __('cipi.server_conflict')
  881. ], 409);
  882. }
  883. if ($server->default) {
  884. $server->ip = str_replace("\n", '', file_get_contents('https://checkip.amazonaws.com'));
  885. } else {
  886. $server->ip = $request->ip;
  887. }
  888. }
  889. if ($request->name) {
  890. $validator = Validator::make($request->all(), [
  891. 'name' => 'required|min:3'
  892. ]);
  893. if ($validator->fails()) {
  894. return response()->json([
  895. 'message' => __('cipi.bad_request'),
  896. 'errors' => $validator->errors()->getMessages()
  897. ], 400);
  898. }
  899. $server->name = $request->name;
  900. }
  901. if ($request->provider) {
  902. $server->provider = $request->provider;
  903. }
  904. if ($request->location) {
  905. $server->location = $request->location;
  906. }
  907. if ($request->cron) {
  908. $server->cron = $request->cron;
  909. $server->save();
  910. CronSSH::dispatch($server)->delay(Carbon::now()->addSeconds(3));
  911. }
  912. if ($request->php) {
  913. if (!in_array($request->php, config('cipi.phpvers'))) {
  914. return response()->json([
  915. 'message' => __('cipi.bad_request'),
  916. 'errors' => 'Invalid PHP version.'
  917. ], 400);
  918. }
  919. PhpCliSSH::dispatch($server, $request->php)->delay(Carbon::now()->addSeconds(3));
  920. $server->php = $request->php;
  921. }
  922. $server->save();
  923. return response()->json([
  924. 'sever_id' => $server->server_id,
  925. 'name' => $server->name,
  926. 'ip' => $server->ip,
  927. 'location' => $server->location,
  928. 'provider' => $server->provider,
  929. 'default' => $server->default,
  930. 'status' => $server->status,
  931. 'php' => $server->php,
  932. 'github_key'=> $server->github_key,
  933. 'build' => $server->build,
  934. 'cron' => $server->cron
  935. ]);
  936. }
  937. /**
  938. * Server ping
  939. *
  940. * @OA\Get(
  941. * path="/api/servers/{server_id}/ping",
  942. * summary="Server ping",
  943. * tags={"Servers"},
  944. * description="Check real time server ping.",
  945. * @OA\Parameter(
  946. * name="Authorization",
  947. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  948. * required=true,
  949. * in="header",
  950. * @OA\Schema(type="string")
  951. * ),
  952. * @OA\Parameter(
  953. * name="server_id",
  954. * description="The id of the server to check.",
  955. * required=true,
  956. * in="path",
  957. * @OA\Schema(type="string")
  958. * ),
  959. * @OA\Response(
  960. * response=200,
  961. * description="Successful server ping check",
  962. * ),
  963. * @OA\Response(
  964. * response=404,
  965. * description="Server not found or not installed"
  966. * ),
  967. * @OA\Response(
  968. * response=401,
  969. * description="Unauthorized access error"
  970. * ),
  971. * @OA\Response(
  972. * response=503,
  973. * description="Server unavailable"
  974. * ),
  975. * )
  976. */
  977. public function ping(string $server_id)
  978. {
  979. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  980. if (!$server) {
  981. return response()->json([
  982. 'message' => __('cipi.server_not_found_message'),
  983. 'errors' => __('cipi.server_not_found')
  984. ], 404);
  985. }
  986. try {
  987. $remote = Http::get('http://'.$server->ip.'/ping_'.$server->server_id.'.php');
  988. if ($remote->status() == 200) {
  989. //
  990. } else {
  991. return response()->json([
  992. 'message' => __('cipi.server_unavailable_message'),
  993. 'errors' => __('cipi.server_unavailable')
  994. ], 503);
  995. }
  996. } catch (\Throwable $th) {
  997. return response()->json([
  998. 'message' => __('cipi.server_unavailable_message'),
  999. 'errors' => __('cipi.server_unavailable')
  1000. ], 503);
  1001. }
  1002. }
  1003. /**
  1004. * Server healthy
  1005. *
  1006. * @OA\Get(
  1007. * path="/api/servers/{server_id}/healthy",
  1008. * summary="Server healthy",
  1009. * tags={"Servers"},
  1010. * description="Check real time server healthy.",
  1011. * @OA\Parameter(
  1012. * name="Authorization",
  1013. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  1014. * required=true,
  1015. * in="header",
  1016. * @OA\Schema(type="string")
  1017. * ),
  1018. * @OA\Parameter(
  1019. * name="server_id",
  1020. * description="The id of the server to check.",
  1021. * required=true,
  1022. * in="path",
  1023. * @OA\Schema(type="string")
  1024. * ),
  1025. * @OA\Response(
  1026. * response=200,
  1027. * description="Successful server healthy check",
  1028. * @OA\JsonContent(
  1029. * @OA\Property(
  1030. * property="cpu",
  1031. * description="Current usage of CPU in %",
  1032. * type="float",
  1033. * example="72.50"
  1034. * ),
  1035. * @OA\Property(
  1036. * property="ram",
  1037. * description="Current usage of RAM in %",
  1038. * type="float",
  1039. * example="56.34",
  1040. * ),
  1041. * @OA\Property(
  1042. * property="hdd",
  1043. * description="Current usage of HDD in %",
  1044. * type="float",
  1045. * example="32",
  1046. * ),
  1047. * )
  1048. * ),
  1049. * @OA\Response(
  1050. * response=404,
  1051. * description="Server not found or not installed"
  1052. * ),
  1053. * @OA\Response(
  1054. * response=400,
  1055. * description="Bad Request"
  1056. * ),
  1057. * @OA\Response(
  1058. * response=401,
  1059. * description="Unauthorized access error"
  1060. * ),
  1061. * @OA\Response(
  1062. * response=500,
  1063. * description="SSH server connection issue"
  1064. * ),
  1065. * )
  1066. */
  1067. public function healthy(string $server_id)
  1068. {
  1069. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  1070. if (!$server) {
  1071. return response()->json([
  1072. 'message' => __('cipi.server_not_found_message'),
  1073. 'errors' => __('cipi.server_not_found')
  1074. ], 404);
  1075. }
  1076. try {
  1077. $remote = Http::get('http://'.$server->ip.'/ping_'.$server->server_id.'.php');
  1078. if ($remote->status() != 200) {
  1079. return response()->json([
  1080. 'cpu' => '0',
  1081. 'ram' => '0',
  1082. 'hdd' => '0'
  1083. ]);
  1084. }
  1085. } catch (\Throwable $th) {
  1086. return response()->json([
  1087. 'cpu' => '0',
  1088. 'ram' => '0',
  1089. 'hdd' => '0'
  1090. ]);
  1091. }
  1092. try {
  1093. $ssh = new SSH2($server->ip, 22);
  1094. if (!$ssh->login('cipi', $server->password)) {
  1095. return response()->json([
  1096. 'message' => __('cipi.server_error_ssh_error_message').$server->server_id,
  1097. 'errors' => __('cipi.server_error')
  1098. ], 500);
  1099. }
  1100. $ssh->setTimeout(360);
  1101. $status = $ssh->exec('echo "`LC_ALL=C top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk \'{print 100 - $1}\'`%;`free -m | awk \'/Mem:/ { printf("%3.1f%%", $3/$2*100) }\'`;`df -h / | awk \'/\// {print $(NF-1)}\'`"');
  1102. $ssh->exec('exit');
  1103. } catch (\Throwable $th) {
  1104. return response()->json([
  1105. 'message' => __('cipi.something_error_message'),
  1106. 'errors' => __('cipi.error')
  1107. ], 500);
  1108. }
  1109. $status = str_replace('%', '', $status);
  1110. $status = str_replace("\n", '', $status);
  1111. $api = explode(';', $status);
  1112. return response()->json([
  1113. 'cpu' => $api[0],
  1114. 'ram' => $api[1],
  1115. 'hdd' => $api[2]
  1116. ]);
  1117. }
  1118. /**
  1119. * Server root password reset
  1120. *
  1121. * @OA\Post(
  1122. * path="/api/servers/{server_id}/rootreset",
  1123. * summary="Server root password reset",
  1124. * tags={"Servers"},
  1125. * description="Reset server root password (for cipi user).",
  1126. * @OA\Parameter(
  1127. * name="Authorization",
  1128. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  1129. * required=true,
  1130. * in="header",
  1131. * @OA\Schema(type="string")
  1132. * ),
  1133. * @OA\Parameter(
  1134. * name="server_id",
  1135. * description="The id of the server.",
  1136. * required=true,
  1137. * in="path",
  1138. * @OA\Schema(type="string")
  1139. * ),
  1140. * @OA\Response(
  1141. * response=200,
  1142. * description="Successful password reset",
  1143. * @OA\JsonContent(
  1144. * @OA\Property(
  1145. * property="password",
  1146. * description="New assigned password for cipi root user",
  1147. * type="string",
  1148. * example="Secret_123"
  1149. * ),
  1150. * )
  1151. * ),
  1152. * @OA\Response(
  1153. * response=404,
  1154. * description="Server not found or not installed"
  1155. * ),
  1156. * @OA\Response(
  1157. * response=401,
  1158. * description="Unauthorized access error"
  1159. * ),
  1160. * )
  1161. */
  1162. public function rootreset(string $server_id)
  1163. {
  1164. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  1165. if (!$server) {
  1166. return response()->json([
  1167. 'message' => __('cipi.server_not_found_message'),
  1168. 'errors' => __('cipi.server_not_found')
  1169. ], 404);
  1170. }
  1171. $last_password = $server->password;
  1172. $new_password = Str::random(24);
  1173. $server->password = $new_password;
  1174. $server->save();
  1175. RootResetSSH::dispatch($server, $new_password, $last_password)->delay(Carbon::now()->addSeconds(1));
  1176. return response()->json([
  1177. 'password' => $server->password
  1178. ]);
  1179. }
  1180. /**
  1181. * Server service restart
  1182. *
  1183. * @OA\Post(
  1184. * path="/api/servers/{server_id}/servicerestart/{service}",
  1185. * summary="Server service restart",
  1186. * tags={"Servers"},
  1187. * description="Restart a server server (nginx, php, mysql, redis or supervisor).",
  1188. * @OA\Parameter(
  1189. * name="Authorization",
  1190. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  1191. * required=true,
  1192. * in="header",
  1193. * @OA\Schema(type="string")
  1194. * ),
  1195. * @OA\Parameter(
  1196. * name="server_id",
  1197. * description="The id of the server.",
  1198. * required=true,
  1199. * in="path",
  1200. * @OA\Schema(type="string")
  1201. * ),
  1202. * @OA\Parameter(
  1203. * name="service",
  1204. * description="The service to restart.",
  1205. * required=true,
  1206. * in="path",
  1207. * @OA\Schema(type="string")
  1208. * ),
  1209. * @OA\Response(
  1210. * response=200,
  1211. * description="Successful service restart"
  1212. * ),
  1213. * @OA\Response(
  1214. * response=404,
  1215. * description="Server not found or not installed"
  1216. * ),
  1217. * @OA\Response(
  1218. * response=400,
  1219. * description="Bad request"
  1220. * ),
  1221. * @OA\Response(
  1222. * response=401,
  1223. * description="Unauthorized access error"
  1224. * ),
  1225. * @OA\Response(
  1226. * response=500,
  1227. * description="SSH server connection issue"
  1228. * ),
  1229. * )
  1230. */
  1231. public function servicerestart(string $server_id, string $service)
  1232. {
  1233. if (!in_array($service, config('cipi.services'))) {
  1234. return response()->json([
  1235. 'message' => __('cipi.invalid_service_error_message'),
  1236. 'errors' => __('cipi.bad_request')
  1237. ], 400);
  1238. }
  1239. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  1240. if (!$server) {
  1241. return response()->json([
  1242. 'message' => __('cipi.server_not_found_message'),
  1243. 'errors' => __('cipi.server_not_found')
  1244. ], 404);
  1245. }
  1246. try {
  1247. $ssh = new SSH2($server->ip, 22);
  1248. if (!$ssh->login('cipi', $server->password)) {
  1249. return response()->json([
  1250. 'message' => __('cipi.server_error_ssh_error_message').$server->server_id,
  1251. 'errors' => __('cipi.server_error')
  1252. ], 500);
  1253. }
  1254. $ssh->setTimeout(360);
  1255. switch ($service) {
  1256. case 'nginx':
  1257. $ssh->exec('sudo systemctl restart nginx.service');
  1258. break;
  1259. case 'php':
  1260. $ssh->exec('sudo service php8.0-fpm restart');
  1261. $ssh->exec('sudo service php7.4-fpm restart');
  1262. $ssh->exec('sudo service php7.3-fpm restart');
  1263. break;
  1264. case 'mysql':
  1265. $ssh->exec('sudo service mysql restart');
  1266. break;
  1267. case 'redis':
  1268. $ssh->exec('sudo systemctl restart redis.service');
  1269. break;
  1270. case 'supervisor':
  1271. $ssh->exec('service supervisor restart');
  1272. break;
  1273. default:
  1274. //
  1275. break;
  1276. }
  1277. $ssh->exec('exit');
  1278. return response()->json([]);
  1279. } catch (\Throwable $th) {
  1280. return response()->json([
  1281. 'message' => __('cipi.something_error_message'),
  1282. 'errors' => __('cipi.error')
  1283. ], 500);
  1284. }
  1285. }
  1286. /**
  1287. * List all server sites
  1288. *
  1289. * @OA\Get(
  1290. * path="/api/servers/{server_id}/sites",
  1291. * summary="List all server sites",
  1292. * tags={"Servers"},
  1293. * description="List all sites in required server.",
  1294. * @OA\Parameter(
  1295. * name="Authorization",
  1296. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  1297. * required=true,
  1298. * in="header",
  1299. * @OA\Schema(type="string")
  1300. * ),
  1301. * @OA\Parameter(
  1302. * name="server_id",
  1303. * description="The id of the server.",
  1304. * required=true,
  1305. * in="path",
  1306. * @OA\Schema(type="string")
  1307. * ),
  1308. * @OA\Response(
  1309. * response=200,
  1310. * description="Successful request",
  1311. * @OA\JsonContent(
  1312. * type="array",
  1313. * @OA\Items(
  1314. * @OA\Property(
  1315. * property="site_id",
  1316. * description="Site unique ID",
  1317. * type="string",
  1318. * example="abc-123-def-456"
  1319. * ),
  1320. * @OA\Property(
  1321. * property="domain",
  1322. * description="Main site domain",
  1323. * type="string",
  1324. * example="domain.ltd"
  1325. * ),
  1326. * @OA\Property(
  1327. * property="username",
  1328. * description="Site username",
  1329. * type="string",
  1330. * example="cp123456"
  1331. * ),
  1332. * @OA\Property(
  1333. * property="php",
  1334. * description="Site PHP version",
  1335. * type="string",
  1336. * example="7.4"
  1337. * ),
  1338. * @OA\Property(
  1339. * property="basepath",
  1340. * description="Site basepath",
  1341. * type="string",
  1342. * example="/public"
  1343. * ),
  1344. * @OA\Property(
  1345. * property="aliases",
  1346. * description="The number of aliases of this site",
  1347. * type="integer",
  1348. * example="8"
  1349. * ),
  1350. * )
  1351. * )
  1352. * ),
  1353. * @OA\Response(
  1354. * response=404,
  1355. * description="Server not found or not installed"
  1356. * ),
  1357. * @OA\Response(
  1358. * response=401,
  1359. * description="Unauthorized access error"
  1360. * )
  1361. * )
  1362. */
  1363. public function sites(string $server_id)
  1364. {
  1365. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  1366. if (!$server) {
  1367. return response()->json([
  1368. 'message' => __('cipi.server_not_found_message'),
  1369. 'errors' => __('cipi.server_not_found')
  1370. ], 404);
  1371. }
  1372. $sites = Site::where('panel', false)->where('server_id', $server->id)->get();
  1373. $response = [];
  1374. foreach ($sites as $site) {
  1375. $data = [
  1376. 'site_id' => $site->site_id,
  1377. 'domain' => $site->domain,
  1378. 'username' => $site->username,
  1379. 'php' => $site->php,
  1380. 'basepath' => $site->basepath,
  1381. 'aliases' => count($site->aliases)
  1382. ];
  1383. array_push($response, $data);
  1384. }
  1385. return response()->json($response);
  1386. }
  1387. /**
  1388. * List all server domains
  1389. *
  1390. * @OA\Get(
  1391. * path="/api/servers/{server_id}/domains",
  1392. * summary="List all server domains",
  1393. * tags={"Servers"},
  1394. * description="List all domains hosted in required server.",
  1395. * @OA\Parameter(
  1396. * name="Authorization",
  1397. * description="Use Apikey prefix (e.g. Authorization: Apikey XYZ)",
  1398. * required=true,
  1399. * in="header",
  1400. * @OA\Schema(type="string")
  1401. * ),
  1402. * @OA\Parameter(
  1403. * name="server_id",
  1404. * description="The id of the server.",
  1405. * required=true,
  1406. * in="path",
  1407. * @OA\Schema(type="string")
  1408. * ),
  1409. * @OA\Response(
  1410. * response=200,
  1411. * description="Successfull response (Domain list array)"
  1412. * ),
  1413. * @OA\Response(
  1414. * response=404,
  1415. * description="Server not found or not installed"
  1416. * ),
  1417. * @OA\Response(
  1418. * response=401,
  1419. * description="Unauthorized access error"
  1420. * )
  1421. * )
  1422. */
  1423. public function domains(string $server_id)
  1424. {
  1425. $server = Server::where('server_id', $server_id)->where('status', 1)->first();
  1426. if (!$server) {
  1427. return response()->json([
  1428. 'message' => __('cipi.server_not_found_message'),
  1429. 'errors' => __('cipi.server_not_found')
  1430. ], 404);
  1431. }
  1432. $response = [];
  1433. foreach ($server->allsites as $site) {
  1434. array_push($response, $site->domain);
  1435. foreach ($site->aliases as $alias) {
  1436. array_push($response, $alias->domain);
  1437. }
  1438. }
  1439. return response()->json($response);
  1440. }
  1441. }