musare.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. #!/bin/bash
  2. export PATH=/usr/local/bin:/usr/bin:/bin
  3. CYAN='\033[33;36m';
  4. RED='\033[0;31m'
  5. YELLOW='\033[0;93m'
  6. GREEN='\033[0;32m'
  7. NC='\033[0m'
  8. scriptLocation=$(dirname -- "$(readlink -fn -- "$0"; echo x)")
  9. cd "${scriptLocation%x}" || exit 1
  10. if [[ -f .env ]]; then
  11. # shellcheck disable=SC1091
  12. source .env
  13. else
  14. echo -e "${RED}Error: .env does not exist${NC}"
  15. exit 2
  16. fi
  17. if [[ ! -x "$(command -v docker)" || ! -x "$(command -v docker-compose)" ]]; then
  18. if [[ -x "$(command -v docker)" && ! -x "$(command -v docker-compose)" ]]; then
  19. echo -e "${RED}Error: docker-compose not installed.${NC}"
  20. elif [[ ! -x "$(command -v docker)" && -x "$(command -v docker-compose)" ]]; then
  21. echo -e "${RED}Error: docker not installed.${NC}"
  22. else
  23. echo -e "${RED}Error: docker and docker-compose not installed.${NC}"
  24. fi
  25. exit 1
  26. fi
  27. handleServices()
  28. {
  29. validServices=(backend frontend mongo redis)
  30. servicesArray=()
  31. invalidServices=false
  32. for x in "$@"; do
  33. if [[ ${validServices[*]} =~ (^|[[:space:]])"$x"($|[[:space:]]) ]]; then
  34. if ! [[ ${servicesArray[*]} =~ (^|[[:space:]])"$x"($|[[:space:]]) ]]; then
  35. servicesArray+=("${x}")
  36. fi
  37. else
  38. if [[ $invalidServices == false ]]; then
  39. invalidServices="${x}"
  40. else
  41. invalidServices="${invalidServices} ${x}"
  42. fi
  43. fi
  44. done
  45. if [[ $invalidServices == false && ${#servicesArray[@]} -gt 0 ]]; then
  46. echo "1|${servicesArray[*]}"
  47. elif [[ $invalidServices == false ]]; then
  48. echo "1|all"
  49. else
  50. echo "0|Invalid Service(s): ${invalidServices}"
  51. fi
  52. }
  53. dockerCommand()
  54. {
  55. validCommands=(start stop restart pull build ps logs)
  56. if [[ ${validCommands[*]} =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; then
  57. servicesString=$(handleServices "${@:3}")
  58. if [[ ${servicesString:0:1} == 1 ]]; then
  59. if [[ ${servicesString:2:4} == "all" ]]; then
  60. servicesString=""
  61. else
  62. servicesString=${servicesString:2}
  63. fi
  64. if [[ ${CONTAINER_MODE} == "dev" ]]; then
  65. composeFiles="-f docker-compose.yml -f docker-compose.dev.yml"
  66. else
  67. composeFiles="-f docker-compose.yml"
  68. fi
  69. if [[ ${2} == "stop" || ${2} == "restart" ]]; then
  70. # shellcheck disable=SC2086
  71. docker-compose ${composeFiles} stop ${servicesString}
  72. fi
  73. if [[ ${2} == "start" || ${2} == "restart" ]]; then
  74. # shellcheck disable=SC2086
  75. docker-compose ${composeFiles} up -d ${servicesString}
  76. fi
  77. if [[ ${2} == "pull" || ${2} == "build" || ${2} == "ps" || ${2} == "logs" ]]; then
  78. # shellcheck disable=SC2086
  79. docker-compose ${composeFiles} "${2}" ${servicesString}
  80. fi
  81. exitValue=$?
  82. if [[ ${exitValue} -gt 0 ]]; then
  83. exit ${exitValue}
  84. fi
  85. else
  86. echo -e "${RED}${servicesString:2}\n${YELLOW}Usage: ${1} restart [backend, frontend, mongo, redis]${NC}"
  87. exit 1
  88. fi
  89. else
  90. echo -e "${RED}Error: Invalid dockerCommand input${NC}"
  91. exit 1
  92. fi
  93. }
  94. case $1 in
  95. start)
  96. echo -e "${CYAN}Musare | Start Services${NC}"
  97. # shellcheck disable=SC2068
  98. dockerCommand "$(basename "$0")" start ${@:2}
  99. ;;
  100. stop)
  101. echo -e "${CYAN}Musare | Stop Services${NC}"
  102. # shellcheck disable=SC2068
  103. dockerCommand "$(basename "$0")" stop ${@:2}
  104. ;;
  105. restart)
  106. echo -e "${CYAN}Musare | Restart Services${NC}"
  107. # shellcheck disable=SC2068
  108. dockerCommand "$(basename "$0")" restart ${@:2}
  109. ;;
  110. build)
  111. echo -e "${CYAN}Musare | Build Services${NC}"
  112. # shellcheck disable=SC2068
  113. dockerCommand "$(basename "$0")" pull ${@:2}
  114. # shellcheck disable=SC2068
  115. dockerCommand "$(basename "$0")" build ${@:2}
  116. ;;
  117. status)
  118. echo -e "${CYAN}Musare | Service Status${NC}"
  119. # shellcheck disable=SC2068
  120. dockerCommand "$(basename "$0")" ps ${@:2}
  121. ;;
  122. reset)
  123. echo -e "${CYAN}Musare | Reset Services${NC}"
  124. servicesString=$(handleServices "${@:2}")
  125. if [[ ${servicesString:0:1} == 1 && ${servicesString:2:4} == "all" ]]; then
  126. echo -e "${RED}Resetting will remove the ${REDIS_DATA_LOCATION} and ${MONGO_DATA_LOCATION} directories.${NC}"
  127. echo -e "${GREEN}Are you sure you want to reset all data? ${YELLOW}[y,n]: ${NC}"
  128. read -r confirm
  129. if [[ "${confirm}" == y* ]]; then
  130. dockerCommand "$(basename "$0")" stop
  131. docker-compose rm -v --force
  132. if [[ -d $REDIS_DATA_LOCATION ]]; then
  133. rm -rf "${REDIS_DATA_LOCATION}"
  134. fi
  135. if [[ -d $MONGO_DATA_LOCATION ]]; then
  136. rm -rf "${MONGO_DATA_LOCATION}"
  137. fi
  138. else
  139. echo -e "${RED}Cancelled reset${NC}"
  140. fi
  141. elif [[ ${servicesString:0:1} == 1 ]]; then
  142. if [[ "${servicesString:2}" == *redis* && "${servicesString:2}" == *mongo* ]]; then
  143. echo -e "${RED}Resetting will remove the ${REDIS_DATA_LOCATION} and ${MONGO_DATA_LOCATION} directories.${NC}"
  144. elif [[ "${servicesString:2}" == *redis* ]]; then
  145. echo -e "${RED}Resetting will remove the ${REDIS_DATA_LOCATION} directory.${NC}"
  146. elif [[ "${servicesString:2}" == *mongo* ]]; then
  147. echo -e "${RED}Resetting will remove the ${MONGO_DATA_LOCATION} directory.${NC}"
  148. fi
  149. echo -e "${GREEN}Are you sure you want to reset all data for $(echo "${servicesString:2}" | tr ' ' ',')? ${YELLOW}[y,n]: ${NC}"
  150. read -r confirm
  151. if [[ "${confirm}" == y* ]]; then
  152. # shellcheck disable=SC2086
  153. dockerCommand "$(basename "$0")" stop ${servicesString:2}
  154. # shellcheck disable=SC2086
  155. docker-compose rm -v --force ${servicesString:2}
  156. if [[ "${servicesString:2}" == *redis* && -d $REDIS_DATA_LOCATION ]]; then
  157. rm -rf "${REDIS_DATA_LOCATION}"
  158. fi
  159. if [[ "${servicesString:2}" == *mongo* && -d $MONGO_DATA_LOCATION ]]; then
  160. rm -rf "${MONGO_DATA_LOCATION}"
  161. fi
  162. else
  163. echo -e "${RED}Cancelled reset${NC}"
  164. fi
  165. else
  166. echo -e "${RED}${servicesString:2}\n${YELLOW}Usage: $(basename "$0") build [backend, frontend, mongo, redis]${NC}"
  167. exit 1
  168. fi
  169. ;;
  170. attach)
  171. echo -e "${CYAN}Musare | Attach${NC}"
  172. if [[ $2 == "backend" ]]; then
  173. containerId=$(docker-compose ps -q backend)
  174. if [[ -z $containerId ]]; then
  175. echo -e "${RED}Error: Backend offline, please start to attach.${NC}"
  176. exit 1
  177. else
  178. echo -e "${YELLOW}Detach with CTRL+P+Q${NC}"
  179. docker attach "$containerId"
  180. fi
  181. elif [[ $2 == "mongo" ]]; then
  182. MONGO_VERSION_INT=${MONGO_VERSION:0:1}
  183. if [[ -z $(docker-compose ps -q mongo) ]]; then
  184. echo -e "${RED}Error: Mongo offline, please start to attach.${NC}"
  185. exit 1
  186. else
  187. echo -e "${YELLOW}Detach with CTRL+D${NC}"
  188. if [[ $MONGO_VERSION_INT -ge 5 ]]; then
  189. docker-compose exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry()" --shell
  190. else
  191. docker-compose exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}"
  192. fi
  193. fi
  194. elif [[ $2 == "redis" ]]; then
  195. if [[ -z $(docker-compose ps -q redis) ]]; then
  196. echo -e "${RED}Error: Redis offline, please start to attach.${NC}"
  197. exit 1
  198. else
  199. echo -e "${YELLOW}Detach with CTRL+C${NC}"
  200. docker-compose exec redis redis-cli -a "${REDIS_PASSWORD}"
  201. fi
  202. else
  203. echo -e "${RED}Invalid service $2\n${YELLOW}Usage: $(basename "$0") attach [backend,mongo,redis]${NC}"
  204. exit 1
  205. fi
  206. ;;
  207. eslint)
  208. echo -e "${CYAN}Musare | ESLint${NC}"
  209. fix=""
  210. if [[ $2 == "fix" || $3 == "fix" || $2 == "--fix" || $3 == "--fix" ]]; then
  211. fix="--fix"
  212. echo -e "${GREEN}Auto-fix enabled${NC}"
  213. fi
  214. case $2 in
  215. frontend)
  216. docker-compose exec frontend npx eslint src --ext .js,.vue $fix
  217. exitValue=$?
  218. ;;
  219. backend)
  220. docker-compose exec backend npx eslint logic $fix
  221. exitValue=$?
  222. ;;
  223. ""|fix|--fix)
  224. docker-compose exec frontend npx eslint src --ext .js,.vue $fix
  225. frontendExitValue=$?
  226. docker-compose exec backend npx eslint logic $fix
  227. backendExitValue=$?
  228. if [[ ${frontendExitValue} -gt 0 || ${backendExitValue} -gt 0 ]]; then
  229. exitValue=1
  230. else
  231. exitValue=0
  232. fi
  233. ;;
  234. *)
  235. echo -e "${RED}Invalid service $2\n${YELLOW}Usage: $(basename "$0") eslint [backend, frontend] [fix]${NC}"
  236. exitValue=1
  237. ;;
  238. esac
  239. if [[ ${exitValue} -gt 0 ]]; then
  240. exit ${exitValue}
  241. fi
  242. ;;
  243. update)
  244. echo -e "${CYAN}Musare | Update${NC}"
  245. git fetch
  246. exitValue=$?
  247. if [[ ${exitValue} -gt 0 ]]; then
  248. exit ${exitValue}
  249. fi
  250. if [[ $(git rev-parse HEAD) == $(git rev-parse @\{u\}) ]]; then
  251. echo -e "${GREEN}Already up to date${NC}"
  252. else
  253. dbChange=$(git log --name-only --oneline HEAD..origin/"$(git rev-parse --abbrev-ref HEAD)" | grep "backend/logic/db/schemas")
  254. fcChange=$(git log --name-only --oneline HEAD..origin/"$(git rev-parse --abbrev-ref HEAD)" | grep "frontend/dist/config/template.json")
  255. bcChange=$(git log --name-only --oneline HEAD..origin/"$(git rev-parse --abbrev-ref HEAD)" | grep "backend/config/template.json")
  256. if [[ ( $2 == "auto" && -z $dbChange && -z $fcChange && -z $bcChange ) || -z $2 ]]; then
  257. echo -e "${CYAN}Updating...${NC}"
  258. git pull
  259. exitValue=$?
  260. if [[ ${exitValue} -gt 0 ]]; then
  261. exit ${exitValue}
  262. fi
  263. dockerCommand "$(basename "$0")" build
  264. dockerCommand "$(basename "$0")" restart
  265. echo -e "${GREEN}Updated!${NC}"
  266. if [[ -n $dbChange ]]; then
  267. echo -e "${RED}Database schema has changed, please run migration!${NC}"
  268. fi
  269. if [[ -n $fcChange ]]; then
  270. echo -e "${RED}Frontend config has changed, please update!${NC}"
  271. fi
  272. if [[ -n $bcChange ]]; then
  273. echo -e "${RED}Backend config has changed, please update!${NC}"
  274. fi
  275. elif [[ $2 == "auto" ]]; then
  276. echo -e "${RED}Auto Update Failed! Database and/or config has changed!${NC}"
  277. exit 1
  278. fi
  279. fi
  280. ;;
  281. logs)
  282. echo -e "${CYAN}Musare | Logs${NC}"
  283. # shellcheck disable=SC2068
  284. dockerCommand "$(basename "$0")" logs ${@:2}
  285. ;;
  286. backup)
  287. echo -e "${CYAN}Musare | Backup${NC}"
  288. if [[ -z "${BACKUP_LOCATION}" ]]; then
  289. backupLocation="${scriptLocation%x}/backups"
  290. else
  291. backupLocation="${BACKUP_LOCATION%/}"
  292. fi
  293. if [[ ! -d "${backupLocation}" ]]; then
  294. echo -e "${YELLOW}Creating backup directory at ${backupLocation}${NC}"
  295. mkdir "${backupLocation}"
  296. fi
  297. if [[ -z "${BACKUP_NAME}" ]]; then
  298. backupLocation="${backupLocation}/musare-$(date +"%Y-%m-%d-%s").dump"
  299. else
  300. backupLocation="${backupLocation}/${BACKUP_NAME}"
  301. fi
  302. echo -e "${YELLOW}Creating backup at ${backupLocation}${NC}"
  303. docker-compose exec -T mongo sh -c "mongodump --authenticationDatabase musare -u ${MONGO_USER_USERNAME} -p ${MONGO_USER_PASSWORD} -d musare --archive" > "${backupLocation}"
  304. ;;
  305. restore)
  306. echo -e "${CYAN}Musare | Restore${NC}"
  307. if [[ -z $2 ]]; then
  308. echo -e "${GREEN}Please enter the full path of the dump you wish to restore: ${NC}"
  309. read -r restoreFile
  310. else
  311. restoreFile=$2
  312. fi
  313. if [[ -z ${restoreFile} ]]; then
  314. echo -e "${RED}Error: no restore path given, cancelled restoration.${NC}"
  315. exit 1
  316. elif [[ -d ${restoreFile} ]]; then
  317. echo -e "${RED}Error: restore path given is a directory, cancelled restoration.${NC}"
  318. exit 1
  319. elif [[ ! -f ${restoreFile} ]]; then
  320. echo -e "${RED}Error: no file at restore path given, cancelled restoration.${NC}"
  321. exit 1
  322. else
  323. docker-compose exec -T mongo sh -c "mongorestore --authenticationDatabase musare -u ${MONGO_USER_USERNAME} -p ${MONGO_USER_PASSWORD} --archive" < "${restoreFile}"
  324. fi
  325. ;;
  326. admin)
  327. echo -e "${CYAN}Musare | Add Admin${NC}"
  328. MONGO_VERSION_INT=${MONGO_VERSION:0:1}
  329. if [[ $2 == "add" ]]; then
  330. if [[ -z $3 ]]; then
  331. echo -e "${GREEN}Please enter the username of the user you wish to make an admin: ${NC}"
  332. read -r adminUser
  333. else
  334. adminUser=$3
  335. fi
  336. if [[ -z $adminUser ]]; then
  337. echo -e "${RED}Error: Username for new admin not provided.${NC}"
  338. exit 1
  339. else
  340. if [[ $MONGO_VERSION_INT -ge 5 ]]; then
  341. docker-compose exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry(); db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'admin'}})"
  342. else
  343. docker-compose exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'admin'}})"
  344. fi
  345. fi
  346. elif [[ $2 == "remove" ]]; then
  347. if [[ -z $3 ]]; then
  348. echo -e "${GREEN}Please enter the username of the user you wish to remove as admin: ${NC}"
  349. read -r adminUser
  350. else
  351. adminUser=$3
  352. fi
  353. if [[ -z $adminUser ]]; then
  354. echo -e "${RED}Error: Username for new admin not provided.${NC}"
  355. exit 1
  356. else
  357. if [[ $MONGO_VERSION_INT -ge 5 ]]; then
  358. docker-compose exec mongo mongosh musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "disableTelemetry(); db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'default'}})"
  359. else
  360. docker-compose exec mongo mongo musare -u "${MONGO_USER_USERNAME}" -p "${MONGO_USER_PASSWORD}" --eval "db.users.updateOne({username: '${adminUser}'}, {\$set: {role: 'default'}})"
  361. fi
  362. fi
  363. else
  364. echo -e "${RED}Invalid command $2\n${YELLOW}Usage: $(basename "$0") admin [add,remove] username${NC}"
  365. exit 1
  366. fi
  367. ;;
  368. "")
  369. echo -e "${CYAN}Musare | Available Commands${NC}"
  370. echo -e "${YELLOW}start - Start services${NC}"
  371. echo -e "${YELLOW}stop - Stop services${NC}"
  372. echo -e "${YELLOW}restart - Restart services${NC}"
  373. echo -e "${YELLOW}status - Service status${NC}"
  374. echo -e "${YELLOW}logs - View logs for services${NC}"
  375. echo -e "${YELLOW}update - Update Musare${NC}"
  376. echo -e "${YELLOW}attach [backend,mongo,redis] - Attach to backend service, mongo or redis shell${NC}"
  377. echo -e "${YELLOW}build - Build services${NC}"
  378. echo -e "${YELLOW}eslint - Run eslint on frontend and/or backend${NC}"
  379. echo -e "${YELLOW}backup - Backup database data to file${NC}"
  380. echo -e "${YELLOW}restore - Restore database data from backup file${NC}"
  381. echo -e "${YELLOW}reset - Reset service data${NC}"
  382. echo -e "${YELLOW}admin [add,remove] - Assign/unassign admin role to/from a user${NC}"
  383. ;;
  384. *)
  385. echo -e "${CYAN}Musare${NC}"
  386. echo -e "${RED}Error: Invalid Command $1${NC}"
  387. echo -e "${CYAN}Available Commands:${NC}"
  388. echo -e "${YELLOW}start - Start services${NC}"
  389. echo -e "${YELLOW}stop - Stop services${NC}"
  390. echo -e "${YELLOW}restart - Restart services${NC}"
  391. echo -e "${YELLOW}status - Service status${NC}"
  392. echo -e "${YELLOW}logs - View logs for services${NC}"
  393. echo -e "${YELLOW}update - Update Musare${NC}"
  394. echo -e "${YELLOW}attach [backend,mongo,redis] - Attach to backend service, mongo or redis shell${NC}"
  395. echo -e "${YELLOW}build - Build services${NC}"
  396. echo -e "${YELLOW}eslint - Run eslint on frontend and/or backend${NC}"
  397. echo -e "${YELLOW}backup - Backup database data to file${NC}"
  398. echo -e "${YELLOW}restore - Restore database data from backup file${NC}"
  399. echo -e "${YELLOW}reset - Reset service data${NC}"
  400. echo -e "${YELLOW}admin [add,remove] - Assign/unassign admin role to/from a user${NC}"
  401. exit 1
  402. ;;
  403. esac