apps.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import { readFileSync } from 'fs';
  2. let templatesJSON = readFileSync('./templates/templates.json');
  3. let templates = JSON.parse(templatesJSON).templates;
  4. templates = templates.sort((a, b) => {
  5. if (a.name < b.name) {
  6. return -1;
  7. }
  8. });
  9. export const Apps = (req, res) => {
  10. let page = Number(req.params.page) || 1;
  11. let list_start = (page-1)*28;
  12. let list_end = (page*28);
  13. let last_page = Math.ceil(templates.length/28);
  14. let prev = '/apps/' + (page-1);
  15. let next = '/apps/' + (page+1);
  16. if (page == 1) {
  17. prev = '/apps/' + (page);
  18. }
  19. if (page == last_page) {
  20. next = '/apps/' + (page);
  21. }
  22. let apps_list = '';
  23. for (let i = list_start; i < list_end && i < templates.length; i++) {
  24. let appCard = readFileSync('./views/partials/appCard.html', 'utf8');
  25. let name = templates[i].name || templates[i].title.toLowerCase();
  26. let desc = templates[i].description.slice(0, 60) + "...";
  27. let description = templates[i].description.replaceAll(". ", ".\n") || "no description available";
  28. let note = templates[i].note ? templates[i].note.replaceAll(". ", ".\n") : "no notes available";
  29. let image = templates[i].image;
  30. let logo = templates[i].logo;
  31. let categories = '';
  32. // set data.catagories to 'other' if data.catagories is empty or undefined
  33. if (templates[i].categories == null || templates[i].categories == undefined || templates[i].categories == '') {
  34. templates[i].categories = ['Other'];
  35. }
  36. for (let c = 0; c < templates[i].categories.length; c++) {
  37. categories += CatagoryColor(templates[i].categories[c]);
  38. }
  39. appCard = appCard.replace(/AppName/g, name);
  40. appCard = appCard.replace(/AppShortName/g, name);
  41. appCard = appCard.replace(/AppDesc/g, desc);
  42. appCard = appCard.replace(/AppLogo/g, logo);
  43. appCard = appCard.replace(/AppCategories/g, categories);
  44. apps_list += appCard;
  45. }
  46. res.render("apps", {
  47. name: req.session.user,
  48. role: req.session.role,
  49. avatar: req.session.avatar,
  50. list_start: list_start + 1,
  51. list_end: list_end,
  52. app_count: templates.length,
  53. prev: prev,
  54. next: next,
  55. apps_list: apps_list
  56. });
  57. }
  58. export const appSearch = async (req, res) => {
  59. let search = req.body.search.split(' ');
  60. let apps_list = '';
  61. let results = [];
  62. let page = Number(req.query.page) || 1;
  63. let list_start = (page - 1) * 28;
  64. let list_end = (page * 28);
  65. let last_page = Math.ceil(templates.length / 28);
  66. let prev = '/apps?page=' + (page - 1);
  67. let next = '/apps?page=' + (page + 1);
  68. if (page == 1) {
  69. prev = '/apps?page=' + (page);
  70. }
  71. if (page == last_page) {
  72. next = '/apps?page=' + (page);
  73. }
  74. function searchTemplates(word) {
  75. for (let i = 0; i < templates.length; i++) {
  76. if ((templates[i].description.includes(word)) || (templates[i].name.includes(word)) || (templates[i].title.includes(word))) {
  77. results.push(templates[i]);
  78. }
  79. }
  80. }
  81. searchTemplates(search);
  82. for (let i = 0; i < results.length; i++) {
  83. let app_card = appCard(results[i]);
  84. apps_list += app_card;
  85. }
  86. res.render("apps", {
  87. name: req.session.user,
  88. role: req.session.role,
  89. avatar: req.session.avatar,
  90. list_start: list_start + 1,
  91. list_end: list_end,
  92. app_count: templates.length,
  93. prev: prev,
  94. next: next,
  95. apps_list: apps_list
  96. });
  97. }
  98. function CatagoryColor(category) {
  99. switch (category) {
  100. case 'Other':
  101. return '<span class="badge bg-blue-lt">Other</span> ';
  102. case 'Productivity':
  103. return '<span class="badge bg-blue-lt">Productivity</span> ';
  104. case 'Tools':
  105. return '<span class="badge bg-blue-lt">Tools</span> ';
  106. case 'Dashboard':
  107. return '<span class="badge bg-blue-lt">Dashboard</span> ';
  108. case 'Communication':
  109. return '<span class="badge bg-azure-lt">Communication</span> ';
  110. case 'Media':
  111. return '<span class="badge bg-azure-lt">Media</span> ';
  112. case 'CMS':
  113. return '<span class="badge bg-azure-lt">CMS</span> ';
  114. case 'Monitoring':
  115. return '<span class="badge bg-indigo-lt">Monitoring</span> ';
  116. case 'LDAP':
  117. return '<span class="badge bg-purple-lt">LDAP</span> ';
  118. case 'Arr':
  119. return '<span class="badge bg-purple-lt">Arr</span> ';
  120. case 'Database':
  121. return '<span class="badge bg-red-lt">Database</span> ';
  122. case 'Paid':
  123. return '<span class="badge bg-red-lt" title="This is a paid product or contains paid features.">Paid</span> ';
  124. case 'Gaming':
  125. return '<span class="badge bg-pink-lt">Gaming</span> ';
  126. case 'Finance':
  127. return '<span class="badge bg-orange-lt">Finance</span> ';
  128. case 'Networking':
  129. return '<span class="badge bg-yellow-lt">Networking</span> ';
  130. case 'Authentication':
  131. return '<span class="badge bg-lime-lt">Authentication</span> ';
  132. case 'Development':
  133. return '<span class="badge bg-green-lt">Development</span> ';
  134. case 'Media Server':
  135. return '<span class="badge bg-teal-lt">Media Server</span> ';
  136. case 'Downloaders':
  137. return '<span class="badge bg-cyan-lt">Downloaders</span> ';
  138. default:
  139. return ''; // default to other if the category is not recognized
  140. }
  141. }
  142. export const InstallModal = async (req, res) => {
  143. let input = req.header('hx-trigger-name');
  144. let result = templates.find(t => t.name == input);
  145. let name = result.name || result.title.toLowerCase();
  146. let short_name = name.slice(0, 25) + "...";
  147. let desc = result.description.replaceAll(". ", ".\n") || "no description available";
  148. let short_desc = desc.slice(0, 60) + "...";
  149. let modal_name = name.replaceAll(" ", "-");
  150. let form_id = name.replaceAll("-", "_");
  151. let note = result.note ? result.note.replaceAll(". ", ".\n") : "no notes available";
  152. let command = result.command ? result.command : "";
  153. let command_check = command ? "checked" : "";
  154. let privileged = result.privileged || "";
  155. let privileged_check = privileged ? "checked" : "";
  156. let repository = result.repository || "";
  157. let image = result.image || "";
  158. let net_host, net_bridge, net_docker = '';
  159. let net_name = 'AppBridge';
  160. let restart_policy = result.restart_policy || 'unless-stopped';
  161. switch (result.network) {
  162. case 'host':
  163. net_host = 'checked';
  164. break;
  165. case 'bridge':
  166. net_bridge = 'checked';
  167. net_name = result.network;
  168. break;
  169. default:
  170. net_docker = 'checked';
  171. }
  172. if (repository != "") {
  173. image = (`${repository.url}/raw/master/${repository.stackfile}`);
  174. }
  175. let [ports_data, volumes_data, env_data, label_data] = [[], [], [], []];
  176. for (let i = 0; i < 12; i++) {
  177. // Get port details
  178. try {
  179. let ports = result.ports[i];
  180. let port_check = ports ? "checked" : "";
  181. let port_external = ports.split(":")[0] ? ports.split(":")[0] : ports.split("/")[0];
  182. let port_internal = ports.split(":")[1] ? ports.split(":")[1].split("/")[0] : ports.split("/")[0];
  183. let port_protocol = ports.split("/")[1] ? ports.split("/")[1] : "";
  184. // remove /tcp or /udp from port_external if it exists
  185. if (port_external.includes("/")) {
  186. port_external = port_external.split("/")[0];
  187. }
  188. ports_data.push({
  189. check: port_check,
  190. external: port_external,
  191. internal: port_internal,
  192. protocol: port_protocol
  193. });
  194. } catch {
  195. ports_data.push({
  196. check: "",
  197. external: "",
  198. internal: "",
  199. protocol: ""
  200. });
  201. }
  202. // Get volume details
  203. try {
  204. let volumes = result.volumes[i];
  205. let volume_check = volumes ? "checked" : "";
  206. let volume_bind = volumes.bind ? volumes.bind : "";
  207. let volume_container = volumes.container ? volumes.container.split(":")[0] : "";
  208. let volume_readwrite = "rw";
  209. if (volumes.readonly == true) {
  210. volume_readwrite = "ro";
  211. }
  212. volumes_data.push({
  213. check: volume_check,
  214. bind: volume_bind,
  215. container: volume_container,
  216. readwrite: volume_readwrite
  217. });
  218. } catch {
  219. volumes_data.push({
  220. check: "",
  221. bind: "",
  222. container: "",
  223. readwrite: ""
  224. });
  225. }
  226. // Get environment details
  227. try {
  228. let env = result.env[i];
  229. let env_check = "";
  230. let env_default = env.default ? env.default : "";
  231. if (env.set) { env_default = env.set;}
  232. let env_description = env.description ? env.description : "";
  233. let env_label = env.label ? env.label : "";
  234. let env_name = env.name ? env.name : "";
  235. env_data.push({
  236. check: env_check,
  237. default: env_default,
  238. description: env_description,
  239. label: env_label,
  240. name: env_name
  241. });
  242. } catch {
  243. env_data.push({
  244. check: "",
  245. default: "",
  246. description: "",
  247. label: "",
  248. name: ""
  249. });
  250. }
  251. // Get label details
  252. try {
  253. let label = result.labels[i];
  254. let label_check = "";
  255. let label_name = label.name ? label.name : "";
  256. let label_value = label.value ? label.value : "";
  257. label_data.push({
  258. check: label_check,
  259. name: label_name,
  260. value: label_value
  261. });
  262. } catch {
  263. label_data.push({
  264. check: "",
  265. name: "",
  266. value: ""
  267. });
  268. }
  269. }
  270. let modal = readFileSync('./views/modals/install.html', 'utf8');
  271. modal = modal.replace(/AppName/g, name);
  272. modal = modal.replace(/AppNote/g, note);
  273. modal = modal.replace(/AppImage/g, image);
  274. modal = modal.replace(/RestartPolicy/g, restart_policy);
  275. modal = modal.replace(/NetHost/g, net_host);
  276. modal = modal.replace(/NetBridge/g, net_bridge);
  277. modal = modal.replace(/NetDocker/g, net_docker);
  278. modal = modal.replace(/NetName/g, net_name);
  279. modal = modal.replace(/ModalName/g, modal_name);
  280. modal = modal.replace(/FormId/g, form_id);
  281. modal = modal.replace(/CommandCheck/g, command_check);
  282. modal = modal.replace(/CommandValue/g, command);
  283. modal = modal.replace(/PrivilegedCheck/g, privileged_check);
  284. for (let i = 0; i < 12; i++) {
  285. modal = modal.replaceAll(`Port${i}Check`, ports_data[i].check);
  286. modal = modal.replaceAll(`Port${i}External`, ports_data[i].external);
  287. modal = modal.replaceAll(`Port${i}Internal`, ports_data[i].internal);
  288. modal = modal.replaceAll(`Port${i}Protocol`, ports_data[i].protocol);
  289. modal = modal.replaceAll(`Volume${i}Check`, volumes_data[i].check);
  290. modal = modal.replaceAll(`Volume${i}Bind`, volumes_data[i].bind);
  291. modal = modal.replaceAll(`Volume${i}Container`, volumes_data[i].container);
  292. modal = modal.replaceAll(`Volume${i}RW`, volumes_data[i].readwrite);
  293. modal = modal.replaceAll(`Env${i}Check`, env_data[i].check);
  294. modal = modal.replaceAll(`Env${i}Default`, env_data[i].default);
  295. modal = modal.replaceAll(`Env${i}Description`, env_data[i].description);
  296. modal = modal.replaceAll(`Env${i}Label`, env_data[i].label);
  297. modal = modal.replaceAll(`Env${i}Name`, env_data[i].name);
  298. modal = modal.replaceAll(`Label${i}Check`, label_data[i].check);
  299. modal = modal.replaceAll(`Label${i}Name`, label_data[i].name);
  300. modal = modal.replaceAll(`Label${i}Value`, label_data[i].value);
  301. }
  302. res.send(modal);
  303. }
  304. export const LearnMore = async (req, res) => {
  305. let name = req.header('hx-trigger-name');
  306. let id = req.header('hx-trigger');
  307. let modal = readFileSync('./views/modals/learnmore.html', 'utf8');
  308. res.send(modal);
  309. }