models.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. # __package__ = 'archivebox.plugins.system'
  2. import os
  3. import shutil
  4. import sys
  5. import inspect
  6. import django
  7. from sqlite3 import dbapi2 as sqlite3
  8. from pathlib import Path
  9. from typing import List, Dict, Any
  10. from django.db import models
  11. from django.utils.functional import cached_property
  12. from solo.models import SingletonModel
  13. from config import bin_path, bin_version, VERSION
  14. from plugins.defaults.models import ArchiveBoxBaseDependency
  15. ConfigDict = Dict[str, Any]
  16. class BashEnvironmentDependency(ArchiveBoxBaseDependency):
  17. singleton_instance_id = 1
  18. id = models.AutoField(primary_key=True)
  19. NAME = 'BASH'
  20. LABEL = "Bash"
  21. REQUIRED = True
  22. PARENT_DEPENDENCIES = []
  23. BIN_DEPENDENCIES: List[str] = ['bash']
  24. APT_DEPENDENCIES: List[str] = []
  25. BREW_DEPENDENCIES: List[str] = []
  26. PIP_DEPENDENCIES: List[str] = []
  27. NPM_DEPENDENCIES: List[str] = []
  28. DEFAULT_BINARY = 'bash'
  29. DEFAULT_START_CMD = None
  30. DEFAULT_STOP_CMD = None
  31. DEFAULT_PID_FILE = None
  32. DEFAULT_ARGS = '-c'
  33. ENABLED = models.BooleanField(default=True, editable=not REQUIRED)
  34. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  35. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  36. VERSION_CMD = models.CharField(max_length=255, default='{BINARY} --version')
  37. # START_CMD = models.CharField(max_length=255, default=DEFAULT_START_CMD)
  38. # WORKERS = models.IntegerField(default=1)
  39. class Meta:
  40. abstract = False
  41. app_label = 'system'
  42. verbose_name = "Shell Environment: bash"
  43. verbose_name_plural = "Shell Environments: bash"
  44. # @task
  45. def install_pkgs(self, os_pkgs=()):
  46. assert self.is_valid, 'Bash environment is not available on this host'
  47. for os_dependency in os_pkgs:
  48. assert bin_path(os_dependency)
  49. return True
  50. class PythonEnvironmentDependency(ArchiveBoxBaseDependency):
  51. singleton_instance_id = 1
  52. id = models.AutoField(primary_key=True)
  53. NAME = 'PYTHON'
  54. LABEL = "Python"
  55. REQUIRED = True
  56. PARENT_DEPENDENCIES = []
  57. BIN_DEPENDENCIES = ['python3']
  58. APT_DEPENDENCIES = []
  59. BREW_DEPENDENCIES = []
  60. PIP_DEPENDENCIES = []
  61. NPM_DEPENDENCIES = []
  62. DEFAULT_BINARY = 'python3'
  63. DEFAULT_START_CMD = None
  64. DEFAULT_STOP_CMD = None
  65. DEFAULT_PID_FILE = None
  66. DEFAULT_ARGS = '-c'
  67. VERSION_CMD = '{BINARY} --version'
  68. ENABLED = models.BooleanField(default=True, editable=not REQUIRED)
  69. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  70. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  71. # START_CMD = models.CharField(max_length=255, default=DEFAULT_START_CMD)
  72. # WORKERS = models.IntegerField(default=1)
  73. class Meta:
  74. abstract = False
  75. app_label = 'system'
  76. verbose_name = "Shell Environment: python3"
  77. class NodeJSEnvironmentDependency(ArchiveBoxBaseDependency, SingletonModel):
  78. singleton_instance_id = 1
  79. id = models.AutoField(primary_key=True)
  80. NAME = 'NODEJS'
  81. LABEL = "NodeJS"
  82. REQUIRED = True
  83. PARENT_DEPENDENCIES = []
  84. BIN_DEPENDENCIES = ['node']
  85. APT_DEPENDENCIES = []
  86. BREW_DEPENDENCIES = []
  87. PIP_DEPENDENCIES = []
  88. NPM_DEPENDENCIES = []
  89. DEFAULT_BINARY = 'node'
  90. DEFAULT_START_CMD = None
  91. DEFAULT_STOP_CMD = None
  92. DEFAULT_PID_FILE = None
  93. DEFAULT_ARGS = '-c'
  94. VERSION_CMD = '{BINARY} --version'
  95. ENABLED = models.BooleanField(default=True, editable=True)
  96. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  97. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  98. # START_CMD = models.CharField(max_length=255, default=DEFAULT_START_CMD)
  99. # WORKERS = models.IntegerField(default=1)
  100. class Meta:
  101. abstract = False
  102. app_label = 'system'
  103. verbose_name = "Shell Environment: node"
  104. class AptEnvironmentDependency(ArchiveBoxBaseDependency, SingletonModel):
  105. singleton_instance_id = 1
  106. id = models.AutoField(primary_key=True)
  107. NAME = 'APT'
  108. LABEL = "apt"
  109. REQUIRED = False
  110. PARENT_DEPENDENCIES = ['BashEnvironmentDependency']
  111. BIN_DEPENDENCIES = ['apt-get']
  112. APT_DEPENDENCIES = []
  113. BREW_DEPENDENCIES = []
  114. PIP_PACKAGES = []
  115. NPM_PACKAGES = []
  116. DEFAULT_BINARY = 'apt-get'
  117. DEFAULT_START_CMD = None
  118. DEFAULT_STOP_CMD = None
  119. DEFAULT_PID_FILE = None
  120. DEFAULT_ARGS = '-qq'
  121. ENABLED = models.BooleanField(default=True, editable=not REQUIRED)
  122. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  123. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  124. class Meta:
  125. abstract = False
  126. app_label = 'system'
  127. verbose_name = "Package Manager: apt"
  128. # @task
  129. def install_pkgs(self, apt_pkgs=()):
  130. assert self.is_valid, 'Apt environment is not available on this host'
  131. # with huey.lock_task('apt-install'):
  132. run(cmd=[self.DEFAULT_BINARY, '-qq', 'update'])
  133. for apt_package in apt_pkgs:
  134. run(cmd=[self.DEFAULT_BINARY, 'install', '-y', apt_package])
  135. return True
  136. class BrewEnvironmentDependency(ArchiveBoxBaseDependency, SingletonModel):
  137. singleton_instance_id = 1
  138. id = models.AutoField(primary_key=True)
  139. NAME = 'BREW'
  140. LABEL = "homebrew"
  141. REQUIRED = False
  142. PARENT_DEPENDENCIES = ['BashEnvironmentDependency']
  143. BIN_DEPENDENCIES = ['brew']
  144. APT_DEPENDENCIES = []
  145. BREW_DEPENDENCIES = []
  146. PIP_PACKAGES = []
  147. NPM_PACKAGES = []
  148. DEFAULT_BINARY = 'brew'
  149. DEFAULT_START_CMD = None
  150. DEFAULT_STOP_CMD = None
  151. DEFAULT_PID_FILE = None
  152. DEFAULT_ARGS = ''
  153. ENABLED = models.BooleanField(default=True, editable=True)
  154. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  155. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  156. class Meta:
  157. abstract = False
  158. app_label = 'system'
  159. verbose_name = "Package Manager: brew"
  160. # @task
  161. def install_pkgs(self, brew_pkgs=()):
  162. assert self.is_valid, 'Brw environment is not available on this host'
  163. run(cmd=[self.DEFAULT_BINARY, 'update'])
  164. for brew_pkg in brew_pkgs:
  165. run(cmd=[self.DEFAULT_BINARY, 'install', brew_pkg])
  166. return True
  167. class PipEnvironmentDependency(ArchiveBoxBaseDependency, SingletonModel):
  168. singleton_instance_id = 1
  169. id = models.AutoField(primary_key=True)
  170. NAME = 'PIP'
  171. LABEL = "pip"
  172. REQUIRED = False
  173. PARENT_DEPENDENCIES = ['BashEnvironmentDependency']
  174. BIN_DEPENDENCIES = ['python3', 'pip3']
  175. APT_DEPENDENCIES = ['python3.11', 'pip3', 'pipx']
  176. BREW_DEPENDENCIES = ['[email protected]', 'pipx']
  177. PIP_PACKAGES = ['setuptools', 'pipx']
  178. NPM_PACKAGES = []
  179. DEFAULT_BINARY = 'pip3'
  180. DEFAULT_START_CMD = None
  181. DEFAULT_STOP_CMD = None
  182. DEFAULT_PID_FILE = None
  183. DEFAULT_ARGS = ''
  184. VERSION_CMD = '{BINARY} --version'
  185. ENABLED = models.BooleanField(default=True, editable=True)
  186. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  187. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  188. class Meta:
  189. abstract = False
  190. app_label = 'system'
  191. verbose_name = "Package Manager: pip"
  192. # @task
  193. def install_pkgs(self, pip_pkgs=()):
  194. assert self.is_valid, 'Pip environment is not available on this host'
  195. for pip_pkg in pip_pkgs:
  196. run(cmd=[self.DEFAULT_BINARY, 'install', '--update', '--ignore-installed', pip_pkg])
  197. return True
  198. class NPMEnvironmentDependency(ArchiveBoxBaseDependency, SingletonModel):
  199. singleton_instance_id = 1
  200. id = models.AutoField(primary_key=True)
  201. NAME = 'NODEJS'
  202. LABEL = "NodeJS"
  203. REQUIRED = False
  204. PARENT_DEPENDENCIES = ['BashEnvironmentDependency']
  205. BIN_DEPENDENCIES = ['node', 'npm']
  206. APT_DEPENDENCIES = ['node', 'npm']
  207. BREW_DEPENDENCIES = ['node', 'npm']
  208. PIP_PACKAGES = []
  209. NPM_PACKAGES = ['npm']
  210. DEFAULT_BINARY = 'node'
  211. DEFAULT_START_CMD = None
  212. DEFAULT_STOP_CMD = None
  213. DEFAULT_PID_FILE = None
  214. DEFAULT_ARGS = ''
  215. VERSION_CMD = '{BINARY} --version'
  216. ENABLED = models.BooleanField(default=True, editable=True)
  217. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY)
  218. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS)
  219. class Meta:
  220. abstract = False
  221. app_label = 'system'
  222. verbose_name = "Package Manager: npm"
  223. # @task
  224. def install_pkgs(self, npm_pkgs=()):
  225. assert self.is_valid, 'NPM environment is not available on this host'
  226. for npm_pkg in npm_pkgs:
  227. run(cmd=[self.DEFAULT_BINARY, 'install', npm_pkg])
  228. return True
  229. class DjangoDependency(ArchiveBoxBaseDependency, SingletonModel):
  230. singleton_instance_id = 1
  231. id = models.AutoField(primary_key=True)
  232. NAME = 'DJANGO'
  233. LABEL = "Django"
  234. REQUIRED = True
  235. PARENT_DEPENDENCIES = []
  236. BIN_DEPENDENCIES = ['django-admin.py']
  237. APT_DEPENDENCIES = []
  238. BREW_DEPENDENCIES = []
  239. PIP_PACKAGES = ['django==3.1.14']
  240. NPM_PACKAGES = []
  241. DEFAULT_BINARY = 'django-admin.py'
  242. DEFAULT_START_CMD = 'archivebox server 0.0.0.0:8000'
  243. DEFAULT_PID_FILE = 'logs/{NAME}_WORKER.pid'
  244. DEFAULT_STOP_CMD = 'kill "$(<{PID_FILE})"'
  245. DEFAULT_ARGS = []
  246. VERSION_CMD = '{BINARY} --version'
  247. ENABLED = models.BooleanField(default=True, editable=False)
  248. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY, editable=False)
  249. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS, editable=False)
  250. class Meta:
  251. abstract = False
  252. app_label = 'system'
  253. verbose_name = "Internal Dependency: django"
  254. @cached_property
  255. def bin_path(self):
  256. return inspect.getfile(django)
  257. @cached_property
  258. def bin_version(self):
  259. return '.'.join(str(v) for v in django.VERSION[:3])
  260. class SQLiteDependency(ArchiveBoxBaseDependency, SingletonModel):
  261. singleton_instance_id = 1
  262. id = models.AutoField(primary_key=True)
  263. NAME = 'SQLITE'
  264. LABEL = "SQLite"
  265. REQUIRED = True
  266. PARENT_DEPENDENCIES = []
  267. BIN_DEPENDENCIES = []
  268. APT_DEPENDENCIES = []
  269. BREW_DEPENDENCIES = []
  270. PIP_PACKAGES = []
  271. NPM_PACKAGES = []
  272. DEFAULT_BINARY = 'sqlite3'
  273. DEFAULT_START_CMD = None
  274. DEFAULT_STOP_CMD = None
  275. DEFAULT_PID_FILE = None
  276. DEFAULT_ARGS = []
  277. VERSION_CMD = 'python3 -c ""'
  278. ENABLED = models.BooleanField(default=True, editable=False)
  279. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY, editable=False)
  280. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS, editable=False)
  281. class Meta:
  282. abstract = False
  283. app_label = 'system'
  284. verbose_name = "Internal Dependency: sqlite3"
  285. @cached_property
  286. def bin_path(self):
  287. return inspect.getfile(sqlite3)
  288. @cached_property
  289. def bin_version(self):
  290. return sqlite3.version
  291. class ArchiveBoxDependency(ArchiveBoxBaseDependency):
  292. singleton_instance_id = 1
  293. id = models.AutoField(primary_key=True)
  294. NAME = 'ARCHIVEBOX'
  295. LABEL = "ArchiveBox"
  296. REQUIRED = True
  297. PARENT_DEPENDENCIES = [
  298. 'PipEnvironmentDependency',
  299. 'DjangoDependency',
  300. 'SQLiteDependency',
  301. ]
  302. BIN_DEPENDENCIES = ['archivebox']
  303. APT_DEPENDENCIES = []
  304. BREW_DEPENDENCIES = []
  305. PIP_PACKAGES = ['archivebox']
  306. NPM_PACKAGES = []
  307. DEFAULT_BINARY = 'archivebox'
  308. DEFAULT_START_CMD = '{BINARY} server 0.0.0.0:8000'
  309. DEFAULT_ARGS = []
  310. VERSION_CMD = 'archivebox --version'
  311. ENABLED = models.BooleanField(default=True, editable=False)
  312. BINARY = models.CharField(max_length=255, default=DEFAULT_BINARY, editable=False)
  313. ARGS = models.CharField(max_length=255, default=DEFAULT_ARGS, editable=False)
  314. class Meta:
  315. abstract = False
  316. app_label = 'system'
  317. verbose_name = "Internal Dependency: archivebox"
  318. @cached_property
  319. def bin_path(self):
  320. return sys.argv[0] or bin_path('archivebox')
  321. @cached_property
  322. def bin_version(self):
  323. # return config['VERSION']
  324. return VERSION