diff --git a/.gitignore b/.gitignore index 51d11d0..411aba3 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ __debug_bin main github.com .all-contributorsrc -dist \ No newline at end of file +dist +CasaOS \ No newline at end of file diff --git a/.goreleaser.debug.yaml b/.goreleaser.debug.yaml new file mode 100644 index 0000000..885e3a0 --- /dev/null +++ b/.goreleaser.debug.yaml @@ -0,0 +1,167 @@ +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com +project_name: casaos +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy +builds: + - id: casaos-amd64 + binary: build/sysroot/usr/bin/casaos + env: + - CGO_ENABLED=1 + - CC=x86_64-linux-gnu-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - amd64 + hooks: + post: + - find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest + - id: casaos-arm64 + binary: build/sysroot/usr/bin/casaos + env: + - CGO_ENABLED=1 + - CC=aarch64-linux-gnu-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - arm64 + hooks: + post: + - find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest + - id: casaos-arm-7 + binary: build/sysroot/usr/bin/casaos + env: + - CGO_ENABLED=1 + - CC=arm-linux-gnueabihf-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - arm + goarm: + - "7" + hooks: + post: + - find build/sysroot -type f | xargs -L 1 realpath --relative-to=build/sysroot > build/sysroot.manifest + - id: casaos-migration-tool-amd64 + binary: build/sysroot/usr/bin/casaos-migration-tool + main: ./cmd/migration-tool + env: + - CGO_ENABLED=1 + - CC=x86_64-linux-gnu-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - amd64 + - id: casaos-migration-tool-arm64 + binary: build/sysroot/usr/bin/casaos-migration-tool + main: ./cmd/migration-tool + env: + - CGO_ENABLED=1 + - CC=aarch64-linux-gnu-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - arm64 + - id: casaos-migration-tool-arm-7 + binary: build/sysroot/usr/bin/casaos-migration-tool + main: ./cmd/migration-tool + env: + - CGO_ENABLED=1 + - CC=arm-linux-gnueabihf-gcc + gcflags: + - all=-N -l + ldflags: + - -extldflags "-static" + tags: + - musl + - netgo + goos: + - linux + goarch: + - arm + goarm: + - "7" +archives: + - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-v{{ .Version }}" + id: casaos + builds: + - casaos-amd64 + - casaos-arm64 + - casaos-arm-7 + replacements: + arm: arm-7 + files: + - build/**/* + - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-migration-tool-v{{ .Version }}" + id: casaos-migration-tool + builds: + - casaos-migration-tool-amd64 + - casaos-migration-tool-arm64 + - casaos-migration-tool-arm-7 + replacements: + arm: arm-7 + files: + - build/sysroot/etc/**/* +checksum: + name_template: "checksums.txt" +snapshot: + name_template: "{{ incpatch .Version }}" +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" +# release: +# github: +# owner: IceWhaleTech +# name: CasaOS +# draft: true +# prerelease: auto +# mode: replace +# name_template: "v{{ .Version }}" +release: + github: + owner: IceWhaleTech + name: CasaOS + draft: true + prerelease: auto + mode: replace + name_template: "v{{ .Version }}" diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 949e04f..690519d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -117,7 +117,7 @@ builds: goarch: - arm goarm: - - "7" + - "7" archives: - name_template: "{{ .Os }}-{{ .Arch }}-{{ .ProjectName }}-v{{ .Version }}" id: casaos @@ -159,9 +159,9 @@ changelog: # name_template: "v{{ .Version }}" release: github: - owner: LinkLeong - name: casaos-alpha + owner: IceWhaleTech + name: CasaOS draft: true prerelease: auto mode: replace - name_template: "v{{ .Version }}" \ No newline at end of file + name_template: "v{{ .Version }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9058ae4..9f92680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +## [0.3.7] + +### Added +- [Storage] Disk merge (Beta), you can merge multiple disks into a single storage space (currently you need to enable this feature from the command line) + +### Changed +- [Files] Changed the cache file storage location, now the file upload size is not limited by the system disk capacity. +- [Scripts] Updated installation and upgrade scripts to support more Debian-based Linux distributions. +- [Engineering] Refactored Local Storage into a standalone service as part of CasaOS modularization. + +### Fixed +- [Apps] App list update mechanism improved, now you can see the latest apps in App Store immediately. +- [Storage] Fixed a lot of known issues + ## [0.3.6-alpha.1] - 2022-09-06 diff --git a/build/scripts/migration/script.d/03-migrate-casaos.sh b/build/scripts/migration/script.d/03-migrate-casaos.sh index ba64e4d..719ff60 100644 --- a/build/scripts/migration/script.d/03-migrate-casaos.sh +++ b/build/scripts/migration/script.d/03-migrate-casaos.sh @@ -60,8 +60,6 @@ BUILD_PATH=$(dirname "${BASH_SOURCE[0]}")/../../.. SOURCE_ROOT=${BUILD_PATH}/sysroot APP_NAME="casaos" -# APP_NAME_FORMAL="CasaOS" -APP_NAME_FORMAL="casaos-alpha" # check if migration is needed SOURCE_BIN_PATH=${SOURCE_ROOT}/usr/bin @@ -85,45 +83,6 @@ if [ "${NEED_MIGRATION}" = "false" ]; then exit 0 fi -MIGRATION_SERVICE_DIR=${1} - -if [ -z "${MIGRATION_SERVICE_DIR}" ]; then - MIGRATION_SERVICE_DIR=${BUILD_PATH}/scripts/migration/service.d/${APP_NAME} -fi -MIGRATION_LIST_FILE=${MIGRATION_SERVICE_DIR}/migration.list -MIGRATION_PATH=() - -CURRENT_VERSION_FOUND="false" - -# a VERSION_PAIR looks like "v0.3.5 v0.3.6-alpha2" -# -# - "v0.3.5" is the current version installed on this host -# - "v0.3.6-alpha2" is the version of the migration tool from GitHub -while read -r VERSION_PAIR; do - if [ -z "${VERSION_PAIR}" ]; then - continue - fi - - # obtain "v0.3.5" from "v0.3.5 v0.3.6-alpha2" - VER1=$(echo "${VERSION_PAIR}" | cut -d' ' -f1) - - # obtain "v0.3.6-alpha2" from "v0.3.5 v0.3.6-alpha2" - VER2=$(echo "${VERSION_PAIR}" | cut -d' ' -f2) - - if [ "${CURRENT_VERSION}" = "${VER1// /}" ] || [ "${CURRENT_VERSION}" = "LEGACY_WITHOUT_VERSION" ]; then - CURRENT_VERSION_FOUND="true" - fi - - if [ "${CURRENT_VERSION_FOUND}" = "true" ]; then - MIGRATION_PATH+=("${VER2// /}") - fi -done < "${MIGRATION_LIST_FILE}" - -if [ ${#MIGRATION_PATH[@]} -eq 0 ]; then - __warning "No migration path found from ${CURRENT_VERSION} to ${SOURCE_VERSION}" - exit 0 -fi - ARCH="unknown" case $(uname -m) in @@ -141,21 +100,59 @@ case $(uname -m) in ;; esac +__info "ARCH: ${ARCH}" + +MIGRATION_SERVICE_DIR=${1} + +if [ -z "${MIGRATION_SERVICE_DIR}" ]; then + MIGRATION_SERVICE_DIR=${BUILD_PATH}/scripts/migration/service.d/${APP_NAME} +fi +MIGRATION_LIST_FILE=${MIGRATION_SERVICE_DIR}/migration.list +MIGRATION_PATH=() + +CURRENT_VERSION_FOUND="false" + +# a VERSION_PAIR looks like "v0.3.5 " +# +# - "v0.3.5" is the current version installed on this host +# - "" is the url of the migration tool +while read -r VERSION_PAIR; do + if [ -z "${VERSION_PAIR}" ]; then + continue + fi + + # obtain "v0.3.5" from "v0.3.5 v0.3.6-alpha2" + VER1=$(echo "${VERSION_PAIR}" | cut -d' ' -f1) + + # obtain "" from "v0.3.5 " + URL=$(eval echo "${VERSION_PAIR}" | cut -d' ' -f2) + + if [ "${CURRENT_VERSION}" = "${VER1// /}" ] || [ "${CURRENT_VERSION}" = "LEGACY_WITHOUT_VERSION" ]; then + CURRENT_VERSION_FOUND="true" + fi + + if [ "${CURRENT_VERSION_FOUND}" = "true" ]; then + MIGRATION_PATH+=("${URL// /}") + fi +done < "${MIGRATION_LIST_FILE}" + +if [ ${#MIGRATION_PATH[@]} -eq 0 ]; then + __warning "No migration path found from ${CURRENT_VERSION} to ${SOURCE_VERSION}" + exit 0 +fi + pushd "${MIGRATION_SERVICE_DIR}" -{ for VER2 in "${MIGRATION_PATH[@]}"; do - - - MIGRATION_TOOL_FILE=linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz +{ for URL in "${MIGRATION_PATH[@]}"; do + MIGRATION_TOOL_FILE=$(basename "${URL}") if [ -f "${MIGRATION_TOOL_FILE}" ]; then __info "Migration tool ${MIGRATION_TOOL_FILE} exists. Skip downloading." continue fi - MIGRATION_TOOL_URL=https://github.com/LinkLeong/"${APP_NAME_FORMAL}"/releases/download/"${VER2}"/linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz - echo "Dowloading ${MIGRATION_TOOL_URL}..." - curl -sL -O "${MIGRATION_TOOL_URL}" + __info "Dowloading ${URL}..." + curl -fsSL -o "${MIGRATION_TOOL_FILE}" -O "${URL}" done } || { popd @@ -163,8 +160,8 @@ pushd "${MIGRATION_SERVICE_DIR}" } { - for VER2 in "${MIGRATION_PATH[@]}"; do - MIGRATION_TOOL_FILE=linux-"${ARCH}"-"${APP_NAME}"-migration-tool-"${VER2}".tar.gz + for URL in "${MIGRATION_PATH[@]}"; do + MIGRATION_TOOL_FILE=$(basename "${URL}") __info "Extracting ${MIGRATION_TOOL_FILE}..." tar zxvf "${MIGRATION_TOOL_FILE}" || __error "Failed to extract ${MIGRATION_TOOL_FILE}" diff --git a/build/scripts/migration/service.d/casaos/migration.list b/build/scripts/migration/service.d/casaos/migration.list index 2035e42..c780280 100644 --- a/build/scripts/migration/service.d/casaos/migration.list +++ b/build/scripts/migration/service.d/casaos/migration.list @@ -1,3 +1,3 @@ -LEGACY_WITHOUT_VERSION v0.3.6 -v0.3.5 v0.3.6 -v0.3.5.1 v0.3.6 +LEGACY_WITHOUT_VERSION https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz +v0.3.5 https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz +v0.3.5.1 https://github.com/IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz diff --git a/build/scripts/setup/service.d/casaos/debian/setup-casaos.sh b/build/scripts/setup/service.d/casaos/debian/setup-casaos.sh index a13b6e1..50c8d91 100644 --- a/build/scripts/setup/service.d/casaos/debian/setup-casaos.sh +++ b/build/scripts/setup/service.d/casaos/debian/setup-casaos.sh @@ -31,15 +31,10 @@ if [ ! -f "${CONF_FILE}" ]; then cp -v "${CONF_FILE_SAMPLE}" "${CONF_FILE}" fi -if systemctl is-active "${APP_NAME}.service" &>/dev/null ;then - echo "server started" -else - # enable and start service - systemctl daemon-reload +rm -rf /etc/systemd/system/casaos.service # remove old service file - echo "Enabling service..." - systemctl enable --force --no-ask-password "${APP_NAME}.service" +systemctl daemon-reload - echo "Starting service..." - systemctl start --force --no-ask-password "${APP_NAME}.service" -fi \ No newline at end of file +# enable service (without starting) +echo "Enabling service..." +systemctl enable --force --no-ask-password "${APP_NAME}.service" diff --git a/build/sysroot/etc/casaos/casaos.conf.sample b/build/sysroot/etc/casaos/casaos.conf.sample index 3e64508..e51f0fa 100644 --- a/build/sysroot/etc/casaos/casaos.conf.sample +++ b/build/sysroot/etc/casaos/casaos.conf.sample @@ -11,7 +11,6 @@ DateFormat = 2006-01-02 DBPath = /var/lib/casaos ShellPath = /usr/share/casaos/shell UserDataPath = /var/lib/casaos/conf -TempPath = /var/lib/casaos/temp [server] RunMode = release diff --git a/build/sysroot/usr/lib/systemd/system/casaos.service b/build/sysroot/usr/lib/systemd/system/casaos.service index 249d72c..aa7ad32 100644 --- a/build/sysroot/usr/lib/systemd/system/casaos.service +++ b/build/sysroot/usr/lib/systemd/system/casaos.service @@ -1,12 +1,13 @@ [Unit] After=casaos-gateway.service ConditionFileNotEmpty=/etc/casaos/casaos.conf -Description=CasaOS Service +Description=CasaOS Main Service [Service] ExecStart=/usr/bin/casaos -c /etc/casaos/casaos.conf PIDFile=/var/run/casaos/casaos.pid Restart=always +Type=notify [Install] WantedBy=multi-user.target diff --git a/build/sysroot/usr/share/casaos/cleanup/script.d/01-cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/script.d/01-cleanup-casaos.sh deleted file mode 100644 index e69de29..0000000 diff --git a/build/sysroot/usr/share/casaos/cleanup/script.d/03-cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/script.d/03-cleanup-casaos.sh new file mode 100755 index 0000000..ce29b6d --- /dev/null +++ b/build/sysroot/usr/share/casaos/cleanup/script.d/03-cleanup-casaos.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e + +readonly APP_NAME_SHORT=casaos + +__get_setup_script_directory_by_os_release() { + pushd "$(dirname "${BASH_SOURCE[0]}")/../service.d/${APP_NAME_SHORT}" &>/dev/null + + { + # shellcheck source=/dev/null + { + source /etc/os-release + { + pushd "${ID}"/"${VERSION_CODENAME}" &>/dev/null + } || { + pushd "${ID}" &>/dev/null + } || { + pushd "${ID_LIKE}" &>/dev/null + } || { + echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})" + exit 1 + } + + pwd + + popd &>/dev/null + + } || { + echo "Unsupported OS: unknown" + exit 1 + } + + } + + popd &>/dev/null +} + +SETUP_SCRIPT_DIRECTORY=$(__get_setup_script_directory_by_os_release) + +readonly SETUP_SCRIPT_DIRECTORY +readonly SETUP_SCRIPT_FILENAME="cleanup-${APP_NAME_SHORT}.sh" +readonly SETUP_SCRIPT_FILEPATH="${SETUP_SCRIPT_DIRECTORY}/${SETUP_SCRIPT_FILENAME}" + +echo "🟩 Running ${SETUP_SCRIPT_FILENAME}..." +$SHELL "${SETUP_SCRIPT_FILEPATH}" "${BUILD_PATH}" diff --git a/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/bullseye/cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/bullseye/cleanup-casaos.sh new file mode 120000 index 0000000..6afabc7 --- /dev/null +++ b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/bullseye/cleanup-casaos.sh @@ -0,0 +1 @@ +../cleanup-casaos.sh \ No newline at end of file diff --git a/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/cleanup-casaos.sh new file mode 100644 index 0000000..989ea55 --- /dev/null +++ b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/debian/cleanup-casaos.sh @@ -0,0 +1,204 @@ +#!/bin/bash + +set -e + +readonly CASA_SERVICES=( + "casaos.service" + "devmon@devmon.service" +) + +readonly CASA_EXEC=casaos +readonly CASA_CONF=/etc/casaos/casaos.conf +readonly CASA_URL=/var/run/casaos/casaos.url +readonly CASA_SERVICE_USR=/usr/lib/systemd/system/casaos.service +readonly CASA_SERVICE_LIB=/lib/systemd/system/casaos.service +readonly CASA_SERVICE_ETC=/etc/systemd/system/casaos.service + +# Old Casa Files +readonly CASA_PATH=/casaOS +readonly CASA_CONF_PATH_OLD=/etc/casaos.conf + +readonly aCOLOUR=( + '\e[38;5;154m' # green | Lines, bullets and separators + '\e[1m' # Bold white | Main descriptions + '\e[90m' # Grey | Credits + '\e[91m' # Red | Update notifications Alert + '\e[33m' # Yellow | Emphasis +) + +Show() { + # OK + if (($1 == 0)); then + echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" + # FAILED + elif (($1 == 1)); then + echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" + # INFO + elif (($1 == 2)); then + echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" + # NOTICE + elif (($1 == 3)); then + echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" + fi +} + +Warn() { + echo -e "${aCOLOUR[3]}$1$COLOUR_RESET" +} + +trap 'onCtrlC' INT +onCtrlC() { + echo -e "${COLOUR_RESET}" + exit 1 +} + +Detecting_CasaOS() { + if [[ ! -x "$(command -v ${CASA_EXEC})" ]]; then + Show 2 "CasaOS is not detected, exit the script." + exit 1 + else + Show 0 "This script will delete the containers you no longer use, and the CasaOS configuration files." + fi +} + +Uninstall_Container() { + if [[ ${UNINSTALL_ALL_CONTAINER} == true && "$(docker ps -aq)" != "" ]]; then + Show 2 "Start deleting containers." + docker stop "$(docker ps -aq)" || Show 1 "Failed to stop all containers." + docker rm "$(docker ps -aq)" || Show 1 "Failed to delete all containers." + fi +} + +Remove_Images() { + if [[ ${REMOVE_IMAGES} == "all" && "$(docker images -q)" != "" ]]; then + Show 2 "Start deleting all images." + docker rmi "$(docker images -q)" || Show 1 "Failed to delete all images." + elif [[ ${REMOVE_IMAGES} == "unuse" && "$(docker images -q)" != "" ]]; then + Show 2 "Start deleting unuse images." + docker image prune -af || Show 1 "Failed to delete unuse images." + fi +} + + +Uninstall_Casaos() { + + for SERVICE in "${CASA_SERVICES[@]}"; do + Show 2 "Stopping ${SERVICE}..." + systemctl disable --now "${SERVICE}" || Show 3 "Failed to disable ${SERVICE}" + done + + # Remove Service file + if [[ -f ${CASA_SERVICE_USR} ]]; then + rm -rvf ${CASA_SERVICE_USR} + fi + + if [[ -f ${CASA_SERVICE_LIB} ]]; then + rm -rvf ${CASA_SERVICE_LIB} + fi + + if [[ -f ${CASA_SERVICE_ETC} ]]; then + rm -rvf ${CASA_SERVICE_ETC} + fi + + # Old Casa Files + if [[ -d ${CASA_PATH} ]]; then + rm -rvf ${CASA_PATH} || Show 1 "Failed to delete legacy CasaOS files." + fi + + if [[ -f ${CASA_CONF_PATH_OLD} ]]; then + rm -rvf ${CASA_CONF_PATH_OLD} + fi + + # New Casa Files + if [[ ${REMOVE_APP_DATA} = true ]]; then + rm -rvf /DATA/AppData || Show 1 "Failed to delete AppData." + fi + + rm -rvf "$(which ${CASA_EXEC})" || Show 3 "Failed to remove ${CASA_EXEC}" + rm -rvf ${CASA_CONF} || Show 3 "Failed to remove ${CASA_CONF}" + rm -rvf ${CASA_URL} || Show 3 "Failed to remove ${CASA_URL}" + + rm -rvf /var/lib/casaos/app_category.json + rm -rvf /var/lib/casaos/app_list.json + rm -rvf /var/lib/casaos/docker_root +} + +Detecting_CasaOS + +while true; do + echo -n -e " ${aCOLOUR[4]}Do you want delete all containers? Y/n :${COLOUR_RESET}" + read -r input + case $input in + [yY][eE][sS] | [yY]) + UNINSTALL_ALL_CONTAINER=true + break + ;; + [nN][oO] | [nN]) + UNINSTALL_ALL_CONTAINER=false + break + ;; + *) + Warn " Invalid input..." + ;; + esac +done + +if [[ ${UNINSTALL_ALL_CONTAINER} == true ]]; then + while true; do + echo -n -e " ${aCOLOUR[4]}Do you want delete all images? Y/n :${COLOUR_RESET}" + read -r input + case $input in + [yY][eE][sS] | [yY]) + REMOVE_IMAGES="all" + break + ;; + [nN][oO] | [nN]) + REMOVE_IMAGES="none" + break + ;; + *) + Warn " Invalid input..." + ;; + esac + done + + while true; do + echo -n -e " ${aCOLOUR[4]}Do you want delete all AppData of CasaOS? Y/n :${COLOUR_RESET}" + read -r input + case $input in + [yY][eE][sS] | [yY]) + REMOVE_APP_DATA=true + break + ;; + [nN][oO] | [nN]) + REMOVE_APP_DATA=false + break + ;; + *) + Warn " Invalid input..." + ;; + esac + done +else + while true; do + echo -n -e " ${aCOLOUR[4]}Do you want to delete all images that are not used by the container? Y/n :${COLOUR_RESET}" + read -r input + case $input in + [yY][eE][sS] | [yY]) + REMOVE_IMAGES="unuse" + break + ;; + [nN][oO] | [nN]) + REMOVE_IMAGES="none" + break + ;; + *) + Warn " Invalid input..." + ;; + esac + done +fi + +Uninstall_Container +Remove_Images +Uninstall_Casaos diff --git a/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/cleanup-casaos.sh new file mode 120000 index 0000000..8862621 --- /dev/null +++ b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/cleanup-casaos.sh @@ -0,0 +1 @@ +../debian/cleanup-casaos.sh \ No newline at end of file diff --git a/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/jammy/cleanup-casaos.sh b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/jammy/cleanup-casaos.sh new file mode 120000 index 0000000..bf358e5 --- /dev/null +++ b/build/sysroot/usr/share/casaos/cleanup/service.d/casaos/ubuntu/jammy/cleanup-casaos.sh @@ -0,0 +1 @@ +../../debian/bullseye/cleanup-casaos.sh \ No newline at end of file diff --git a/build/sysroot/usr/share/casaos/shell/update.sh b/build/sysroot/usr/share/casaos/shell/update.sh index 3802105..c01abb4 100644 --- a/build/sysroot/usr/share/casaos/shell/update.sh +++ b/build/sysroot/usr/share/casaos/shell/update.sh @@ -9,4 +9,4 @@ ### -curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | bash \ No newline at end of file +curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | bash diff --git a/cmd/migration-tool/log.go b/cmd/migration-tool/log.go index e20da69..ed69983 100644 --- a/cmd/migration-tool/log.go +++ b/cmd/migration-tool/log.go @@ -1,13 +1,3 @@ -/* - * @Author: LinkLeong link@icewhale.org - * @Date: 2022-08-30 22:15:30 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-08-30 22:15:47 - * @FilePath: /CasaOS/cmd/migration-tool/log.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ package main import ( diff --git a/cmd/migration-tool/main.go b/cmd/migration-tool/main.go index a09d5b8..d28663c0 100644 --- a/cmd/migration-tool/main.go +++ b/cmd/migration-tool/main.go @@ -28,25 +28,29 @@ const ( casaosServiceName = "casaos.service" ) -var _logger *Logger -var sqliteDB *gorm.DB +var ( + _logger *Logger + sqliteDB *gorm.DB +) -var configFlag = "" -var dbFlag = "" +var ( + configFlag = "" + dbFlag = "" +) func init() { config.InitSetup(configFlag) - config.UpdateSetup() if len(dbFlag) == 0 { dbFlag = config.AppInfo.DBPath + "/db" } sqliteDB = sqlite.GetDb(dbFlag) - //gredis.GetRedisConn(config.RedisInfo), + // gredis.GetRedisConn(config.RedisInfo), service.MyService = service.NewService(sqliteDB, "") } + func main() { versionFlag := flag.Bool("v", false, "version") debugFlag := flag.Bool("d", true, "debug") @@ -79,7 +83,7 @@ func main() { } migrationTools := []interfaces.MigrationTool{ - NewMigrationToolFor_035(), + // nothing to migrate from last version } var selectedMigrationTool interfaces.MigrationTool @@ -110,8 +114,7 @@ func main() { panic(err) } - selectedMigrationTool.PostMigrate() - _logger.Info("casaos migration ok") - //panic(err) - + if err := selectedMigrationTool.PostMigrate(); err != nil { + _logger.Error("Migration succeeded, but post-migration failed: %s", err) + } } diff --git a/cmd/migration-tool/migration-034-035.go b/cmd/migration-tool/migration-034-035.go deleted file mode 100644 index 4fa2ed3..0000000 --- a/cmd/migration-tool/migration-034-035.go +++ /dev/null @@ -1,182 +0,0 @@ -/* - * @Author: LinkLeong link@icewhale.org - * @Date: 2022-08-24 17:36:00 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-09-05 11:24:27 - * @FilePath: /CasaOS/cmd/migration-tool/migration-034-035.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ -package main - -import ( - "io" - "io/ioutil" - "os" - "path" - "strings" - - interfaces "github.com/IceWhaleTech/CasaOS-Common" - "github.com/IceWhaleTech/CasaOS-Common/utils/version" - "github.com/IceWhaleTech/CasaOS/pkg/config" - "github.com/IceWhaleTech/CasaOS/pkg/utils/command" - "github.com/IceWhaleTech/CasaOS/pkg/utils/file" - "github.com/IceWhaleTech/CasaOS/service" -) - -type migrationTool struct{} - -func (u *migrationTool) IsMigrationNeeded() (bool, error) { - - majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion() - if err != nil { - if err == version.ErrLegacyVersionNotFound { - return false, nil - } - - return false, err - } - - if majorVersion > 0 { - return false, nil - } - - if minorVersion > 3 { - return false, nil - } - - if minorVersion == 3 && patchVersion > 5 { - return false, nil - } - - _logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...") - return true, nil - -} - -func (u *migrationTool) PreMigrate() error { - - return nil -} - -func (u *migrationTool) Migrate() error { - - if service.MyService.System().GetSysInfo().KernelArch == "aarch64" && config.ServerInfo.USBAutoMount != "True" && strings.Contains(service.MyService.System().GetDeviceTree(), "Raspberry Pi") { - service.MyService.System().UpdateUSBAutoMount("False") - service.MyService.System().ExecUSBAutoMountShell("False") - } - newAPIUrl := "https://api.casaos.io/casaos-api" - if config.ServerInfo.ServerApi == "https://api.casaos.zimaboard.com" { - config.ServerInfo.ServerApi = newAPIUrl - config.Cfg.Section("server").Key("ServerApi").SetValue(newAPIUrl) - config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath) - } - command.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/assist.sh | bash") - if !file.CheckNotExist("/casaOS") { - command.OnlyExec("source /casaOS/server/shell/update.sh ;") - command.OnlyExec("source " + config.AppInfo.ShellPath + "/delete-old-service.sh ;") - } - - service.MyService.App().ImportApplications(true) - - src := "/casaOS/server/conf/conf.ini" - if file.Exists(src) { - dst := "/etc/casaos/casaos.conf" - source, err := os.Open(src) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(dst) - if err != nil { - return err - } - defer destination.Close() - _, err = io.Copy(destination, source) - if err != nil { - return err - } - - } - - if file.Exists("/casaOS/server/db") { - var fds []os.FileInfo - var err error - to := "/var/lib/casaos/db" - file.IsNotExistMkDir(to) - from := "/casaOS/server/db" - if fds, err = ioutil.ReadDir(from); err != nil { - return err - } - - for _, fd := range fds { - srcfp := path.Join(from, fd.Name()) - dstfp := path.Join(to, fd.Name()) - source, err := os.Open(srcfp) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(dstfp) - if err != nil { - return err - } - defer destination.Close() - _, err = io.Copy(destination, source) - if err != nil { - return err - } - } - - } - - if file.Exists("/casaOS/server/conf") { - var fds []os.FileInfo - var err error - to := "/var/lib/casaos/conf" - file.IsNotExistMkDir(to) - from := "/casaOS/server/conf" - if fds, err = ioutil.ReadDir(from); err != nil { - return err - } - - for _, fd := range fds { - fExt := path.Ext(fd.Name()) - if fExt != ".json" { - continue - } - srcfp := path.Join(from, fd.Name()) - dstfp := path.Join(to, fd.Name()) - source, err := os.Open(srcfp) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(dstfp) - if err != nil { - return err - } - defer destination.Close() - _, err = io.Copy(destination, source) - if err != nil { - return err - } - } - - } - - _logger.Info("update done") - return nil -} - -func (u *migrationTool) PostMigrate() error { - return nil -} - -func NewMigrationToolFor_035() interfaces.MigrationTool { - return &migrationTool{} -} diff --git a/cmd/migration-tool/migration-sample.go b/cmd/migration-tool/migration-sample.go new file mode 100644 index 0000000..c45858d --- /dev/null +++ b/cmd/migration-tool/migration-sample.go @@ -0,0 +1,60 @@ +/* + * @Author: LinkLeong link@icewhale.org + * @Date: 2022-08-24 17:36:00 + * @LastEditors: LinkLeong + * @LastEditTime: 2022-09-05 11:24:27 + * @FilePath: /CasaOS/cmd/migration-tool/migration-034-035.go + * @Description: + * @Website: https://www.casaos.io + * Copyright (c) 2022 by icewhale, All Rights Reserved. + */ +package main + +import ( + interfaces "github.com/IceWhaleTech/CasaOS-Common" + "github.com/IceWhaleTech/CasaOS-Common/utils/version" +) + +type migrationTool struct{} + +func (u *migrationTool) IsMigrationNeeded() (bool, error) { + majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion() + if err != nil { + if err == version.ErrLegacyVersionNotFound { + return false, nil + } + + return false, err + } + + if majorVersion > 0 { + return false, nil + } + + if minorVersion > 3 { + return false, nil + } + + if minorVersion == 3 && patchVersion > 5 { + return false, nil + } + + _logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...") + return true, nil +} + +func (u *migrationTool) PreMigrate() error { + return nil +} + +func (u *migrationTool) Migrate() error { + return nil +} + +func (u *migrationTool) PostMigrate() error { + return nil +} + +func NewMigrationToolFor_036() interfaces.MigrationTool { + return &migrationTool{} +} diff --git a/common/notify.go b/common/notify.go new file mode 100644 index 0000000..a5cee48 --- /dev/null +++ b/common/notify.go @@ -0,0 +1,89 @@ +package common + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" +) + +const ( + CasaOSURLFilename = "casaos.url" + APICasaOSNotify = "/v1/notify" +) + +type NotifyService interface { + SendNotify(path string, message map[string]interface{}) error + SendSystemStatusNotify(message map[string]interface{}) error +} +type notifyService struct { + address string +} + +func (n *notifyService) SendNotify(path string, message map[string]interface{}) error { + + url := strings.TrimSuffix(n.address, "/") + APICasaOSNotify + "/" + path + body, err := json.Marshal(message) + if err != nil { + return err + } + response, err := http.Post(url, "application/json", bytes.NewBuffer(body)) + if err != nil { + return err + } + + if response.StatusCode != http.StatusOK { + return errors.New("failed to send notify (status code: " + fmt.Sprint(response.StatusCode) + ")") + } + return nil + +} + +// disk: "sys_disk":{"size":56866869248,"avail":5855485952,"health":true,"used":48099700736} +// usb: "sys_usb":[{"name": "sdc","size": 7747397632,"model": "DataTraveler_2.0","avail": 7714418688,"children": null}] +func (n *notifyService) SendSystemStatusNotify(message map[string]interface{}) error { + + url := strings.TrimSuffix(n.address, "/") + APICasaOSNotify + "/system_status" + fmt.Println(url) + body, err := json.Marshal(message) + if err != nil { + return err + } + response, err := http.Post(url, "application/json", bytes.NewBuffer(body)) + if err != nil { + return err + } + + if response.StatusCode != http.StatusOK { + return errors.New("failed to send notify (status code: " + fmt.Sprint(response.StatusCode) + ")") + } + return nil + +} +func NewNotifyService(runtimePath string) (NotifyService, error) { + casaosAddressFile := filepath.Join(runtimePath, CasaOSURLFilename) + + buf, err := os.ReadFile(casaosAddressFile) + if err != nil { + return nil, err + } + + address := string(buf) + + response, err := http.Get(address + "/ping") + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + return nil, errors.New("failed to ping casaos service") + } + + return ¬ifyService{ + address: address, + }, nil +} diff --git a/common/notify_test.go b/common/notify_test.go new file mode 100644 index 0000000..1e9c543 --- /dev/null +++ b/common/notify_test.go @@ -0,0 +1,29 @@ +package common + +import "testing" + +func TestSendNotify(t *testing.T) { + notify, err := NewNotifyService("/var/run/casaos") + if err != nil { + t.Fatal(err) + } + err = notify.SendNotify("test", map[string]interface{}{ + "test": "test", + }) + if err != nil { + t.Fatal(err) + } +} + +func TestSendSystemStatusNotify(t *testing.T) { + notify, err := NewNotifyService("/var/run/casaos") + if err != nil { + t.Fatal(err) + } + err = notify.SendSystemStatusNotify(map[string]interface{}{ + "sys_usb": `[{"name": "sdc","size": 7747397632,"model": "DataTraveler_2.0","avail": 7714418688,"children": null}]`, + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/common/share.go b/common/share.go new file mode 100644 index 0000000..a0da4b2 --- /dev/null +++ b/common/share.go @@ -0,0 +1,78 @@ +package common + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" +) + +const ( + APICasaOSShare = "/v1/samba/shares" +) + +type ShareService interface { + DeleteShare(id string) error +} +type shareService struct { + address string +} + +func (n *shareService) DeleteShare(id string) error { + url := strings.TrimSuffix(n.address, "/") + APICasaOSShare + "/" + id + fmt.Println(url) + message := "{}" + body, err := json.Marshal(message) + if err != nil { + return err + } + + client := &http.Client{} + + // Create request + req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(body)) + if err != nil { + return err + } + + // Fetch Request + response, err := client.Do(req) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return errors.New("failed to send share (status code: " + fmt.Sprint(response.StatusCode) + ")") + } + return nil + +} + +func NewShareService(runtimePath string) (ShareService, error) { + casaosAddressFile := filepath.Join(runtimePath, CasaOSURLFilename) + + buf, err := os.ReadFile(casaosAddressFile) + if err != nil { + return nil, err + } + + address := string(buf) + + response, err := http.Get(address + "/ping") + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + return nil, errors.New("failed to ping casaos service") + } + + return &shareService{ + address: address, + }, nil +} diff --git a/common/share_test.go b/common/share_test.go new file mode 100644 index 0000000..ce79e03 --- /dev/null +++ b/common/share_test.go @@ -0,0 +1,14 @@ +package common + +import "testing" + +func TestDeleteShare(t *testing.T) { + share, err := NewShareService("/var/run/casaos") + if err != nil { + t.Fatal(err) + } + err = share.DeleteShare("1") + if err != nil { + t.Fatal(err) + } +} diff --git a/conf/conf.conf.sample b/conf/conf.conf.sample index 5ce1d0e..1364418 100644 --- a/conf/conf.conf.sample +++ b/conf/conf.conf.sample @@ -11,7 +11,6 @@ DateFormat = 2006-01-02 DBPath = /var/lib/casaos ShellPath = /usr/share/casaos/shell UserDataPath = /var/lib/casaos/conf -TempPath = /var/lib/casaos/temp [server] RunMode = release diff --git a/go.mod b/go.mod index 6209c3e..6c454be 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ go 1.16 require ( github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d - github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220909125858-92fc5b2e0ae5 + github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220929035515-b1287110d6d8 github.com/IceWhaleTech/CasaOS-Gateway v0.3.6 github.com/Microsoft/go-winio v0.5.0 // indirect github.com/ambelovsky/go-structs v1.1.0 // indirect github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109 github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect github.com/containerd/containerd v1.5.7 // indirect + github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/disintegration/imaging v1.6.2 github.com/docker/distribution v2.8.0+incompatible // indirect github.com/docker/docker v20.10.7+incompatible @@ -34,7 +35,6 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pilebones/go-udev v0.9.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron v1.2.0 github.com/satori/go.uuid v1.2.0 diff --git a/go.sum b/go.sum index 9e68d0e..0bf42e2 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d/go.mod h1:lW9x+yEjqKdPbE3+cf2fGPJXCw/hChX3Omi9QHTLFsQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220901034123-ca130f6b5ce9/go.mod h1:2MiivEMzvh41codhEKUcn46WK3Ffesop/04qa9jsvQk= -github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220909125858-92fc5b2e0ae5 h1:vgAf0jVKCBo3wyjOZ4z9tB77lVrgIfz2CcQX2+4JTSI= -github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220909125858-92fc5b2e0ae5/go.mod h1:2MiivEMzvh41codhEKUcn46WK3Ffesop/04qa9jsvQk= +github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220929035515-b1287110d6d8 h1:r8nhgQ6tnrn6ikXN9aLH/K4H4H64Nc0hZ6jyW2B22x0= +github.com/IceWhaleTech/CasaOS-Common v0.0.0-20220929035515-b1287110d6d8/go.mod h1:2MiivEMzvh41codhEKUcn46WK3Ffesop/04qa9jsvQk= github.com/IceWhaleTech/CasaOS-Gateway v0.3.6 h1:2tQQo85+jzbbjqIsKKn77QlAA73bc7vZsVCFvWnK4mg= github.com/IceWhaleTech/CasaOS-Gateway v0.3.6/go.mod h1:hnZwGUzcOyiufMpVO7l3gu2gAm6Ws4TY4Nlj3kMshXA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -814,8 +814,6 @@ github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZO github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pilebones/go-udev v0.9.0 h1:N1uEO/SxUwtIctc0WLU0t69JeBxIYEYnj8lT/Nabl9Q= -github.com/pilebones/go-udev v0.9.0/go.mod h1:T2eI2tUSK0hA2WS5QLjXJUfQkluZQu+18Cqvem3CaXI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/main.go b/main.go index b616e52..ede006d 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/http" + "path/filepath" "time" "github.com/IceWhaleTech/CasaOS-Gateway/common" @@ -12,10 +13,13 @@ import ( "github.com/IceWhaleTech/CasaOS/pkg/cache" "github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/sqlite" + "github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" "github.com/IceWhaleTech/CasaOS/route" "github.com/IceWhaleTech/CasaOS/service" "github.com/IceWhaleTech/CasaOS/types" + "github.com/coreos/go-systemd/daemon" + "go.uber.org/zap" "github.com/robfig/cron" "gorm.io/gorm" @@ -25,9 +29,11 @@ const LOCALHOST = "127.0.0.1" var sqliteDB *gorm.DB -var configFlag = flag.String("c", "", "config address") -var dbFlag = flag.String("db", "", "db path") -var versionFlag = flag.Bool("v", false, "version") +var ( + configFlag = flag.String("c", "", "config address") + dbFlag = flag.String("db", "", "db path") + versionFlag = flag.Bool("v", false, "version") +) func init() { flag.Parse() @@ -36,7 +42,6 @@ func init() { return } config.InitSetup(*configFlag) - config.UpdateSetup() loger.LogInit() if len(*dbFlag) == 0 { @@ -44,7 +49,7 @@ func init() { } sqliteDB = sqlite.GetDb(*dbFlag) - //gredis.GetRedisConn(config.RedisInfo), + // gredis.GetRedisConn(config.RedisInfo), service.MyService = service.NewService(sqliteDB, config.CommonInfo.RuntimePath) @@ -57,7 +62,6 @@ func init() { // go service.LoopFriend() // go service.MyService.App().CheckNewImage() - } // @title casaOS API @@ -77,22 +81,21 @@ func main() { return } go route.SocketInit(service.NotifyMsg) - go route.MonitoryUSB() - //model.Setup() - //gredis.Setup() + // model.Setup() + // gredis.Setup() r := route.InitRouter() - //service.SyncTask(sqliteDB) + // service.SyncTask(sqliteDB) cron2 := cron.New() - //every day execution + // every day execution err := cron2.AddFunc("0/5 * * * * *", func() { if service.ClientCount > 0 { - //route.SendNetINfoBySocket() - //route.SendCPUBySocket() - //route.SendMemBySocket() + // route.SendNetINfoBySocket() + // route.SendCPUBySocket() + // route.SendMemBySocket() // route.SendDiskBySocket() - //route.SendUSBBySocket() + // route.SendUSBBySocket() route.SendAllHardwareStatusBySocket() } }) @@ -107,7 +110,7 @@ func main() { if err != nil { panic(err) } - routers := []string{"sys", "apps", "container", "app-categories", "port", "file", "folder", "batch", "image", "disks", "storage", "samba"} + routers := []string{"sys", "apps", "container", "app-categories", "port", "file", "folder", "batch", "image", "samba", "notify"} for _, v := range routers { err = service.MyService.Gateway().CreateRoute(&common.Route{ Path: "/v1/" + v, @@ -121,7 +124,7 @@ func main() { } go func() { time.Sleep(time.Second * 2) - //v0.3.6 + // v0.3.6 if config.ServerInfo.HttpPort != "" { changePort := common.ChangePortRequest{} changePort.Port = config.ServerInfo.HttpPort @@ -133,15 +136,29 @@ func main() { } }() - // s := &http.Server{ - // Addr: listener.Addr().String(), //fmt.Sprintf(":%v", config.ServerInfo.HttpPort), - // Handler: r, - // ReadTimeout: 60 * time.Second, - // WriteTimeout: 60 * time.Second, - // MaxHeaderBytes: 1 << 20, - // } - // s.ListenAndServe() - err = http.Serve(listener, r) + urlFilePath := filepath.Join(config.CommonInfo.RuntimePath, "casaos.url") + err = file.CreateFileAndWriteContent(urlFilePath, "http://"+listener.Addr().String()) + if err != nil { + loger.Error("Management service is listening...", + zap.Any("address", listener.Addr().String()), + zap.Any("filepath", urlFilePath), + ) + } + + if supported, err := daemon.SdNotify(false, daemon.SdNotifyReady); err != nil { + loger.Error("Failed to notify systemd that casaos main service is ready", zap.Any("error", err)) + } else if supported { + loger.Info("Notified systemd that casaos main service is ready") + } else { + loger.Info("This process is not running as a systemd service.") + } + + s := &http.Server{ + Handler: r, + ReadHeaderTimeout: 5 * time.Second, // fix G112: Potential slowloris attack (see https://github.com/securego/gosec) + } + + err = s.Serve(listener) // not using http.serve() to fix G114: Use of net/http serve function that has no support for setting timeouts (see https://github.com/securego/gosec) if err != nil { panic(err) } diff --git a/middleware/gin.go b/middleware/gin.go deleted file mode 100644 index d8fd836..0000000 --- a/middleware/gin.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * @Author: LinkLeong link@icewhale.com - * @Date: 2021-10-08 10:29:08 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-07-22 11:06:07 - * @FilePath: /CasaOS/middleware/gin.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ -package middleware - -import ( - "fmt" - "net/http" - "strings" - - "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" - "github.com/gin-gonic/gin" - "go.uber.org/zap" -) - -func Cors() gin.HandlerFunc { - return func(c *gin.Context) { - method := c.Request.Method - - c.Header("Access-Control-Allow-Origin", "*") - c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") - //允许跨域设置可以返回其他子段,可以自定义字段 - c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Connection,Host,Origin,Referer,User-Agent,X-Requested-With") - // 允许浏览器(客户端)可以解析的头部 (重要) - c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers") - //c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With") - //设置缓存时间 - c.Header("Access-Control-Max-Age", "172800") - c.Header("Access-Control-Allow-Credentials", "true") - c.Set("Content-Type", "application/json") - //} - - //允许类型校验 - if method == "OPTIONS" { - c.JSON(http.StatusOK, "ok!") - } - - defer func() { - if err := recover(); err != nil { - fmt.Println(err) - } - }() - - c.Next() - } -} -func WriteLog() gin.HandlerFunc { - return func(c *gin.Context) { - if !strings.Contains(c.Request.URL.String(), "password") { - loger.Info("request:", zap.Any("path", c.Request.URL.String()), zap.Any("param", c.Params), zap.Any("query", c.Request.URL.Query()), zap.Any("method", c.Request.Method)) - c.Next() - } - - } -} diff --git a/model/disk.go b/model/disk.go deleted file mode 100644 index 75d3bc5..0000000 --- a/model/disk.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - * @Author: LinkLeong link@icewhale.com - * @Date: 2022-07-13 10:43:45 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-08-03 14:45:35 - * @FilePath: /CasaOS/model/disk.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ -package model - -type LSBLKModel struct { - Name string `json:"name"` - FsType string `json:"fstype"` - Size uint64 `json:"size"` - FSSize string `json:"fssize"` - Path string `json:"path"` - Model string `json:"model"` //设备标识符 - RM bool `json:"rm"` //是否为可移动设备 - RO bool `json:"ro"` //是否为只读设备 - State string `json:"state"` - PhySec int `json:"phy-sec"` //物理扇区大小 - Type string `json:"type"` - Vendor string `json:"vendor"` //供应商 - Rev string `json:"rev"` //修订版本 - FSAvail string `json:"fsavail"` //可用空间 - FSUse string `json:"fsuse%"` //已用百分比 - MountPoint string `json:"mountpoint"` - Format string `json:"format"` - Health string `json:"health"` - HotPlug bool `json:"hotplug"` - UUID string `json:"uuid"` - FSUsed string `json:"fsused"` - Temperature int `json:"temperature"` - Tran string `json:"tran"` - MinIO uint64 `json:"min-io"` - UsedPercent float64 `json:"used_percent"` - Serial string `json:"serial"` - Children []LSBLKModel `json:"children"` - SubSystems string `json:"subsystems"` - Label string `json:"label"` - //详情特有 - StartSector uint64 `json:"start_sector,omitempty"` - Rota bool `json:"rota"` //true(hhd) false(ssd) - DiskType string `json:"disk_type"` - EndSector uint64 `json:"end_sector,omitempty"` -} - -type Drive struct { - Name string `json:"name"` - Size uint64 `json:"size"` - Model string `json:"model"` - Health string `json:"health"` - Temperature int `json:"temperature"` - DiskType string `json:"disk_type"` - NeedFormat bool `json:"need_format"` - Serial string `json:"serial"` - Path string `json:"path"` - ChildrenNumber int `json:"children_number"` -} - -type DriveUSB struct { - Name string `json:"name"` - Size uint64 `json:"size"` - Model string `json:"model"` - Avail uint64 `json:"avail"` - Children []USBChildren `json:"children"` -} -type USBChildren struct { - Name string `json:"name"` - Size uint64 `json:"size"` - Avail uint64 `json:"avail"` - MountPoint string `json:"mount_point"` -} - -type Storage struct { - MountPoint string `json:"mount_point"` - Size string `json:"size"` - Avail string `json:"avail"` //可用空间 - Type string `json:"type"` - Path string `json:"path"` - DriveName string `json:"drive_name"` - Label string `json:"label"` -} -type Storages struct { - DiskName string `json:"disk_name"` - Size uint64 `json:"size"` - Path string `json:"path"` - Children []Storage `json:"children"` -} - -type Summary struct { - Size uint64 `json:"size"` - Avail uint64 `json:"avail"` //可用空间 - Health bool `json:"health"` - Used uint64 `json:"used"` -} diff --git a/model/docker.go b/model/docker.go index a083319..34412cd 100644 --- a/model/docker.go +++ b/model/docker.go @@ -16,3 +16,9 @@ type DockerStatsModel struct { Data interface{} `json:"data"` Previous interface{} `json:"previous"` } + +// reference - https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file +type DockerDaemonConfigurationModel struct { + // e.g. `/var/lib/docker` + Root string `json:"data-root,omitempty"` +} diff --git a/model/receive/app.go b/model/receive/app.go deleted file mode 100644 index 3ccae50..0000000 --- a/model/receive/app.go +++ /dev/null @@ -1 +0,0 @@ -package receive diff --git a/model/sys_common.go b/model/sys_common.go index 1a78867..f9476a4 100644 --- a/model/sys_common.go +++ b/model/sys_common.go @@ -12,12 +12,12 @@ package model import "time" -//系统配置 +// 系统配置 type SysInfoModel struct { Name string //系统名称 } -//服务配置 +// 服务配置 type ServerModel struct { HttpPort string RunMode string @@ -26,9 +26,10 @@ type ServerModel struct { Token string USBAutoMount string SocketPort string + UpdateUrl string } -//服务配置 +// 服务配置 type APPModel struct { LogPath string LogSaveName string @@ -40,20 +41,19 @@ type APPModel struct { DateFormat string DBPath string ShellPath string - TempPath string } type CommonModel struct { RuntimePath string } -//公共返回模型 +// 公共返回模型 type Result struct { Success int `json:"success" example:"200"` Message string `json:"message" example:"ok"` Data interface{} `json:"data" example:"返回结果"` } -//redis配置文件 +// redis配置文件 type RedisModel struct { Host string Password string diff --git a/pkg/config/init.go b/pkg/config/init.go index c79d71f..b2de00f 100644 --- a/pkg/config/init.go +++ b/pkg/config/init.go @@ -23,17 +23,17 @@ import ( "github.com/go-ini/ini" ) -//系统配置 +// 系统配置 var SysInfo = &model.SysInfoModel{} -//用户相关 +// 用户相关 var AppInfo = &model.APPModel{} var CommonInfo = &model.CommonModel{} //var RedisInfo = &model.RedisModel{} -//server相关 +// server相关 var ServerInfo = &model.ServerModel{} var SystemConfigInfo = &model.SystemConfig{} @@ -44,7 +44,7 @@ var FileSettingInfo = &model.FileSetting{} var Cfg *ini.File -//初始化设置,获取系统的部分信息。 +// 初始化设置,获取系统的部分信息。 func InitSetup(config string) { var configDir = USERCONFIGURL @@ -86,9 +86,6 @@ func InitSetup(config string) { if len(AppInfo.UserDataPath) == 0 { AppInfo.UserDataPath = "/var/lib/casaos/conf" } - if len(AppInfo.TempPath) == 0 { - AppInfo.TempPath = "/var/lib/casaos/temp" - } if len(CommonInfo.RuntimePath) == 0 { CommonInfo.RuntimePath = "/var/run/casaos" } @@ -97,7 +94,7 @@ func InitSetup(config string) { } -//映射 +// 映射 func mapTo(section string, v interface{}) { err := Cfg.Section(section).MapTo(v) if err != nil { diff --git a/pkg/config/update.go b/pkg/config/update.go deleted file mode 100644 index 7dbae82..0000000 --- a/pkg/config/update.go +++ /dev/null @@ -1,32 +0,0 @@ -package config - -import ( - "runtime" - - "github.com/IceWhaleTech/CasaOS/pkg/utils/file" -) - -//检查目录是否存在 -func mkdirDATAAll() { - sysType := runtime.GOOS - var dirArray []string - if sysType == "linux" { - dirArray = []string{"/DATA/AppData", "/DATA/Documents", "/DATA/Downloads", "/DATA/Gallery", "/DATA/Media/Movies", "/DATA/Media/TV Shows", "/DATA/Media/Music"} - } - - if sysType == "windows" { - dirArray = []string{"C:\\CasaOS\\DATA\\AppData", "C:\\CasaOS\\DATA\\Documents", "C:\\CasaOS\\DATA\\Downloads", "C:\\CasaOS\\DATA\\Gallery", "C:\\CasaOS\\DATA\\Media/Movies", "C:\\CasaOS\\DATA\\Media\\TV Shows", "C:\\CasaOS\\DATA\\Media\\Music"} - } - if sysType == "darwin" { - dirArray = []string{"./CasaOS/DATA/AppData", "./CasaOS/DATA/Documents", "./CasaOS/DATA/Downloads", "./CasaOS/DATA/Gallery", "./CasaOS/DATA/Media/Movies", "./CasaOS/DATA/Media/TV Shows", "./CasaOS/DATA/Media/Music"} - } - - for _, v := range dirArray { - file.IsNotExistMkDir(v) - } - -} - -func UpdateSetup() { - mkdirDATAAll() -} diff --git a/pkg/docker/helper.go b/pkg/docker/helper.go index be76b7d..0aef117 100644 --- a/pkg/docker/helper.go +++ b/pkg/docker/helper.go @@ -15,7 +15,6 @@ import ( ) func NewSshClient(user, password string, port string) (*ssh.Client, error) { - // connet to ssh // addr = fmt.Sprintf("%s:%d", host, port) @@ -23,10 +22,10 @@ func NewSshClient(user, password string, port string) (*ssh.Client, error) { Timeout: time.Second * 5, User: user, HostKeyCallback: ssh.InsecureIgnoreHostKey(), - //HostKeyCallback: , - //HostKeyCallback: hostKeyCallBackFunc(h.Host), + // HostKeyCallback: , + // HostKeyCallback: hostKeyCallBackFunc(h.Host), } - //if h.Type == "password" { + // if h.Type == "password" { config.Auth = []ssh.AuthMethod{ssh.Password(password)} //} else { // config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(h.Key)} @@ -90,11 +89,11 @@ func (w *wsBufferWriter) Write(p []byte) (int, error) { defer w.mu.Unlock() return w.buffer.Write(p) } + func (s *SshConn) Close() { if s.Session != nil { s.Session.Close() } - } const ( @@ -102,16 +101,15 @@ const ( wsMsgResize = "resize" ) -//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin +// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string { - //tells other go routine quit + // tells other go routine quit username := "" for { - //read websocket msg + // read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { - return "" } @@ -125,8 +123,8 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string { //} switch msgObj.Type { case wsMsgCmd: - //handle xterm.js stdin - //decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) + // handle xterm.js stdin + // decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) decodeBytes := []byte(msgObj.Cmd) if msgObj.Cmd == "\u007f" { if len(username) == 0 { @@ -144,7 +142,7 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string { if err := wsConn.WriteMessage(websocket.TextMessage, decodeBytes); err != nil { logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed") } - //write input cmd to log buffer + // write input cmd to log buffer if _, err := logBuff.Write(decodeBytes); err != nil { logrus.WithError(err).Error("write received cmd into log buffer failed") } @@ -154,11 +152,11 @@ func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string { } func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string { - //tells other go routine quit + // tells other go routine quit password := "" for { - //read websocket msg + // read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { logrus.WithError(err).Error("reading webSocket message failed") @@ -175,8 +173,8 @@ func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string //} switch msgObj.Type { case wsMsgCmd: - //handle xterm.js stdin - //decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) + // handle xterm.js stdin + // decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) if msgObj.Cmd == "\r" { return password } @@ -194,16 +192,16 @@ func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string } } -//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin +// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) { - //tells other go routine quit + // tells other go routine quit defer setQuit(exitCh) for { select { case <-exitCh: return default: - //read websocket msg + // read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { logrus.WithError(err).Error("reading webSocket message failed") @@ -227,15 +225,15 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe switch msgObj.Type { case wsMsgResize: - //handle xterm.js size change + // handle xterm.js size change if msgObj.Cols > 0 && msgObj.Rows > 0 { if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil { logrus.WithError(err).Error("ssh pty change windows size failed") } } case wsMsgCmd: - //handle xterm.js stdin - //decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) + // handle xterm.js stdin + // decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) decodeBytes := []byte(msgObj.Cmd) if err != nil { logrus.WithError(err).Error("websock cmd string base64 decoding failed") @@ -243,7 +241,7 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil { logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed") } - //write input cmd to log buffer + // write input cmd to log buffer if _, err := logBuff.Write(decodeBytes); err != nil { logrus.WithError(err).Error("write received cmd into log buffer failed") } @@ -253,17 +251,17 @@ func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffe } func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) { - //tells other go routine quit - //defer setQuit(exitCh) + // tells other go routine quit + // defer setQuit(exitCh) - //every 120ms write combine output bytes into websocket response + // every 120ms write combine output bytes into websocket response tick := time.NewTicker(time.Millisecond * time.Duration(120)) - //for range time.Tick(120 * time.Millisecond){} + // for range time.Tick(120 * time.Millisecond){} defer tick.Stop() for { select { case <-tick.C: - //write combine output bytes into websocket response + // write combine output bytes into websocket response if err := flushComboOutput(ssConn.ComboOutput, wsConn); err != nil { logrus.WithError(err).Error("ssh sending combo output to webSocket failed") return @@ -273,6 +271,7 @@ func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) } } } + func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error { if w.buffer.Len() != 0 { err := wsConn.WriteMessage(websocket.TextMessage, w.buffer.Bytes()) @@ -284,16 +283,16 @@ func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error { return nil } -//ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin +// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) { - //tells other go routine quit + // tells other go routine quit defer setQuit(exitCh) for { select { case <-exitCh: return default: - //read websocket msg + // read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { logrus.WithError(err).Error("reading webSocket message failed") @@ -317,15 +316,15 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit switch msgObj.Type { case wsMsgResize: - //handle xterm.js size change + // handle xterm.js size change if msgObj.Cols > 0 && msgObj.Rows > 0 { if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil { logrus.WithError(err).Error("ssh pty change windows size failed") } } case wsMsgCmd: - //handle xterm.js stdin - //decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) + // handle xterm.js stdin + // decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) decodeBytes := []byte(msgObj.Cmd) if err != nil { logrus.WithError(err).Error("websock cmd string base64 decoding failed") @@ -333,7 +332,7 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil { logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed") } - //write input cmd to log buffer + // write input cmd to log buffer if _, err := logBuff.Write(decodeBytes); err != nil { logrus.WithError(err).Error("write received cmd into log buffer failed") } @@ -341,6 +340,7 @@ func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exit } } } + func (ssConn *SshConn) SessionWait(quitChan chan bool) { if err := ssConn.Session.Wait(); err != nil { logrus.WithError(err).Error("ssh session wait failed") @@ -395,7 +395,7 @@ func WsReaderCopy(reader *websocket.Conn, writer io.Writer) { if err = json2.Unmarshal(p, &msgObj); err != nil { writer.Write(p) } else if msgObj.Type == wsMsgResize { - //writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r")) + // writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r")) } } } diff --git a/pkg/sqlite/db.go b/pkg/sqlite/db.go index 819da65..e5d7595 100644 --- a/pkg/sqlite/db.go +++ b/pkg/sqlite/db.go @@ -43,7 +43,7 @@ func GetDb(dbPath string) *gorm.DB { } gdb = db - err = db.AutoMigrate(&model2.AppNotify{}, &model2.AppListDBModel{}, &model2.SerialDisk{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}) + err = db.AutoMigrate(&model2.AppNotify{}, &model2.AppListDBModel{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}) db.Exec("DROP TABLE IF EXISTS o_application") db.Exec("DROP TABLE IF EXISTS o_friend") db.Exec("DROP TABLE IF EXISTS o_person_download") diff --git a/pkg/utils/command/command_helper.go b/pkg/utils/command/command_helper.go index 9a2fded..a47b288 100644 --- a/pkg/utils/command/command_helper.go +++ b/pkg/utils/command/command_helper.go @@ -35,8 +35,8 @@ func ExecResultStrArray(cmdStr string) []string { fmt.Println(err) return nil } - //str, err := ioutil.ReadAll(stdout) - var networklist = []string{} + // str, err := ioutil.ReadAll(stdout) + networklist := []string{} outputBuf := bufio.NewReader(stdout) for { output, _, err := outputBuf.ReadLine() @@ -54,6 +54,8 @@ func ExecResultStrArray(cmdStr string) []string { func ExecResultStr(cmdStr string) string { cmd := exec.Command("/bin/bash", "-c", cmdStr) + println(cmd.String()) + stdout, err := cmd.StdoutPipe() if err != nil { fmt.Println(err) @@ -73,7 +75,7 @@ func ExecResultStr(cmdStr string) string { return string(str) } -//执行 lsblk 命令 +// 执行 lsblk 命令 func ExecLSBLK() []byte { output, err := exec.Command("lsblk", "-O", "-J", "-b").Output() if err != nil { @@ -83,7 +85,7 @@ func ExecLSBLK() []byte { return output } -//执行 lsblk 命令 +// 执行 lsblk 命令 func ExecLSBLKByPath(path string) []byte { output, err := exec.Command("lsblk", path, "-O", "-J", "-b").Output() if err != nil { @@ -93,7 +95,7 @@ func ExecLSBLKByPath(path string) []byte { return output } -//exec smart +// exec smart func ExecSmartCTLByPath(path string) []byte { timeout := 3 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) @@ -107,6 +109,5 @@ func ExecSmartCTLByPath(path string) []byte { } func ExecEnabledSMART(path string) { - exec.Command("smartctl", "-s on", path).Output() } diff --git a/pkg/utils/file/file.go b/pkg/utils/file/file.go index b08dcda..10e91f7 100644 --- a/pkg/utils/file/file.go +++ b/pkg/utils/file/file.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "io/fs" "io/ioutil" "log" "mime/multipart" @@ -60,7 +61,7 @@ func MkDir(src string) error { if err != nil { return err } - os.Chmod(src, 0777) + os.Chmod(src, 0o777) return nil } @@ -103,7 +104,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) { return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err) } - f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) + f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return nil, fmt.Errorf("Fail to OpenFile :%v", err) } @@ -113,7 +114,7 @@ func MustOpen(fileName, filePath string) (*os.File, error) { // 判断所给路径文件/文件夹是否存在 func Exists(path string) bool { - _, err := os.Stat(path) //os.Stat获取文件信息 + _, err := os.Stat(path) // os.Stat获取文件信息 if err != nil { if os.IsExist(err) { return true @@ -147,7 +148,7 @@ func CreateFile(path string) error { } func CreateFileAndWriteContent(path string, content string) error { - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) if err != nil { return err } @@ -163,7 +164,7 @@ func CreateFileAndWriteContent(path string, content string) error { // IsNotExistMkDir create a directory if it does not exist func IsNotExistCreateFile(src string) error { - if notExist := CheckNotExist(src); notExist == true { + if notExist := CheckNotExist(src); notExist { if err := CreateFile(src); err != nil { return err } @@ -267,7 +268,7 @@ func CopySingleFile(src, dst, style string) error { return os.Chmod(dst, srcinfo.Mode()) } -//Check for duplicate file names +// Check for duplicate file names func GetNoDuplicateFileName(fullPath string) string { path, fileName := filepath.Split(fullPath) fileSuffix := path2.Ext(fileName) @@ -293,7 +294,7 @@ func CopyDir(src string, dst string, style string) error { } return nil } - //dstPath := dst + // dstPath := dst lastPath := src[strings.LastIndex(src, "/")+1:] dst += "/" + lastPath // for i := 0; Exists(dst); i++ { @@ -314,7 +315,7 @@ func CopyDir(src string, dst string, style string) error { } for _, fd := range fds { srcfp := path.Join(src, fd.Name()) - dstfp := dst //path.Join(dst, fd.Name()) + dstfp := dst // path.Join(dst, fd.Name()) if fd.IsDir() { if err = CopyDir(srcfp, dstfp, style); err != nil { @@ -336,10 +337,17 @@ func WriteToPath(data []byte, path, name string) error { } else { fullPath += "/" + name } - IsNotExistCreateFile(fullPath) + return WriteToFullPath(data, fullPath, 0o666) +} + +func WriteToFullPath(data []byte, fullPath string, perm fs.FileMode) error { + if err := IsNotExistCreateFile(fullPath); err != nil { + return err + } + file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, - 0666, + perm, ) if err != nil { return err @@ -350,26 +358,31 @@ func WriteToPath(data []byte, path, name string) error { return err } -//最终拼接 +// 最终拼接 func SpliceFiles(dir, path string, length int, startPoint int) error { - fullPath := path - IsNotExistCreateFile(fullPath) + if err := IsNotExistCreateFile(fullPath); err != nil { + return err + } file, _ := os.OpenFile(fullPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, - 0666, + 0o666, ) + defer file.Close() + bufferedWriter := bufio.NewWriter(file) - for i := 0; i < length+startPoint; i++ { + + // todo: here should have a goroutine to remove each partial file after it is read, to save disk space + + for i := 0; i < length+startPoint-1; i++ { data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint)) if err != nil { return err } - _, err = bufferedWriter.Write(data) - if err != nil { + if _, err := bufferedWriter.Write(data); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster write return err } } @@ -380,7 +393,6 @@ func SpliceFiles(dir, path string, length int, startPoint int) error { } func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) { - switch t { case "zip", "": return ".zip", archiver.NewZip(), nil @@ -400,8 +412,8 @@ func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) { return "", nil, errors.New("format not implemented") } } -func AddFile(ar archiver.Writer, path, commonPath string) error { +func AddFile(ar archiver.Writer, path, commonPath string) error { info, err := os.Stat(path) if err != nil { return err @@ -447,6 +459,7 @@ func AddFile(ar archiver.Writer, path, commonPath string) error { return nil } + func CommonPrefix(sep byte, paths ...string) string { // Handle special cases. switch len(paths) { @@ -513,7 +526,7 @@ func GetFileOrDirSize(path string) (int64, error) { return fileInfo.Size(), nil } -//getFileSize get file size by path(B) +// getFileSize get file size by path(B) func DirSizeB(path string) (int64, error) { var size int64 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { diff --git a/route/init.go b/route/init.go index e2d09c7..a35de29 100644 --- a/route/init.go +++ b/route/init.go @@ -3,65 +3,19 @@ package route import ( "fmt" "os" - "strconv" "strings" "time" - "github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/samba" - "github.com/IceWhaleTech/CasaOS/pkg/utils/command" "github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" "github.com/IceWhaleTech/CasaOS/service" - model2 "github.com/IceWhaleTech/CasaOS/service/model" "go.uber.org/zap" ) func InitFunction() { - CheckSerialDiskMount() go InitNetworkMount() } - -func CheckSerialDiskMount() { - // check mount point - dbList := service.MyService.Disk().GetSerialAll() - - list := service.MyService.Disk().LSBLK(true) - mountPoint := make(map[string]string, len(dbList)) - //remount - for _, v := range dbList { - mountPoint[v.UUID] = v.MountPoint - } - for _, v := range list { - command.ExecEnabledSMART(v.Path) - if v.Children != nil { - for _, h := range v.Children { - //if len(h.MountPoint) == 0 && len(v.Children) == 1 && h.FsType == "ext4" { - if m, ok := mountPoint[h.UUID]; ok { - //mount point check - volume := m - if !file.CheckNotExist(m) { - for i := 0; file.CheckNotExist(volume); i++ { - volume = m + strconv.Itoa(i+1) - } - } - service.MyService.Disk().MountDisk(h.Path, volume) - if volume != m { - ms := model2.SerialDisk{} - ms.UUID = v.UUID - ms.MountPoint = volume - service.MyService.Disk().UpdateMountPoint(ms) - } - - } - //} - } - } - } - service.MyService.Disk().RemoveLSBLKCache() - command.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;AutoRemoveUnuseDir") -} - func InitNetworkMount() { time.Sleep(time.Second * 10) connections := service.MyService.Connections().GetConnectionsList() diff --git a/route/periodical.go b/route/periodical.go index 6fbdd74..7d12ca2 100644 --- a/route/periodical.go +++ b/route/periodical.go @@ -14,20 +14,12 @@ package route import ( - "os" - "os/signal" - "reflect" - "strconv" "strings" - "syscall" "time" "unsafe" "github.com/IceWhaleTech/CasaOS/model" - "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" "github.com/IceWhaleTech/CasaOS/service" - "github.com/pilebones/go-udev/netlink" - "go.uber.org/zap" ) func SendNetINfoBySocket() { @@ -61,104 +53,6 @@ func SendMemBySocket() { service.MyService.Notify().SendMemInfoBySocket(service.MyService.System().GetMemInfo()) } -func SendDiskBySocket() { - list := service.MyService.Disk().LSBLK(true) - - summary := model.Summary{} - healthy := true - findSystem := 0 - - for i := 0; i < len(list); i++ { - if len(list[i].Children) > 0 && findSystem == 0 { - - for j := 0; j < len(list[i].Children); j++ { - - if len(list[i].Children[j].Children) > 0 { - for _, v := range list[i].Children[j].Children { - if v.MountPoint == "/" { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } else { - if list[i].Children[j].MountPoint == "/" { - s, _ := strconv.ParseUint(list[i].Children[j].FSSize, 10, 64) - a, _ := strconv.ParseUint(list[i].Children[j].FSAvail, 10, 64) - u, _ := strconv.ParseUint(list[i].Children[j].FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } - - } - if findSystem == 1 { - findSystem += 1 - continue - } - if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" || strings.Contains(list[i].SubSystems, "virtio") || (list[i].Tran == "ata" && list[i].Type == "disk") { - temp := service.MyService.Disk().SmartCTL(list[i].Path) - if reflect.DeepEqual(temp, model.SmartctlA{}) { - healthy = true - } else { - healthy = temp.SmartStatus.Passed - } - - //list[i].Temperature = temp.Temperature.Current - - if len(list[i].Children) > 0 { - for _, v := range list[i].Children { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - } - } - - } - } - - summary.Health = healthy - service.MyService.Notify().SendDiskInfoBySocket(summary) -} - -func SendUSBBySocket() { - usbList := service.MyService.Disk().LSBLK(false) - usb := []model.DriveUSB{} - for _, v := range usbList { - if v.Tran == "usb" { - isMount := false - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - isMount = true - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - - } - } - if isMount { - usb = append(usb, temp) - } - } - } - service.MyService.Notify().SendUSBInfoBySocket(usb) -} - func SendAllHardwareStatusBySocket() { netList := service.MyService.System().GetNetInfo() @@ -183,131 +77,44 @@ func SendAllHardwareStatusBySocket() { cpuData["temperature"] = service.MyService.System().GetCPUTemperature() cpuData["power"] = service.MyService.System().GetCPUPower() - list := service.MyService.Disk().LSBLK(true) - - summary := model.Summary{} - healthy := true - findSystem := 0 - - for i := 0; i < len(list); i++ { - if len(list[i].Children) > 0 && findSystem == 0 { - - for j := 0; j < len(list[i].Children); j++ { - - if len(list[i].Children[j].Children) > 0 { - for _, v := range list[i].Children[j].Children { - if v.MountPoint == "/" { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } else { - if list[i].Children[j].MountPoint == "/" { - s, _ := strconv.ParseUint(list[i].Children[j].FSSize, 10, 64) - a, _ := strconv.ParseUint(list[i].Children[j].FSAvail, 10, 64) - u, _ := strconv.ParseUint(list[i].Children[j].FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } - - } - if findSystem == 1 { - findSystem += 1 - continue - } - if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" || strings.Contains(list[i].SubSystems, "virtio") || (list[i].Tran == "ata" && list[i].Type == "disk") { - temp := service.MyService.Disk().SmartCTL(list[i].Path) - if reflect.DeepEqual(temp, model.SmartctlA{}) { - healthy = true - } else { - healthy = temp.SmartStatus.Passed - } - if len(list[i].Children) > 0 { - for _, v := range list[i].Children { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - summary.Size += s - summary.Avail += a - summary.Used += u - } - } - - } - } - - summary.Health = healthy - - usbList := service.MyService.Disk().LSBLK(false) - usb := []model.DriveUSB{} - for _, v := range usbList { - if v.Tran == "usb" { - isMount := false - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - isMount = true - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - } - } - if isMount { - usb = append(usb, temp) - } - - } - } memInfo := service.MyService.System().GetMemInfo() - service.MyService.Notify().SendAllHardwareStatusBySocket(summary, usb, memInfo, cpuData, newNet) + service.MyService.Notify().SendAllHardwareStatusBySocket(memInfo, cpuData, newNet) } -func MonitoryUSB() { - var matcher netlink.Matcher - conn := new(netlink.UEventConn) - if err := conn.Connect(netlink.UdevEvent); err != nil { - loger.Error("udev err", zap.Any("Unable to connect to Netlink Kobject UEvent socket", err)) - } - defer conn.Close() +// func MonitoryUSB() { +// var matcher netlink.Matcher - queue := make(chan netlink.UEvent) - errors := make(chan error) - quit := conn.Monitor(queue, errors, matcher) +// conn := new(netlink.UEventConn) +// if err := conn.Connect(netlink.UdevEvent); err != nil { +// loger.Error("udev err", zap.Any("Unable to connect to Netlink Kobject UEvent socket", err)) +// } +// defer conn.Close() - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - go func() { - <-signals - close(quit) - os.Exit(0) - }() +// queue := make(chan netlink.UEvent) +// errors := make(chan error) +// quit := conn.Monitor(queue, errors, matcher) - for { - select { - case uevent := <-queue: - if uevent.Env["DEVTYPE"] == "disk" { - time.Sleep(time.Microsecond * 500) - SendUSBBySocket() - continue - } - case err := <-errors: - loger.Error("udev err", zap.Any("err", err)) - } - } +// signals := make(chan os.Signal, 1) +// signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) +// go func() { +// <-signals +// close(quit) +// os.Exit(0) +// }() -} +// for { +// select { +// case uevent := <-queue: +// if uevent.Env["DEVTYPE"] == "disk" { +// time.Sleep(time.Microsecond * 500) +// SendUSBBySocket() +// continue +// } +// case err := <-errors: +// loger.Error("udev err", zap.Any("err", err)) +// } +// } + +// } diff --git a/route/route.go b/route/route.go index 2ce2dc3..19f2b3b 100644 --- a/route/route.go +++ b/route/route.go @@ -1,8 +1,10 @@ package route import ( - jwt2 "github.com/IceWhaleTech/CasaOS-Common/utils/jwt" - "github.com/IceWhaleTech/CasaOS/middleware" + "os" + + "github.com/IceWhaleTech/CasaOS-Common/middleware" + "github.com/IceWhaleTech/CasaOS-Common/utils/jwt" "github.com/IceWhaleTech/CasaOS/pkg/config" v1 "github.com/IceWhaleTech/CasaOS/route/v1" @@ -11,13 +13,22 @@ import ( ) func InitRouter() *gin.Engine { + ginMode := gin.ReleaseMode + if config.ServerInfo.RunMode != "" { + ginMode = config.ServerInfo.RunMode + } + if os.Getenv(gin.EnvGinMode) != "" { + ginMode = os.Getenv(gin.EnvGinMode) + } + gin.SetMode(ginMode) - r := gin.Default() - + r := gin.New() + r.Use(gin.Recovery()) r.Use(middleware.Cors()) - r.Use(middleware.WriteLog()) r.Use(gzip.Gzip(gzip.DefaultCompression)) - gin.SetMode(config.ServerInfo.RunMode) + if ginMode != gin.ReleaseMode { + r.Use(middleware.WriteLog()) + } // r.StaticFS("/ui", http.FS(web.Static)) // r.GET("/", WebUIHome) @@ -35,15 +46,17 @@ func InitRouter() *gin.Engine { // r.GET("/v1/users/image", v1.GetUserImage) // r.GET("/v1/users/status", v1.GetUserStatus) //init/check - //r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check + // r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug r.GET("/v1/sys/socket-port", v1.GetSystemSocketPort) //sys/socket_port r.GET("/v1/sys/version/check", v1.GetSystemCheckVersion) - + r.GET("/ping", func(ctx *gin.Context) { + ctx.String(200, "pong") + }) v1Group := r.Group("/v1") - v1Group.Use(jwt2.JWT()) + v1Group.Use(jwt.ExceptLocalhost()) { // v1UsersGroup := v1Group.Group("/users") // v1UsersGroup.Use() @@ -71,32 +84,35 @@ func InitRouter() *gin.Engine { v1AppsGroup := v1Group.Group("/apps") v1AppsGroup.Use() { - v1AppsGroup.GET("", v1.AppList) //list + v1AppsGroup.GET("", v1.AppList) // list v1AppsGroup.GET("/:id", v1.AppInfo) } v1ContainerGroup := v1Group.Group("/container") v1ContainerGroup.Use() { + v1ContainerGroup.GET("", v1.MyAppList) ///my/list v1ContainerGroup.GET("/usage", v1.AppUsageList) v1ContainerGroup.GET("/:id", v1.ContainerUpdateInfo) ///update/:id/info v1ContainerGroup.GET("/:id/logs", v1.ContainerLog) // /app/logs/:id - v1ContainerGroup.GET("/networks", v1.GetDockerNetworks) //app/install/config + v1ContainerGroup.GET("/networks", v1.GetDockerNetworks) // app/install/config - v1ContainerGroup.GET("/:id/state", v1.GetContainerState) //app/state/:id ?state=install_progress + v1ContainerGroup.GET("/:id/state", v1.GetContainerState) // app/state/:id ?state=install_progress // there are problems, temporarily do not deal with - v1ContainerGroup.GET("/:id/terminal", v1.DockerTerminal) //app/terminal/:id - v1ContainerGroup.POST("", v1.InstallApp) //app/install - //v1ContainerGroup.GET("/:id", v1.ContainerInfo) // /app/info/:id + v1ContainerGroup.GET("/:id/terminal", v1.DockerTerminal) // app/terminal/:id + v1ContainerGroup.POST("", v1.InstallApp) // app/install + // v1ContainerGroup.GET("/:id", v1.ContainerInfo) // /app/info/:id v1ContainerGroup.PUT("/:id", v1.UpdateSetting) ///update/:id/setting v1ContainerGroup.PUT("/:id/state", v1.ChangAppState) // /app/state/:id - v1ContainerGroup.DELETE("/:id", v1.UnInstallApp) //app/uninstall/:id - //Not used + v1ContainerGroup.DELETE("/:id", v1.UnInstallApp) // app/uninstall/:id + // Not used v1ContainerGroup.PUT("/:id/latest", v1.PutAppUpdate) - //Not used + // Not used v1ContainerGroup.POST("/share", v1.ShareAppFile) + v1ContainerGroup.GET("/info", v1.GetDockerDaemonConfiguration) + v1ContainerGroup.PUT("/info", v1.PutDockerDaemonConfiguration) } v1AppCategoriesGroup := v1Group.Group("/app-categories") @@ -108,19 +124,19 @@ func InitRouter() *gin.Engine { v1SysGroup := v1Group.Group("/sys") v1SysGroup.Use() { - v1SysGroup.GET("/version", v1.GetSystemCheckVersion) //version/check + v1SysGroup.GET("/version", v1.GetSystemCheckVersion) // version/check v1SysGroup.POST("/update", v1.SystemUpdate) - v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) //hardware/info + v1SysGroup.GET("/hardware", v1.GetSystemHardwareInfo) // hardware/info v1SysGroup.GET("/wsssh", v1.WsSsh) v1SysGroup.POST("/ssh-login", v1.PostSshLogin) - //v1SysGroup.GET("/config", v1.GetSystemConfig) //delete - //v1SysGroup.POST("/config", v1.PostSetSystemConfig) - v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) //error/logs - //v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete - //v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete + // v1SysGroup.GET("/config", v1.GetSystemConfig) //delete + // v1SysGroup.POST("/config", v1.PostSetSystemConfig) + v1SysGroup.GET("/logs", v1.GetCasaOSErrorLogs) // error/logs + // v1SysGroup.GET("/widget/config", v1.GetWidgetConfig)//delete + // v1SysGroup.POST("/widget/config", v1.PostSetWidgetConfig)//delete v1SysGroup.POST("/stop", v1.PostKillCasaOS) @@ -130,37 +146,34 @@ func InitRouter() *gin.Engine { // v1SysGroup.GET("/disk", v1.GetSystemDiskInfo) // v1SysGroup.GET("/network", v1.GetSystemNetInfo) - v1SysGroup.PUT("/usb-auto-mount", v1.PutSystemUSBAutoMount) ///sys/usb/:status - v1SysGroup.GET("/usb-auto-mount", v1.GetSystemUSBAutoMount) ///sys/usb/status - v1SysGroup.GET("/server-info", nil) v1SysGroup.PUT("/server-info", nil) v1SysGroup.GET("/apps-state", v1.GetSystemAppsStatus) - //v1SysGroup.GET("/port", v1.GetCasaOSPort) - //v1SysGroup.PUT("/port", v1.PutCasaOSPort) + // v1SysGroup.GET("/port", v1.GetCasaOSPort) + // v1SysGroup.PUT("/port", v1.PutCasaOSPort) v1SysGroup.GET("/proxy", v1.GetSystemProxy) } v1PortGroup := v1Group.Group("/port") v1PortGroup.Use() { - v1PortGroup.GET("/", v1.GetPort) //app/port - v1PortGroup.GET("/state/:port", v1.PortCheck) //app/check/:port + v1PortGroup.GET("/", v1.GetPort) // app/port + v1PortGroup.GET("/state/:port", v1.PortCheck) // app/check/:port } v1FileGroup := v1Group.Group("/file") v1FileGroup.Use() { - v1FileGroup.GET("", v1.GetDownloadSingleFile) //download/:path + v1FileGroup.GET("", v1.GetDownloadSingleFile) // download/:path v1FileGroup.POST("", v1.PostCreateFile) v1FileGroup.PUT("", v1.PutFileContent) v1FileGroup.PUT("/name", v1.RenamePath) - //file/rename - v1FileGroup.GET("/content", v1.GetFilerContent) //file/read + // file/rename + v1FileGroup.GET("/content", v1.GetFilerContent) // file/read - //File uploads need to be handled separately, and will not be modified here + // File uploads need to be handled separately, and will not be modified here v1FileGroup.POST("/upload", v1.PostFileUpload) v1FileGroup.GET("/upload", v1.GetFileUpload) - //v1FileGroup.GET("/download", v1.UserFileDownloadCommonService) + // v1FileGroup.GET("/download", v1.UserFileDownloadCommonService) } v1FolderGroup := v1Group.Group("/folder") v1FolderGroup.Use() @@ -173,9 +186,9 @@ func InitRouter() *gin.Engine { v1BatchGroup.Use() { - v1BatchGroup.DELETE("", v1.DeleteFile) //file/delete + v1BatchGroup.DELETE("", v1.DeleteFile) // file/delete v1BatchGroup.DELETE("/:id/task", v1.DeleteOperateFileOrDir) - v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) //file/operate + v1BatchGroup.POST("/task", v1.PostOperateFileOrDir) // file/operate v1BatchGroup.GET("", v1.GetDownloadFile) } v1ImageGroup := v1Group.Group("/image") @@ -183,42 +196,6 @@ func InitRouter() *gin.Engine { { v1ImageGroup.GET("", v1.GetFileImage) } - - v1DisksGroup := v1Group.Group("/disks") - v1DisksGroup.Use() - { - //v1DiskGroup.GET("/check", v1.GetDiskCheck) //delete - //v1DisksGroup.GET("", v1.GetDiskInfo) - - //v1DisksGroup.POST("", v1.PostMountDisk) - v1DisksGroup.GET("", v1.GetDiskList) - v1DisksGroup.GET("/usb", v1.GetDisksUSBList) - v1DisksGroup.DELETE("/usb", v1.DeleteDiskUSB) - v1DisksGroup.DELETE("", v1.DeleteDisksUmount) - // //format storage - // v1DiskGroup.POST("/format", v1.PostDiskFormat) - - // //mount SATA disk - // v1DiskGroup.POST("/mount", v1.PostMountDisk) - - // //umount sata disk - // v1DiskGroup.POST("/umount", v1.PostDiskUmount) - - //v1DiskGroup.GET("/type", v1.FormatDiskType)//delete - - v1DisksGroup.DELETE("/part", v1.RemovePartition) //disk/delpart - } - - v1StorageGroup := v1Group.Group("/storage") - v1StorageGroup.Use() - { - v1StorageGroup.POST("", v1.PostDiskAddPartition) - - v1StorageGroup.PUT("", v1.PostDiskFormat) - - v1StorageGroup.DELETE("", v1.PostDiskUmount) - v1StorageGroup.GET("", v1.GetStorageList) - } v1SambaGroup := v1Group.Group("/samba") v1SambaGroup.Use() { @@ -238,6 +215,13 @@ func InitRouter() *gin.Engine { v1SharesGroup.GET("/status", v1.GetSambaStatus) } } + v1NotifyGroup := v1Group.Group("/notify") + v1NotifyGroup.Use() + { + v1NotifyGroup.POST("/:path", v1.PostNotifyMessage) + // merge to system + v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify) + } } return r } diff --git a/route/v1/app.go b/route/v1/app.go index 1a9f278..8e39347 100644 --- a/route/v1/app.go +++ b/route/v1/app.go @@ -3,9 +3,12 @@ package v1 import ( "encoding/json" "io/ioutil" + "net/http" + "path/filepath" "strconv" "github.com/IceWhaleTech/CasaOS/model" + "github.com/IceWhaleTech/CasaOS/pkg/utils/command" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/file" @@ -14,6 +17,11 @@ import ( "github.com/gin-gonic/gin" ) +const ( + dockerRootDirFilePath = "/var/lib/casaos/docker_root" + dockerDaemonConfigurationFilePath = "/etc/docker/daemon.json" +) + // @Summary 获取远程列表 // @Produce application/json // @Accept application/json @@ -27,8 +35,7 @@ import ( // @Success 200 {string} string "ok" // @Router /app/list [get] func AppList(c *gin.Context) { - - //service.MyService.Docker().DockerContainerCommit("test2") + // service.MyService.Docker().DockerContainerCommit("test2") index := c.DefaultQuery("index", "1") size := c.DefaultQuery("size", "10000") @@ -137,7 +144,7 @@ func MyAppList(c *gin.Context) { func AppUsageList(c *gin.Context) { list := service.MyService.App().GetHardwareUsage() c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list}) - //c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: nil}) + // c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: nil}) } // @Summary 应用详情 @@ -149,7 +156,6 @@ func AppUsageList(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /app/appinfo/{id} [get] func AppInfo(c *gin.Context) { - id := c.Param("id") language := c.GetHeader("Language") info, err := service.MyService.Casa().GetServerAppInfo(id, "", language) @@ -211,7 +217,7 @@ func AppInfo(c *gin.Context) { // return c1.Type < c2.Type // } - //sort + // sort // if info.NetworkModel != "host" { // sort.PortsSort(portOrder).Sort(info.Configures.TcpPorts) // sort.PortsSort(portOrder).Sort(info.Configures.UdpPorts) @@ -262,3 +268,88 @@ func ShareAppFile(c *gin.Context) { content := service.MyService.Casa().ShareAppFile(str) c.JSON(common_err.SUCCESS, json.RawMessage(content)) } + +func GetDockerDaemonConfiguration(c *gin.Context) { + // info, err := service.MyService.Docker().GetDockerInfo() + // if err != nil { + // c.JSON(common_err.SERVICE_ERROR, &model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) + // return + // } + data := make(map[string]interface{}) + + if file.Exists(dockerRootDirFilePath) { + buf := file.ReadFullFile(dockerRootDirFilePath) + err := json.Unmarshal(buf, &data) + if err != nil { + c.JSON(common_err.CLIENT_ERROR, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err}) + return + } + } + c.JSON(common_err.SUCCESS, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) +} + +func PutDockerDaemonConfiguration(c *gin.Context) { + request := make(map[string]interface{}) + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: err}) + return + } + + value, ok := request["docker_root_dir"] + if !ok { + c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "`docker_root_dir` should not empty"}) + return + } + + dockerConfig := model.DockerDaemonConfigurationModel{} + if file.Exists(dockerDaemonConfigurationFilePath) { + byteResult := file.ReadFullFile(dockerDaemonConfigurationFilePath) + err := json.Unmarshal(byteResult, &dockerConfig) + if err != nil { + c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to deserialize " + dockerDaemonConfigurationFilePath, Data: err}) + return + } + } + + dockerRootDir := value.(string) + if dockerRootDir == "/" { + dockerConfig.Root = "" // omitempty - empty string will not be serialized + } else { + if !file.Exists(dockerRootDir) { + c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS), Data: common_err.GetMsg(common_err.DIR_NOT_EXISTS)}) + return + } + + dockerConfig.Root = filepath.Join(dockerRootDir, "docker") + + if err := file.IsNotExistMkDir(dockerConfig.Root); err != nil { + c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to create " + dockerConfig.Root, Data: err}) + return + } + } + + if buf, err := json.Marshal(request); err != nil { + c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: "error when trying to serialize docker root json", Data: err}) + return + } else { + if err := file.WriteToFullPath(buf, dockerRootDirFilePath, 0o644); err != nil { + c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to write " + dockerRootDirFilePath, Data: err}) + return + } + } + + if buf, err := json.Marshal(dockerConfig); err != nil { + c.JSON(http.StatusBadRequest, &model.Result{Success: common_err.CLIENT_ERROR, Message: "error when trying to serialize docker config", Data: dockerConfig}) + return + } else { + if err := file.WriteToFullPath(buf, dockerDaemonConfigurationFilePath, 0o644); err != nil { + c.JSON(http.StatusInternalServerError, &model.Result{Success: common_err.SERVICE_ERROR, Message: "error when trying to write to " + dockerDaemonConfigurationFilePath, Data: err}) + return + } + } + + println(command.ExecResultStr("systemctl daemon-reload")) + println(command.ExecResultStr("systemctl restart docker")) + + c.JSON(http.StatusOK, &model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: request}) +} diff --git a/route/v1/disk.go b/route/v1/disk.go deleted file mode 100644 index e72e3ff..0000000 --- a/route/v1/disk.go +++ /dev/null @@ -1,622 +0,0 @@ -package v1 - -import ( - "fmt" - "net/http" - "path/filepath" - "reflect" - "strconv" - "strings" - "time" - - "github.com/IceWhaleTech/CasaOS/model" - "github.com/IceWhaleTech/CasaOS/model/notify" - "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" - "github.com/IceWhaleTech/CasaOS/pkg/utils/encryption" - "github.com/IceWhaleTech/CasaOS/pkg/utils/file" - - "github.com/IceWhaleTech/CasaOS-Common/utils/jwt" - "github.com/IceWhaleTech/CasaOS/service" - model2 "github.com/IceWhaleTech/CasaOS/service/model" - "github.com/gin-gonic/gin" - "github.com/shirou/gopsutil/v3/disk" -) - -var diskMap = make(map[string]string) - -// @Summary disk list -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /disk/list [get] -func GetDiskList(c *gin.Context) { - path := c.Query("path") - if len(path) > 0 { - m := service.MyService.Disk().GetDiskInfo(path) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: m}) - return - } - t := c.DefaultQuery("type", "") - list := service.MyService.Disk().LSBLK(false) - if t == "usb" { - data := []model.DriveUSB{} - for _, v := range list { - if v.Tran == "usb" { - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - } - } - data = append(data, temp) - } - } - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) - return - } - - dbList := service.MyService.Disk().GetSerialAll() - part := make(map[string]int64, len(dbList)) - for _, v := range dbList { - part[v.MountPoint] = v.CreatedAt - } - findSystem := 0 - - disks := []model.Drive{} - storage := []model.Storage{} - avail := []model.Drive{} - for i := 0; i < len(list); i++ { - disk := model.Drive{} - if list[i].Rota { - disk.DiskType = "HDD" - } else { - disk.DiskType = "SSD" - } - disk.Serial = list[i].Serial - disk.Name = list[i].Name - disk.Size = list[i].Size - disk.Path = list[i].Path - disk.Model = list[i].Model - disk.ChildrenNumber = len(list[i].Children) - if len(list[i].Children) > 0 && findSystem == 0 { - for j := 0; j < len(list[i].Children); j++ { - if len(list[i].Children[j].Children) > 0 { - for _, v := range list[i].Children[j].Children { - if v.MountPoint == "/" { - stor := model.Storage{} - stor.MountPoint = v.MountPoint - stor.Size = v.FSSize - stor.Avail = v.FSAvail - stor.Path = v.Path - stor.Type = v.FsType - stor.DriveName = "System" - disk.Model = "System" - if strings.Contains(v.SubSystems, "mmc") { - disk.DiskType = "MMC" - } else if strings.Contains(v.SubSystems, "usb") { - disk.DiskType = "USB" - } - disk.Health = "true" - - disks = append(disks, disk) - storage = append(storage, stor) - findSystem = 1 - break - } - } - } else { - if list[i].Children[j].MountPoint == "/" { - stor := model.Storage{} - stor.MountPoint = list[i].Children[j].MountPoint - stor.Size = list[i].Children[j].FSSize - stor.Avail = list[i].Children[j].FSAvail - stor.Path = list[i].Children[j].Path - stor.Type = list[i].Children[j].FsType - stor.DriveName = "System" - disk.Model = "System" - if strings.Contains(list[i].Children[j].SubSystems, "mmc") { - disk.DiskType = "MMC" - } else if strings.Contains(list[i].Children[j].SubSystems, "usb") { - disk.DiskType = "USB" - } - disk.Health = "true" - - disks = append(disks, disk) - storage = append(storage, stor) - findSystem = 1 - break - } - } - - } - } - if findSystem == 1 { - findSystem += 1 - continue - } - - if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" || strings.Contains(list[i].SubSystems, "virtio") || (list[i].Tran == "ata" && list[i].Type == "disk") { - temp := service.MyService.Disk().SmartCTL(list[i].Path) - if reflect.DeepEqual(temp, model.SmartctlA{}) { - temp.SmartStatus.Passed = true - } - isAvail := true - for _, v := range list[i].Children { - if v.MountPoint != "" { - stor := model.Storage{} - stor.MountPoint = v.MountPoint - stor.Size = v.FSSize - stor.Avail = v.FSAvail - stor.Path = v.Path - stor.Type = v.FsType - stor.DriveName = list[i].Name - storage = append(storage, stor) - isAvail = false - } - } - - if isAvail { - //if len(list[i].Children) == 1 && list[i].Children[0].FsType == "ext4" { - disk.NeedFormat = false - avail = append(avail, disk) - // } else { - // disk.NeedFormat = true - // avail = append(avail, disk) - // } - } - - disk.Temperature = temp.Temperature.Current - disk.Health = strconv.FormatBool(temp.SmartStatus.Passed) - - disks = append(disks, disk) - } - } - data := make(map[string]interface{}, 3) - data["drive"] = disks - data["storage"] = storage - data["avail"] = avail - - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) -} - -// @Summary disk list -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /disk/list [get] -func GetDisksUSBList(c *gin.Context) { - list := service.MyService.Disk().LSBLK(false) - data := []model.DriveUSB{} - for _, v := range list { - if v.Tran == "usb" { - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Label - if temp.Name == "" { - temp.Name = v.Name - } - temp.Size = v.Size - children := []model.USBChildren{} - for _, child := range v.Children { - - if len(child.MountPoint) > 0 { - tempChildren := model.USBChildren{} - tempChildren.MountPoint = child.MountPoint - tempChildren.Size, _ = strconv.ParseUint(child.FSSize, 10, 64) - tempChildren.Avail, _ = strconv.ParseUint(child.FSAvail, 10, 64) - tempChildren.Name = child.Label - if len(tempChildren.Name) == 0 { - tempChildren.Name = filepath.Base(child.MountPoint) - } - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - children = append(children, tempChildren) - temp.Avail += avail - } - } - - temp.Children = children - data = append(data, temp) - } - } - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) - -} - -func DeleteDisksUmount(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - - path := js["path"] - pwd := js["password"] - - if len(path) == 0 { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - return - } - token := c.GetHeader("Authorization") - if len(token) == 0 { - token = c.Query("token") - } - claims, err := jwt.ParseToken(token, true) - if err != nil { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if encryption.GetMD5ByStr(pwd) != claims.Password { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if _, ok := diskMap[path]; ok { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_BUSYING, Message: common_err.GetMsg(common_err.DISK_BUSYING)}) - return - } - - diskInfo := service.MyService.Disk().GetDiskInfo(path) - for _, v := range diskInfo.Children { - service.MyService.Disk().UmountPointAndRemoveDir(v.Path) - //delete data - service.MyService.Disk().DeleteMountPoint(v.Path, v.MountPoint) - - service.MyService.Shares().DeleteShareByPath(v.MountPoint) - } - - service.MyService.Disk().RemoveLSBLKCache() - - //send notify to client - msg := notify.StorageMessage{} - msg.Action = "REMOVED" - msg.Path = path - msg.Volume = "" - msg.Size = 0 - msg.Type = "" - service.MyService.Notify().SendStorageBySocket(msg) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: path}) -} - -func DeleteDiskUSB(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - mountPoint := js["mount_point"] - if file.CheckNotExist(mountPoint) { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.DIR_NOT_EXISTS, Message: common_err.GetMsg(common_err.DIR_NOT_EXISTS)}) - return - } - service.MyService.Disk().UmountUSB(mountPoint) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: mountPoint}) -} - -// @Summary get disk list -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /disk/lists [get] -func GetPlugInDisks(c *gin.Context) { - - list := service.MyService.Disk().LSBLK(true) - var result []*disk.UsageStat - for _, item := range list { - result = append(result, service.MyService.Disk().GetDiskInfoByPath(item.Path)) - } - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: result}) -} - -// @Summary disk detail -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Param path query string true "for example /dev/sda" -// @Success 200 {string} string "ok" -// @Router /disk/info [get] -func GetDiskInfo(c *gin.Context) { - path := c.Query("path") - if len(path) == 0 { - c.JSON(http.StatusOK, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - } - m := service.MyService.Disk().GetDiskInfo(path) - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: m}) -} - -// @Summary 获取支持的格式 -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /disk/type [get] -func FormatDiskType(c *gin.Context) { - var strArr = [4]string{"fat32", "ntfs", "ext4", "exfat"} - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: strArr}) - -} - -// @Summary 删除分区 -// @Produce application/json -// @Accept multipart/form-data -// @Tags disk -// @Security ApiKeyAuth -// @Param path formData string true "磁盘路径 例如/dev/sda1" -// @Success 200 {string} string "ok" -// @Router /disk/delpart [delete] -func RemovePartition(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - path := js["path"] - - if len(path) == 0 { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - } - var p = path[:len(path)-1] - var n = path[len(path)-1:] - service.MyService.Disk().DelPartition(p, n) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} - -// @Summary add storage -// @Produce application/json -// @Accept multipart/form-data -// @Tags disk -// @Security ApiKeyAuth -// @Param path formData string true "disk path e.g. /dev/sda" -// @Param serial formData string true "serial" -// @Param name formData string true "name" -// @Param format formData bool true "need format(true)" -// @Success 200 {string} string "ok" -// @Router /disk/storage [post] -func PostDiskAddPartition(c *gin.Context) { - - js := make(map[string]interface{}) - c.ShouldBind(&js) - path := js["path"].(string) - name := js["name"].(string) - format := js["format"].(bool) - - if len(path) == 0 { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - return - } - if _, ok := diskMap[path]; ok { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_BUSYING, Message: common_err.GetMsg(common_err.DISK_BUSYING)}) - return - } - - //diskInfo := service.MyService.Disk().GetDiskInfo(path) - - // if !file.CheckNotExist("/DATA/" + name) { - // // /mnt/name exist - // c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.NAME_NOT_AVAILABLE, Message: common_err.GetMsg(common_err.NAME_NOT_AVAILABLE)}) - // return - // } - diskMap[path] = "busying" - currentDisk := service.MyService.Disk().GetDiskInfo(path) - if format { - // format := service.MyService.Disk().FormatDisk(path+"1", "ext4") - // if len(format) == 0 { - // delete(diskMap, path) - // c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FORMAT_ERROR, Message: common_err.GetMsg(common_err.FORMAT_ERROR)}) - // return - // } - service.MyService.Disk().AddPartition(path) - } - - // formatBool := true - // for formatBool { - // currentDisk = service.MyService.Disk().GetDiskInfo(path) - // if len(currentDisk.Children) > 0 { - // formatBool = false - // break - // } - // time.Sleep(time.Second) - // } - currentDisk = service.MyService.Disk().GetDiskInfo(path) - // if len(currentDisk.Children) != 1 { - // c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_NEEDS_FORMAT, Message: common_err.GetMsg(common_err.DISK_NEEDS_FORMAT)}) - // return - // } - fmt.Println(name) - if len(name) == 0 { - name = "Storage" - } - fmt.Println(name) - for i := 0; i < len(currentDisk.Children); i++ { - childrenName := currentDisk.Children[i].Label - if len(childrenName) == 0 { - //childrenName = name + "_" + currentDisk.Children[i].Name - childrenName = name + "_" + strconv.Itoa(i+1) - } - mountPath := "/DATA/" + childrenName - if !file.CheckNotExist(mountPath) { - ls := service.MyService.System().GetDirPath(mountPath) - if len(ls) > 0 { - // exist - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.NAME_NOT_AVAILABLE, Message: common_err.GetMsg(common_err.NAME_NOT_AVAILABLE)}) - return - } - } - m := model2.SerialDisk{} - m.MountPoint = mountPath - m.Path = currentDisk.Children[i].Path - m.UUID = currentDisk.Children[i].UUID - m.State = 0 - m.CreatedAt = time.Now().Unix() - service.MyService.Disk().SaveMountPoint(m) - //mount dir - service.MyService.Disk().MountDisk(currentDisk.Children[i].Path, mountPath) - } - - service.MyService.Disk().RemoveLSBLKCache() - - delete(diskMap, path) - - //send notify to client - msg := notify.StorageMessage{} - msg.Action = "ADDED" - msg.Path = currentDisk.Children[0].Path - msg.Volume = "/DATA/" - msg.Size = currentDisk.Children[0].Size - msg.Type = currentDisk.Children[0].Tran - service.MyService.Notify().SendStorageBySocket(msg) - - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} - -// @Param pwd formData string true "user password" -// @Param volume formData string true "mount point" -// @Success 200 {string} string "ok" -// @Router /disk/format [post] -func PostDiskFormat(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - path := js["path"] - t := "ext4" - pwd := js["password"] - volume := js["volume"] - token := c.GetHeader("Authorization") - if len(token) == 0 { - token = c.Query("token") - } - claims, err := jwt.ParseToken(token, true) - if err != nil { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if encryption.GetMD5ByStr(pwd) != claims.Password { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if len(path) == 0 || len(t) == 0 { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - return - } - if _, ok := diskMap[path]; ok { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_BUSYING, Message: common_err.GetMsg(common_err.DISK_BUSYING)}) - return - } - diskMap[path] = "busying" - service.MyService.Disk().UmountPointAndRemoveDir(path) - format := service.MyService.Disk().FormatDisk(path, t) - if len(format) == 0 { - delete(diskMap, path) - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FORMAT_ERROR, Message: common_err.GetMsg(common_err.FORMAT_ERROR)}) - return - } - service.MyService.Disk().MountDisk(path, volume) - service.MyService.Disk().RemoveLSBLKCache() - delete(diskMap, path) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} - -// @Summary remove mount point -// @Produce application/json -// @Accept multipart/form-data -// @Tags disk -// @Security ApiKeyAuth -// @Param path formData string true "e.g. /dev/sda1" -// @Param mount_point formData string true "e.g. /mnt/volume1" -// @Param pwd formData string true "user password" -// @Success 200 {string} string "ok" -// @Router /disk/umount [post] -func PostDiskUmount(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - - path := js["path"] - mountPoint := js["volume"] - pwd := js["password"] - - if len(path) == 0 || len(mountPoint) == 0 { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) - return - } - token := c.GetHeader("Authorization") - if len(token) == 0 { - token = c.Query("token") - } - claims, err := jwt.ParseToken(token, true) - if err != nil { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if encryption.GetMD5ByStr(pwd) != claims.Password { - c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.PWD_INVALID, Message: common_err.GetMsg(common_err.PWD_INVALID)}) - return - } - - if _, ok := diskMap[path]; ok { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.DISK_BUSYING, Message: common_err.GetMsg(common_err.DISK_BUSYING)}) - return - } - - service.MyService.Disk().UmountPointAndRemoveDir(path) - //delete data - service.MyService.Disk().DeleteMountPoint(path, mountPoint) - service.MyService.Disk().RemoveLSBLKCache() - - //send notify to client - msg := notify.StorageMessage{} - msg.Action = "REMOVED" - msg.Path = path - msg.Volume = mountPoint - msg.Size = 0 - msg.Type = "" - service.MyService.Notify().SendStorageBySocket(msg) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} - -// @Summary confirm delete disk -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Param id path string true "id" -// @Success 200 {string} string "ok" -// @Router /disk/remove/{id} [delete] -func DeleteDisk(c *gin.Context) { - id := c.Param("id") - service.MyService.Disk().DeleteMount(id) - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} - -// @Summary check mount point -// @Produce application/json -// @Accept application/json -// @Tags disk -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /disk/init [get] -func GetDiskCheck(c *gin.Context) { - - dbList := service.MyService.Disk().GetSerialAll() - list := service.MyService.Disk().LSBLK(true) - - mapList := make(map[string]string) - - for _, v := range list { - mapList[v.Serial] = "1" - } - - for _, v := range dbList { - if _, ok := mapList[v.UUID]; !ok { - //disk undefind - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: "disk undefind"}) - return - } - } - - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) -} diff --git a/route/v1/docker.go b/route/v1/docker.go index 05ebddf..4b7d4a4 100644 --- a/route/v1/docker.go +++ b/route/v1/docker.go @@ -761,7 +761,7 @@ func ChangAppState(c *gin.Context) { func ContainerLog(c *gin.Context) { appId := c.Param("id") log, _ := service.MyService.Docker().DockerContainerLog(appId) - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: log}) + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: string(log)}) } // @Summary 获取容器状态 @@ -877,7 +877,7 @@ func UpdateSetting(c *gin.Context) { if err != nil { service.MyService.Docker().DockerContainerUpdateName(m.ContainerName, id) service.MyService.Docker().DockerContainerStart(id) - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR)}) + c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) return } // echo -e "hellow\nworld" >> diff --git a/route/v1/file.go b/route/v1/file.go index 0b2dbc7..2b1fd11 100644 --- a/route/v1/file.go +++ b/route/v1/file.go @@ -16,12 +16,13 @@ import ( "sync" "github.com/IceWhaleTech/CasaOS/model" - "github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" "github.com/IceWhaleTech/CasaOS/pkg/utils/file" + "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" "github.com/IceWhaleTech/CasaOS/service" "github.com/gin-gonic/gin" uuid "github.com/satori/go.uuid" + "go.uber.org/zap" ) // @Summary 读取文件 @@ -48,7 +49,7 @@ func GetFilerContent(c *gin.Context) { }) return } - //文件读取任务是将文件内容读取到内存中。 + // 文件读取任务是将文件内容读取到内存中。 info, err := ioutil.ReadFile(filePath) if err != nil { c.JSON(common_err.SERVICE_ERROR, model.Result{ @@ -84,7 +85,6 @@ func GetLocalFile(c *gin.Context) { return } c.File(path) - return } // @Summary download @@ -97,7 +97,6 @@ func GetLocalFile(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /file/download [get] func GetDownloadFile(c *gin.Context) { - t := c.Query("format") files := c.Query("files") @@ -136,11 +135,11 @@ func GetDownloadFile(c *gin.Context) { } if !info.IsDir() { - //打开文件 + // 打开文件 fileTmp, _ := os.Open(filePath) defer fileTmp.Close() - //获取文件的名称 + // 获取文件的名称 fileName := path.Base(filePath) c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName)) c.File(filePath) @@ -180,7 +179,6 @@ func GetDownloadFile(c *gin.Context) { log.Printf("Failed to archive %s: %v", fname, err) } } - } func GetDownloadSingleFile(c *gin.Context) { @@ -203,7 +201,7 @@ func GetDownloadSingleFile(c *gin.Context) { defer fileTmp.Close() fileName := path.Base(filePath) - //c.Header("Content-Disposition", "inline") + // c.Header("Content-Disposition", "inline") c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName)) c.File(filePath) } @@ -236,33 +234,8 @@ func DirPath(c *gin.Context) { info[i].Type = "application" } } - } else if path == "/DATA" { - disk := make(map[string]string) - lsblk := service.MyService.Disk().LSBLK(true) - for _, v := range lsblk { - if len(v.Children) > 0 { - t := v.Tran - for _, c := range v.Children { - if len(c.Children) > 0 { - for _, gc := range c.Children { - if len(gc.MountPoint) > 0 { - disk[gc.MountPoint] = t - } - } - } - if len(c.MountPoint) > 0 { - disk[c.MountPoint] = t - } - } - - } - } - for i := 0; i < len(info); i++ { - if v, ok := disk[info[i].Path]; ok { - info[i].Type = v - } - } } + for i := 0; i < len(info); i++ { if v, ok := sharesMap[info[i].Path]; ok { ex := make(map[string]interface{}) @@ -274,7 +247,7 @@ func DirPath(c *gin.Context) { info[i].Extensions = ex } } - //Hide the files or folders in operation + // Hide the files or folders in operation fileQueue := make(map[string]string) if len(service.OpStrArr) > 0 { for _, v := range service.OpStrArr { @@ -292,6 +265,9 @@ func DirPath(c *gin.Context) { pathList := []model.Path{} for i := 0; i < len(info); i++ { + if info[i].Name == ".temp" && info[i].IsDir { + continue + } if _, ok := fileQueue[info[i].Path]; !ok { pathList = append(pathList, info[i]) } @@ -384,7 +360,6 @@ func PostCreateFile(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /file/upload [get] func GetFileUpload(c *gin.Context) { - relative := c.Query("relativePath") fileName := c.Query("filename") chunkNumber := c.Query("chunkNumber") @@ -392,7 +367,7 @@ func GetFileUpload(c *gin.Context) { path := c.Query("path") dirPath := "" hash := file.GetHashByContent([]byte(fileName)) - tempDir := config.AppInfo.TempPath + "/" + hash + strconv.Itoa(totalChunks) + "/" + tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/" if fileName != relative { dirPath = strings.TrimSuffix(relative, fileName) tempDir += dirPath @@ -428,55 +403,91 @@ func PostFileUpload(c *gin.Context) { hash := file.GetHashByContent([]byte(fileName)) if len(path) == 0 { - c.JSON(common_err.INVALID_PARAMS, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) + loger.Error("path should not be empty") + c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)}) return } - tempDir := config.AppInfo.TempPath + "/" + hash + strconv.Itoa(totalChunks) + "/" + tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/" if fileName != relative { dirPath = strings.TrimSuffix(relative, fileName) tempDir += dirPath - file.MkDir(path + "/" + dirPath) + if err := file.MkDir(path + "/" + dirPath); err != nil { + loger.Error("error when trying to create `"+path+"/"+dirPath+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } } path += "/" + relative if !file.CheckNotExist(tempDir + chunkNumber) { - file.RMDir(tempDir + chunkNumber) + if err := file.RMDir(tempDir + chunkNumber); err != nil { + loger.Error("error when trying to remove existing `"+tempDir+chunkNumber+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } } if totalChunks > 1 { - file.IsNotExistMkDir(tempDir) - - out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644) - defer out.Close() - _, err := io.Copy(out, f) - if err != nil { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) + if err := file.IsNotExistMkDir(tempDir); err != nil { + loger.Error("error when trying to create `"+tempDir+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) return } + + out, err := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0o644) + if err != nil { + loger.Error("error when trying to open `"+tempDir+chunkNumber+"` for creation", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } + + defer out.Close() + + if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy + loger.Error("error when trying to write to `"+tempDir+chunkNumber+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } + + fileNum, err := ioutil.ReadDir(tempDir) + if err != nil { + loger.Error("error when trying to read number of files under `"+tempDir+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } + + if totalChunks == len(fileNum) { + if err := file.SpliceFiles(tempDir, path, totalChunks, 1); err != nil { + loger.Error("error when trying to splice files under `"+tempDir+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } + + if err := file.RMDir(tempDir); err != nil { + loger.Error("error when trying to remove `"+tempDir+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) + return + } + } } else { - out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) - defer out.Close() - _, err := io.Copy(out, f) + out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644) if err != nil { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) + loger.Error("error when trying to open `"+path+"` for creation", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()}) return } - c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) - return - } - fileNum, err := ioutil.ReadDir(tempDir) - if err != nil { - c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) - return - } - if totalChunks == len(fileNum) { - file.SpliceFiles(tempDir, path, totalChunks, 1) - file.RMDir(tempDir) - } - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) + defer out.Close() + + if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy + loger.Error("error when trying to write to `"+path+"`", zap.Error(err)) + c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()}) + return + } + } + c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) } // @Summary copy or move file @@ -488,7 +499,6 @@ func PostFileUpload(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /file/operate [post] func PostOperateFileOrDir(c *gin.Context) { - list := model.FileOperate{} c.ShouldBind(&list) @@ -538,7 +548,6 @@ func PostOperateFileOrDir(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /file/delete [delete] func DeleteFile(c *gin.Context) { - paths := []string{} c.ShouldBind(&paths) if len(paths) == 0 { @@ -570,7 +579,6 @@ func DeleteFile(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /file/update [put] func PutFileContent(c *gin.Context) { - fi := model.FileUpdate{} c.ShouldBind(&fi) @@ -580,7 +588,7 @@ func PutFileContent(c *gin.Context) { c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)}) return } - //err := os.Remove(path) + // err := os.Remove(path) err := os.RemoveAll(fi.FilePath) if err != nil { c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err}) diff --git a/route/v1/notiry.go b/route/v1/notiry.go new file mode 100644 index 0000000..eef2a1b --- /dev/null +++ b/route/v1/notiry.go @@ -0,0 +1,23 @@ +package v1 + +import ( + "github.com/IceWhaleTech/CasaOS/model" + "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" + "github.com/IceWhaleTech/CasaOS/service" + "github.com/gin-gonic/gin" +) + +func PostNotifyMessage(c *gin.Context) { + path := c.Param("path") + message := make(map[string]interface{}) + c.ShouldBind(&message) + service.MyService.Notify().SendNotify(path, message) + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) +} + +func PostSystemStatusNotify(c *gin.Context) { + message := make(map[string]interface{}) + c.ShouldBind(&message) + service.MyService.Notify().SettingSystemTempData(message) + c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) +} diff --git a/route/v1/storage.go b/route/v1/storage.go deleted file mode 100644 index b6bb3e3..0000000 --- a/route/v1/storage.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - * @Author: LinkLeong link@icewhale.com - * @Date: 2022-07-11 16:02:29 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-08-17 19:14:50 - * @FilePath: /CasaOS/route/v1/storage.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ -package v1 - -import ( - "path/filepath" - "reflect" - - "github.com/IceWhaleTech/CasaOS/model" - "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" - "github.com/IceWhaleTech/CasaOS/service" - "github.com/gin-gonic/gin" -) - -func GetStorageList(c *gin.Context) { - system := c.Query("system") - storages := []model.Storages{} - disks := service.MyService.Disk().LSBLK(false) - diskNumber := 1 - children := 1 - findSystem := 0 - for _, d := range disks { - if d.Tran != "usb" { - tempSystemDisk := false - children = 1 - tempDisk := model.Storages{ - DiskName: d.Model, - Path: d.Path, - Size: d.Size, - } - - storageArr := []model.Storage{} - temp := service.MyService.Disk().SmartCTL(d.Path) - if reflect.DeepEqual(temp, model.SmartctlA{}) { - temp.SmartStatus.Passed = true - } - for _, v := range d.Children { - if v.MountPoint != "" { - if findSystem == 0 { - if v.MountPoint == "/" { - tempDisk.DiskName = "System" - findSystem = 1 - tempSystemDisk = true - } - if len(v.Children) > 0 { - for _, c := range v.Children { - if c.MountPoint == "/" { - tempDisk.DiskName = "System" - findSystem = 1 - tempSystemDisk = true - break - } - } - } - } - - stor := model.Storage{} - stor.MountPoint = v.MountPoint - stor.Size = v.FSSize - stor.Avail = v.FSAvail - stor.Path = v.Path - stor.Type = v.FsType - stor.DriveName = v.Name - if len(v.Label) == 0 { - if stor.MountPoint == "/" { - stor.Label = "System" - } else { - stor.Label = filepath.Base(stor.MountPoint) - } - - children += 1 - } else { - stor.Label = v.Label - } - storageArr = append(storageArr, stor) - } - } - - if len(storageArr) > 0 { - if tempSystemDisk && len(system) > 0 { - tempStorageArr := []model.Storage{} - for i := 0; i < len(storageArr); i++ { - if storageArr[i].MountPoint != "/boot/efi" && storageArr[i].Type != "swap" { - tempStorageArr = append(tempStorageArr, storageArr[i]) - } - } - tempDisk.Children = tempStorageArr - storages = append(storages, tempDisk) - diskNumber += 1 - } else if !tempSystemDisk { - tempDisk.Children = storageArr - storages = append(storages, tempDisk) - diskNumber += 1 - } - - } - } - } - - c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: storages}) -} diff --git a/route/v1/system.go b/route/v1/system.go index 9f908d2..9872ee3 100644 --- a/route/v1/system.go +++ b/route/v1/system.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "net/http" "os" - "reflect" "strconv" "strings" "time" @@ -16,14 +15,12 @@ import ( "github.com/IceWhaleTech/CasaOS/model" "github.com/IceWhaleTech/CasaOS/pkg/config" "github.com/IceWhaleTech/CasaOS/pkg/utils/common_err" - "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" port2 "github.com/IceWhaleTech/CasaOS/pkg/utils/port" "github.com/IceWhaleTech/CasaOS/pkg/utils/version" "github.com/IceWhaleTech/CasaOS/service" model2 "github.com/IceWhaleTech/CasaOS/service/model" "github.com/IceWhaleTech/CasaOS/types" "github.com/gin-gonic/gin" - "go.uber.org/zap" ) // @Summary check version @@ -79,7 +76,7 @@ func GetCasaOSErrorLogs(c *gin.Context) { c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: service.MyService.System().GetCasaOSLogs(line)}) } -//系统配置 +// 系统配置 func GetSystemConfigDebug(c *gin.Context) { array := service.MyService.System().GetSystemConfigDebug() disk := service.MyService.System().GetDiskInfo() @@ -167,101 +164,6 @@ func PostKillCasaOS(c *gin.Context) { os.Exit(0) } -// @Summary Turn off usb auto-mount -// @Produce application/json -// @Accept application/json -// @Tags sys -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /sys/usb/off [put] -func PutSystemUSBAutoMount(c *gin.Context) { - js := make(map[string]string) - c.ShouldBind(&js) - status := js["state"] - if status == "on" { - service.MyService.System().UpdateUSBAutoMount("True") - service.MyService.System().ExecUSBAutoMountShell("True") - } else { - service.MyService.System().UpdateUSBAutoMount("False") - service.MyService.System().ExecUSBAutoMountShell("False") - } - go func() { - usbList := service.MyService.Disk().LSBLK(false) - usb := []model.DriveUSB{} - for _, v := range usbList { - if v.Tran == "usb" { - isMount := false - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - isMount = true - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - - } - } - if isMount { - usb = append(usb, temp) - } - } - } - service.MyService.Notify().SendUSBInfoBySocket(usb) - }() - c.JSON(common_err.SUCCESS, - model.Result{ - Success: common_err.SUCCESS, - Message: common_err.GetMsg(common_err.SUCCESS), - }) -} - -// @Summary Turn off usb auto-mount -// @Produce application/json -// @Accept application/json -// @Tags sys -// @Security ApiKeyAuth -// @Success 200 {string} string "ok" -// @Router /sys/usb [get] -func GetSystemUSBAutoMount(c *gin.Context) { - state := "True" - if config.ServerInfo.USBAutoMount == "False" { - state = "False" - } - go func() { - usbList := service.MyService.Disk().LSBLK(false) - usb := []model.DriveUSB{} - for _, v := range usbList { - if v.Tran == "usb" { - isMount := false - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - isMount = true - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - - } - } - if isMount { - usb = append(usb, temp) - } - } - } - service.MyService.Notify().SendUSBInfoBySocket(usb) - }() - c.JSON(common_err.SUCCESS, - model.Result{ - Success: common_err.SUCCESS, - Message: common_err.GetMsg(common_err.SUCCESS), - Data: state, - }) -} - func GetSystemAppsStatus(c *gin.Context) { systemAppList := service.MyService.App().GetSystemAppList() appList := []model2.MyAppList{} @@ -278,12 +180,12 @@ func GetSystemAppsStatus(c *gin.Context) { Id: v.ID, Port: v.Labels["web"], Index: v.Labels["index"], - //Order: m.Labels["order"], + // Order: m.Labels["order"], Image: v.Image, Latest: false, - //Type: m.Labels["origin"], - //Slogan: m.Slogan, - //Rely: m.Rely, + // Type: m.Labels["origin"], + // Slogan: m.Slogan, + // Rely: m.Rely, Host: v.Labels["host"], Protocol: v.Labels["protocol"], }) @@ -304,7 +206,6 @@ func GetSystemAppsStatus(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /sys/hardware/info [get] func GetSystemHardwareInfo(c *gin.Context) { - data := make(map[string]string, 1) data["drive_model"] = service.MyService.System().GetDeviceTree() c.JSON(common_err.SUCCESS, @@ -323,99 +224,7 @@ func GetSystemHardwareInfo(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /sys/utilization [get] func GetSystemUtilization(c *gin.Context) { - var data = make(map[string]interface{}, 6) - - list := service.MyService.Disk().LSBLK(true) - - summary := model.Summary{} - healthy := true - findSystem := 0 - - for i := 0; i < len(list); i++ { - if len(list[i].Children) > 0 && findSystem == 0 { - - for j := 0; j < len(list[i].Children); j++ { - - if len(list[i].Children[j].Children) > 0 { - for _, v := range list[i].Children[j].Children { - if v.MountPoint == "/" { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - loger.Info("disk info", zap.Any("/ total:", s)) - loger.Info("disk path", zap.Any("path", v.Path)) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } else { - if list[i].Children[j].MountPoint == "/" { - s, _ := strconv.ParseUint(list[i].Children[j].FSSize, 10, 64) - a, _ := strconv.ParseUint(list[i].Children[j].FSAvail, 10, 64) - u, _ := strconv.ParseUint(list[i].Children[j].FSUsed, 10, 64) - loger.Info("disk info", zap.Any("/ total:", s)) - loger.Info("disk path", zap.Any("path", list[i].Path)) - summary.Size += s - summary.Avail += a - summary.Used += u - findSystem = 1 - break - } - } - } - - } - if findSystem == 1 { - findSystem += 1 - continue - } - if list[i].Tran == "sata" || list[i].Tran == "nvme" || list[i].Tran == "spi" || list[i].Tran == "sas" || strings.Contains(list[i].SubSystems, "virtio") || (list[i].Tran == "ata" && list[i].Type == "disk") { - temp := service.MyService.Disk().SmartCTL(list[i].Path) - if reflect.DeepEqual(temp, model.SmartctlA{}) { - healthy = true - } else { - healthy = temp.SmartStatus.Passed - } - if len(list[i].Children) > 0 { - for _, v := range list[i].Children { - s, _ := strconv.ParseUint(v.FSSize, 10, 64) - a, _ := strconv.ParseUint(v.FSAvail, 10, 64) - u, _ := strconv.ParseUint(v.FSUsed, 10, 64) - loger.Info("disk info", zap.Any("/ total:", s)) - loger.Info("disk path", zap.Any("path", list[i].Path)) - summary.Size += s - summary.Avail += a - summary.Used += u - } - } - - } - } - - summary.Health = healthy - data["disk"] = summary - usbList := service.MyService.Disk().LSBLK(false) - usb := []model.DriveUSB{} - for _, v := range usbList { - if v.Tran == "usb" { - temp := model.DriveUSB{} - temp.Model = v.Model - temp.Name = v.Name - temp.Size = v.Size - - for _, child := range v.Children { - if len(child.MountPoint) > 0 { - avail, _ := strconv.ParseUint(child.FSAvail, 10, 64) - temp.Avail += avail - } - } - usb = append(usb, temp) - } - } - data["usb"] = usb + data := make(map[string]interface{}) cpu := service.MyService.System().GetCpuPercent() num := service.MyService.System().GetCpuCoreNum() cpuData := make(map[string]interface{}) @@ -427,7 +236,7 @@ func GetSystemUtilization(c *gin.Context) { data["cpu"] = cpuData data["mem"] = service.MyService.System().GetMemInfo() - //拼装网络信息 + // 拼装网络信息 netList := service.MyService.System().GetNetInfo() newNet := []model.IOCountersStat{} nets := service.MyService.System().GetNet(true) @@ -444,7 +253,9 @@ func GetSystemUtilization(c *gin.Context) { } data["net"] = newNet - + for k, v := range service.MyService.Notify().GetSystemTempMap() { + data[k] = v + } c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) } @@ -456,7 +267,6 @@ func GetSystemUtilization(c *gin.Context) { // @Success 200 {string} string "ok" // @Router /sys/socket/port [get] func GetSystemSocketPort(c *gin.Context) { - c.JSON(common_err.SUCCESS, model.Result{ Success: common_err.SUCCESS, @@ -479,7 +289,6 @@ func GetSystemCupInfo(c *gin.Context) { data["percent"] = cpu data["num"] = num c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data}) - } // @Summary get mem info @@ -492,7 +301,6 @@ func GetSystemCupInfo(c *gin.Context) { func GetSystemMemInfo(c *gin.Context) { mem := service.MyService.System().GetMemInfo() c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: mem}) - } // @Summary get disk info diff --git a/service/casa.go b/service/casa.go index f5a71d3..4d80bf4 100644 --- a/service/casa.go +++ b/service/casa.go @@ -130,10 +130,6 @@ func (o *casaService) AsyncGetServerList() (collection model.ServerAppListCollec errr := json2.Unmarshal(results, &collection) if errr != nil { loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results))) - } else { - if collection.Version == o.GetCasaosVersion().Version { - return collection, err - } } head := make(map[string]string) @@ -204,10 +200,6 @@ func (o *casaService) AsyncGetServerCategoryList() ([]model.CategoryList, error) err := json2.Unmarshal(results, &list) if err != nil { loger.Error("marshal error", zap.Any("err", err), zap.Any("content", string(results))) - } else { - if list.Version == o.GetCasaosVersion().Version { - return list.Item, nil - } } item := []model.CategoryList{} head := make(map[string]string) diff --git a/service/disk.go b/service/disk.go deleted file mode 100644 index dfe8219..0000000 --- a/service/disk.go +++ /dev/null @@ -1,285 +0,0 @@ -package service - -import ( - json2 "encoding/json" - "fmt" - "reflect" - "strconv" - "strings" - "time" - - "github.com/IceWhaleTech/CasaOS/model" - "github.com/IceWhaleTech/CasaOS/pkg/config" - command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command" - "github.com/IceWhaleTech/CasaOS/pkg/utils/loger" - model2 "github.com/IceWhaleTech/CasaOS/service/model" - "github.com/shirou/gopsutil/v3/disk" - "github.com/tidwall/gjson" - "go.uber.org/zap" - "gorm.io/gorm" -) - -type DiskService interface { - GetPlugInDisk() []string - LSBLK(isUseCache bool) []model.LSBLKModel - SmartCTL(path string) model.SmartctlA - FormatDisk(path, format string) []string - UmountPointAndRemoveDir(path string) []string - GetDiskInfo(path string) model.LSBLKModel - DelPartition(path, num string) string - AddPartition(path string) string - GetDiskInfoByPath(path string) *disk.UsageStat - MountDisk(path, volume string) - GetSerialAll() []model2.SerialDisk - SaveMountPoint(m model2.SerialDisk) - DeleteMountPoint(path, mountPoint string) - DeleteMount(id string) - UpdateMountPoint(m model2.SerialDisk) - RemoveLSBLKCache() - UmountUSB(path string) -} -type diskService struct { - db *gorm.DB -} - -func (d *diskService) RemoveLSBLKCache() { - key := "system_lsblk" - Cache.Delete(key) -} -func (d *diskService) UmountUSB(path string) { - r := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;UDEVILUmount " + path) - fmt.Println(r) -} -func (d *diskService) SmartCTL(path string) model.SmartctlA { - - key := "system_smart_" + path - if result, ok := Cache.Get(key); ok { - - res, ok := result.(model.SmartctlA) - if ok { - return res - } - } - var m model.SmartctlA - str := command2.ExecSmartCTLByPath(path) - if str == nil { - loger.Error("failed to exec shell ", zap.Any("err", "smartctl exec error")) - Cache.Add(key, m, time.Minute*10) - return m - } - - err := json2.Unmarshal([]byte(str), &m) - if err != nil { - loger.Error("Failed to unmarshal json", zap.Any("err", err)) - } - if !reflect.DeepEqual(m, model.SmartctlA{}) { - Cache.Add(key, m, time.Hour*24) - } - return m -} - -//通过脚本获取外挂磁盘 -func (d *diskService) GetPlugInDisk() []string { - return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetPlugInDisk") -} - -//格式化硬盘 -func (d *diskService) FormatDisk(path, format string) []string { - r := command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;FormatDisk " + path + " " + format) - return r -} - -//移除挂载点,删除目录 -func (d *diskService) UmountPointAndRemoveDir(path string) []string { - r := command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;UMountPorintAndRemoveDir " + path) - return r -} - -//删除分区 -func (d *diskService) DelPartition(path, num string) string { - r := command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;DelPartition " + path + " " + num) - fmt.Println(r) - return "" -} - -//part -func (d *diskService) AddPartition(path string) string { - command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;AddPartition " + path) - return "" -} - -func (d *diskService) AddAllPartition(path string) { - -} - -//获取硬盘详情 -func (d *diskService) GetDiskInfoByPath(path string) *disk.UsageStat { - diskInfo, err := disk.Usage(path + "1") - - if err != nil { - fmt.Println(err) - } - diskInfo.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.UsedPercent), 64) - diskInfo.InodesUsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.1f", diskInfo.InodesUsedPercent), 64) - return diskInfo -} - -//get disk details -func (d *diskService) LSBLK(isUseCache bool) []model.LSBLKModel { - key := "system_lsblk" - var n []model.LSBLKModel - - if result, ok := Cache.Get(key); ok && isUseCache { - - res, ok := result.([]model.LSBLKModel) - if ok { - return res - } - } - - str := command2.ExecLSBLK() - if str == nil { - loger.Error("Failed to exec shell", zap.Any("err", "lsblk exec error")) - return nil - } - var m []model.LSBLKModel - err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &m) - if err != nil { - loger.Error("Failed to unmarshal json", zap.Any("err", err)) - } - - var c []model.LSBLKModel - - var fsused uint64 - - var health = true - for _, i := range m { - if i.Type != "loop" && !i.RO { - fsused = 0 - for _, child := range i.Children { - if child.RM { - child.Health = strings.TrimSpace(command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDiskHealthState " + child.Path)) - if strings.ToLower(strings.TrimSpace(child.State)) != "ok" { - health = false - } - f, _ := strconv.ParseUint(child.FSUsed, 10, 64) - fsused += f - } else { - health = false - } - c = append(c, child) - } - //i.Format = strings.TrimSpace(command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDiskType " + i.Path)) - if health { - i.Health = "OK" - } - i.FSUsed = strconv.FormatUint(fsused, 10) - i.Children = c - if fsused > 0 { - i.UsedPercent, err = strconv.ParseFloat(fmt.Sprintf("%.4f", float64(fsused)/float64(i.Size)), 64) - if err != nil { - loger.Error("Failed to parse float", zap.Any("err", err)) - } - } - n = append(n, i) - health = true - c = []model.LSBLKModel{} - fsused = 0 - } - } - if len(n) > 0 { - Cache.Add(key, n, time.Second*100) - } - return n -} - -func (d *diskService) GetDiskInfo(path string) model.LSBLKModel { - str := command2.ExecLSBLKByPath(path) - if str == nil { - loger.Error("Failed to exec shell", zap.Any("err", "lsblk exec error")) - return model.LSBLKModel{} - } - - var ml []model.LSBLKModel - err := json2.Unmarshal([]byte(gjson.Get(string(str), "blockdevices").String()), &ml) - if err != nil { - loger.Error("Failed to unmarshal json", zap.Any("err", err)) - return model.LSBLKModel{} - } - - m := model.LSBLKModel{} - if len(ml) > 0 { - m = ml[0] - } - return m - // 下面为计算是否可以继续分区的部分,暂时不需要 - chiArr := make(map[string]string) - chiList := command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetPartitionSectors " + m.Path) - if len(chiList) == 0 { - loger.Error("chiList length error", zap.Any("err", "chiList length error")) - } - for i := 0; i < len(chiList); i++ { - tempArr := strings.Split(chiList[i], ",") - chiArr[tempArr[0]] = chiList[i] - } - var maxSector uint64 = 0 - for i := 0; i < len(m.Children); i++ { - tempArr := strings.Split(chiArr[m.Children[i].Path], ",") - m.Children[i].StartSector, _ = strconv.ParseUint(tempArr[1], 10, 64) - m.Children[i].EndSector, _ = strconv.ParseUint(tempArr[2], 10, 64) - if m.Children[i].EndSector > maxSector { - maxSector = m.Children[i].EndSector - } - - } - diskEndSector := command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDiskSizeAndSectors " + m.Path) - - if len(diskEndSector) < 2 { - loger.Error("diskEndSector length error", zap.Any("err", "diskEndSector length error")) - } - diskEndSectorInt, _ := strconv.ParseUint(diskEndSector[len(diskEndSector)-1], 10, 64) - if (diskEndSectorInt-maxSector)*m.MinIO/1024/1024 > 100 { - //添加可以分区情况 - p := model.LSBLKModel{} - p.Path = "可以添加" - m.Children = append(m.Children, p) - } - return m -} - -func (d *diskService) MountDisk(path, volume string) { - //fmt.Println("source " + config.AppInfo.ShellPath + "/helper.sh ;do_mount " + path + " " + volume) - r := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;do_mount " + path + " " + volume) - fmt.Println(r) -} - -func (d *diskService) SaveMountPoint(m model2.SerialDisk) { - d.db.Where("uuid = ?", m.UUID).Delete(&model2.SerialDisk{}) - d.db.Create(&m) -} - -func (d *diskService) UpdateMountPoint(m model2.SerialDisk) { - d.db.Model(&model2.SerialDisk{}).Where("uui = ?", m.UUID).Update("mount_point", m.MountPoint) -} - -func (d *diskService) DeleteMount(id string) { - - d.db.Delete(&model2.SerialDisk{}).Where("id = ?", id) -} - -func (d *diskService) DeleteMountPoint(path, mountPoint string) { - - d.db.Where("path = ? AND mount_point = ?", path, mountPoint).Delete(&model2.SerialDisk{}) - - command2.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;do_umount " + path) -} - -func (d *diskService) GetSerialAll() []model2.SerialDisk { - var m []model2.SerialDisk - d.db.Find(&m) - return m -} - -func NewDiskService(db *gorm.DB) DiskService { - return &diskService{db: db} -} diff --git a/service/docker.go b/service/docker.go index 9fe885f..f453c0b 100644 --- a/service/docker.go +++ b/service/docker.go @@ -52,13 +52,14 @@ type DockerService interface { DockerContainerStop(id string) error DockerContainerUpdateName(name, id string) (err error) DockerContainerUpdate(m model.CustomizationPostData, id string) (err error) - DockerContainerLog(name string) (string, error) + DockerContainerLog(name string) ([]byte, error) DockerContainerCommit(name string) DockerContainerList() []types.Container DockerNetworkModelList() []types.NetworkResource DockerImageInfo(image string) (types.ImageInspect, error) GetNetWorkNameByNetWorkID(id string) (string, error) ContainerExecShell(container_id string) string + GetDockerInfo() (types.Info, error) } type dockerService struct { @@ -94,7 +95,7 @@ func (ds *dockerService) ContainerExecShell(container_id string) string { return exec.ID } -//创建默认网络 +// 创建默认网络 func DockerNetwork() { cli, _ := client2.NewClientWithOpts(client2.FromEnv) @@ -109,7 +110,7 @@ func DockerNetwork() { cli.NetworkCreate(context.Background(), docker.NETWORKNAME, types.NetworkCreate{}) } -//根据网络id获取网络名 +// 根据网络id获取网络名 func (ds *dockerService) GetNetWorkNameByNetWorkID(id string) (string, error) { cli, _ := client2.NewClientWithOpts(client2.FromEnv) defer cli.Close() @@ -122,7 +123,7 @@ func (ds *dockerService) GetNetWorkNameByNetWorkID(id string) (string, error) { return "", err } -//拉取镜像 +// 拉取镜像 func DockerPull() { cli, _ := client2.NewClientWithOpts(client2.FromEnv) @@ -141,7 +142,7 @@ func DockerPull() { } -//拉取镜像 +// 拉取镜像 func DockerEx() { cli, _ := client2.NewClientWithOpts(client2.FromEnv) @@ -292,7 +293,7 @@ func DockerLogs() { //正式内容 -//检查镜像是否存在 +// 检查镜像是否存在 func (ds *dockerService) IsExistImage(imageName string) bool { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -311,7 +312,7 @@ func (ds *dockerService) IsExistImage(imageName string) bool { return false } -//安装镜像 +// 安装镜像 func (ds *dockerService) DockerPullImage(imageName string, icon, name string) error { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -365,12 +366,12 @@ func (ds *dockerService) DockerContainerCopyCreate(info *types.ContainerJSON) (c return container.ID, err } -//param imageName 镜像名称 -//param containerDbId 数据库的id -//param port 容器内部主端口 -//param mapPort 容器主端口映射到外部的端口 -//param tcp 容器其他tcp端口 -//param udp 容器其他udp端口 +// param imageName 镜像名称 +// param containerDbId 数据库的id +// param port 容器内部主端口 +// param mapPort 容器主端口映射到外部的端口 +// param tcp 容器其他tcp端口 +// param udp 容器其他udp端口 func (ds *dockerService) DockerContainerCreate(m model.CustomizationPostData, id string) (containerId string, err error) { if len(m.NetworkModel) == 0 { m.NetworkModel = "bridge" @@ -581,7 +582,7 @@ func (ds *dockerService) DockerContainerCreate(m model.CustomizationPostData, id return containerDb.ID, err } -//删除容器 +// 删除容器 func (ds *dockerService) DockerContainerRemove(name string, update bool) error { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -605,7 +606,7 @@ func (ds *dockerService) DockerContainerRemove(name string, update bool) error { return err } -//删除镜像 +// 删除镜像 func (ds *dockerService) DockerImageRemove(name string) error { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -653,7 +654,7 @@ Loop: return err } -//停止镜像 +// 停止镜像 func (ds *dockerService) DockerContainerStop(id string) error { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -664,7 +665,7 @@ func (ds *dockerService) DockerContainerStop(id string) error { return err } -//启动容器 +// 启动容器 func (ds *dockerService) DockerContainerStart(name string) error { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -675,24 +676,27 @@ func (ds *dockerService) DockerContainerStart(name string) error { return err } -//查看日志 -func (ds *dockerService) DockerContainerLog(name string) (string, error) { +// 查看日志 +func (ds *dockerService) DockerContainerLog(name string) ([]byte, error) { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { - return "", err + return []byte(""), err } defer cli.Close() - body, err := cli.ContainerLogs(context.Background(), name, types.ContainerLogsOptions{ShowStderr: true, ShowStdout: true}) + //body, err := cli.ContainerAttach(context.Background(), name, types.ContainerAttachOptions{Logs: true, Stream: false, Stdin: false, Stdout: false, Stderr: false}) + body, err := cli.ContainerLogs(context.Background(), name, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true}) + if err != nil { - return "", err + return []byte(""), err } defer body.Close() content, err := ioutil.ReadAll(body) + //content, err := ioutil.ReadAll(body) if err != nil { - return "", err + return []byte(""), err } - return string(content), nil + return content, nil } func DockerContainerStats1() error { @@ -714,7 +718,7 @@ func DockerContainerStats1() error { return nil } -//获取容器状态 +// 获取容器状态 func (ds *dockerService) DockerContainerStats(name string) (string, error) { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -733,7 +737,7 @@ func (ds *dockerService) DockerContainerStats(name string) (string, error) { return string(sts), nil } -//备份容器 +// 备份容器 func (ds *dockerService) DockerContainerCommit(name string) { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -778,7 +782,7 @@ func (ds *dockerService) DockerListByImage(image, version string) (*types.Contai return &containers[0], nil } -//获取容器详情 +// 获取容器详情 func (ds *dockerService) DockerContainerInfo(name string) (*types.ContainerJSON, error) { cli, err := client2.NewClientWithOpts(client2.FromEnv) @@ -793,13 +797,13 @@ func (ds *dockerService) DockerContainerInfo(name string) (*types.ContainerJSON, return &d, nil } -//更新容器 -//param shares cpu优先级 -//param containerDbId 数据库的id -//param port 容器内部主端口 -//param mapPort 容器主端口映射到外部的端口 -//param tcp 容器其他tcp端口 -//param udp 容器其他udp端口 +// 更新容器 +// param shares cpu优先级 +// param containerDbId 数据库的id +// param port 容器内部主端口 +// param mapPort 容器主端口映射到外部的端口 +// param tcp 容器其他tcp端口 +// param udp 容器其他udp端口 func (ds *dockerService) DockerContainerUpdate(m model.CustomizationPostData, id string) (err error) { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -834,9 +838,9 @@ func (ds *dockerService) DockerContainerUpdate(m model.CustomizationPostData, id return } -//更新容器名称 -//param name 容器名称 -//param id 老的容器名称 +// 更新容器名称 +// param name 容器名称 +// param id 老的容器名称 func (ds *dockerService) DockerContainerUpdateName(name, id string) (err error) { cli, err := client2.NewClientWithOpts(client2.FromEnv) if err != nil { @@ -851,7 +855,7 @@ func (ds *dockerService) DockerContainerUpdateName(name, id string) (err error) return } -//获取网络列表 +// 获取网络列表 func (ds *dockerService) DockerNetworkModelList() []types.NetworkResource { cli, _ := client2.NewClientWithOpts(client2.FromEnv) @@ -863,6 +867,17 @@ func NewDockerService() DockerService { return &dockerService{rootDir: command2.ExecResultStr(`source ./shell/helper.sh ;GetDockerRootDir`)} } +func (ds *dockerService) GetDockerInfo() (types.Info, error) { + cli, err := client2.NewClientWithOpts(client2.FromEnv) + if err != nil { + return types.Info{}, err + } + defer cli.Close() + + return cli.Info(context.Background()) + +} + // ---------------------------------------test------------------------------------ //func ServiceCreate() { // cli, err := client2.NewClientWithOpts(client2.FromEnv) diff --git a/service/model/o_disk.go b/service/model/o_disk.go deleted file mode 100644 index 1121e87..0000000 --- a/service/model/o_disk.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * @Author: LinkLeong link@icewhale.org - * @Date: 2021-12-07 17:14:41 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-08-17 18:46:43 - * @FilePath: /CasaOS/service/model/o_disk.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. - */ -package model - -//SerialAdvanced Technology Attachment (STAT) -type SerialDisk struct { - Id uint `gorm:"column:id;primary_key" json:"id"` - UUID string `json:"uuid"` - Path string `json:"path"` - State int `json:"state"` - MountPoint string `json:"mount_point"` - CreatedAt int64 `json:"created_at"` -} - -func (p *SerialDisk) TableName() string { - return "o_disk" -} diff --git a/service/notify.go b/service/notify.go index 3981e55..5e31ebe 100644 --- a/service/notify.go +++ b/service/notify.go @@ -31,16 +31,38 @@ type NotifyServer interface { SendNetInfoBySocket(netList []model2.IOCountersStat) SendCPUInfoBySocket(cpu map[string]interface{}) SendMemInfoBySocket(mem map[string]interface{}) - SendUSBInfoBySocket(list []model2.DriveUSB) - SendDiskInfoBySocket(disk model2.Summary) SendFileOperateNotify(nowSend bool) SendInstallAppBySocket(app notify.Application) - SendAllHardwareStatusBySocket(disk model2.Summary, list []model2.DriveUSB, mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) + SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) SendStorageBySocket(message notify.StorageMessage) + SendNotify(path string, message map[string]interface{}) + SettingSystemTempData(message map[string]interface{}) + GetSystemTempMap() map[string]interface{} } type notifyServer struct { - db *gorm.DB + db *gorm.DB + SystemTempMap map[string]interface{} +} + +func (i *notifyServer) SettingSystemTempData(message map[string]interface{}) { + for k, v := range message { + i.SystemTempMap[k] = v + } +} + +func (i *notifyServer) SendNotify(path string, message map[string]interface{}) { + msg := gosf.Message{} + msg.Body = message + msg.Success = true + msg.Text = path + + notify := notify.Message{} + notify.Path = path + notify.Msg = msg + + NotifyMsg <- notify + } func (i *notifyServer) SendStorageBySocket(message notify.StorageMessage) { @@ -58,12 +80,9 @@ func (i *notifyServer) SendStorageBySocket(message notify.StorageMessage) { NotifyMsg <- notify } -func (i *notifyServer) SendAllHardwareStatusBySocket(disk model2.Summary, list []model2.DriveUSB, mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) { +func (i *notifyServer) SendAllHardwareStatusBySocket(mem map[string]interface{}, cpu map[string]interface{}, netList []model2.IOCountersStat) { body := make(map[string]interface{}) - body["sys_disk"] = disk - - body["sys_usb"] = list body["sys_mem"] = mem @@ -71,6 +90,10 @@ func (i *notifyServer) SendAllHardwareStatusBySocket(disk model2.Summary, list [ body["sys_net"] = netList + for k, v := range i.SystemTempMap { + body[k] = v + } + msg := gosf.Message{} msg.Body = body msg.Success = true @@ -239,38 +262,6 @@ func (i *notifyServer) SendFileOperateNotify(nowSend bool) { } -func (i *notifyServer) SendDiskInfoBySocket(disk model2.Summary) { - body := make(map[string]interface{}) - body["data"] = disk - - msg := gosf.Message{} - msg.Body = body - msg.Success = true - msg.Text = "sys_disk" - - notify := notify.Message{} - notify.Path = "sys_disk" - notify.Msg = msg - - NotifyMsg <- notify -} - -func (i *notifyServer) SendUSBInfoBySocket(list []model2.DriveUSB) { - body := make(map[string]interface{}) - body["data"] = list - - msg := gosf.Message{} - msg.Body = body - msg.Success = true - msg.Text = "sys_usb" - - notify := notify.Message{} - notify.Path = "sys_usb" - notify.Msg = msg - - NotifyMsg <- notify -} - func (i *notifyServer) SendMemInfoBySocket(mem map[string]interface{}) { body := make(map[string]interface{}) body["data"] = mem @@ -452,7 +443,11 @@ func SendMeg() { // } // } +func (i *notifyServer) GetSystemTempMap() map[string]interface{} { + + return i.SystemTempMap -func NewNotifyService(db *gorm.DB) NotifyServer { - return ¬ifyServer{db: db} +} +func NewNotifyService(db *gorm.DB) NotifyServer { + return ¬ifyServer{db: db, SystemTempMap: make(map[string]interface{})} } diff --git a/service/service.go b/service/service.go index 16594b9..e2164c0 100644 --- a/service/service.go +++ b/service/service.go @@ -30,7 +30,6 @@ type Repository interface { //User() UserService Docker() DockerService Casa() CasaService - Disk() DiskService Notify() NotifyServer Rely() RelyService System() SystemService @@ -51,7 +50,6 @@ func NewService(db *gorm.DB, RuntimePath string) Repository { app: NewAppService(db), docker: NewDockerService(), casa: NewCasaService(), - disk: NewDiskService(db), notify: NewNotifyService(db), rely: NewRelyService(db), system: NewSystemService(), @@ -65,7 +63,6 @@ type store struct { app AppService docker DockerService casa CasaService - disk DiskService notify NotifyServer rely RelyService system SystemService @@ -107,7 +104,3 @@ func (c *store) Docker() DockerService { func (c *store) Casa() CasaService { return c.casa } - -func (c *store) Disk() DiskService { - return c.disk -} diff --git a/service/system.go b/service/system.go index 8717e4c..497a469 100644 --- a/service/system.go +++ b/service/system.go @@ -30,8 +30,6 @@ type SystemService interface { UpdateAssist() UpSystemPort(port string) GetTimeZone() string - UpdateUSBAutoMount(state string) - ExecUSBAutoMountShell(state string) UpAppOrderFile(str, id string) GetAppOrderFile(id string) []byte GetNet(physics bool) []string @@ -53,14 +51,7 @@ type SystemService interface { GetCPUTemperature() int GetCPUPower() map[string]string } -type systemService struct { -} - -func (s *systemService) UpdateUSBAutoMount(state string) { - config.ServerInfo.USBAutoMount = state - config.Cfg.Section("server").Key("USBAutoMount").SetValue(state) - config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath) -} +type systemService struct{} func (c *systemService) MkdirAll(path string) (int, error) { _, err := os.Stat(path) @@ -76,8 +67,8 @@ func (c *systemService) MkdirAll(path string) (int, error) { } return common_err.SERVICE_ERROR, err } -func (c *systemService) RenameFile(oldF, newF string) (int, error) { +func (c *systemService) RenameFile(oldF, newF string) (int, error) { _, err := os.Stat(newF) if err == nil { return common_err.DIR_ALREADY_EXISTS, nil @@ -92,6 +83,7 @@ func (c *systemService) RenameFile(oldF, newF string) (int, error) { } return common_err.SERVICE_ERROR, err } + func (c *systemService) CreateFile(path string) (int, error) { _, err := os.Stat(path) if err == nil { @@ -104,9 +96,11 @@ func (c *systemService) CreateFile(path string) (int, error) { } return common_err.SERVICE_ERROR, err } + func (c *systemService) GetDeviceTree() string { return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetDeviceTree") } + func (c *systemService) GetSysInfo() host.InfoStat { info, _ := host.Info() return *info @@ -128,9 +122,7 @@ func (c *systemService) GetNetState(name string) string { } func (c *systemService) GetDirPathOne(path string) (m model.Path) { - f, err := os.Stat(path) - if err != nil { return } @@ -175,6 +167,7 @@ func (c *systemService) GetDirPath(path string) []model.Path { } return dirs } + func (c *systemService) GetCpuInfo() []cpu.InfoStat { info, _ := cpu.Info() return info @@ -207,6 +200,7 @@ func (c *systemService) GetNetInfo() []net.IOCountersStat { parts, _ := net.IOCounters(true) return parts } + func (c *systemService) GetNet(physics bool) []string { t := "1" if physics { @@ -221,10 +215,16 @@ func (s *systemService) UpdateSystemVersion(version string) { } file.CreateFile(config.AppInfo.LogPath + "/upgrade.log") //go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/LinkLeong/casaos-alpha/main/update.sh | bash") - go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | bash") + if len(config.ServerInfo.UpdateUrl) > 0 { + go command2.OnlyExec("curl -fsSL " + config.ServerInfo.UpdateUrl + " | bash") + } else { + go command2.OnlyExec("curl -fsSL https://raw.githubusercontent.com/IceWhaleTech/get/main/update.sh | bash") + } + //s.log.Error(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version) //s.log.Error(command2.ExecResultStr(config.AppInfo.ProjectPath + "/shell/tool.sh -r " + version)) } + func (s *systemService) UpdateAssist() { command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/assist.sh") } @@ -233,14 +233,6 @@ func (s *systemService) GetTimeZone() string { return command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;GetTimeZone") } -func (s *systemService) ExecUSBAutoMountShell(state string) { - if state == "False" { - command2.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;USB_Stop_Auto") - } else { - command2.OnlyExec("source " + config.AppInfo.ShellPath + "/helper.sh ;USB_Start_Auto") - } -} - func (s *systemService) GetSystemConfigDebug() []string { return command2.ExecResultStrArray("source " + config.AppInfo.ShellPath + "/helper.sh ;GetSysInfo") } @@ -248,9 +240,11 @@ func (s *systemService) GetSystemConfigDebug() []string { func (s *systemService) UpAppOrderFile(str, id string) { file.WriteToPath([]byte(str), config.AppInfo.DBPath+"/"+id, "app_order.json") } + func (s *systemService) GetAppOrderFile(id string) []byte { return file.ReadFullFile(config.AppInfo.UserDataPath + "/" + id + "/app_order.json") } + func (s *systemService) UpSystemPort(port string) { if len(port) > 0 && port != config.ServerInfo.HttpPort { config.Cfg.Section("server").Key("HttpPort").SetValue(port) @@ -258,6 +252,7 @@ func (s *systemService) UpSystemPort(port string) { } config.Cfg.SaveTo(config.SystemConfigInfo.ConfigPath) } + func (s *systemService) GetCasaOSLogs(lineNumber int) string { file, err := os.Open(filepath.Join(config.AppInfo.LogPath, fmt.Sprintf("%s.%s", config.AppInfo.LogSaveName, @@ -294,8 +289,8 @@ func GetDeviceAllIP() []string { func (s *systemService) IsServiceRunning(name string) bool { status := command2.ExecResultStr("source " + config.AppInfo.ShellPath + "/helper.sh ;CheckServiceStatus smbd") return strings.TrimSpace(status) == "running" - } + func (s *systemService) GetCPUTemperature() int { outPut := "" if file.Exists("/sys/class/thermal/thermal_zone0/temp") { @@ -313,6 +308,7 @@ func (s *systemService) GetCPUTemperature() int { } return celsius } + func (s *systemService) GetCPUPower() map[string]string { data := make(map[string]string, 2) data["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) @@ -323,6 +319,7 @@ func (s *systemService) GetCPUPower() map[string]string { } return data } + func NewSystemService() SystemService { return &systemService{} } diff --git a/types/system.go b/types/system.go index 24ef182..e77a7d6 100644 --- a/types/system.go +++ b/types/system.go @@ -1,15 +1,14 @@ -/* - * @Author: LinkLeong link@icewhale.com - * @Date: 2022-02-17 18:53:22 - * @LastEditors: LinkLeong - * @LastEditTime: 2022-09-06 14:27:42 - * @FilePath: /CasaOS/types/system.go - * @Description: - * @Website: https://www.casaos.io - * Copyright (c) 2022 by icewhale, All Rights Reserved. +/*@Author: LinkLeong link@icewhale.com + *@Date: 2022-02-17 18:53:22 + *@LastEditors: LinkLeong + *@LastEditTime: 2022-09-06 14:27:42 + *@FilePath: /CasaOS/types/system.go + *@Description: + *@Website: https://www.casaos.io + *Copyright (c) 2022 by icewhale, All Rights Reserved. */ package types -const CURRENTVERSION = "0.3.6" +const CURRENTVERSION = "0.3.7" const BODY = " "