musare.sh 16 KB

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