manage.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. import os
  2. import io
  3. import sys
  4. import platform
  5. import shutil
  6. import time
  7. import subprocess
  8. import request
  9. import json
  10. import datetime
  11. import socket
  12. import re
  13. from threading import Thread
  14. from api.utils import shell_execute, docker, const
  15. from api.model.app import App
  16. from api.model.response import Response
  17. from api.model.config import Config
  18. from api.model.status_reason import StatusReason
  19. from api.utils.common_log import myLogger
  20. from redis import Redis
  21. from rq import Queue, Worker, Connection
  22. from rq.registry import StartedJobRegistry, FinishedJobRegistry, DeferredJobRegistry, FailedJobRegistry, ScheduledJobRegistry, CanceledJobRegistry
  23. from api.exception.command_exception import CommandException
  24. # 指定 Redis 容器的主机名和端口
  25. redis_conn = Redis(host='websoft9-redis', port=6379)
  26. # 使用指定的 Redis 连接创建 RQ 队列
  27. q = Queue(connection=redis_conn,default_timeout=3600)
  28. # APP列表是否有正在安装的应用
  29. def check_list_status(applist):
  30. installing = False
  31. for app in applist:
  32. status = app['status']
  33. if status == "created":
  34. installing = True
  35. break
  36. return installing
  37. # 获取所有app的信息
  38. def get_my_app(app_id):
  39. installed_list = get_apps_from_compose()
  40. installing_list = get_apps_from_queue()
  41. if check_list_status(installed_list):
  42. time.sleep(3)
  43. installed_list = get_apps_from_compose()
  44. app_list = installing_list + installed_list
  45. find = False
  46. ret = {}
  47. if app_id != None:
  48. for app in app_list:
  49. if app_id == app['app_id']:
  50. ret = app
  51. find = True
  52. break
  53. if not find:
  54. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "This App doesn't exist!", "")
  55. else:
  56. ret = app_list
  57. myLogger.info_logger("app list result ok")
  58. return ret
  59. # 获取具体某个app的信息
  60. def get_app_status(app_id):
  61. code, message = docker.check_app_id(app_id)
  62. if code == None:
  63. app = get_my_app(app_id)
  64. # 将app_list 过滤出app_id的app,并缩减信息,使其符合文档的要求
  65. ret = {}
  66. ret['app_id'] = app['app_id']
  67. ret['status'] = app['status']
  68. ret['status_reason'] = app['status_reason']
  69. else:
  70. raise CommandException(code, message, '')
  71. return ret
  72. def install_app(app_name, customer_name, app_version):
  73. myLogger.info_logger("Install app ...")
  74. ret = {}
  75. ret['ResponseData'] = {}
  76. app_id = app_name + "_" + customer_name
  77. ret['ResponseData']['app_id'] = app_id
  78. code, message = check_app(app_name, customer_name, app_version)
  79. if code == None:
  80. q.enqueue(install_app_delay, app_name, customer_name, app_version, job_id=app_id)
  81. else:
  82. ret['Error'] = get_error_info(code, message, "")
  83. return ret
  84. def start_app(app_id):
  85. code, message = docker.check_app_id(app_id)
  86. if code == None:
  87. info, flag = app_exits_in_docker(app_id)
  88. if flag:
  89. app_path = info.split()[-1].rsplit('/', 1)[0]
  90. cmd = "docker compose -f " + app_path + "/docker-compose.yml start"
  91. shell_execute.execute_command_output_all(cmd)
  92. else:
  93. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  94. else:
  95. raise CommandException(code, message, '')
  96. def stop_app(app_id):
  97. code, message = docker.check_app_id(app_id)
  98. myLogger.info_logger(message)
  99. if code == None:
  100. info, flag = app_exits_in_docker(app_id)
  101. if flag:
  102. app_path = info.split()[-1].rsplit('/', 1)[0]
  103. cmd = "docker compose -f " + app_path + "/docker-compose.yml stop"
  104. shell_execute.execute_command_output_all(cmd)
  105. else:
  106. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  107. else:
  108. myLogger.info_logger("check app failed")
  109. raise CommandException(code, message, "")
  110. def restart_app(app_id):
  111. code, message = docker.check_app_id(app_id)
  112. if code == None:
  113. info, flag = app_exits_in_docker(app_id)
  114. if flag:
  115. app_path = info.split()[-1].rsplit('/', 1)[0]
  116. cmd = "docker compose -f " + app_path + "/docker-compose.yml restart"
  117. shell_execute.execute_command_output_all(cmd)
  118. else:
  119. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  120. else:
  121. raise CommandException(code, message, "")
  122. def delete_app_failedjob(job_id):
  123. myLogger.info_logger("delete_app_failedjob")
  124. failed = FailedJobRegistry(queue=q)
  125. failed.remove(job_id, delete_job=True)
  126. def uninstall_app(app_id):
  127. code, message = docker.check_app_id(app_id)
  128. if code == None:
  129. app_name = app_id.split('_')[0]
  130. customer_name = app_id.split('_')[1]
  131. app_path = ""
  132. info, code_exist = app_exits_in_docker(app_id)
  133. if code_exist:
  134. app_path = info.split()[-1].rsplit('/', 1)[0]
  135. cmd = "docker compose -f " + app_path + "/docker-compose.yml down -v"
  136. lib_path = '/data/library/apps/' + app_name
  137. if app_path != lib_path:
  138. cmd = cmd + " && sudo rm -rf " + app_path
  139. shell_execute.execute_command_output_all(cmd)
  140. else:
  141. if check_app_rq(app_id):
  142. delete_app_failedjob(app_id)
  143. else:
  144. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "AppID is not exist", "")
  145. # Force to delete
  146. try:
  147. cmd = " sudo rm -rf " + app_path
  148. shell_execute.execute_command_output_all(cmd)
  149. except CommandException as ce:
  150. myLogger.info_logger("Delete app compose exception")
  151. else:
  152. raise CommandException(code, message, "")
  153. def check_app(app_name, customer_name, app_version):
  154. message = ""
  155. code = None
  156. app_id = app_name + "_" + customer_name
  157. if app_name == None:
  158. code = const.ERROR_CLIENT_PARAM_BLANK
  159. message = "app_name is null"
  160. elif customer_name == None:
  161. code = const.ERROR_CLIENT_PARAM_BLANK
  162. message = "customer_name is null"
  163. elif app_version == None:
  164. code = const.ERROR_CLIENT_PARAM_BLANK
  165. message = "app_version is null"
  166. elif not docker.check_app_websoft9(app_name):
  167. code = const.ERROR_CLIENT_PARAM_NOTEXIST
  168. message = "It is not support to install " + app_name
  169. elif re.match('^[a-z0-9]+$', customer_name) == None:
  170. code = const.ERROR_CLIENT_PARAM_Format
  171. message = "APP name can only be composed of numbers and lowercase letters"
  172. elif docker.check_directory("/data/apps/" + customer_name):
  173. code = const.ERROR_CLIENT_PARAM_REPEAT
  174. message = "Repeat installation: " + customer_name
  175. elif not docker.check_vm_resource(app_name):
  176. code = const.ERROR_SERVER_RESOURCE
  177. message = "Insufficient system resources (cpu, memory, disk space)"
  178. elif check_app_docker(app_id):
  179. code = const.ERROR_CLIENT_PARAM_REPEAT
  180. message = "Repeat installation: " + customer_name
  181. elif check_app_rq(app_id):
  182. code = const.ERROR_CLIENT_PARAM_REPEAT
  183. message = "Repeat installation: " + customer_name
  184. return code, message
  185. def prepare_app(app_name, customer_name):
  186. library_path = "/data/library/apps/" + app_name
  187. install_path = "/data/apps/" + customer_name
  188. shell_execute.execute_command_output_all("cp -r " + library_path + " " + install_path)
  189. def install_app_delay(app_name, customer_name, app_version):
  190. myLogger.info_logger("-------RQ install start --------")
  191. job_id = app_name + "_" + customer_name
  192. try:
  193. # 因为这个时候还没有复制文件夹,是从/data/library里面文件读取json来检查的,应该是app_name,而不是customer_name
  194. resource_flag = docker.check_vm_resource(app_name)
  195. if resource_flag == True:
  196. myLogger.info_logger("job check ok, continue to install app")
  197. env_path = "/data/apps/" + customer_name + "/.env"
  198. # prepare_app(app_name, customer_name)
  199. docker.check_app_compose(app_name, customer_name)
  200. myLogger.info_logger("start JobID=" + job_id)
  201. docker.modify_env(env_path, 'APP_NAME', customer_name)
  202. docker.modify_env(env_path, "APP_VERSION", app_version)
  203. cmd = "cd /data/apps/" + customer_name + " && sudo docker compose pull && sudo docker compose up -d"
  204. output = shell_execute.execute_command_output_all(cmd)
  205. myLogger.info_logger("-------Install result--------")
  206. myLogger.info_logger(output["code"])
  207. myLogger.info_logger(output["result"])
  208. else:
  209. error_info= "##websoft9##" + const.ERROR_SERVER_RESOURCE + "##websoft9##" + "Insufficient system resources (cpu, memory, disk space)" + "##websoft9##" + "Insufficient system resources (cpu, memory, disk space)"
  210. myLogger.info_logger(error_info)
  211. raise Exception(error_info)
  212. except CommandException as ce:
  213. myLogger.info_logger(customer_name + " install failed(docker)!")
  214. uninstall_app(job_id)
  215. error_info= "##websoft9##" + ce.code + "##websoft9##" + ce.message + "##websoft9##" + ce.detail
  216. myLogger.info_logger(error_info)
  217. raise Exception(error_info)
  218. except Exception as e:
  219. myLogger.info_logger(customer_name + " install failed(system)!")
  220. uninstall_app(job_id)
  221. error_info= "##websoft9##" + const.ERROR_SERVER_SYSTEM + "##websoft9##" + 'system original error' + "##websoft9##" + str(e)
  222. myLogger.info_logger(error_info)
  223. raise Exception(error_info)
  224. def app_exits_in_docker(app_id):
  225. customer_name = app_id.split('_')[1]
  226. app_name = app_id.split('_')[0]
  227. flag = False
  228. info = ""
  229. cmd = "docker compose ls -a | grep \'/" + customer_name + "/\'"
  230. try:
  231. output = shell_execute.execute_command_output_all(cmd)
  232. if int(output["code"]) == 0:
  233. info = output["result"]
  234. app_path = info.split()[-1].rsplit('/', 1)[0]
  235. is_official = check_if_official_app(app_path + '/variables.json')
  236. if is_official:
  237. name = docker.read_var(app_path + '/variables.json', 'name')
  238. if name == app_name:
  239. flag = True
  240. elif app_name == customer_name:
  241. flag = True
  242. myLogger.info_logger("APP in docker")
  243. except CommandException as ce:
  244. myLogger.info_logger("APP not in docker")
  245. return info, flag
  246. def split_app_id(app_id):
  247. return app_id.split("_")[1]
  248. def get_apps_from_compose():
  249. myLogger.info_logger("Search all of apps ...")
  250. cmd = "docker compose ls -a --format json"
  251. output = shell_execute.execute_command_output_all(cmd)
  252. output_list = json.loads(output["result"])
  253. myLogger.info_logger(len(output_list))
  254. ip = "localhost"
  255. try:
  256. ip_result = shell_execute.execute_command_output_all("cat /data/apps/stackhub/docker/w9appmanage/public_ip")
  257. ip = ip_result["result"].rstrip('\n')
  258. except Exception:
  259. ip = "127.0.0.1"
  260. app_list = []
  261. for app_info in output_list:
  262. volume = app_info["ConfigFiles"] # volume
  263. app_path = volume.rsplit('/', 1)[0]
  264. customer_name = volume.split('/')[-2]
  265. app_id = "" # app_id
  266. app_name = ""
  267. trade_mark = ""
  268. port = 0
  269. url = ""
  270. admin_url = ""
  271. image_url = ""
  272. user_name = ""
  273. password = ""
  274. official_app = False
  275. if customer_name in ['w9appmanage', 'w9nginxproxymanager','w9redis','w9portainer'] and app_path == '/data/apps/stackhub/docker/' + customer_name:
  276. continue
  277. # get code
  278. status = app_info["Status"].split("(")[0]
  279. if status == "running" or status == "exited" or status == "restarting":
  280. myLogger.info_logger("ok")
  281. elif status == "created":
  282. status = "failed"
  283. else:
  284. continue
  285. var_path = app_path + "/variables.json"
  286. official_app = check_if_official_app(var_path)
  287. if official_app:
  288. app_name = docker.read_var(var_path, 'name')
  289. app_id = app_name + "_" + customer_name # app_id
  290. # get trade_mark
  291. trade_mark = docker.read_var(var_path, 'trademark')
  292. image_url = get_Image_url(app_name)
  293. # get env info
  294. path = app_path + "/.env"
  295. # get port and url
  296. try:
  297. http_port = list(docker.read_env(
  298. path, "APP_HTTP_PORT").values())[0]
  299. port = int(http_port)
  300. easy_url = "http://" + ip + ":" + str(port)
  301. url = get_url(app_name, easy_url)
  302. admin_url = get_admin_url(app_name, url)
  303. except IndexError:
  304. try:
  305. db_port = list(docker.read_env(path, "APP_DB.*_PORT").values())[0]
  306. port = int(db_port)
  307. except IndexError:
  308. pass
  309. # get user_name
  310. try:
  311. user_name = list(docker.read_env(path, "APP_USER").values())[0]
  312. except IndexError:
  313. pass
  314. # get password
  315. try:
  316. password = list(docker.read_env(path, "POWER_PASSWORD").values())[0]
  317. except IndexError:
  318. pass
  319. else:
  320. app_name = customer_name
  321. app_id = customer_name + "_" + customer_name
  322. if status in ['running', 'exited']:
  323. config = Config(port=port, compose_file=volume, url=url, admin_url=admin_url,
  324. user_name=user_name, password=password, default_domain="", set_domain="")
  325. else:
  326. config = None
  327. if status == "failed":
  328. status_reason = StatusReason(Code=const.ERROR_SERVER_SYSTEM, Message="system original error", Detail="unknown error")
  329. else:
  330. status_reason = None
  331. app = App(app_id=app_id, app_name=app_name, customer_name=customer_name, trade_mark=trade_mark, status=status,
  332. status_reason=status_reason, official_app=official_app, image_url=image_url,
  333. config=config)
  334. app_list.append(app.dict())
  335. return app_list
  336. def check_if_official_app(var_path):
  337. if docker.check_directory(var_path):
  338. if docker.read_var(var_path, 'name') != "" and docker.read_var(var_path, 'trademark') != "" and docker.read_var(
  339. var_path, 'requirements') != "":
  340. requirements = docker.read_var(var_path, 'requirements')
  341. try:
  342. cpu = requirements['cpu']
  343. mem = requirements['memory']
  344. disk = requirements['disk']
  345. return True
  346. except KeyError:
  347. return False
  348. else:
  349. return False
  350. def check_app_docker(app_id):
  351. customer_name = app_id.split('_')[1]
  352. app_name = app_id.split('_')[0]
  353. flag = False
  354. cmd = "docker compose ls -a | grep \'/" + customer_name + "/\'"
  355. try:
  356. shell_execute.execute_command_output_all(cmd)
  357. flag = True
  358. myLogger.info_logger("APP in docker")
  359. except CommandException as ce:
  360. myLogger.info_logger("APP not in docker")
  361. return flag
  362. def check_app_rq(app_id):
  363. myLogger.info_logger("check_app_rq")
  364. started = StartedJobRegistry(queue=q)
  365. failed = FailedJobRegistry(queue=q)
  366. run_job_ids = started.get_job_ids()
  367. failed_job_ids = failed.get_job_ids()
  368. queue_job_ids = q.job_ids
  369. myLogger.info_logger(queue_job_ids)
  370. myLogger.info_logger(run_job_ids)
  371. myLogger.info_logger(failed_job_ids)
  372. if queue_job_ids and app_id in queue_job_ids:
  373. myLogger.info_logger("App in RQ")
  374. return True
  375. if failed_job_ids and app_id in failed_job_ids:
  376. myLogger.info_logger("App in RQ")
  377. return True
  378. if run_job_ids and app_id in run_job_ids:
  379. myLogger.info_logger("App in RQ")
  380. return True
  381. myLogger.info_logger("App not in RQ")
  382. return False
  383. def get_apps_from_queue():
  384. myLogger.info_logger("get queque apps...")
  385. # 获取 StartedJobRegistry 实例
  386. started = StartedJobRegistry(queue=q)
  387. finish = FinishedJobRegistry(queue=q)
  388. deferred = DeferredJobRegistry(queue=q)
  389. failed = FailedJobRegistry(queue=q)
  390. scheduled = ScheduledJobRegistry(queue=q)
  391. cancel = CanceledJobRegistry(queue=q)
  392. # 获取正在执行的作业 ID 列表
  393. run_job_ids = started.get_job_ids()
  394. finish_job_ids = finish.get_job_ids()
  395. wait_job_ids = deferred.get_job_ids()
  396. failed_jobs = failed.get_job_ids()
  397. scheduled_jobs = scheduled.get_job_ids()
  398. cancel_jobs = cancel.get_job_ids()
  399. myLogger.info_logger(q.jobs)
  400. myLogger.info_logger(run_job_ids)
  401. myLogger.info_logger(failed_jobs)
  402. myLogger.info_logger(cancel_jobs)
  403. myLogger.info_logger(wait_job_ids)
  404. myLogger.info_logger(finish_job_ids)
  405. myLogger.info_logger(scheduled_jobs)
  406. installing_list = []
  407. for job_id in run_job_ids:
  408. app = get_rq_app(job_id, 'installing', "", "", "")
  409. installing_list.append(app)
  410. for job in q.jobs:
  411. app = get_rq_app(job.id, 'installing', "", "", "")
  412. installing_list.append(app)
  413. for job_id in failed_jobs:
  414. job = q.fetch_job(job_id)
  415. exc_info = job.exc_info
  416. code = exc_info.split('##websoft9##')[1]
  417. message = exc_info.split('##websoft9##')[2]
  418. detail = exc_info.split('##websoft9##')[3]
  419. app = get_rq_app(job_id, 'failed', code, message, detail)
  420. installing_list.append(app)
  421. return installing_list
  422. def get_rq_app(id, status, code, message, detail):
  423. app_name = id.split('_')[0]
  424. customer_name = id.split('_')[1]
  425. # 当app还在RQ时,可能文件夹还没创建,无法获取trade_mark
  426. trade_mark = ""
  427. image_url = get_Image_url(app_name)
  428. config = None
  429. if status == "installing" :
  430. status_reason = None
  431. else:
  432. status_reason = StatusReason(Code=code, Message=message, Detail=detail)
  433. app = App(app_id=id, app_name=app_name, customer_name=customer_name, trade_mark=trade_mark,
  434. status=status, status_reason=status_reason, official_app=True, image_url=image_url,
  435. config=config)
  436. return app.dict()
  437. def get_Image_url(app_name):
  438. image_url = "static/images/" + app_name + "-websoft9.png"
  439. return image_url
  440. def get_url(app_name, easy_url):
  441. url = easy_url
  442. if app_name == "joomla":
  443. url = easy_url + "/administrator"
  444. elif app_name == "other":
  445. url = easy_url + "/administrator"
  446. else:
  447. url = easy_url
  448. return url
  449. def get_admin_url(app_name, url):
  450. admin_url = ""
  451. if app_name == "wordpress":
  452. admin_url = url + "/wp-admin"
  453. elif app_name == "other":
  454. admin_url = url + "/admin"
  455. else:
  456. admin_url = ""
  457. return admin_url
  458. def get_error_info(code, message, detail):
  459. error = {}
  460. error['Code'] = code
  461. error['Message'] = message
  462. error['Detail'] = detail
  463. return error
  464. def app_domain_list(app_id):
  465. code, message = docker.check_app_id(app_id)
  466. if code == None:
  467. info, flag = app_exits_in_docker(app_id)
  468. if flag:
  469. myLogger.info_logger("Check app_id ok")
  470. else:
  471. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  472. else:
  473. raise CommandException(code, message, "")
  474. domains = []
  475. proxy = get_proxy(app_id)
  476. if proxy != None:
  477. domains = proxy["domain_names"]
  478. return domains
  479. def app_domain_delete(app_id):
  480. code, message = docker.check_app_id(app_id)
  481. if code == None:
  482. info, flag = app_exits_in_docker(app_id)
  483. if flag:
  484. myLogger.info_logger("Check app_id ok")
  485. else:
  486. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  487. else:
  488. raise CommandException(code, message, "")
  489. proxy = get_proxy(app_id)
  490. if proxy != None:
  491. proxy_id = proxy["id"]
  492. token = get_token()
  493. url = "http://127.0.0.1:9092/api/nginx/proxy-hosts/" + proxy_id
  494. headers = {
  495. 'Authorization': token,
  496. 'Content-Type': 'application/json'
  497. }
  498. requests.get(url, headers=headers)
  499. else:
  500. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "App has no proxy", "")
  501. def app_domain_update(app_id, domains):
  502. check_domains(domains)
  503. code, message = docker.check_app_id(app_id)
  504. if code == None:
  505. info, flag = app_exits_in_docker(app_id)
  506. if flag:
  507. myLogger.info_logger("Check app_id ok")
  508. else:
  509. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  510. else:
  511. raise CommandException(code, message, "")
  512. proxy = get_proxy(app_id)
  513. if proxy != None:
  514. proxy_id = proxy["id"]
  515. token = get_token()
  516. url = "http://127.0.0.1:9092/api/nginx/proxy-hosts/" + proxy_id
  517. headers = {
  518. 'Authorization': token,
  519. 'Content-Type': 'application/json'
  520. }
  521. port = ""
  522. host = app_id.split('_')[1]
  523. data = {
  524. "domain_names": domains,
  525. "forward_scheme": "http",
  526. "forward_host": host,
  527. "forward_port": port,
  528. "access_list_id": "0",
  529. "certificate_id": 0,
  530. "meta": {
  531. "letsencrypt_agree": False,
  532. "dns_challenge": False
  533. },
  534. "advanced_config": "",
  535. "locations": [],
  536. "block_exploits": False,
  537. "caching_enabled": False,
  538. "allow_websocket_upgrade": False,
  539. "http2_support": False,
  540. "hsts_enabled": False,
  541. "hsts_subdomains": False,
  542. "ssl_forced": False
  543. }
  544. requests.put(url, data=json.dumps(data), headers=headers)
  545. set_domain(domain[0],app_id)
  546. return domains
  547. else:
  548. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "App has no proxy", "")
  549. def app_domain_add(app_id, domains):
  550. check_domains(domains)
  551. code, message = docker.check_app_id(app_id)
  552. if code == None:
  553. info, flag = app_exits_in_docker(app_id)
  554. if flag:
  555. myLogger.info_logger("Check app_id ok")
  556. else:
  557. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "APP is not exist", "")
  558. else:
  559. raise CommandException(code, message, "")
  560. token = get_token()
  561. url = "http://127.0.0.1:9092/api/nginx/proxy-hosts"
  562. headers = {
  563. 'Authorization': token,
  564. 'Content-Type': 'application/json'
  565. }
  566. port = ""
  567. host = app_id.split('_')[1]
  568. data = {
  569. "domain_names": domains,
  570. "forward_scheme": "http",
  571. "forward_host": host,
  572. "forward_port": port,
  573. "access_list_id": "0",
  574. "certificate_id": 0,
  575. "meta": {
  576. "letsencrypt_agree": False,
  577. "dns_challenge": False
  578. },
  579. "advanced_config": "",
  580. "locations": [],
  581. "block_exploits": False,
  582. "caching_enabled": False,
  583. "allow_websocket_upgrade": False,
  584. "http2_support": False,
  585. "hsts_enabled": False,
  586. "hsts_subdomains": False,
  587. "ssl_forced": False
  588. }
  589. requests.post(url, data=json.dumps(data), headers=headers)
  590. set_domain(domain[0],app_id)
  591. return domains
  592. def check_domains(domains):
  593. if domains is None or len(domains) == 0:
  594. raise CommandException(const.ERROR_CLIENT_PARAM_BLANK, "Domains is blank", "")
  595. else:
  596. for domain in domains:
  597. if is_valid_domain(domain):
  598. if check_real_domain(domain) == False:
  599. raise CommandException(const.ERROR_CLIENT_PARAM_NOTEXIST, "Domain and server not match", "")
  600. else:
  601. raise CommandException(const.ERROR_CLIENT_PARAM_Format, "Domains format error", "")
  602. def is_valid_domain(domain):
  603. pattern = r"^[a-zA-Z0-9][a-zA-Z0-9\-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$"
  604. return bool(re.match(pattern, domain))
  605. def check_real_domain(domain):
  606. domain_real = True
  607. try:
  608. cmd = "ping -c 1 " + domain + " | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | uniq"
  609. domain_ip = shell_execute.execute_command_output_all(cmd)["result"]
  610. ip_result = shell_execute.execute_command_output_all("cat /data/apps/stackhub/docker/w9appmanage/public_ip")
  611. ip_save = ip_result["result"].rstrip('\n')
  612. if domain_ip == ip_save:
  613. myLogger.info_logger("Domain check ok!")
  614. else:
  615. domain_real = False
  616. except CommandException as ce:
  617. domain_real = False
  618. return domain_real
  619. def get_token():
  620. url = 'http://127.0.0.1:9092/api/tokens'
  621. headers = {'Content-type': 'application/json'}
  622. cmd = "cat /usr/share/cockpit/nginx/config.json | jq -r '.NGINXPROXYMANAGER_PASSWORD'"
  623. password = shell_execute.execute_command_output_all(cmd)["result"]
  624. param = {
  625. "identity": "help@websoft9.com",
  626. "scope": "user",
  627. "secret": password
  628. }
  629. response = requests.post(url, data=json.dumps(param), headers=headers)
  630. token = "Bearer " + response.json()["token"]
  631. return token
  632. def get_proxy(app_id):
  633. customer_name = app_id.split('_')[1]
  634. proxy_host = None
  635. token = get_token()
  636. url = "http://127.0.0.1:9092/api/nginx/proxy-hosts"
  637. headers = {
  638. 'Authorization': token,
  639. 'Content-Type': 'application/json'
  640. }
  641. response = requests.get(url, headers=headers)
  642. for proxy in response.json():
  643. portainer_name = proxy["forward_host"]
  644. if customer_name == portainer_name:
  645. proxy_host = proxy
  646. break;
  647. return proxy_host
  648. def set_domain(domain,app_id):
  649. customer_name = app_id.split('_')[1]
  650. cmd = "sed -i 's/APP_URL=.*/APP_URL=" + domain + "/g /data/apps/" + customer_name +"/.env"
  651. shell_execute.execute_command_output_all(cmd)