#!/bin/bash # © 2023-2024 Ivy (prpl.wtf) # Learn more @ blueprint.zip # Source code available on github.com/blueprintframework/framework # This should allow Blueprint to run in Docker. Please note that changing the $FOLDER variable after running # the Blueprint installation script will not change anything in any files besides blueprint.sh. FOLDER=$(realpath "$(dirname "$0")") # This stores the webserver ownership user which Blueprint uses when applying webserver permissions. OWNERSHIP="www-data:www-data" #; # This stores options for permissions related to running install scripts the webserver user. WEBUSER="www-data" #; USERSHELL="/bin/bash" #; # Defines the version Blueprint will display as the active one. VERSION="beta-F248-1" # Default GitHub repository to use when upgrading Blueprint. REPOSITORY="BlueprintFramework/framework" # Check if the script is being sourced - and if so - load bash autocompletion. if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then _blueprint_completions() { local cur cmd opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" cmd="${COMP_WORDS[1]}" case "${cmd}" in -install|-add|-i) opts="$(find "$FOLDER"/*.blueprint | sed -e "s|^$FOLDER/||g" -e "s|.blueprint$||g")" ;; -remove|-r) opts="$(sed "s|,||g" "$FOLDER/.blueprint/extensions/blueprint/private/db/installed_extensions")" ;; -export) opts="expose" ;; -debug) opts="100 200" ;; -upgrade) opts="remote" ;; *) opts="-install -add -remove -init -build -export -wipe -version -help -info -debug -upgrade -rerun-install" ;; esac if [[ ${cur} == * ]]; then # shellcheck disable=SC2207 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi } complete -F _blueprint_completions blueprint return 0 fi # Check for panels that are using Docker, which should have better support in the future. if [[ -f "/.dockerenv" ]]; then DOCKER="y" FOLDER="/app" else DOCKER="n" fi # This has caused a bunch of errors but is just here to make sure people actually upload the # "blueprint" folder onto their panel when installing Blueprint. Pick your poison. if [[ -d "$FOLDER/blueprint" ]]; then mv "$FOLDER/blueprint" "$FOLDER/.blueprint"; fi if [[ $VERSION != "" ]]; then # This function makes sure some placeholders get replaced with the current Blueprint version. if [[ ! -f "$FOLDER/.blueprint/extensions/blueprint/private/db/version" ]]; then sed -E -i "s*::v*$VERSION*g" "$FOLDER/app/BlueprintFramework/Services/PlaceholderService/BlueprintPlaceholderService.php" sed -E -i "s*::v*$VERSION*g" "$FOLDER/.blueprint/extensions/blueprint/public/index.html" touch "$FOLDER/.blueprint/extensions/blueprint/private/db/version" fi fi # Write environment variables. export BLUEPRINT__FOLDER=$FOLDER export BLUEPRINT__VERSION=$VERSION export BLUEPRINT__DEBUG="$FOLDER"/.blueprint/extensions/blueprint/private/debug/logs.txt export NODE_OPTIONS=--openssl-legacy-provider # Write internal variables. __BuildDir=".blueprint/extensions/blueprint/private/build" # Automatically navigate to the Pterodactyl directory when running the core. cd "$FOLDER" || return # Import libraries. source scripts/libraries/parse_yaml.sh || missinglibs+="[parse_yaml]" source scripts/libraries/grabenv.sh || missinglibs+="[grabenv]" source scripts/libraries/logFormat.sh || missinglibs+="[logFormat]" source scripts/libraries/misc.sh || missinglibs+="[misc]" # -config # usage: "cITEM=VALUE bash blueprint.sh -config" if [[ "$1" == "-config" ]]; then # cTELEMETRY_ID # Update the telemetry id. if [[ "$cTELEMETRY_ID" != "" ]]; then echo "$cTELEMETRY_ID" > .blueprint/extensions/blueprint/private/db/telemetry_id fi # cDEVELOPER # Enable/Disable developer mode. if [[ "$cDEVELOPER" != "" ]]; then if [[ "$cDEVELOPER" == "true" ]]; then dbAdd "blueprint.developerEnabled" else dbRemove "blueprint.developerEnabled" fi fi echo . exit 0 fi cdhalt() { PRINT FATAL "Attempted navigation into nonexistent directory, halting process."; exit 1; } depend() { # Check for compatible node versions nodeVer=$(node -v) if [[ $nodeVer != "v17."* ]] \ && [[ $nodeVer != "v18."* ]] \ && [[ $nodeVer != "v19."* ]] \ && [[ $nodeVer != "v20."* ]] \ && [[ $nodeVer != "v21."* ]] \ && [[ $nodeVer != "v22."* ]]; then DEPEND_MISSING=true fi # Check for required (both internal and external) dependencies. if \ ! [ -x "$(command -v unzip)" ] || # unzip ! [ -x "$(command -v node)" ] || # node ! [ -x "$(command -v yarn)" ] || # yarn ! [ -x "$(command -v zip)" ] || # zip ! [ -x "$(command -v curl)" ] || # curl ! [ -x "$(command -v php)" ] || # php ! [ -x "$(command -v git)" ] || # git ! [ -x "$(command -v grep)" ] || # grep ! [ -x "$(command -v sed)" ] || # sed ! [ -x "$(command -v awk)" ] || # awk ! [ -x "$(command -v tput)" ] || # tput ! [ "$(ls "node_modules/"*"cross-env"* 2> /dev/null)" ] || # cross-env ! [ "$(ls "node_modules/"*"webpack"* 2> /dev/null)" ] || # webpack ! [ "$(ls "node_modules/"*"react"* 2> /dev/null)" ] || # react [[ $missinglibs != "" ]]; then # internal DEPEND_MISSING=true fi # Exit when missing dependencies. if [[ $DEPEND_MISSING == true ]]; then PRINT FATAL "Some framework dependencies are not installed or detected." if [[ $nodeVer != "v18."* ]] \ && [[ $nodeVer != "v19."* ]] \ && [[ $nodeVer != "v20."* ]] \ && [[ $nodeVer != "v21."* ]] \ && [[ $nodeVer != "v22."* ]]; then PRINT FATAL "Required dependency \"node\" is using an unsupported version." fi if ! [ -x "$(command -v unzip)" ]; then PRINT FATAL "Required dependency \"unzip\" is not installed or detected."; fi if ! [ -x "$(command -v node)" ]; then PRINT FATAL "Required dependency \"node\" is not installed or detected."; fi if ! [ -x "$(command -v yarn)" ]; then PRINT FATAL "Required dependency \"yarn\" is not installed or detected."; fi if ! [ -x "$(command -v zip)" ]; then PRINT FATAL "Required dependency \"zip\" is not installed or detected."; fi if ! [ -x "$(command -v curl)" ]; then PRINT FATAL "Required dependency \"curl\" is not installed or detected."; fi if ! [ -x "$(command -v php)" ]; then PRINT FATAL "Required dependency \"php\" is not installed or detected."; fi if ! [ -x "$(command -v git)" ]; then PRINT FATAL "Required dependency \"git\" is not installed or detected."; fi if ! [ -x "$(command -v grep)" ]; then PRINT FATAL "Required dependency \"grep\" is not installed or detected."; fi if ! [ -x "$(command -v sed)" ]; then PRINT FATAL "Required dependency \"sed\" is not installed or detected."; fi if ! [ -x "$(command -v awk)" ]; then PRINT FATAL "Required dependency \"awk\" is not installed or detected."; fi if ! [ -x "$(command -v tput)" ]; then PRINT FATAL "Required dependency \"tput\" is not installed or detected."; fi if ! [ "$(ls "node_modules/"*"cross-env"* 2> /dev/null)" ]; then PRINT FATAL "Required dependency \"cross-env\" is not installed or detected."; fi if ! [ "$(ls "node_modules/"*"webpack"* 2> /dev/null)" ]; then PRINT FATAL "Required dependency \"webpack\" is not installed or detected."; fi if ! [ "$(ls "node_modules/"*"react"* 2> /dev/null)" ]; then PRINT FATAL "Required dependency \"react\" is not installed or detected."; fi if [[ $missinglibs == *"[parse_yaml]"* ]]; then PRINT FATAL "Required internal dependency \"internal:parse_yaml\" is not installed or detected."; fi if [[ $missinglibs == *"[grabEnv]"* ]]; then PRINT FATAL "Required internal dependency \"internal:grabEnv\" is not installed or detected."; fi if [[ $missinglibs == *"[logFormat]"* ]]; then PRINT FATAL "Required internal dependency \"internal:logFormat\" is not installed or detected."; fi if [[ $missinglibs == *"[misc]"* ]]; then PRINT FATAL "Required internal dependency \"internal:misc\" is not installed or detected."; fi exit 1 fi } # Assign variables for extension flags. assignflags() { F_ignorePlaceholders=false F_forceLegacyPlaceholders=false F_hasInstallScript=false F_hasRemovalScript=false F_hasExportScript=false F_developerIgnoreInstallScript=false F_developerIgnoreRebuild=false F_developerForceMigrate=false F_developerKeepApplicationCache=false F_developerEscalateExportScript=false if [[ ( $flags == *"ignorePlaceholders,"* ) || ( $flags == *"ignorePlaceholders" ) ]]; then F_ignorePlaceholders=true ;fi if [[ ( $flags == *"forceLegacyPlaceholders,"* ) || ( $flags == *"forceLegacyPlaceholders" ) ]]; then F_forceLegacyPlaceholders=true ;fi if [[ ( $flags == *"hasInstallScript,"* ) || ( $flags == *"hasInstallScript" ) ]]; then F_hasInstallScript=true ;fi if [[ ( $flags == *"hasRemovalScript,"* ) || ( $flags == *"hasRemovalScript" ) ]]; then F_hasRemovalScript=true ;fi if [[ ( $flags == *"hasExportScript,"* ) || ( $flags == *"hasExportScript" ) ]]; then F_hasExportScript=true ;fi if [[ ( $flags == *"developerIgnoreInstallScript,"* ) || ( $flags == *"developerIgnoreInstallScript" ) ]]; then F_developerIgnoreInstallScript=true ;fi if [[ ( $flags == *"developerIgnoreRebuild,"* ) || ( $flags == *"developerIgnoreRebuild" ) ]]; then F_developerIgnoreRebuild=true ;fi if [[ ( $flags == *"developerForceMigrate,"* ) || ( $flags == *"developerForceMigrate" ) ]]; then F_developerForceMigrate=true ;fi if [[ ( $flags == *"developerKeepApplicationCache,"* ) || ( $flags == *"developerKeepApplicationCache" ) ]]; then F_developerKeepApplicationCache=true ;fi if [[ ( $flags == *"developerEscalateExportScript,"* ) || ( $flags == *"developerEscalateExportScript" ) ]]; then F_developerEscalateExportScript=true ;fi } # Adds the "blueprint" command to the /usr/local/bin directory and configures the correct permissions for it. placeshortcut() { PRINT INFO "Placing Blueprint command shortcut.." { touch /usr/local/bin/blueprint chmod u+x \ "$FOLDER/blueprint.sh" \ /usr/local/bin/blueprint } >> "$BLUEPRINT__DEBUG" echo -e \ "#!/bin/bash \n" \ "if [[ \"\${BASH_SOURCE[0]}\" != \"\${0}\" ]]; then source \"$FOLDER/blueprint.sh\"; return 0; fi; "\ "bash $FOLDER/blueprint.sh -bash \$@;" \ > /usr/local/bin/blueprint } if ! [ -x "$(command -v blueprint)" ]; then placeshortcut; fi if [[ $1 != "-bash" ]]; then if dbValidate "blueprint.setupFinished"; then PRINT FATAL "Installation process has already been finished before, consider using the 'blueprint' command." exit 2 else # Only run if Blueprint is not in the process of upgrading. if [[ $1 != "--post-upgrade" ]]; then # Print Blueprint icon with ascii characters. echo -e " ██\n██ ██\n ████\n"; fi PRINT INFO "Searching and validating framework dependencies.." # Check if required dependencies are installed depend # Place Blueprint shortcut placeshortcut # Link directories. PRINT INFO "Linking directories and filesystems.." { ln -s -r -T "$FOLDER/.blueprint/extensions/blueprint/public" "$FOLDER/public/extensions/blueprint" ln -s -r -T "$FOLDER/.blueprint/extensions/blueprint/assets" "$FOLDER/public/assets/extensions/blueprint" ln -s -r -T "$FOLDER/scripts/libraries" "$FOLDER/.blueprint/lib" } 2>> "$BLUEPRINT__DEBUG" php artisan storage:link &>> "$BLUEPRINT__DEBUG" PRINT INFO "Replacing internal placeholders.." # Copy "Blueprint" extension page logo from assets. cp "$FOLDER/.blueprint/assets/logo.jpg" "$FOLDER/.blueprint/extensions/blueprint/assets/logo.jpg" # Put application into maintenance. PRINT INPUT "Would you like to put your application into maintenance while Blueprint is installing? (Y/n)" read -r YN if [[ ( $YN == "y"* ) || ( $YN == "Y"* ) || ( $YN == "" ) ]]; then MAINTENANCE="true" PRINT INFO "Put application into maintenance mode." php artisan down &>> "$BLUEPRINT__DEBUG" else MAINTENANCE="false" PRINT INFO "Putting application into maintenance has been skipped." fi # Flush cache. PRINT INFO "Flushing view, config and route cache.." { php artisan view:cache php artisan config:cache php artisan route:clear php artisan cache:clear } &>> "$BLUEPRINT__DEBUG" updateCacheReminder # Run migrations if Blueprint is not upgrading. if [[ ( $1 != "--post-upgrade" ) && ( $DOCKER != "y" ) ]]; then PRINT INPUT "Would you like to migrate your database? (Y/n)" read -r YN if [[ ( $YN == "y"* ) || ( $YN == "Y"* ) || ( $YN == "" ) ]]; then PRINT INFO "Running database migrations.." php artisan migrate --force else PRINT INFO "Database migrations have been skipped." fi fi # Make sure all files have correct permissions. PRINT INFO "Changing Pterodactyl file ownership to '$OWNERSHIP'.." find "$FOLDER/" \ -path "$FOLDER/node_modules" -prune \ -o -exec chown "$OWNERSHIP" {} + &>> "$BLUEPRINT__DEBUG" # Rebuild panel assets. PRINT INFO "Rebuilding panel assets.." yarn run build:production --progress if [[ $DOCKER != "y" ]]; then # Sync some database values. PRINT INFO "Syncing Blueprint-related database values.." php artisan bp:sync fi if [[ $DOCKER != "y" ]] && [[ $MAINTENANCE == "true" ]]; then # Put application into production. PRINT INFO "Put application into production." php artisan up &>> "$BLUEPRINT__DEBUG" fi # Finish installation if [[ $1 != "--post-upgrade" ]]; then PRINT SUCCESS "Blueprint has completed its installation process." fi dbAdd "blueprint.setupFinished" # Let the panel know the user has finished installation. sed -i "s/NOTINSTALLED/INSTALLED/g" "$FOLDER/app/BlueprintFramework/Services/PlaceholderService/BlueprintPlaceholderService.php" exit 0 fi fi # help, -help, --help, # h, -h, --h if [[ ( $2 == "help" ) || ( $2 == "-help" ) || ( $2 == "--help" ) || ( $2 == "h" ) || ( $2 == "-h" ) || ( $2 == "--h" ) || ( $2 == "" ) ]]; then VCMD="y" source ./scripts/commands/misc/help.sh HelpCommand fi # -i, -install, -add if [[ ( $2 == "-i" ) || ( $2 == "-install" ) || ( $2 == "-add" ) ]]; then VCMD="y" if [[ $3 == "" ]]; then PRINT FATAL "Expected at least 1 argument but got 0.";exit 2;fi if [[ ( $3 == "./"* ) || ( $3 == "../"* ) || ( $3 == "/"* ) ]]; then PRINT FATAL "Cannot import extensions from external paths.";exit 2;fi PRINT INFO "Searching and validating framework dependencies.." # Check if required programs and libraries are installed. depend source ./scripts/commands/extensions/install.sh # Install selected extensions current=0 extensions=$(shiftArgs "$@") total=$(echo "$extensions" | wc -w) for extension in $extensions; do (( current++ )) InstallCommand "$extension" "$current" "$total" done if [[ $InstalledExtensions != "" ]]; then # Finalize transaction PRINT INFO "Finalizing transaction.." if [[ ( $YARN == "y" ) && ( $IgnoreRebuild != "true" ) ]]; then PRINT INFO "Rebuilding panel assets.." yarn run build:production --progress fi # Link filesystems PRINT INFO "Linking filesystems.." php artisan storage:link &>> "$BLUEPRINT__DEBUG" # Flush cache. PRINT INFO "Flushing view, config and route cache.." { php artisan view:cache php artisan config:cache php artisan route:clear if [[ $KeepApplicationCache != "true" ]]; then php artisan cache:clear; fi } &>> "$BLUEPRINT__DEBUG" # Make sure all files have correct permissions. PRINT INFO "Changing Pterodactyl file ownership to '$OWNERSHIP'.." find "$FOLDER/" \ -path "$FOLDER/node_modules" -prune \ -o -exec chown "$OWNERSHIP" {} + &>> "$BLUEPRINT__DEBUG" # Database migrations if [[ ( $database_migrations != "" ) && ( $DOCKER != "y" ) ]] || [[ $DeveloperForcedMigrate == "true" ]]; then if [[ ( $YN == "y"* ) || ( $YN == "Y"* ) || ( $YN == "" ) ]] || [[ $DeveloperForcedMigrate == "true" ]]; then PRINT INFO "Running database migrations.." php artisan migrate --force else PRINT INFO "Database migrations have been skipped." fi fi sendTelemetry "INSTALL_EXTENSIONS" >> "$BLUEPRINT__DEBUG" CorrectPhrasing="have" if [[ $total = 1 ]]; then CorrectPhrasing="has"; fi PRINT SUCCESS "$InstalledExtensions $CorrectPhrasing been installed." exit 0 else exit 1 fi fi # -r, -remove if [[ ( $2 == "-r" ) || ( $2 == "-remove" ) ]]; then VCMD="y" if [[ $3 == "" ]]; then PRINT FATAL "Expected at least 1 argument but got 0.";exit 2;fi source ./scripts/commands/extensions/remove.sh # Remove selected extensions current=0 extensions=$(shiftArgs "$@") total=$(echo "$extensions" | wc -w) for extension in $extensions; do (( current++ )) RemoveCommand "$extension" "$current" "$total" done exit 0 # success fi # -v, -version if [[ ( $2 == "-v" ) || ( $2 == "-version" ) ]]; then VCMD="y" echo -e ${VERSION} fi # -debug if [[ $2 == "-debug" ]]; then VCMD="y" if ! [[ $3 =~ [0-9] ]] && [[ $3 != "" ]]; then PRINT FATAL "Amount of debug lines must be a number."; exit 2; fi if [[ $3 -lt 1 ]]; then PRINT FATAL "Provide the amount of debug lines to print as an argument, which must be greater than one (1)."; exit 2; fi echo -e "\x1b[30;47;1m --- DEBUG START --- \x1b[0m" echo -e "$(v="$(<.blueprint/extensions/blueprint/private/debug/logs.txt)";printf -- "%s" "$v"|tail -"$3")" echo -e "\x1b[30;47;1m --- DEBUG END --- \x1b[0m" fi # -init if [[ ( $2 == "-init" || $2 == "-I" ) ]]; then VCMD="y" # Check for developer mode through the database library. if ! dbValidate "blueprint.developerEnabled"; then PRINT FATAL "Developer mode is not enabled.";exit 2; fi # To prevent accidental wiping of your dev directory, you are unable to initialize another extension # until you wipe the contents of the .blueprint/dev directory. if [[ -n $(find .blueprint/dev -maxdepth 1 -type f -not -name ".gitkeep" -print -quit) ]]; then PRINT FATAL "Development directory contains files. To protect you against accidental data loss, you are unable to initialize another extension unless you clear the '.blueprint/dev' folder." exit 2 fi ask_template() { PRINT INPUT "Choose an extension template:" echo -e "$(curl 'https://raw.githubusercontent.com/BlueprintFramework/templates/main/repository' 2>> "$BLUEPRINT__DEBUG")" read -r ASKTEMPLATE REDO_TEMPLATE=false # Template should not be empty if [[ ${ASKTEMPLATE} == "" ]]; then PRINT WARNING "Template should not be empty." REDO_TEMPLATE=true fi # Unknown template. if [[ $(echo -e "$(curl "https://raw.githubusercontent.com/BlueprintFramework/templates/main/${ASKTEMPLATE}/TemplateConfiguration.yml" 2>> "$BLUEPRINT__DEBUG")") == "404: Not Found" ]]; then PRINT WARNING "Unknown template, please choose a valid option." REDO_TEMPLATE=true fi # Ask again if response does not pass validation. if [[ ${REDO_TEMPLATE} == true ]]; then ASKTEMPLATE=""; ask_template; fi } ask_name() { INPUT_DEFAULT="SpaceInvaders" PRINT INPUT "Name [$INPUT_DEFAULT]:" read -r ASKNAME REDO_NAME=false # Name should not be empty if [[ ${ASKNAME} == "" ]]; then ASKNAME="$INPUT_DEFAULT" fi # Ask again if response does not pass validation. if [[ ${REDO_NAME} == true ]]; then ASKNAME=""; ask_name; fi } ask_identifier() { INPUT_DEFAULT="spaceinvaders" PRINT INPUT "Identifier [$INPUT_DEFAULT]:" read -r ASKIDENTIFIER REDO_IDENTIFIER=false # Identifier should not be empty if [[ ${ASKIDENTIFIER} == "" ]]; then ASKIDENTIFIER="$INPUT_DEFAULT" fi # Identifier should be a-z. if ! [[ ${ASKIDENTIFIER} =~ [a-z] ]]; then PRINT WARNING "Identifier should only contain a-z characters." REDO_IDENTIFIER=true fi # Ask again if response does not pass validation. if [[ ${REDO_IDENTIFIER} == true ]]; then ASKIDENTIFIER=""; ask_identifier; fi } ask_description() { INPUT_DEFAULT="Shoot down space aliens!" PRINT INPUT "Description [$INPUT_DEFAULT]:" read -r ASKDESCRIPTION REDO_DESCRIPTION=false # Description should not be empty if [[ ${ASKDESCRIPTION} == "" ]]; then ASKDESCRIPTION="$INPUT_DEFAULT" fi # Ask again if response does not pass validation. if [[ ${REDO_DESCRIPTION} == true ]]; then ASKDESCRIPTION=""; ask_description; fi } ask_version() { INPUT_DEFAULT="1.0" PRINT INPUT "Version [$INPUT_DEFAULT]:" read -r ASKVERSION REDO_VERSION=false # Version should not be empty if [[ ${ASKVERSION} == "" ]]; then ASKVERSION="$INPUT_DEFAULT" fi # Ask again if response does not pass validation. if [[ ${REDO_VERSION} == true ]]; then ASKVERSION=""; ask_version; fi } ask_author() { INPUT_DEFAULT="byte" PRINT INPUT "Author [$INPUT_DEFAULT]:" read -r ASKAUTHOR REDO_AUTHOR=false # Author should not be empty if [[ ${ASKAUTHOR} == "" ]]; then ASKAUTHOR="$INPUT_DEFAULT" fi # Ask again if response does not pass validation. if [[ ${REDO_AUTHOR} == true ]]; then ASKAUTHOR=""; ask_author; fi } ask_template ask_name ask_identifier ask_description ask_version ask_author tnum=${ASKTEMPLATE} PRINT INFO "Fetching templates.." if [[ $(php artisan bp:latest) != "$VERSION" ]]; then PRINT WARNING "Active Blueprint version is not latest, you might run into compatibility issues."; fi cd .blueprint/tmp || cdhalt git clone "https://github.com/BlueprintFramework/templates.git" cd "${FOLDER}"/.blueprint || cdhalt cp -R tmp/templates/* extensions/blueprint/private/build/templates/ rm -R tmp/templates cd "${FOLDER}" || cdhalt eval "$(parse_yaml $__BuildDir/templates/"${tnum}"/TemplateConfiguration.yml t_)" PRINT INFO "Building template.." mkdir -p .blueprint/tmp/init cp -R $__BuildDir/templates/"${tnum}"/contents/* .blueprint/tmp/init/ sed -i \ -e "s~␀name␀~${ASKNAME}~g" \ -e "s~␀identifier␀~${ASKIDENTIFIER}~g" \ -e "s~␀description␀~${ASKDESCRIPTION}~g" \ -e "s~␀ver␀~${ASKVERSION}~g" \ -e "s~␀author␀~${ASKAUTHOR}~g" \ -e "s~␀version␀~${VERSION}~g" \ -e "s~\[name\]~${ASKNAME}~g" \ -e "s~\[identifier\]~${ASKIDENTIFIER}~g" \ -e "s~\[description\]~${ASKDESCRIPTION}~g" \ -e "s~\[ver\]~${ASKVERSION}~g" \ -e "s~\[author\]~${ASKAUTHOR}~g" \ -e "s~\[version\]~${VERSION}~g" \ ".blueprint/tmp/init/conf.yml" # Return files to folder. cp -R .blueprint/tmp/init/* .blueprint/dev/ # Remove tmp files. PRINT INFO "Cleaning up build files.." rm -R \ ".blueprint/tmp" \ "$__BuildDir/templates/"* mkdir -p .blueprint/tmp PRINT SUCCESS "Extension files initialized and imported to '.blueprint/dev'." sendTelemetry "INITIALIZE_DEVELOPMENT_EXTENSION" >> "$BLUEPRINT__DEBUG" fi # -build if [[ ( $2 == "-build" || $2 == "-b" ) ]]; then VCMD="y" # Check for developer mode through the database library. if ! dbValidate "blueprint.developerEnabled"; then PRINT FATAL "Developer mode is not enabled.";exit 2; fi if [[ -z $(find .blueprint/dev -maxdepth 1 -type f -not -name ".gitkeep" -print -quit) ]]; then PRINT FATAL "Development directory is empty." exit 2 fi PRINT INFO "Starting developer extension installation.." blueprint -i "[developer-build]" fi # -export if [[ ( $2 == "-export" || $2 == "-e" ) ]]; then VCMD="y" # Check for developer mode through the database library. if ! dbValidate "blueprint.developerEnabled"; then PRINT FATAL "Developer mode is not enabled.";exit 2; fi if [[ -z $(find .blueprint/dev -maxdepth 1 -type f -not -name ".gitkeep" -print -quit) ]]; then PRINT FATAL "Development directory is empty." exit 2 fi PRINT INFO "Start packaging extension.." cd .blueprint || cdhalt rm dev/.gitkeep 2>> "$BLUEPRINT__DEBUG" eval "$(parse_yaml dev/conf.yml conf_)"; identifier="${conf_info_identifier}" cp -r dev/* tmp/ cd tmp || cdhalt # Assign variables to extension flags. flags="$conf_info_flags" PRINT INFO "Reading and assigning extension flags.." assignflags if $F_hasExportScript; then chmod +x "${conf_data_directory}""/export.sh" # Run script while also parsing some useful variables for the export script to use. if $F_developerEscalateExportScript; then EXTENSION_IDENTIFIER="$conf_info_identifier" \ EXTENSION_TARGET="$conf_info_target" \ EXTENSION_VERSION="$conf_info_version" \ PTERODACTYL_DIRECTORY="$FOLDER" \ BLUEPRINT_EXPORT_DIRECTORY="$FOLDER/.blueprint/tmp" \ BLUEPRINT_VERSION="$VERSION" \ bash "${conf_data_directory}"/export.sh else su "$WEBUSER" -s "$USERSHELL" -c " cd \"$FOLDER\"/.blueprint/tmp; EXTENSION_IDENTIFIER=\"$conf_info_identifier\" \ EXTENSION_TARGET=\"$conf_info_target\" \ EXTENSION_VERSION=\"$conf_info_version\" \ PTERODACTYL_DIRECTORY=\"$FOLDER\" \ BLUEPRINT_EXPORT_DIRECTORY=\"$FOLDER/.blueprint/tmp\" \ BLUEPRINT_VERSION=\"$VERSION\" \ bash \"${conf_data_directory}\"/export.sh " fi echo -e "\e[0m\x1b[0m\033[0m" fi zip -r extension.zip ./* cd "${FOLDER}" || cdhalt cp .blueprint/tmp/extension.zip "${identifier}.blueprint" rm -R .blueprint/tmp mkdir -p .blueprint/tmp if [[ $3 == "expose"* ]]; then PRINT INFO "Generating download url.. (expires after 2 minutes)" randstr=${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM} mkdir .blueprint/extensions/blueprint/assets/exports/${randstr} cp "${identifier}".blueprint .blueprint/extensions/blueprint/assets/exports/${randstr}/"${identifier}".blueprint PRINT SUCCESS "Extension has been exported to '$(grabAppUrl)/assets/extensions/blueprint/exports/${randstr}/${identifier}.blueprint' and '${FOLDER}/${identifier}.blueprint'." sleep 120 && rm -R .blueprint/extensions/blueprint/assets/exports/${randstr} 2>> "$BLUEPRINT__DEBUG" & else PRINT SUCCESS "Extension has been exported to '${FOLDER}/${identifier}.blueprint'." fi sendTelemetry "EXPORT_DEVELOPMENT_EXTENSION" >> "$BLUEPRINT__DEBUG" fi # -wipe if [[ ( $2 == "-wipe" || $2 == "-w" ) ]]; then VCMD="y" # Check for developer mode through the database library. if ! dbValidate "blueprint.developerEnabled"; then PRINT FATAL "Developer mode is not enabled.";exit 2; fi if [[ -z $(find .blueprint/dev -maxdepth 1 -type f -not -name ".gitkeep" -print -quit) ]]; then PRINT FATAL "Development directory is empty." exit 2 fi PRINT INPUT "You are about to wipe all of your development files, are you sure you want to continue? This cannot be undone. (y/N)" read -r YN if [[ ( ( ${YN} != "y"* ) && ( ${YN} != "Y"* ) ) || ( ( ${YN} == "" ) ) ]]; then PRINT INFO "Development files removal cancelled.";exit 1;fi PRINT INFO "Clearing development folder.." rm -R \ .blueprint/dev/* \ .blueprint/dev/.* \ 2>> "$BLUEPRINT__DEBUG" PRINT SUCCESS "Development folder has been cleared." fi # -info if [[ ( $2 == "-info" || $2 == "-f" ) ]]; then VCMD="y" fetchversion() { printf "\x1b[0m\x1b[37m"; if [[ $VERSION != "" ]]; then echo $VERSION; else echo "none"; fi } fetchfolder() { printf "\x1b[0m\x1b[37m"; if [[ $FOLDER != "" ]]; then echo "$FOLDER"; else echo "none"; fi } fetchurl() { printf "\x1b[0m\x1b[37m"; if [[ $(grabAppUrl) != "" ]]; then grabAppUrl; else echo "none"; fi } fetchlocale() { printf "\x1b[0m\x1b[37m"; if [[ $(grabAppLocale) != "" ]]; then grabAppLocale; else echo "none"; fi } fetchtimezone() { printf "\x1b[0m\x1b[37m"; if [[ $(grabAppTimezone) != "" ]]; then grabAppTimezone; else echo "none"; fi } fetchextensions() { printf "\x1b[0m\x1b[37m"; tr -cd ',' <.blueprint/extensions/blueprint/private/db/installed_extensions | wc -c | tr -d ' '; } fetchdeveloper() { printf "\x1b[0m\x1b[37m"; if dbValidate "blueprint.developerEnabled"; then echo "true"; else echo "false"; fi } fetchtelemetry() { printf "\x1b[0m\x1b[37m"; if [[ $(cat .blueprint/extensions/blueprint/private/db/telemetry_id) == "KEY_NOT_UPDATED" ]]; then echo "false"; else echo "true"; fi } fetchnode() { printf "\x1b[0m\x1b[37m"; if [[ $(node -v) != "" ]]; then node -v; else echo "none"; fi } fetchyarn() { printf "\x1b[0m\x1b[37m"; if [[ $(yarn -v) != "" ]]; then yarn -v; else echo "none"; fi } echo " " echo -e "\x1b[34;1m ⣿⣿ Version: $(fetchversion)" echo -e "\x1b[34;1m ⣿⣿ ⣿⣿ Folder: $(fetchfolder)" echo -e "\x1b[34;1m ⣿⣿⣿⣿ URL: $(fetchurl)" echo -e "\x1b[34;1m Locale: $(fetchlocale)" echo -e "\x1b[34;1m Timezone: $(fetchtimezone)" echo -e "\x1b[34;1m Extensions: $(fetchextensions)" echo -e "\x1b[34;1m Developer: $(fetchdeveloper)" echo -e "\x1b[34;1m Telemetry: $(fetchtelemetry)" echo -e "\x1b[34;1m Node: $(fetchnode)" echo -e "\x1b[34;1m Yarn: $(fetchyarn)" echo -e "\x1b[0m" fi # -rerun-install if [[ $2 == "-rerun-install" ]]; then VCMD="y" PRINT WARNING "This is an advanced feature, only proceed if you know what you are doing." dbRemove "blueprint.setupFinished" cd "${FOLDER}" || cdhalt bash blueprint.sh fi # -upgrade if [[ $2 == "-upgrade" ]]; then VCMD="y" PRINT WARNING "This is an advanced feature, only proceed if you know what you are doing." # Confirmation question for developer upgrade. if [[ $3 == "remote" ]]; then PRINT INPUT "Upgrading to the latest development build will update Blueprint to a remote version which might differ from the latest release. Continue? (y/N)" read -r YN if [[ ( ${YN} != "y"* ) && ( ${YN} != "Y"* ) ]]; then PRINT INFO "Upgrade cancelled.";exit 1;fi YN="" fi # Confirmation question for both developer and stable upgrade. PRINT INPUT "Upgrading will wipe your .blueprint folder and will deactivate all active extensions. Continue? (y/N)" read -r YN if [[ ( ${YN} != "y"* ) && ( ${YN} != "Y"* ) ]]; then PRINT INFO "Upgrade cancelled.";exit 1;fi YN="" # Last confirmation question for both developer and stable upgrade. PRINT INPUT "This is the last warning before upgrading/wiping Blueprint. Type 'continue' to continue, all other input will be taken as 'no'." read -r YN if [[ ${YN} != "continue" ]]; then PRINT INFO "Upgrade cancelled.";exit 1;fi YN="" if [[ $3 == "remote" ]]; then PRINT INFO "Fetching and pulling latest commit.." else PRINT INFO "Fetching and pulling latest release.."; fi mkdir "$FOLDER/.tmp" cp blueprint.sh .blueprint.sh.bak HAS_DEV=false if [[ -n $(find .blueprint/dev -maxdepth 1 -type f -not -name ".gitkeep" -print -quit) ]]; then PRINT INFO "Backing up extension development files.." mkdir -p "$FOLDER/.tmp/dev" cp .blueprint/dev/* "$FOLDER/.tmp/dev/" -Rf HAS_DEV=true fi mkdir -p "$FOLDER/.tmp/files" cd "$FOLDER/.tmp/files" || cdhalt if [[ $3 == "remote" ]]; then if [[ $4 == "" ]]; then REMOTE_REPOSITORY="$REPOSITORY" else REMOTE_REPOSITORY="$4"; fi # download latest commit git clone https://github.com/"$REMOTE_REPOSITORY".git main else # download latest release LOCATION=$(curl -s https://api.github.com/repos/"$REPOSITORY"/releases/latest \ | grep "zipball_url" \ | awk '{ print $2 }' \ | sed 's/,$//' \ | sed 's/"//g' ) \ ; curl -L -o main.zip "$LOCATION" unzip main.zip rm main.zip mv ./* main fi if [[ ! -d "main" ]]; then cd "$FOLDER" || cdhalt rm -r "$FOLDER/.tmp" &>> "$BLUEPRINT__DEBUG" rm "$FOLDER/.blueprint.sh.bak" &>> "$BLUEPRINT__DEBUG" PRINT FATAL "Remote does not exist or encountered an error, try again later." exit 1 fi # Remove some files/directories that don't have to be moved to the Pterodactyl folder. rm -r \ "main/.github" \ "main/.git" \ "main/.gitignore" \ "main/README.md" \ &>> "$BLUEPRINT__DEBUG" # Copy fetched release files to the Pterodactyl directory and remove temp files. cp -r main/* "$FOLDER"/ rm -r \ "main" \ "$FOLDER"/.blueprint \ "$FOLDER"/.tmp/files cd "$FOLDER" || cdhalt # Clean up folders with potentially broken symlinks. rm \ "resources/views/blueprint/admin/wrappers/"* \ "resources/views/blueprint/dashboard/wrappers/"* \ "routes/blueprint/application/"* \ "routes/blueprint/client/"* \ "routes/blueprint/web/"* \ &>> /dev/null # cannot forward to debug dir because it does not exist chmod +x blueprint.sh sed -i -E \ -e "s|OWNERSHIP=\"www-data:www-data\" #;|OWNERSHIP=\"$OWNERSHIP\" #;|g" \ -e "s|WEBUSER=\"www-data\" #;|WEBUSER=\"$WEBUSER\" #;|g" \ -e "s|USERSHELL=\"/bin/bash\" #;|USERSHELL=\"$USERSHELL\" #;|g" \ "$FOLDER/blueprint.sh" mv "$FOLDER/blueprint" "$FOLDER/.blueprint" bash blueprint.sh --post-upgrade # Ask user if they'd like to migrate their database. PRINT INPUT "Would you like to migrate your database? (Y/n)" read -r YN if [[ ( $YN == "y"* ) || ( $YN == "Y"* ) || ( $YN == "" ) ]]; then PRINT INFO "Running database migrations.." php artisan migrate --force php artisan up &>> "$BLUEPRINT__DEBUG" else PRINT INFO "Database migrations have been skipped." fi YN="" if [[ ${HAS_DEV} == true ]]; then PRINT INFO "Restoring extension development files.." mkdir -p .blueprint/dev cp "$FOLDER/.tmp/dev/"* .blueprint/dev -r rm "$FOLDER/.tmp/dev" -rf fi rm -r "$FOLDER/.tmp" # Post-upgrade checks. PRINT INFO "Validating update.." score=0 if dbValidate "blueprint.setupFinished"; then score=$((score+1)) else PRINT WARNING "'blueprint.setupFinished' could not be detected or found."; fi # Finalize upgrade. if [[ ${score} == 1 ]]; then PRINT SUCCESS "Upgrade finished." rm .blueprint.sh.bak exit 0 # success elif [[ ${score} == 0 ]]; then PRINT FATAL "All checks have failed. The 'blueprint.sh' file has been reverted." rm blueprint.sh mv .blueprint.sh.bak blueprint.sh exit 1 # error else PRINT FATAL "Some checks have failed. The 'blueprint.sh' file has been reverted." rm blueprint.sh mv .blueprint.sh.bak blueprint.sh exit 1 # error fi fi # When the users attempts to run an invalid command. if [[ ${VCMD} != "y" && $1 == "-bash" ]]; then # This is logged as a "fatal" error since it's something that is making Blueprint run unsuccessfully. PRINT FATAL "'$2' is not a valid command or argument. Use argument '-help' for a list of commands." exit 2 fi