From 599d333afb4cc82e85e48a06d7f1b62f8f558937 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:41:13 +0800 Subject: [PATCH 01/35] refactor: refactor code structure --- .air.toml | 4 +- .gitignore | 4 +- .vscode/settings.json | 2 +- Dockerfile | 26 +--- README.md | 18 ++- README_cn.md | 16 +-- {internal/cmd => cmd/gen}/gen.go | 12 +- cmd/server/server.go | 86 ++++++++++++ config/app.go | 28 ---- config/config.go | 9 -- fs_dev.go | 12 -- fs_prod.go | 20 --- go.mod | 26 ++-- go.sum | 68 ++++++---- internal/app/app.go | 128 +----------------- internal/app/booter.go | 45 ------ internal/app/bootstrap.go | 94 ++++++++++--- internal/app/build/var.go | 11 ++ internal/app/config.go | 42 ++++++ internal/app/const.go | 3 + internal/app/mode.go | 25 ++++ internal/app/router.go | 5 - {config => internal/app}/settings.go | 30 +--- internal/app/version.go | 16 +++ internal/cfg/cfg.go | 89 ++++++------ internal/cfg/cfg_darwin.go | 7 +- internal/cfg/cfg_dev.go | 15 -- internal/cfg/cfg_linux.go | 7 +- internal/cfg/cfg_prod.go | 7 - internal/cfg/cfg_windows.go | 4 +- internal/cfg/entity.go | 73 ---------- internal/cmd/main.go | 27 ---- internal/cmd/server.go | 32 ----- internal/db/config.go | 7 + internal/db/db.go | 22 +-- {ipa => internal/ipa}/ipa.go | 0 {ipa => internal/ipa}/package_info.go | 0 .../manager}/device_manager.go | 6 +- .../manager}/device_manager_avahi.go | 2 +- .../manager}/device_manager_mdns.go | 2 +- .../manager}/device_manager_usbmuxd.go | 2 +- {manager => internal/manager}/lockdown.go | 9 +- {manager => internal/manager}/manager.go | 2 +- internal/mode/mode.go | 31 ----- {model => internal/model}/device.go | 0 {model => internal/model}/installed_app.go | 0 {model => internal/model}/ipa_file.go | 0 {model => internal/model}/lockdown_device.go | 0 {model => internal/model}/service_status.go | 0 {model => internal/model}/usbmuxd_device.go | 0 {model => internal/model}/usbmuxd_image.go | 0 {notify => internal/notify}/notify.go | 8 +- {notify => internal/notify}/wecom/wecom.go | 0 {service => internal/service}/db.go | 8 +- {service => internal/service}/service.go | 42 +++--- {service => internal/service}/service_test.go | 4 - {task => internal/task}/task.go | 36 ++--- {tty => internal/tty}/lib/function.go | 4 +- {tty => internal/tty}/lib/message.go | 0 {tty => internal/tty}/pipeline.go | 2 +- {tty => internal/tty}/tty.go | 4 +- internal/version/build.go | 25 ---- main.go | 50 +++---- {router => web}/api_result.go | 2 +- web/fs_dev.go | 12 ++ web/fs_prod.go | 20 +++ {router => web}/router.go | 56 ++++---- {view => web/static}/index.html | 0 {view => web/static}/package-lock.json | 0 {view => web/static}/package.json | 0 {view => web/static}/postcss.config.js | 0 {view => web/static}/public/img/dummy.jpg | Bin {view => web/static}/src/App.vue | 0 {view => web/static}/src/api/api.js | 0 {view => web/static}/src/app.css | 0 .../static}/src/assets/icons/appletv.svg | 0 .../static}/src/assets/icons/checkmark.svg | 0 .../static}/src/assets/icons/dismiss.svg | 0 .../static}/src/assets/icons/github.svg | 0 .../static}/src/assets/icons/help.svg | 0 .../static}/src/assets/icons/language.svg | 0 .../static}/src/assets/icons/settings.svg | 0 .../static}/src/assets/icons/warning.svg | 0 {view => web/static}/src/i18n.js | 0 .../static}/src/locales/en/translation.json | 0 .../src/locales/zh_cn/translation.json | 0 {view => web/static}/src/main.js | 0 {view => web/static}/src/page/home/index.vue | 0 .../static}/src/page/install/index.vue | 32 +---- {view => web/static}/src/page/layout.vue | 0 {view => web/static}/src/page/pair/index.vue | 0 .../static}/src/page/settings/index.vue | 0 {view => web/static}/src/router/index.js | 0 {view => web/static}/src/utils/crypto.js | 0 {view => web/static}/src/utils/request.js | 0 {view => web/static}/tailwind.config.js | 0 {view => web/static}/vite.config.js | 0 {view => web/static}/yarn.lock | 0 web/web.go | 31 +++++ 99 files changed, 610 insertions(+), 800 deletions(-) rename {internal/cmd => cmd/gen}/gen.go (82%) create mode 100644 cmd/server/server.go delete mode 100644 config/app.go delete mode 100644 config/config.go delete mode 100644 fs_dev.go delete mode 100644 fs_prod.go delete mode 100644 internal/app/booter.go create mode 100644 internal/app/build/var.go create mode 100644 internal/app/config.go create mode 100644 internal/app/const.go create mode 100644 internal/app/mode.go delete mode 100644 internal/app/router.go rename {config => internal/app}/settings.go (76%) create mode 100644 internal/app/version.go delete mode 100644 internal/cfg/cfg_dev.go delete mode 100644 internal/cfg/cfg_prod.go delete mode 100644 internal/cfg/entity.go delete mode 100644 internal/cmd/main.go delete mode 100644 internal/cmd/server.go create mode 100644 internal/db/config.go rename {ipa => internal/ipa}/ipa.go (100%) rename {ipa => internal/ipa}/package_info.go (100%) rename {manager => internal/manager}/device_manager.go (95%) rename {manager => internal/manager}/device_manager_avahi.go (98%) rename {manager => internal/manager}/device_manager_mdns.go (98%) rename {manager => internal/manager}/device_manager_usbmuxd.go (97%) rename {manager => internal/manager}/lockdown.go (78%) rename {manager => internal/manager}/manager.go (90%) delete mode 100644 internal/mode/mode.go rename {model => internal/model}/device.go (100%) rename {model => internal/model}/installed_app.go (100%) rename {model => internal/model}/ipa_file.go (100%) rename {model => internal/model}/lockdown_device.go (100%) rename {model => internal/model}/service_status.go (100%) rename {model => internal/model}/usbmuxd_device.go (100%) rename {model => internal/model}/usbmuxd_image.go (100%) rename {notify => internal/notify}/notify.go (87%) rename {notify => internal/notify}/wecom/wecom.go (100%) rename {service => internal/service}/db.go (93%) rename {service => internal/service}/service.go (74%) rename {service => internal/service}/service_test.go (84%) rename {task => internal/task}/task.go (83%) rename {tty => internal/tty}/lib/function.go (87%) rename {tty => internal/tty}/lib/message.go (100%) rename {tty => internal/tty}/pipeline.go (98%) rename {tty => internal/tty}/tty.go (93%) delete mode 100644 internal/version/build.go rename {router => web}/api_result.go (95%) create mode 100644 web/fs_dev.go create mode 100644 web/fs_prod.go rename {router => web}/router.go (85%) rename {view => web/static}/index.html (100%) rename {view => web/static}/package-lock.json (100%) rename {view => web/static}/package.json (100%) rename {view => web/static}/postcss.config.js (100%) rename {view => web/static}/public/img/dummy.jpg (100%) rename {view => web/static}/src/App.vue (100%) rename {view => web/static}/src/api/api.js (100%) rename {view => web/static}/src/app.css (100%) rename {view => web/static}/src/assets/icons/appletv.svg (100%) rename {view => web/static}/src/assets/icons/checkmark.svg (100%) rename {view => web/static}/src/assets/icons/dismiss.svg (100%) rename {view => web/static}/src/assets/icons/github.svg (100%) rename {view => web/static}/src/assets/icons/help.svg (100%) rename {view => web/static}/src/assets/icons/language.svg (100%) rename {view => web/static}/src/assets/icons/settings.svg (100%) rename {view => web/static}/src/assets/icons/warning.svg (100%) rename {view => web/static}/src/i18n.js (100%) rename {view => web/static}/src/locales/en/translation.json (100%) rename {view => web/static}/src/locales/zh_cn/translation.json (100%) rename {view => web/static}/src/main.js (100%) rename {view => web/static}/src/page/home/index.vue (100%) rename {view => web/static}/src/page/install/index.vue (90%) rename {view => web/static}/src/page/layout.vue (100%) rename {view => web/static}/src/page/pair/index.vue (100%) rename {view => web/static}/src/page/settings/index.vue (100%) rename {view => web/static}/src/router/index.js (100%) rename {view => web/static}/src/utils/crypto.js (100%) rename {view => web/static}/src/utils/request.js (100%) rename {view => web/static}/tailwind.config.js (100%) rename {view => web/static}/vite.config.js (100%) rename {view => web/static}/yarn.lock (100%) create mode 100644 web/web.go diff --git a/.air.toml b/.air.toml index 1641df7..2154641 100644 --- a/.air.toml +++ b/.air.toml @@ -7,7 +7,7 @@ tmp_dir = "tmp" [build] # Just plain old shell command. You could use `make` as well. -cmd = "go build -tags dev -o ./tmp/main ." +cmd = "go build -o ./tmp/main ." # Binary file yields from `cmd`. bin = "tmp/main" # Customize binary. @@ -15,7 +15,7 @@ full_bin = "APP_ENV=dev APP_USER=air ./tmp/main server -vv" # Watch these filename extensions. include_ext = ["go", "tpl", "tmpl", "html", "vue", "js", "css", "woff", "ttf"] # Ignore these filename extensions or directories. -exclude_dir = ["tmp", "libs", "vendor", "view"] +exclude_dir = ["tmp", "libs", "vendor", "view", "web/static"] # Watch these directories if you specified. include_dir = [] # Exclude files. diff --git a/.gitignore b/.gitignore index e6fd2a1..4e0795e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,9 +17,9 @@ vendor/ tmp/ dist/ -build/ +/build node_modules/ -go-docker-skeleton +atvloadly main AltServerData/ atvloadly \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 616e01a..9c84d82 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,7 @@ "json" ], "i18n-ally.localesPaths": [ - "view/src/locales" + "web/static/src/locales" ], "i18n-ally.keystyle": "nested" } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2cd0335..75e21dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,28 +27,19 @@ RUN case ${TARGETARCH} in \ && dpkg -i ./libimobiledevice_1.3.1-1_${PKG_ARCH}.deb \ && dpkg -i ./usbmuxd2_1.0.0-1_${PKG_ARCH}.deb -# 安装anisette-server,用于模拟本机为MacBook +# 安装Sideloader RUN case ${TARGETARCH} in \ "amd64") PKG_ARCH=x86_64 ;; \ "arm64") PKG_ARCH=aarch64 ;; \ esac \ && cd /tmp \ - && wget https://github.com/Dadoum/Provision/releases/download/2.1.0/anisette-server-${PKG_ARCH} \ - && mv anisette-server-${PKG_ARCH} /usr/bin/anisette-server \ - && chmod +x /usr/bin/anisette-server - -# 安装AltStore -RUN case ${TARGETARCH} in \ - "amd64") PKG_ARCH=x86_64 ;; \ - "arm64") PKG_ARCH=aarch64 ;; \ - esac \ - && cd /tmp \ - && wget https://github.com/NyaMisty/AltServer-Linux/releases/download/v0.0.5/AltServer-${PKG_ARCH} \ - && mv AltServer-${PKG_ARCH} /usr/bin/AltServer \ - && chmod +x /usr/bin/AltServer + && wget https://github.com/bitxeno/Sideloader/releases/download/1.0-alpha/sideloader-cli-${PKG_ARCH}-linux-gnu.tar.gz \ + && tar zxf sideloader-cli-${PKG_ARCH}-linux-gnu.tar.gz \ + && mv sideloader-cli-${PKG_ARCH}-linux-gnu /usr/bin/sideloader \ + && chmod +x /usr/bin/sideloader # 安装tzdata支持更新时区 -RUN DEBIAN_FRONTEND=noninteractive TZ=Asia/Shanghai apt-get -y install tzdata +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install tzdata # 清空apt缓存和临时数据,减小镜像大小 RUN apt-get clean @@ -66,21 +57,18 @@ RUN rm -rf /var/lib/lockdown && mkdir -p /data/lockdown && ln -s /data/lockdown # 生成启动脚本 -COPY ./doc/scripts/anisette-server /etc/init.d/anisette-server -RUN chmod +x /etc/init.d/anisette-server COPY ./doc/scripts/usbmuxd /etc/init.d/usbmuxd RUN chmod +x /etc/init.d/usbmuxd RUN printf '#!/bin/sh \n\n\ mkdir -p /data/lockdown \n\ -mkdir -p /data/AltServer \n\ +mkdir -p /data/Sideloader \n\ if [ ! -f "/data/config.yaml" ]; then \n\ cp /doc/config.yaml /data/config.yaml \n\ fi \n\ /etc/init.d/usbmuxd start \n\ -/etc/init.d/anisette-server start \n\ /usr/bin/%s server -p ${SERVICE_PORT:-80} -c /data/config.yaml \n\ \n\ diff --git a/README.md b/README.md index 933a602..65a15cc 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@
-[![platform](https://img.shields.io/badge/platform-linux%20%7C%20openwrt-989898)](https://github.com/bitxeno/atvloadly/releases) -[![release](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/latest_tag?label=docker%20latest)](https://github.com/bitxeno/atvloadly/pkgs/container/atvloadly) -[![image size](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/size)](https://github.com/bitxeno/atvloadly/pkgs/container/atvloadly) -[![license](https://img.shields.io/github/license/bitxeno/atvloadly)](https://github.com/bitxeno/atvloadly/blob/master/LICENSE) +[![platform](https://img.shields.io/badge/platform-linux%20%7C%20openwrt-989898)](https://github.com/bitxeno/atvloadly/internal/releases) +[![release](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/latest_tag?label=docker%20latest)](https://github.com/bitxeno/atvloadly/internal/pkgs/container/atvloadly) +[![image size](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/size)](https://github.com/bitxeno/atvloadly/internal/pkgs/container/atvloadly) +[![license](https://img.shields.io/github/license/bitxeno/atvloadly)](https://github.com/bitxeno/atvloadly/internal/blob/master/LICENSE) [![Telegram](https://img.shields.io/badge/telegram-2CA5E0?logo=telegram&logoColor=white)](https://t.me/atvloadly)
@@ -22,7 +22,7 @@ English | [中文](./README_cn.md) > ⚠️ **Not supported on tvOS 17.0 and above systems.** ⚠️ -atvloadly is a web service that supports sideloading app on Apple TV. It uses [AltServer](https://github.com/NyaMisty/AltServer-Linux) as the underlying technology for sideloading and automatically refreshes the app to ensure its long-term availability. +atvloadly is a web service that supports sideloading app on Apple TV. It uses [Sideloader](https://github.com/Dadoum/Sideloader) as the underlying technology for sideloading and automatically refreshes the app to ensure its long-term availability. ## Features @@ -98,12 +98,16 @@ atvloadly is a web service that supports sideloading app on Apple TV. It uses [A 3. Can App-specific passwords be used for passwords? Is it more secure this way? -> AltServer currently does not support it. +> Sideloader currently does not support it. + +4. 2 Step Verification +> When your Apple account has 2 factor verification enabled, you'll automatically be asked to verify your identity. If you have a trusted device configured for your account, then a code will appear on the device. If you don't have any devices configured, but have trusted a phone number, then a code will be sent to your phone. +> https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification ## How to build -[>> wiki](https://github.com/bitxeno/atvloadly/wiki/How-to-build) +[>> wiki](https://github.com/bitxeno/atvloadly/internal/wiki/How-to-build) ## Donation diff --git a/README_cn.md b/README_cn.md index 746fddc..b0151de 100644 --- a/README_cn.md +++ b/README_cn.md @@ -5,10 +5,10 @@
-[![platform](https://img.shields.io/badge/platform-linux%20%7C%20openwrt-989898)](https://github.com/bitxeno/atvloadly/releases) -[![release](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/latest_tag?label=docker%20latest)](https://github.com/bitxeno/atvloadly/pkgs/container/atvloadly) -[![image size](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/size)](https://github.com/bitxeno/atvloadly/pkgs/container/atvloadly) -[![license](https://img.shields.io/github/license/bitxeno/atvloadly)](https://github.com/bitxeno/atvloadly/blob/master/LICENSE) +[![platform](https://img.shields.io/badge/platform-linux%20%7C%20openwrt-989898)](https://github.com/bitxeno/atvloadly/internal/releases) +[![release](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/latest_tag?label=docker%20latest)](https://github.com/bitxeno/atvloadly/internal/pkgs/container/atvloadly) +[![image size](https://ghcr-badge.egpl.dev/bitxeno/atvloadly/size)](https://github.com/bitxeno/atvloadly/internal/pkgs/container/atvloadly) +[![license](https://img.shields.io/github/license/bitxeno/atvloadly)](https://github.com/bitxeno/atvloadly/internal/blob/master/LICENSE) [![Telegram](https://img.shields.io/badge/telegram-2CA5E0?logo=telegram&logoColor=white)](https://t.me/atvloadly) @@ -22,7 +22,7 @@ > ⚠️ **不支持 tvOS 17.0 以上系统** ⚠️ -atvloadly 是一个支持在 AppleTV 上侧载应用的 web 服务。底层通过使用 [AltServer](https://github.com/NyaMisty/AltServer-Linux) 实现侧载,并会自动刷新 App 以保证其长期可用性。 +atvloadly 是一个支持在 AppleTV 上侧载应用的 web 服务。底层通过使用 [Sideloader](https://github.com/Dadoum/Sideloader) 实现侧载,并会自动刷新 App 以保证其长期可用性。 ## 主要功能 @@ -100,17 +100,17 @@ atvloadly 是一个支持在 AppleTV 上侧载应用的 web 服务。底层通 3、密码可以使用App-specific password吗,这样安全些 -> AltServer 目前不支持 +> Sideloader 目前不支持 ## 推荐开源 App -[>> wiki](https://github.com/bitxeno/atvloadly/wiki/AppleTV-App) +[>> wiki](https://github.com/bitxeno/atvloadly/internal/wiki/AppleTV-App) ## 如何开发编译 -[>> wiki](https://github.com/bitxeno/atvloadly/wiki/How-to-build) +[>> wiki](https://github.com/bitxeno/atvloadly/internal/wiki/How-to-build) ## 赞助 diff --git a/internal/cmd/gen.go b/cmd/gen/gen.go similarity index 82% rename from internal/cmd/gen.go rename to cmd/gen/gen.go index 1cbba0f..d60b060 100644 --- a/internal/cmd/gen.go +++ b/cmd/gen/gen.go @@ -1,11 +1,11 @@ -package cmd +package gen import ( "github.com/urfave/cli/v2" ) var ( - genFlags = []cli.Flag{ + flags = []cli.Flag{ &cli.StringFlag{ Name: "config", Aliases: []string{"c"}, @@ -14,15 +14,15 @@ var ( }, } - genCommand = &cli.Command{ + Command = &cli.Command{ Name: "gen", Usage: "Generate example server code", - Flags: genFlags, - Action: generateAction, + Flags: flags, + Action: action, } ) -func generateAction(c *cli.Context) error { +func action(c *cli.Context) error { // if utils.Exists(app.ConfigFilePath) { // fmt.Printf("Config has exist. >>> %s\n", app.ConfigFilePath) // return nil diff --git a/cmd/server/server.go b/cmd/server/server.go new file mode 100644 index 0000000..17bbcde --- /dev/null +++ b/cmd/server/server.go @@ -0,0 +1,86 @@ +package server + +import ( + "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/manager" + "github.com/bitxeno/atvloadly/internal/task" + "github.com/bitxeno/atvloadly/web" + "github.com/urfave/cli/v2" +) + +var ( + flags = []cli.Flag{ + &cli.IntFlag{ + Name: "port", + Aliases: []string{"p"}, + Usage: "Define an alternate web server port", + }, + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Usage: "Load configuration from `FILE`", + }, + &cli.BoolFlag{ + Name: "debug", + Aliases: []string{"vv"}, + Usage: "Enable debug output", + Value: false, + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"vvv"}, + Usage: "Enable verbose output", + Value: false, + }, + } + + Command = &cli.Command{ + Name: "server", + Usage: "Run web server", + Flags: flags, + Action: action, + } +) + +func action(c *cli.Context) error { + // init config + debug := false + if c.Bool("debug") || c.Bool("verbose") { + debug = true + } + conf, err := app.InitConfig(c.String("config"), debug) + if err != nil { + return err + } + if err = app.InitSettings(conf, debug); err != nil { + return err + } + + // init logger + if c.Bool("debug") { + conf.Log.Level = "debug" + conf.Db.Debug = true + } + if c.Bool("verbose") { + conf.Log.Level = "trace" + conf.Db.Debug = true + } + if err := app.InitLogger(conf); err != nil { + return err + } + + // init db + if err := app.InitDb(conf); err != nil { + return err + } + + // start jobs + _ = task.ScheduleRefreshApps() + manager.StartDeviceManager() + + port := conf.Server.Port + if c.Int("port") > 0 { + port = c.Int("port") + } + return web.Run(conf.Server.ListenAddr, port) +} diff --git a/config/app.go b/config/app.go deleted file mode 100644 index 000f288..0000000 --- a/config/app.go +++ /dev/null @@ -1,28 +0,0 @@ -package config - -import ( - "github.com/bitxeno/atvloadly/internal/app" - "github.com/creasty/defaults" -) - -var App AppConfiguration - -type AppConfiguration struct { - LockdownDir string `koanf:"lockdown_dir" default:"/var/lib/lockdown"` - DeveloperDiskImage struct { - ImageSource string `koanf:"image_source" json:"image_source" default:"https://github.com/haikieu/xcode-developer-disk-image-all-platforms/raw/master/DiskImages/AppleTVOS.platform/DeviceSupport/{0}.zip"` - CNProxy string `koanf:"cn_proxy" json:"cn_proxy" default:"https://mirror.ghproxy.com"` - } `koanf:"developer_disk_image" json:"developer_disk_image"` -} - -func loadApp() error { - // set default value - if err := defaults.Set(&App); err != nil { - return err - } - if err := app.Cfg().BindStruct("app", &App); err != nil { - return err - } - - return nil -} diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 0e85038..0000000 --- a/config/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package config - -func Load() error { - if err := loadApp(); err != nil { - return err - } - - return loadSettings() -} diff --git a/fs_dev.go b/fs_dev.go deleted file mode 100644 index 2f448cf..0000000 --- a/fs_dev.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build dev - -package main - -import ( - "io/fs" - "os" -) - -func getViewAssets() fs.FS { - return os.DirFS("view/dist") -} diff --git a/fs_prod.go b/fs_prod.go deleted file mode 100644 index 8a4e7a9..0000000 --- a/fs_prod.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build !dev - -package main - -import ( - "embed" - "io/fs" -) - -//go:embed all:view/dist/* -var view embed.FS - -func getViewAssets() fs.FS { - embed, err := fs.Sub(view, "view/dist") - if err != nil { - panic(err) - } - - return embed -} diff --git a/go.mod b/go.mod index 56fe866..d27256b 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,8 @@ require ( github.com/creasty/defaults v1.5.2 github.com/electricbubble/gidevice v0.6.2 github.com/fatih/color v1.9.0 + github.com/glebarez/sqlite v1.11.0 + github.com/go-errors/errors v1.5.1 github.com/go-resty/resty/v2 v2.6.0 github.com/godbus/dbus/v5 v5.0.4 github.com/gofiber/contrib/websocket v1.0.0 @@ -18,17 +20,17 @@ require ( github.com/iineva/ipa-server v0.0.0-20210613102156-5a07a87df9a8 github.com/json-iterator/go v1.1.12 github.com/knadh/koanf v1.5.0 + github.com/mitchellh/go-ps v1.0.0 github.com/nikoksr/notify v0.41.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/rs/zerolog v1.29.1 github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a - github.com/shirou/gopsutil/v3 v3.23.5 + github.com/shirou/gopsutil/v4 v4.24.5 github.com/silenceper/wechat/v2 v2.1.5 github.com/urfave/cli/v2 v2.3.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gorm.io/driver/sqlite v1.1.4 - gorm.io/gorm v1.21.14 + gorm.io/gorm v1.25.7 howett.net/plist v1.0.0 ) @@ -41,15 +43,17 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/creack/pty v1.1.17 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fasthttp/websocket v1.5.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc // indirect github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect github.com/google/uuid v1.3.0 // indirect github.com/iamacarpet/go-winpty v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.2 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -57,7 +61,6 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mattn/go-sqlite3 v1.14.5 // indirect github.com/miekg/dns v1.1.41 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -67,6 +70,7 @@ require ( github.com/pelletier/go-toml v1.9.4 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect @@ -75,17 +79,21 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/tinylib/msgp v1.1.8 // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.10.0 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.9.0 // indirect + golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.22.5 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/sqlite v1.23.1 // indirect ) replace github.com/iineva/bom v0.0.0-20210605043415-7d45ba1bcca3 => github.com/bitxeno/bom v0.0.0-20230705034132-b3d31612b8ac diff --git a/go.sum b/go.sum index c257974..d0a01c7 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -130,6 +132,12 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= +github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -193,10 +201,10 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -278,9 +286,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= @@ -345,8 +352,6 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -360,6 +365,8 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -470,6 +477,9 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/qiniu/go-sdk/v7 v7.9.5/go.mod h1:Eeqk1/Km3f1MuLUUkg2JCSg/dVkydKbBvEdJJqFgn9g= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -501,12 +511,11 @@ github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJv github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y= -github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY= +github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM= +github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shogo82148/androidbinary v1.0.2/go.mod h1:c3BBft4TLXkvqry+EEN8z9ZtyY09r7jLMvFB/L/KrfI= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/silenceper/wechat/v2 v2.1.5 h1:eIlv61v2bAFBG9ZE75zuRC0ALHEZEUq8JlJ9tfKvatg= @@ -530,8 +539,6 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -539,9 +546,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -550,10 +555,10 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -572,8 +577,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= @@ -722,12 +727,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -844,11 +849,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= -gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= -gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.14 h1:NAR9A/3SoyiPVHouW/rlpMUZvuQZ6Z6UYGz+2tosSQo= -gorm.io/gorm v1.21.14/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -856,6 +858,14 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt howett.net/plist v0.0.0-20201203080718-1454fab16a06/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= +modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= +modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/internal/app/app.go b/internal/app/app.go index c82bc68..5bda8dd 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,139 +1,19 @@ package app import ( - "fmt" - "math" - - "github.com/bitxeno/atvloadly/internal/cfg" - "github.com/bitxeno/atvloadly/internal/cmd" "github.com/bitxeno/atvloadly/internal/db" - "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/internal/mode" - "github.com/bitxeno/atvloadly/internal/version" - "github.com/fatih/color" - "github.com/gofiber/fiber/v2" - "github.com/urfave/cli/v2" "gorm.io/gorm" ) -var instance *Application - -type Application struct { - Name string - Desc string - Version version.Info - Mode mode.AppMode // 用于加载不同环境变量和配置文件 - Port int - DebugLog bool - VerboseLog bool - - server *fiber.App - routers []RouteFunc -} - -func New(name string, desc string) *Application { - instance = &Application{ - Name: name, - Desc: desc, - Version: version.Get(), - Mode: mode.Get(), - DebugLog: false, - VerboseLog: false, - server: fiber.New(fiber.Config{ - BodyLimit: math.MaxInt, - }), - routers: []RouteFunc{}, - } - - return instance -} - -func (a *Application) AddBoot(fn BootFunc) { - addBoot(fn) -} - -func (a *Application) AddBoots(boots ...Bootstrapper) { - addBoots(boots...) -} - -func (a *Application) Route(fn RouteFunc) { - a.routers = append(a.routers, fn) -} - -func (a *Application) Run(arguments []string) (err error) { - return cmd.Run(a.Name, a.Desc, arguments, func(c *cli.Context) error { - a.Port = c.Int("port") - a.DebugLog = c.Bool("debug") - a.VerboseLog = c.Bool("verbose") - - a.runWeb(c.String("config")) - return nil - }) -} - -func (a *Application) runWeb(configFile string) { - // load config. 配置优先级:命令行参数 > 环境变量 > 配置文件 - if configFile != "" { - cfg.Server.SetPath(configFile) - } - cfg.Server.Load() - - // run bootstrap middleware - bootLauncher.Prepend( - BootFunc(initLogger), - BootFunc(initDb), - ) - bootLauncher.Run() - - // add web router - for _, router := range a.routers { - router(a.server) - } - - // run web server - listenPort := a.Port - if listenPort <= 0 { - listenPort = cfg.Server.Port - } - - a.printAppVersion() - err := a.server.Listen(fmt.Sprintf("%s:%d", cfg.Server.ListenAddr, listenPort)) - if err != nil { - log.Error(err.Error()) - } -} - -func (a *Application) printAppVersion() { - color.New(color.FgGreen).Printf("Starting %s version: ", a.Name) - color.New(color.FgCyan).Printf("%s@%s@%v\n", a.Version.Version, a.Version.BuildDate, a.Mode) -} - -func (a *Application) SetConfigPath(path string) { - cfg.Server.SetPath(path) -} - -func Environment() mode.AppMode { - return instance.Mode -} - -func DevelopmentMode() bool { - return mode.IsDevelopmentMode() -} - -func Name() string { - return instance.Name -} - -func Version() version.Info { - return instance.Version +func Environment() AppMode { + return Mode } func ReloadConfig() { - cfg.Server.Reload() } -func Cfg() *cfg.ServerConfiguration { - return cfg.Server +func Cfg() *Configuration { + return Config } func Db() *gorm.DB { diff --git a/internal/app/booter.go b/internal/app/booter.go deleted file mode 100644 index 0aacc71..0000000 --- a/internal/app/booter.go +++ /dev/null @@ -1,45 +0,0 @@ -package app - -import "github.com/bitxeno/atvloadly/internal/log" - -var bootLauncher = &Launcher{} - -type Bootstrapper interface { - // Name() string - Boot() error -} - -type BootFunc func() error - -func (bf BootFunc) Boot() error { - return bf() -} - -type Launcher struct { - Boots []Bootstrapper -} - -func (l *Launcher) Add(boots ...Bootstrapper) { - l.Boots = append(l.Boots, boots...) -} - -func (l *Launcher) Prepend(boots ...Bootstrapper) { - l.Boots = append(boots, l.Boots...) -} - -func (l *Launcher) Run() { - for _, boot := range l.Boots { - err := boot.Boot() - if err != nil { - log.Panic(err.Error()) - } - } -} - -func addBoots(boots ...Bootstrapper) { - bootLauncher.Boots = append(bootLauncher.Boots, boots...) -} - -func addBoot(fn BootFunc) { - bootLauncher.Boots = append(bootLauncher.Boots, BootFunc(fn)) -} diff --git a/internal/app/bootstrap.go b/internal/app/bootstrap.go index 62801cc..8038567 100644 --- a/internal/app/bootstrap.go +++ b/internal/app/bootstrap.go @@ -1,34 +1,94 @@ package app import ( + "path/filepath" + "github.com/bitxeno/atvloadly/internal/cfg" + "github.com/bitxeno/atvloadly/internal/db" "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/internal/mode" - "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/bitxeno/atvloadly/internal/model" + "github.com/creasty/defaults" ) -func initLogger() error { +// load config from file +func InitConfig(path string, debug bool) (*Configuration, error) { + var configuration Configuration + if err := defaults.Set(&configuration); err != nil { + return nil, err + } + if path == "" { + path = filepath.Join(cfg.DefaultConfigDir(), "config.yaml") + } + c, err := cfg.Load(path) + if err != nil { + return nil, err + } + if err := c.BindStruct(&configuration); err != nil { + return nil, err + } + if configuration.Server.DataDir == "" { + configuration.Server.DataDir = cfg.DefaultConfigDir() + } + Config = &configuration + + if debug { + c.PrintConfig() + } + + return &configuration, nil +} + +// load settings from file +func InitSettings(conf *Configuration, debug bool) error { + var settings SettingsConfiguration + if err := defaults.Set(&settings); err != nil { + return err + } + + confDir := conf.Server.DataDir + if confDir == "" { + confDir = cfg.DefaultConfigDir() + } + path := filepath.Join(confDir, "settings.json") + c, err := cfg.Load(path) + if err != nil { + return err + } + if err := c.BindStruct(&settings); err != nil { + return err + } + go startSaveSettingsJob(path) + Settings = &settings + + if debug { + c.PrintConfig() + } + + return nil +} + +func InitLogger(conf *Configuration) error { // set normal log - log.AddFileOutput(cfg.Server.Log) - if instance.DebugLog || mode.IsDevelopmentMode() { + log.AddFileOutput(conf.Log.LogFile) + if conf.Log.Level == "debug" { log.SetDebugLevel() } - if instance.VerboseLog { + if conf.Log.Level == "trace" { log.SetTraceLevel() } - - // set fiber web server access log - instance.server.Use(logger.New()) - accessWriter := log.CreateRollingLogFile(cfg.Server.AccessLog) - if accessWriter != nil { - instance.server.Use(logger.New(logger.Config{ - Output: accessWriter, - })) - log.Infof("Web access log file path: %s", cfg.Server.AccessLog) - } return nil } -func initDb() error { +func InitDb(conf *Configuration) error { + if conf.Db.Path == "" { + conf.Db.Path = conf.Server.DataDir + } + if conf.Db.Path == "" { + conf.Db.Path = cfg.DefaultConfigDir() + } + if err := db.Open(conf.Db).AutoMigrate(&model.InstalledApp{}); err != nil { + return err + } + return nil } diff --git a/internal/app/build/var.go b/internal/app/build/var.go new file mode 100644 index 0000000..438b743 --- /dev/null +++ b/internal/app/build/var.go @@ -0,0 +1,11 @@ +package build + +var ( + /*********Will auto update by ci build *********/ + Version = "unknown" + Commit = "unknown" + BuildDate = "unknown" + + Mode = "development" + /*********Will auto update by ci build *********/ +) diff --git a/internal/app/config.go b/internal/app/config.go new file mode 100644 index 0000000..8232cfb --- /dev/null +++ b/internal/app/config.go @@ -0,0 +1,42 @@ +package app + +import ( + "fmt" + + "github.com/bitxeno/atvloadly/internal/db" +) + +var ( + Config *Configuration +) + +// configuration holds any kind of configuration that comes from the outside world and +// is necessary for running the application. +type Configuration struct { + App struct { + LockdownDir string `koanf:"lockdown_dir" default:"/var/lib/lockdown"` + DeveloperDiskImage struct { + ImageSource string `koanf:"image_source" json:"image_source" default:"https://github.com/haikieu/xcode-developer-disk-image-all-platforms/raw/master/DiskImages/AppleTVOS.platform/DeviceSupport/{0}.zip"` + CNProxy string `koanf:"cn_proxy" json:"cn_proxy" default:"https://mirror.ghproxy.com"` + } `koanf:"developer_disk_image" json:"developer_disk_image"` + } + + Log struct { + Level string `koanf:"level" default:"info"` + TimeFormat string `koanf:"time_format" default:"2006-01-02 15:04:05.000"` + LogFile string `koanf:"log_file"` + AccessLog string `koanf:"access_log"` + } `koanf:"log" json:"log"` + + Server struct { + ListenAddr string `koanf:"listen_addr" default:"0.0.0.0"` + Port int `koanf:"port" default:"9000"` + DataDir string `koanf:"data_dir"` + } `koanf:"app" json:"app"` + + Db db.Config `koanf:"db" json:"db"` +} + +func SideloaderDataDir() string { + return fmt.Sprintf("%s/Sideloader", Config.Server.DataDir) +} diff --git a/internal/app/const.go b/internal/app/const.go new file mode 100644 index 0000000..1161a2a --- /dev/null +++ b/internal/app/const.go @@ -0,0 +1,3 @@ +package app + +const () diff --git a/internal/app/mode.go b/internal/app/mode.go new file mode 100644 index 0000000..3eabc61 --- /dev/null +++ b/internal/app/mode.go @@ -0,0 +1,25 @@ +package app + +import "github.com/bitxeno/atvloadly/internal/app/build" + +const ( + DevelopmentMode AppMode = "development" + ProductionMode AppMode = "production" + TestMode AppMode = "test" +) + +var Mode = AppMode(build.Mode) + +type AppMode string + +func IsDevelopmentMode() bool { + return build.Mode == string(DevelopmentMode) +} + +func IsTestMode() bool { + return build.Mode == string(TestMode) +} + +func IsProductionMode() bool { + return build.Mode == string(ProductionMode) +} diff --git a/internal/app/router.go b/internal/app/router.go deleted file mode 100644 index 6eaab8a..0000000 --- a/internal/app/router.go +++ /dev/null @@ -1,5 +0,0 @@ -package app - -import "github.com/gofiber/fiber/v2" - -type RouteFunc func(*fiber.App) diff --git a/config/settings.go b/internal/app/settings.go similarity index 76% rename from config/settings.go rename to internal/app/settings.go index 53b4ec6..e0c08c0 100644 --- a/config/settings.go +++ b/internal/app/settings.go @@ -1,19 +1,17 @@ -package config +package app import ( - "io/ioutil" "math" "os" - "path/filepath" "time" - "github.com/bitxeno/atvloadly/internal/cfg" "github.com/bitxeno/atvloadly/internal/log" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/creasty/defaults" ) -var Settings SettingsConfiguration +var ( + Settings *SettingsConfiguration +) var saveTimer *time.Timer = time.NewTimer(math.MaxInt64) const ( @@ -49,24 +47,6 @@ type SettingsConfiguration struct { } `koanf:"notification" json:"notification"` } -func loadSettings() error { - conf := cfg.New() - conf.SetPath(filepath.Join(cfg.Server.WorkDir, "settings.json")) - conf.Load() - - // set default value - if err := defaults.Set(&Settings); err != nil { - return err - } - if err := conf.BindStruct("", &Settings); err != nil { - return err - } - - saveTimer.Stop() - go startSaveSettingsJob(conf.Path()) - return nil -} - func SaveSettings() { saveTimer.Reset(100 * time.Millisecond) } @@ -83,7 +63,7 @@ func startSaveSettingsJob(settingsPath string) { } data := utils.ToIndentJSON(Settings) - if err := ioutil.WriteFile(settingsPath, data, os.ModePerm); err != nil { + if err := os.WriteFile(settingsPath, data, os.ModePerm); err != nil { log.Err(err).Msg("Save settings error.") } else { log.Infof("Save settings success. %s", settingsPath) diff --git a/internal/app/version.go b/internal/app/version.go new file mode 100644 index 0000000..ff3a042 --- /dev/null +++ b/internal/app/version.go @@ -0,0 +1,16 @@ +package app + +import "github.com/bitxeno/atvloadly/internal/app/build" + +var Version = AppVersion{ + Commit: build.Commit, + Version: build.Version, + BuildDate: build.BuildDate, +} + +// Info holds build information +type AppVersion struct { + Commit string `json:"commit"` + Version string `json:"version"` + BuildDate string `json:"build_date"` +} diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go index 21a9475..b5a08ae 100644 --- a/internal/cfg/cfg.go +++ b/internal/cfg/cfg.go @@ -2,88 +2,83 @@ package cfg import ( "fmt" + "log" "path/filepath" - "github.com/bitxeno/atvloadly/internal/log" "github.com/bitxeno/atvloadly/internal/utils" + "github.com/creasty/defaults" + "github.com/go-errors/errors" "github.com/knadh/koanf" "github.com/knadh/koanf/parsers/json" "github.com/knadh/koanf/parsers/yaml" "github.com/knadh/koanf/providers/file" ) -type Configuration struct { +type configuration struct { ko *koanf.Koanf path string } -func New() *Configuration { - return &Configuration{ +func New() *configuration { + return &configuration{ ko: koanf.New("."), - path: filepath.Join(defaultConfigDir(), "config.yaml"), + path: filepath.Join(DefaultConfigDir(), "config.yaml"), } } -func (c *Configuration) SetPath(path string) { - if filepath.IsAbs(path) { +func (c *configuration) load(path string) error { + if path != "" { c.path = path - } else { - c.path = filepath.Join(defaultConfigDir(), path) } -} - -func (c *Configuration) Path() string { - return c.path -} - -func (c *Configuration) Load() { - // read config - if utils.Exists(c.path) { - ext := filepath.Ext(c.path) - if ext == ".yaml" || ext == ".yml" { - if err := c.ko.Load(file.Provider(c.path), yaml.Parser()); err != nil { - log.Panicf("Yaml config file read failed. error: %s \n", err) - } - } - if ext == ".json" { - if err := c.ko.Load(file.Provider(c.path), json.Parser()); err != nil { - log.Panicf("Json config file read failed. error: %s \n", err) - } - } - } else { - fmt.Printf("Config file not exists. file: %s\n", c.path) + // set default value + if err := defaults.Set(c); err != nil { + return errors.New(err) } - c.printConfig() -} - -func (c *Configuration) MustLoad() { + // read config from file + ko := koanf.New(".") if !utils.Exists(c.path) { - log.Panicf("Config file not exists. file: %s \n", c.path) + fmt.Printf("[WARN] Config file not exists. path: %s\n", c.path) + return nil } - // read config + fmt.Printf("Load config from path: %s\n", c.path) ext := filepath.Ext(c.path) if ext == ".yaml" || ext == ".yml" { - if err := c.ko.Load(file.Provider(c.path), yaml.Parser()); err != nil { - log.Panicf("Config file read failed. error: %s \n", err) + if err := ko.Load(file.Provider(c.path), yaml.Parser()); err != nil { + log.Panicf("Yaml config file read failed. error: %s \n", err) } } - if ext == ".json" { - if err := c.ko.Load(file.Provider(c.path), json.Parser()); err != nil { - log.Panicf("Config file read failed. error: %s \n", err) + if err := ko.Load(file.Provider(c.path), json.Parser()); err != nil { + log.Panicf("Json config file read failed. error: %s \n", err) } } - c.printConfig() + return ko.Unmarshal("", &c) +} + +func (c *configuration) BindStruct(dst any) error { + return c.ko.Unmarshal("", dst) } -func (c *Configuration) BindStruct(key string, dst any) error { - return c.ko.Unmarshal(key, dst) +func (c *configuration) Reload() { + _ = c.load("") } -func (c *Configuration) Reload() { - c.Load() +func (c *configuration) PrintConfig() { + configName := filepath.Base(c.path) + fmt.Printf("##################### Load %s begin #####################\n", configName) + c.ko.Print() + fmt.Printf("##################### Load %s end #####################\n", configName) +} + +func Load(path string) (*configuration, error) { + conf := New() + if err := conf.load(path); err != nil { + return nil, err + } + + return conf, nil } diff --git a/internal/cfg/cfg_darwin.go b/internal/cfg/cfg_darwin.go index 229dc0f..7681c2d 100644 --- a/internal/cfg/cfg_darwin.go +++ b/internal/cfg/cfg_darwin.go @@ -1,14 +1,17 @@ +//go:build darwin + package cfg import ( + "log" "os" "path/filepath" ) -func defaultConfigDir() string { +func DefaultConfigDir() string { execPath, err := os.Executable() if err != nil { - panic(err) + log.Panic(err) } execName := filepath.Base(execPath) diff --git a/internal/cfg/cfg_dev.go b/internal/cfg/cfg_dev.go deleted file mode 100644 index 2543062..0000000 --- a/internal/cfg/cfg_dev.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build dev - -package cfg - -import ( - "fmt" - "path/filepath" -) - -func (c *Configuration) printConfig() { - configName := filepath.Base(c.path) - fmt.Printf("##################### Load %s begin #####################\n", configName) - c.ko.Print() - fmt.Printf("##################### Load %s end #####################\n", configName) -} diff --git a/internal/cfg/cfg_linux.go b/internal/cfg/cfg_linux.go index 229dc0f..0ce99eb 100644 --- a/internal/cfg/cfg_linux.go +++ b/internal/cfg/cfg_linux.go @@ -1,14 +1,17 @@ +//go:build linux + package cfg import ( + "log" "os" "path/filepath" ) -func defaultConfigDir() string { +func DefaultConfigDir() string { execPath, err := os.Executable() if err != nil { - panic(err) + log.Panic(err) } execName := filepath.Base(execPath) diff --git a/internal/cfg/cfg_prod.go b/internal/cfg/cfg_prod.go deleted file mode 100644 index 252e95b..0000000 --- a/internal/cfg/cfg_prod.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !dev - -package cfg - -func (c *Configuration) printConfig() { - -} diff --git a/internal/cfg/cfg_windows.go b/internal/cfg/cfg_windows.go index 48dd5d7..024a5f9 100644 --- a/internal/cfg/cfg_windows.go +++ b/internal/cfg/cfg_windows.go @@ -1,5 +1,7 @@ +//go:build windows + package cfg -func defaultConfigDir() string { +func DefaultConfigDir() string { return "." } diff --git a/internal/cfg/entity.go b/internal/cfg/entity.go deleted file mode 100644 index 89da567..0000000 --- a/internal/cfg/entity.go +++ /dev/null @@ -1,73 +0,0 @@ -package cfg - -import ( - "path/filepath" - - "github.com/bitxeno/atvloadly/internal/log" - "github.com/creasty/defaults" -) - -var Server = newServerConfiguration() - -type ServerConfiguration struct { - Configuration - - ListenAddr string `koanf:"listen_addr"` - Port int `koanf:"port" default:"9000"` - TimeFormat string `koanf:"time_format" default:"2006-01-02 15:04:05"` - LogTimeFormat string `koanf:"log_time_format" default:"2006-01-02 15:04:05.000"` - WorkDir string `koanf:"work_dir"` - Log string `koanf:"log"` - AccessLog string `koanf:"access_log"` -} - -func newServerConfiguration() *ServerConfiguration { - return &ServerConfiguration{ - Configuration: *New(), - } -} - -func (c *ServerConfiguration) Load() { - c.Configuration.Load() - - // set default value - if err := defaults.Set(c); err != nil { - log.Panicf("Config set default failed. error: %s \n", err) - } - - if err := c.ko.Unmarshal("server", c); err != nil { - log.Panicf("Config unable to decode into struct, %v\n", err) - } - - // set default data dir - if c.WorkDir == "" { - c.WorkDir = defaultConfigDir() - } -} - -func (c *ServerConfiguration) MustLoad() { - c.Configuration.MustLoad() - - // set default value - if err := defaults.Set(c); err != nil { - log.Panicf("Config set default failed. error: %s \n", err) - } - - if err := c.ko.Unmarshal("server", c); err != nil { - log.Panicf("Config unable to decode into struct, %v\n", err) - } - - // set default data dir - if c.WorkDir == "" { - c.WorkDir = defaultConfigDir() - } -} - -func (c *ServerConfiguration) Reload() { - c.Load() -} - -func (c *ServerConfiguration) DbPath() string { - return filepath.Join(c.WorkDir, "app.db") - -} diff --git a/internal/cmd/main.go b/internal/cmd/main.go deleted file mode 100644 index 4bcf29b..0000000 --- a/internal/cmd/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/bitxeno/atvloadly/internal/version" - "github.com/urfave/cli/v2" -) - -func Run(name string, desc string, arguments []string, webServerAction cli.ActionFunc) (err error) { - cliApp := &cli.App{ - Name: name, - Usage: desc, - Version: version.Version, - Commands: []*cli.Command{ - genCommand, - { - Name: "server", - Usage: "Run web server", - Flags: serverFlags, - Action: webServerAction, - }, - }, - } - - return cliApp.Run(os.Args) -} diff --git a/internal/cmd/server.go b/internal/cmd/server.go deleted file mode 100644 index 589c411..0000000 --- a/internal/cmd/server.go +++ /dev/null @@ -1,32 +0,0 @@ -package cmd - -import ( - "github.com/urfave/cli/v2" -) - -var ( - serverFlags = []cli.Flag{ - &cli.IntFlag{ - Name: "port", - Aliases: []string{"p"}, - Usage: "web server port", - }, - &cli.StringFlag{ - Name: "config", - Aliases: []string{"c"}, - Usage: "Load configuration from `FILE`", - }, - &cli.BoolFlag{ - Name: "debug", - Aliases: []string{"vv"}, - Usage: "Change to debug log level", - Value: false, - }, - &cli.BoolFlag{ - Name: "verbose", - Aliases: []string{"vvv"}, - Usage: "Change to verbose log level", - Value: false, - }, - } -) diff --git a/internal/db/config.go b/internal/db/config.go new file mode 100644 index 0000000..4531278 --- /dev/null +++ b/internal/db/config.go @@ -0,0 +1,7 @@ +package db + +type Config struct { + Path string `koanf:"path"` + FileName string `koanf:"filename" default:"app.db"` + Debug bool `koanf:"debug" default:"false"` +} diff --git a/internal/db/db.go b/internal/db/db.go index e141a30..8d4e99b 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -5,10 +5,8 @@ import ( "os" "path/filepath" - "github.com/bitxeno/atvloadly/internal/cfg" "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/internal/mode" - "gorm.io/driver/sqlite" + "github.com/glebarez/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) @@ -18,18 +16,20 @@ var instance *sqliteDb type sqliteDb struct { db *gorm.DB path string + conf Config } -func New() *sqliteDb { - dbDir := cfg.Server.WorkDir +func new(conf Config) *sqliteDb { + dbDir := conf.Path if _, err := os.Stat(dbDir); err != nil { if err := os.MkdirAll(dbDir, os.ModePerm); err != nil { - panic("failed to create database directory") + log.Panicf("failed to create database directory. path: %s", dbDir) } } return &sqliteDb{ - path: filepath.Join(dbDir, "app.db"), + path: filepath.Join(dbDir, conf.FileName), + conf: conf, } } @@ -44,11 +44,11 @@ func (s *sqliteDb) Open() *sqliteDb { } conf := &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)} - if mode.Get() == mode.DevelopmentMode { + if s.conf.Debug { conf.Logger = logger.Default.LogMode(logger.Info) } - fmt.Printf("Load db path: %s\n", s.path) + fmt.Printf("Load database from path: %s\n", s.path) db, err := gorm.Open(sqlite.Open(s.path), conf) if err != nil { panic("failed to open database") @@ -79,8 +79,8 @@ func Store() *gorm.DB { return instance.db } -func Open() *sqliteDb { - instance = New().Open() +func Open(conf Config) *sqliteDb { + instance = new(conf).Open() return instance } diff --git a/ipa/ipa.go b/internal/ipa/ipa.go similarity index 100% rename from ipa/ipa.go rename to internal/ipa/ipa.go diff --git a/ipa/package_info.go b/internal/ipa/package_info.go similarity index 100% rename from ipa/package_info.go rename to internal/ipa/package_info.go diff --git a/manager/device_manager.go b/internal/manager/device_manager.go similarity index 95% rename from manager/device_manager.go rename to internal/manager/device_manager.go index 8350dae..34b17a7 100644 --- a/manager/device_manager.go +++ b/internal/manager/device_manager.go @@ -7,10 +7,10 @@ import ( "sync" "time" - "github.com/bitxeno/atvloadly/config" + "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/log" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/model" ) var deviceManager = newDeviceManager() @@ -92,7 +92,7 @@ func (dm *DeviceManager) GetMountImageInfo(udid string) (*model.UsbmuxdImage, er return nil, err } - imageInfo := model.NewUsbmuxdImage(*devInfo, config.App.DeveloperDiskImage.ImageSource) + imageInfo := model.NewUsbmuxdImage(*devInfo, app.Config.App.DeveloperDiskImage.ImageSource) imageMounted, err := dm.CheckHasMountImage(udid) if err == nil { imageInfo.ImageMounted = imageMounted diff --git a/manager/device_manager_avahi.go b/internal/manager/device_manager_avahi.go similarity index 98% rename from manager/device_manager_avahi.go rename to internal/manager/device_manager_avahi.go index 03cdde1..5a47467 100644 --- a/manager/device_manager_avahi.go +++ b/internal/manager/device_manager_avahi.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/bitxeno/atvloadly/internal/log" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/model" "github.com/godbus/dbus/v5" "github.com/holoplot/go-avahi" ) diff --git a/manager/device_manager_mdns.go b/internal/manager/device_manager_mdns.go similarity index 98% rename from manager/device_manager_mdns.go rename to internal/manager/device_manager_mdns.go index d98897f..cf85210 100644 --- a/manager/device_manager_mdns.go +++ b/internal/manager/device_manager_mdns.go @@ -9,8 +9,8 @@ import ( "time" "github.com/bitxeno/atvloadly/internal/log" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/model" "github.com/grandcat/zeroconf" ) diff --git a/manager/device_manager_usbmuxd.go b/internal/manager/device_manager_usbmuxd.go similarity index 97% rename from manager/device_manager_usbmuxd.go rename to internal/manager/device_manager_usbmuxd.go index 23d50fe..619322a 100644 --- a/manager/device_manager_usbmuxd.go +++ b/internal/manager/device_manager_usbmuxd.go @@ -9,8 +9,8 @@ import ( "strings" "time" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/model" gidevice "github.com/electricbubble/gidevice" ) diff --git a/manager/lockdown.go b/internal/manager/lockdown.go similarity index 78% rename from manager/lockdown.go rename to internal/manager/lockdown.go index 03aa996..e181ff3 100644 --- a/manager/lockdown.go +++ b/internal/manager/lockdown.go @@ -3,17 +3,16 @@ package manager import ( "bytes" "fmt" - "io/ioutil" "os" - "github.com/bitxeno/atvloadly/config" + "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/model" "howett.net/plist" ) func loadLockdownDevices() (map[string]model.LockdownDevice, error) { - files, err := ioutil.ReadDir(config.App.LockdownDir) + files, err := os.ReadDir(app.Config.App.LockdownDir) if err != nil { fmt.Println(err) // return nil, err @@ -28,7 +27,7 @@ func loadLockdownDevices() (map[string]model.LockdownDevice, error) { continue } - buf, err := os.ReadFile(fmt.Sprintf("%s/%s", config.App.LockdownDir, file.Name())) + buf, err := os.ReadFile(fmt.Sprintf("%s/%s", app.Config.App.LockdownDir, file.Name())) if err != nil { return nil, err } diff --git a/manager/manager.go b/internal/manager/manager.go similarity index 90% rename from manager/manager.go rename to internal/manager/manager.go index 941520a..1cbfeae 100644 --- a/manager/manager.go +++ b/internal/manager/manager.go @@ -1,6 +1,6 @@ package manager -import "github.com/bitxeno/atvloadly/model" +import "github.com/bitxeno/atvloadly/internal/model" func StartDeviceManager() { go deviceManager.Start() diff --git a/internal/mode/mode.go b/internal/mode/mode.go deleted file mode 100644 index 46e37c3..0000000 --- a/internal/mode/mode.go +++ /dev/null @@ -1,31 +0,0 @@ -package mode - -var ( - /*********Will auto update by ci build *********/ - Mode = "development" - /*********Will auto update by ci build *********/ -) - -const ( - DevelopmentMode AppMode = "development" - ProductionMode AppMode = "production" - TestMode AppMode = "test" -) - -type AppMode string - -func Get() AppMode { - return AppMode(Mode) -} - -func IsDevelopmentMode() bool { - return Mode == string(DevelopmentMode) -} - -func IsTestMode() bool { - return Mode == string(TestMode) -} - -func IsProductionMode() bool { - return Mode == string(ProductionMode) -} diff --git a/model/device.go b/internal/model/device.go similarity index 100% rename from model/device.go rename to internal/model/device.go diff --git a/model/installed_app.go b/internal/model/installed_app.go similarity index 100% rename from model/installed_app.go rename to internal/model/installed_app.go diff --git a/model/ipa_file.go b/internal/model/ipa_file.go similarity index 100% rename from model/ipa_file.go rename to internal/model/ipa_file.go diff --git a/model/lockdown_device.go b/internal/model/lockdown_device.go similarity index 100% rename from model/lockdown_device.go rename to internal/model/lockdown_device.go diff --git a/model/service_status.go b/internal/model/service_status.go similarity index 100% rename from model/service_status.go rename to internal/model/service_status.go diff --git a/model/usbmuxd_device.go b/internal/model/usbmuxd_device.go similarity index 100% rename from model/usbmuxd_device.go rename to internal/model/usbmuxd_device.go diff --git a/model/usbmuxd_image.go b/internal/model/usbmuxd_image.go similarity index 100% rename from model/usbmuxd_image.go rename to internal/model/usbmuxd_image.go diff --git a/notify/notify.go b/internal/notify/notify.go similarity index 87% rename from notify/notify.go rename to internal/notify/notify.go index b590ee2..4761030 100644 --- a/notify/notify.go +++ b/internal/notify/notify.go @@ -4,9 +4,9 @@ import ( "context" "errors" - "github.com/bitxeno/atvloadly/config" + "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/notify/wecom" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/notify/wecom" "github.com/nikoksr/notify" "github.com/nikoksr/notify/service/bark" "github.com/nikoksr/notify/service/telegram" @@ -14,10 +14,10 @@ import ( ) func Send(title string, message string) error { - return SendWithConfig(title, message, config.Settings) + return SendWithConfig(title, message, *app.Settings) } -func SendWithConfig(title string, message string, settings config.SettingsConfiguration) error { +func SendWithConfig(title string, message string, settings app.SettingsConfiguration) error { if !settings.Notification.Enabled { return errors.New("未启用") } diff --git a/notify/wecom/wecom.go b/internal/notify/wecom/wecom.go similarity index 100% rename from notify/wecom/wecom.go rename to internal/notify/wecom/wecom.go diff --git a/service/db.go b/internal/service/db.go similarity index 93% rename from service/db.go rename to internal/service/db.go index e3515ee..de8b439 100644 --- a/service/db.go +++ b/internal/service/db.go @@ -6,10 +6,10 @@ import ( "path/filepath" "time" - "github.com/bitxeno/atvloadly/internal/cfg" + conf "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/db" "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/model" + "github.com/bitxeno/atvloadly/internal/model" "gorm.io/gorm" ) @@ -62,7 +62,7 @@ func SaveApp(app model.InstalledApp) (*model.InstalledApp, error) { cur.Password = app.Password // 把ipa/icon移动到id目录 - saveDir := filepath.Join(cfg.Server.WorkDir, "ipa", fmt.Sprintf("%d", app.ID)) + saveDir := filepath.Join(conf.Config.Server.DataDir, "ipa", fmt.Sprintf("%d", app.ID)) if cur.IpaPath != "" { ipaPath := filepath.Join(saveDir, "app.ipa") if err := os.Rename(cur.IpaPath, ipaPath); err != nil { @@ -103,7 +103,7 @@ func SaveApp(app model.InstalledApp) (*model.InstalledApp, error) { } // 把ipa/icon移动到id目录 - saveDir := filepath.Join(cfg.Server.WorkDir, "ipa", fmt.Sprintf("%d", app.ID)) + saveDir := filepath.Join(conf.Config.Server.DataDir, "ipa", fmt.Sprintf("%d", app.ID)) if err := os.MkdirAll(saveDir, os.ModePerm); err != nil { panic("failed to create directory :" + saveDir) } diff --git a/service/service.go b/internal/service/service.go similarity index 74% rename from service/service.go rename to internal/service/service.go index 3d104c0..83c09ae 100644 --- a/service/service.go +++ b/internal/service/service.go @@ -11,14 +11,13 @@ import ( "strings" "github.com/artdarek/go-unzip/pkg/unzip" - "github.com/bitxeno/atvloadly/config" - "github.com/bitxeno/atvloadly/internal/cfg" + "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/http" "github.com/bitxeno/atvloadly/internal/log" + "github.com/bitxeno/atvloadly/internal/manager" + "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/manager" - "github.com/bitxeno/atvloadly/model" - "github.com/shirou/gopsutil/v3/process" + ps "github.com/mitchellh/go-ps" ) var ( @@ -44,25 +43,16 @@ func GetServiceStatus() []model.ServiceStatus { Running: checkProcessExists(proc), }) - proc = "anisette-server" - status = append(status, model.ServiceStatus{ - Name: proc, - Running: checkProcessExists(proc), - }) - return status } func checkProcessExists(name string) bool { - processes, err := process.Processes() + processes, err := ps.Processes() if err != nil { return false } for _, p := range processes { - n, err := p.Name() - if err != nil { - return false - } + n := p.Executable() if n == name { return true } @@ -109,8 +99,8 @@ func downloadDeveloperDiskImage(imageInfo *model.UsbmuxdImage) (dmg string, sign // download current version DeveloperDiskImage err := downloadDeveloperDiskImageByVersion(imageInfo.DeveloperDiskImageUrl, imageInfo.DeveloperDiskImageVersion) if err == nil { - dmg = filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", imageInfo.DeveloperDiskImageVersion, "DeveloperDiskImage.dmg") - signature = filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", imageInfo.DeveloperDiskImageVersion, "DeveloperDiskImage.dmg.signature") + dmg = filepath.Join(app.Config.Server.DataDir, "DeveloperDiskImage", imageInfo.DeveloperDiskImageVersion, "DeveloperDiskImage.dmg") + signature = filepath.Join(app.Config.Server.DataDir, "DeveloperDiskImage", imageInfo.DeveloperDiskImageVersion, "DeveloperDiskImage.dmg.signature") return } @@ -118,11 +108,11 @@ func downloadDeveloperDiskImage(imageInfo *model.UsbmuxdImage) (dmg string, sign // current version DeveloperDiskImage not found, try fallback to last minor version for fallbackMinor := imageInfo.VersionMinor - 1; fallbackMinor > 0; fallbackMinor-- { fallbackVersion := fmt.Sprintf("%d.%d", imageInfo.VersionMajor, fallbackMinor) - fallbackImageUrl := strings.Replace(config.App.DeveloperDiskImage.ImageSource, "{0}", fallbackVersion, -1) + fallbackImageUrl := strings.Replace(app.Config.App.DeveloperDiskImage.ImageSource, "{0}", fallbackVersion, -1) log.Warnf("try downgrade developer disk image to version: %s", fallbackVersion) if err := downloadDeveloperDiskImageByVersion(fallbackImageUrl, fallbackVersion); err == nil { - dmg = filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", fallbackVersion, "DeveloperDiskImage.dmg") - signature = filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", fallbackVersion, "DeveloperDiskImage.dmg.signature") + dmg = filepath.Join(app.Config.Server.DataDir, "DeveloperDiskImage", fallbackVersion, "DeveloperDiskImage.dmg") + signature = filepath.Join(app.Config.Server.DataDir, "DeveloperDiskImage", fallbackVersion, "DeveloperDiskImage.dmg.signature") return } } @@ -137,22 +127,22 @@ func downloadDeveloperDiskImage(imageInfo *model.UsbmuxdImage) (dmg string, sign } func downloadDeveloperDiskImageByVersion(url string, version string) error { - imageVersionDir := filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", version) + imageVersionDir := filepath.Join(app.Config.Server.DataDir, "DeveloperDiskImage", version) dmg := filepath.Join(imageVersionDir, "DeveloperDiskImage.dmg") signature := filepath.Join(imageVersionDir, "DeveloperDiskImage.dmg.signature") if utils.Exists(dmg) && utils.Exists(signature) { return nil } - tmpPath := filepath.Join(cfg.Server.WorkDir, "tmp", "DeveloperDiskImage.zip") - tmpUnzipPath := filepath.Join(cfg.Server.WorkDir, "tmp", "DeveloperDiskImage") + tmpPath := filepath.Join(app.Config.Server.DataDir, "tmp", "DeveloperDiskImage.zip") + tmpUnzipPath := filepath.Join(app.Config.Server.DataDir, "tmp", "DeveloperDiskImage") _ = os.RemoveAll(tmpUnzipPath) // download current version DeveloperDiskImage hasDownloaded := false - if config.App.DeveloperDiskImage.CNProxy != "" { + if app.Config.App.DeveloperDiskImage.CNProxy != "" { // download by proxy - cnProxyUrl := strings.TrimSuffix(config.App.DeveloperDiskImage.CNProxy, "/") + "/" + url + cnProxyUrl := strings.TrimSuffix(app.Config.App.DeveloperDiskImage.CNProxy, "/") + "/" + url if resp, err := http.NewClient().R().SetOutput(tmpPath).Get(cnProxyUrl); err == nil && resp.IsSuccess() { hasDownloaded = true } diff --git a/service/service_test.go b/internal/service/service_test.go similarity index 84% rename from service/service_test.go rename to internal/service/service_test.go index 581bba6..20d1b63 100644 --- a/service/service_test.go +++ b/internal/service/service_test.go @@ -2,13 +2,9 @@ package service import ( "testing" - - "github.com/bitxeno/atvloadly/config" ) func TestDownloadDeveloperDiskImageByVersion(t *testing.T) { - _ = config.Load() - err := downloadDeveloperDiskImageByVersion("https://github.com/haikieu/xcode-developer-disk-image-all-platforms/raw/master/DiskImages/AppleTVOS.platform/DeviceSupport/16.4.zip", "16.4") if err != nil { t.Error(err) diff --git a/task/task.go b/internal/task/task.go similarity index 83% rename from task/task.go rename to internal/task/task.go index e1cf879..a3ed3b3 100644 --- a/task/task.go +++ b/internal/task/task.go @@ -12,14 +12,12 @@ import ( "strings" "time" - "github.com/bitxeno/atvloadly/config" "github.com/bitxeno/atvloadly/internal/app" - "github.com/bitxeno/atvloadly/internal/cfg" "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/manager" - "github.com/bitxeno/atvloadly/model" - "github.com/bitxeno/atvloadly/notify" - "github.com/bitxeno/atvloadly/service" + "github.com/bitxeno/atvloadly/internal/manager" + "github.com/bitxeno/atvloadly/internal/model" + "github.com/bitxeno/atvloadly/internal/notify" + "github.com/bitxeno/atvloadly/internal/service" "github.com/robfig/cron/v3" ) @@ -45,19 +43,19 @@ func (t *Task) RunSchedule() error { t.c = nil } - if !config.Settings.Task.Enabled { + if !app.Settings.Task.Enabled { log.Info("app刷新定时任务未启用") return nil } t.c = cron.New() - if _, err := t.c.AddFunc(config.Settings.Task.CrodTime, t.Run); err != nil { - log.Err(err).Msgf("app刷新定时任务启动失败,定时格式错误:%s", config.Settings.Task.CrodTime) + if _, err := t.c.AddFunc(app.Settings.Task.CrodTime, t.Run); err != nil { + log.Err(err).Msgf("app刷新定时任务启动失败,定时格式错误:%s", app.Settings.Task.CrodTime) t.c = nil return err } - log.Infof("app刷新定时任务已启动,时间: %s", config.Settings.Task.CrodTime) + log.Infof("app刷新定时任务已启动,时间: %s", app.Settings.Task.CrodTime) t.c.Start() return nil @@ -120,7 +118,7 @@ func (t *Task) Run() { // 发送安装失败通知 if len(failedList) > 0 { - _ = notify.Send(fmt.Sprintf("%s自动刷新任务执行失败", app.Name()), failedMsg) + _ = notify.Send("atvloadly 自动刷新任务执行失败", failedMsg) } } @@ -128,7 +126,7 @@ func (t *Task) checkNeedRefresh(v model.InstalledApp) bool { now := time.Now() // 过期时间少于一天时,再安装 - if config.Settings.Task.Mode == config.OneDayAgoMode { + if app.Settings.Task.Mode == app.OneDayAgoMode { expireTime := v.RefreshedDate.AddDate(0, 0, 6) if expireTime.Before(now) { return true @@ -136,7 +134,7 @@ func (t *Task) checkNeedRefresh(v model.InstalledApp) bool { } // 每天安装 - if config.Settings.Task.Mode == config.DailyMode { + if app.Settings.Task.Mode == app.DailyMode { if v.RefreshedDate.Format("2006-01-02") != now.Format("2006-01-02") { return true } @@ -188,13 +186,9 @@ func (t *Task) runInternal(v model.InstalledApp) error { return err } - // 为每个appleid创建对应的工作目录,用于存储AltServer生成的签名证书 - dirName := regValidName.ReplaceAllString(strings.ToLower(v.Account), "") - workdir := filepath.Join(cfg.Server.WorkDir, "AltServer", dirName) - - cmd := exec.Command("AltServer", "-u", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) - cmd.Dir = workdir - cmd.Env = []string{"ALTSERVER_ANISETTE_SERVER=http://127.0.0.1:6969"} + cmd := exec.Command("sideloader", "install", "--quiet", "--udid", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) + cmd.Dir = app.Config.Server.DataDir + cmd.Env = []string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()} stdin, err := cmd.StdinPipe() if err != nil { log.Err(err).Msg("Error obtaining stdin: ") @@ -259,7 +253,7 @@ func (t *Task) writeLog(v model.InstalledApp, data []byte) { // 打码密码字符串 data = bytes.Replace(data, []byte(v.Password), []byte("******"), -1) - saveDir := filepath.Join(cfg.Server.WorkDir, "log") + saveDir := filepath.Join(app.Config.Server.DataDir, "log") if err := os.MkdirAll(saveDir, os.ModePerm); err != nil { log.Error("failed to create directory :" + saveDir) return diff --git a/tty/lib/function.go b/internal/tty/lib/function.go similarity index 87% rename from tty/lib/function.go rename to internal/tty/lib/function.go index d23146d..579c2eb 100644 --- a/tty/lib/function.go +++ b/internal/tty/lib/function.go @@ -2,13 +2,13 @@ package lib import ( "crypto/x509" - "io/ioutil" "log" + "os" ) // ReadCertPool get CertPool by crt file func ReadCertPool(crt string) *x509.CertPool { - _crt, err := ioutil.ReadFile(crt) + _crt, err := os.ReadFile(crt) if err != nil { log.Fatalln("Read crt file failed:", err.Error()) } diff --git a/tty/lib/message.go b/internal/tty/lib/message.go similarity index 100% rename from tty/lib/message.go rename to internal/tty/lib/message.go diff --git a/tty/pipeline.go b/internal/tty/pipeline.go similarity index 98% rename from tty/pipeline.go rename to internal/tty/pipeline.go index 0a52e04..8f896c2 100644 --- a/tty/pipeline.go +++ b/internal/tty/pipeline.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - "github.com/bitxeno/atvloadly/tty/lib" + "github.com/bitxeno/atvloadly/internal/tty/lib" "github.com/gofiber/contrib/websocket" "github.com/runletapp/go-console" ) diff --git a/tty/tty.go b/internal/tty/tty.go similarity index 93% rename from tty/tty.go rename to internal/tty/tty.go index 8ffd27c..eac5b59 100644 --- a/tty/tty.go +++ b/internal/tty/tty.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/bitxeno/atvloadly/internal/cfg" + "github.com/bitxeno/atvloadly/internal/app" "github.com/gofiber/contrib/websocket" ) @@ -41,7 +41,7 @@ func (t *TTY) Close() { func (t *TTY) Start() { if t.cwd != "" { - if _, err := t.pl.pty.Write([]byte(fmt.Sprintf("cd \"%s\"\n", cfg.Server.WorkDir))); err != nil { + if _, err := t.pl.pty.Write([]byte(fmt.Sprintf("cd \"%s\"\n", app.Config.Server.DataDir))); err != nil { _ = t.conn.WriteMessage(websocket.TextMessage, []byte(err.Error())) return } diff --git a/internal/version/build.go b/internal/version/build.go deleted file mode 100644 index 808dd50..0000000 --- a/internal/version/build.go +++ /dev/null @@ -1,25 +0,0 @@ -package version - -var ( - /*********Will auto update by ci build *********/ - Version = "unknown" - Commit = "unknown" - BuildDate = "unknown" - /*********Will auto update by ci build *********/ -) - -// Info holds build information -type Info struct { - Commit string `json:"commit"` - Version string `json:"version"` - BuildDate string `json:"build_date"` -} - -// Get creates and initialized Info object -func Get() Info { - return Info{ - Commit: Commit, - Version: Version, - BuildDate: BuildDate, - } -} diff --git a/main.go b/main.go index 9673c3c..659c958 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,11 @@ import ( "fmt" "os" - "github.com/bitxeno/atvloadly/config" - "github.com/bitxeno/atvloadly/internal/app" - "github.com/bitxeno/atvloadly/internal/db" - _ "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/manager" - "github.com/bitxeno/atvloadly/model" - "github.com/bitxeno/atvloadly/router" - "github.com/bitxeno/atvloadly/task" - "github.com/gofiber/fiber/v2" + "github.com/bitxeno/atvloadly/cmd/gen" + "github.com/bitxeno/atvloadly/cmd/server" + "github.com/bitxeno/atvloadly/internal/app/build" + "github.com/go-errors/errors" + "github.com/urfave/cli/v2" ) const ( @@ -23,25 +19,23 @@ const ( ) func main() { - app := app.New(AppName, AppDesc) - app.Route(func(f *fiber.App) { - router.Create(f, getViewAssets()) - }) - app.AddBoot(func() error { - if err := config.Load(); err != nil { - return err - } - if err := db.Open().AutoMigrate(&model.InstalledApp{}); err != nil { - return err - } - _ = task.ScheduleRefreshApps() - manager.StartDeviceManager() - return nil - }) - if err := app.Run(os.Args); err != nil { - code := 1 - fmt.Fprintln(os.Stderr, err) - os.Exit(code) + cliApp := &cli.App{ + Name: AppName, + Usage: AppDesc, + Version: build.Version, + Commands: []*cli.Command{ + gen.Command, + server.Command, + }, + } + + if err := cliApp.Run(os.Args); err != nil { + if e, ok := err.(*errors.Error); ok { + fmt.Fprintln(os.Stderr, e.ErrorStack()) + } else { + fmt.Fprintln(os.Stderr, err) + } + os.Exit(1) } } diff --git a/router/api_result.go b/web/api_result.go similarity index 95% rename from router/api_result.go rename to web/api_result.go index 7a10311..c3b7027 100644 --- a/router/api_result.go +++ b/web/api_result.go @@ -1,4 +1,4 @@ -package router +package web type ApiResult struct { Code int `json:"code"` diff --git a/web/fs_dev.go b/web/fs_dev.go new file mode 100644 index 0000000..5c13a2a --- /dev/null +++ b/web/fs_dev.go @@ -0,0 +1,12 @@ +//go:build dev + +package web + +import ( + "io/fs" + "os" +) + +func StaticAssets() fs.FS { + return os.DirFS("static/dist") +} diff --git a/web/fs_prod.go b/web/fs_prod.go new file mode 100644 index 0000000..50cdfc3 --- /dev/null +++ b/web/fs_prod.go @@ -0,0 +1,20 @@ +//go:build !dev + +package web + +import ( + "embed" + "io/fs" +) + +//go:embed all:static/dist/* +var static embed.FS + +func StaticAssets() fs.FS { + embed, err := fs.Sub(static, "static/dist") + if err != nil { + panic(err) + } + + return embed +} diff --git a/router/router.go b/web/router.go similarity index 85% rename from router/router.go rename to web/router.go index 2c56c93..2a58f67 100644 --- a/router/router.go +++ b/web/router.go @@ -1,34 +1,32 @@ -package router +package web import ( "fmt" "image/png" - "io/fs" "net/http" "os" "path/filepath" "time" - "github.com/bitxeno/atvloadly/config" - "github.com/bitxeno/atvloadly/internal/cfg" + "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/ipa" + "github.com/bitxeno/atvloadly/internal/manager" + "github.com/bitxeno/atvloadly/internal/model" + "github.com/bitxeno/atvloadly/internal/notify" + "github.com/bitxeno/atvloadly/internal/service" + "github.com/bitxeno/atvloadly/internal/task" + "github.com/bitxeno/atvloadly/internal/tty" "github.com/bitxeno/atvloadly/internal/utils" - "github.com/bitxeno/atvloadly/ipa" - "github.com/bitxeno/atvloadly/manager" - "github.com/bitxeno/atvloadly/model" - "github.com/bitxeno/atvloadly/notify" - "github.com/bitxeno/atvloadly/service" - "github.com/bitxeno/atvloadly/task" - "github.com/bitxeno/atvloadly/tty" "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/filesystem" ) -func Create(app *fiber.App, f fs.FS) { - app.Use("/", filesystem.New(filesystem.Config{ - Root: http.FS(f), +func route(fi *fiber.App) { + fi.Use("/", filesystem.New(filesystem.Config{ + Root: http.FS(StaticAssets()), })) - app.Use("/ws", func(c *fiber.Ctx) error { + fi.Use("/ws", func(c *fiber.Ctx) error { // IsWebSocketUpgrade returns true if the client // requested upgrade to the WebSocket protocol. if websocket.IsWebSocketUpgrade(c) { @@ -37,7 +35,7 @@ func Create(app *fiber.App, f fs.FS) { } return fiber.ErrUpgradeRequired }) - app.Get("/ws/tty", websocket.New(func(c *websocket.Conn) { + fi.Get("/ws/tty", websocket.New(func(c *websocket.Conn) { term, err := tty.New(c, "bash") if err != nil { _ = c.WriteMessage(websocket.TextMessage, []byte(err.Error())) @@ -45,11 +43,11 @@ func Create(app *fiber.App, f fs.FS) { } defer term.Close() - term.SetCWD(cfg.Server.WorkDir) - term.SetENV([]string{"ALTSERVER_ANISETTE_SERVER=\"http://127.0.0.1:6969\""}) + term.SetCWD(app.Config.Server.DataDir) + term.SetENV([]string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()}) term.Start() })) - app.Get("/apps/:id/icon", func(c *fiber.Ctx) error { + fi.Get("/apps/:id/icon", func(c *fiber.Ctx) error { id := utils.MustParseInt(c.Params("id")) t, err := service.GetApp(uint(id)) @@ -63,24 +61,24 @@ func Create(app *fiber.App, f fs.FS) { return c.Status(http.StatusNotFound).SendString("") } }) - app.Get("/apps/:id/log", func(c *fiber.Ctx) error { + fi.Get("/apps/:id/log", func(c *fiber.Ctx) error { id := utils.MustParseInt(c.Params("id")) - path := filepath.Join(cfg.Server.WorkDir, "log", fmt.Sprintf("task_%d.log", id)) + path := filepath.Join(app.Config.Server.DataDir, "log", fmt.Sprintf("task_%d.log", id)) return c.Status(http.StatusOK).SendFile(path, false) }) // api路由 - api := app.Group("/api") + api := fi.Group("/api") api.Get("/hello", func(c *fiber.Ctx) error { return c.SendString("hello world.") }) api.Get("/settings", func(c *fiber.Ctx) error { - return c.Status(http.StatusOK).JSON(apiSuccess(config.Settings)) + return c.Status(http.StatusOK).JSON(apiSuccess(app.Settings)) }) api.Post("/settings/:key", func(c *fiber.Ctx) error { - var settings config.SettingsConfiguration + var settings app.SettingsConfiguration if err := c.BodyParser(&settings); err != nil { return c.Status(http.StatusOK).JSON(apiError("Invalid argument. error: " + err.Error())) } @@ -88,15 +86,15 @@ func Create(app *fiber.App, f fs.FS) { key := c.Params("key") switch key { case "notification": - config.Settings.Notification = settings.Notification + app.Settings.Notification = settings.Notification case "task": - config.Settings.Task = settings.Task + app.Settings.Task = settings.Task if err := task.ReloadTask(); err != nil { return c.Status(http.StatusOK).JSON(apiError("时间格式错误: " + err.Error())) } } - config.SaveSettings() + app.SaveSettings() return c.Status(http.StatusOK).JSON(apiSuccess(true)) }) @@ -162,7 +160,7 @@ func Create(app *fiber.App, f fs.FS) { result := []model.IpaFile{} for _, file := range files { - saveDir := filepath.Join(cfg.Server.WorkDir, "tmp") + saveDir := filepath.Join(app.Config.Server.DataDir, "tmp") if err := os.MkdirAll(saveDir, os.ModePerm); err != nil { return c.Status(http.StatusOK).JSON(apiError("failed to create directory :" + saveDir)) } @@ -282,7 +280,7 @@ func Create(app *fiber.App, f fs.FS) { }) api.Post("/notify/send/test", func(c *fiber.Ctx) error { - var settings config.SettingsConfiguration + var settings app.SettingsConfiguration if err := c.BodyParser(&settings); err != nil { return c.Status(http.StatusOK).JSON(apiError("Invalid argument. error: " + err.Error())) } diff --git a/view/index.html b/web/static/index.html similarity index 100% rename from view/index.html rename to web/static/index.html diff --git a/view/package-lock.json b/web/static/package-lock.json similarity index 100% rename from view/package-lock.json rename to web/static/package-lock.json diff --git a/view/package.json b/web/static/package.json similarity index 100% rename from view/package.json rename to web/static/package.json diff --git a/view/postcss.config.js b/web/static/postcss.config.js similarity index 100% rename from view/postcss.config.js rename to web/static/postcss.config.js diff --git a/view/public/img/dummy.jpg b/web/static/public/img/dummy.jpg similarity index 100% rename from view/public/img/dummy.jpg rename to web/static/public/img/dummy.jpg diff --git a/view/src/App.vue b/web/static/src/App.vue similarity index 100% rename from view/src/App.vue rename to web/static/src/App.vue diff --git a/view/src/api/api.js b/web/static/src/api/api.js similarity index 100% rename from view/src/api/api.js rename to web/static/src/api/api.js diff --git a/view/src/app.css b/web/static/src/app.css similarity index 100% rename from view/src/app.css rename to web/static/src/app.css diff --git a/view/src/assets/icons/appletv.svg b/web/static/src/assets/icons/appletv.svg similarity index 100% rename from view/src/assets/icons/appletv.svg rename to web/static/src/assets/icons/appletv.svg diff --git a/view/src/assets/icons/checkmark.svg b/web/static/src/assets/icons/checkmark.svg similarity index 100% rename from view/src/assets/icons/checkmark.svg rename to web/static/src/assets/icons/checkmark.svg diff --git a/view/src/assets/icons/dismiss.svg b/web/static/src/assets/icons/dismiss.svg similarity index 100% rename from view/src/assets/icons/dismiss.svg rename to web/static/src/assets/icons/dismiss.svg diff --git a/view/src/assets/icons/github.svg b/web/static/src/assets/icons/github.svg similarity index 100% rename from view/src/assets/icons/github.svg rename to web/static/src/assets/icons/github.svg diff --git a/view/src/assets/icons/help.svg b/web/static/src/assets/icons/help.svg similarity index 100% rename from view/src/assets/icons/help.svg rename to web/static/src/assets/icons/help.svg diff --git a/view/src/assets/icons/language.svg b/web/static/src/assets/icons/language.svg similarity index 100% rename from view/src/assets/icons/language.svg rename to web/static/src/assets/icons/language.svg diff --git a/view/src/assets/icons/settings.svg b/web/static/src/assets/icons/settings.svg similarity index 100% rename from view/src/assets/icons/settings.svg rename to web/static/src/assets/icons/settings.svg diff --git a/view/src/assets/icons/warning.svg b/web/static/src/assets/icons/warning.svg similarity index 100% rename from view/src/assets/icons/warning.svg rename to web/static/src/assets/icons/warning.svg diff --git a/view/src/i18n.js b/web/static/src/i18n.js similarity index 100% rename from view/src/i18n.js rename to web/static/src/i18n.js diff --git a/view/src/locales/en/translation.json b/web/static/src/locales/en/translation.json similarity index 100% rename from view/src/locales/en/translation.json rename to web/static/src/locales/en/translation.json diff --git a/view/src/locales/zh_cn/translation.json b/web/static/src/locales/zh_cn/translation.json similarity index 100% rename from view/src/locales/zh_cn/translation.json rename to web/static/src/locales/zh_cn/translation.json diff --git a/view/src/main.js b/web/static/src/main.js similarity index 100% rename from view/src/main.js rename to web/static/src/main.js diff --git a/view/src/page/home/index.vue b/web/static/src/page/home/index.vue similarity index 100% rename from view/src/page/home/index.vue rename to web/static/src/page/home/index.vue diff --git a/view/src/page/install/index.vue b/web/static/src/page/install/index.vue similarity index 90% rename from view/src/page/install/index.vue rename to web/static/src/page/install/index.vue index f152eef..5c28aaa 100644 --- a/view/src/page/install/index.vue +++ b/web/static/src/page/install/index.vue @@ -206,14 +206,8 @@ export default { .then((res) => { let ipa = res.data[0]; _this.ipa = ipa; - - // 为每个appleid创建对应的工作目录,用于存储AltServer生成的签名证书 - let dirName = _this.form.account - .toLowerCase() - .replace(/[^0-9a-zA-Z]+/gi, ""); - let workdir = `./AltServer/${dirName}`; _this.websocketsend( - `mkdir -p ${workdir} && cd ${workdir} && AltServer -u ${_this.device.udid} -a '${_this.form.account}' -p '${_this.form.password}' '${ipa.path}'` + `sideloader --udid ${_this.device.udid} -a '${_this.form.account}' -p '${_this.form.password}' '${ipa.path}'` ); }) .catch((error) => { @@ -287,44 +281,32 @@ export default { ); _this.cmd.line = _this.cmd.line.replace(_this.form.password, "******"); - if ( - _this.cmd.line.indexOf("Signing Progress") === -1 && - _this.cmd.line.indexOf("AltServer -u") === -1 - ) { + // if ( + // _this.cmd.line.indexOf("slideloader") === -1 + // ) { _this.log.output += _this.cmd.line; // The textbox follows the scroll to the bottom _this.$nextTick(() => { const textarea = document.querySelector("#log"); textarea.scrollTop = textarea.scrollHeight; }); - } + // } _this.cmd.line = ""; } // input 2FA authentication code - if (_this.cmd.output.indexOf("Enter two factor code") !== -1) { + if (_this.cmd.output.indexOf("A code has been sent to your devices, please type it here") !== -1) { _this.cmd.output = ""; _this.dialogVisible = true; return; } - // override innstall with the same Apple ID as before. - if ( - _this.cmd.output.indexOf( - "Installing AltStore with Multiple AltServers Not Supported" - ) !== -1 - ) { - if (_this.cmd.output.indexOf("Press any key to continue...") !== -1) { - _this.cmd.output = ""; - _this.websocketsend(""); - return; - } - } // pairing error if ( _this.cmd.output.indexOf("Could not install") !== -1 || + _this.cmd.output.indexOf("Error:") !== -1 || _this.cmd.output.indexOf("command not found") !== -1 ) { _this.cmd.output = ""; diff --git a/view/src/page/layout.vue b/web/static/src/page/layout.vue similarity index 100% rename from view/src/page/layout.vue rename to web/static/src/page/layout.vue diff --git a/view/src/page/pair/index.vue b/web/static/src/page/pair/index.vue similarity index 100% rename from view/src/page/pair/index.vue rename to web/static/src/page/pair/index.vue diff --git a/view/src/page/settings/index.vue b/web/static/src/page/settings/index.vue similarity index 100% rename from view/src/page/settings/index.vue rename to web/static/src/page/settings/index.vue diff --git a/view/src/router/index.js b/web/static/src/router/index.js similarity index 100% rename from view/src/router/index.js rename to web/static/src/router/index.js diff --git a/view/src/utils/crypto.js b/web/static/src/utils/crypto.js similarity index 100% rename from view/src/utils/crypto.js rename to web/static/src/utils/crypto.js diff --git a/view/src/utils/request.js b/web/static/src/utils/request.js similarity index 100% rename from view/src/utils/request.js rename to web/static/src/utils/request.js diff --git a/view/tailwind.config.js b/web/static/tailwind.config.js similarity index 100% rename from view/tailwind.config.js rename to web/static/tailwind.config.js diff --git a/view/vite.config.js b/web/static/vite.config.js similarity index 100% rename from view/vite.config.js rename to web/static/vite.config.js diff --git a/view/yarn.lock b/web/static/yarn.lock similarity index 100% rename from view/yarn.lock rename to web/static/yarn.lock diff --git a/web/web.go b/web/web.go new file mode 100644 index 0000000..657b4bb --- /dev/null +++ b/web/web.go @@ -0,0 +1,31 @@ +package web + +import ( + "fmt" + + "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/log" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func Run(addr string, port int) error { + server := fiber.New() + route(server) + + // set fiber web server access log + server.Use(logger.New()) + accessWriter := log.CreateRollingLogFile(app.Config.Log.AccessLog) + if accessWriter != nil { + server.Use(logger.New(logger.Config{ + Output: accessWriter, + })) + log.Infof("Web access log file path: %s", app.Config.Log.AccessLog) + } + + if err := server.Listen(fmt.Sprintf("%s:%d", addr, port)); err != nil { + log.Error(err.Error()) + return err + } + return nil +} From 06811748a97686b925ef0249f166fe70dafb4666 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 06:54:11 +0000 Subject: [PATCH 02/35] feat: add tvos17 support --- .github/workflows/beta.yml | 95 ---------- .github/workflows/build.yml | 19 +- .github/workflows/clean_package.yml | 8 +- .github/workflows/issue_close_inactive.yml | 6 +- .github/workflows/release-nightly.yml | 192 +++++++++++++++++++++ .github/workflows/release.yml | 163 ++++++++++++----- Dockerfile | 2 +- go.mod | 10 +- go.sum | 23 +-- internal/app/config.go | 2 +- internal/task/task.go | 52 ++++-- web/static/src/page/install/index.vue | 24 +-- web/static/yarn.lock | 10 -- web/web.go | 5 +- 14 files changed, 384 insertions(+), 227 deletions(-) delete mode 100644 .github/workflows/beta.yml create mode 100644 .github/workflows/release-nightly.yml diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml deleted file mode 100644 index 943cbc2..0000000 --- a/.github/workflows/beta.yml +++ /dev/null @@ -1,95 +0,0 @@ -# Attention: -# - Need goto [Settings -> Actions -> general] -# - Set [workflow permissions] to "Read and write permissions" -name: "🚀 Beta" - -# on events -on: - workflow_dispatch: - -# jobs -jobs: - beta: - name: Generate beta builds - strategy: - matrix: - go_version: [1.18.x] - runs-on: ubuntu-latest - steps: - # step 1: checkout repository code - - name: Checkout the repository - uses: actions/checkout@v3 - - # step 2: setup build envirement - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go_version }} - - uses: actions/setup-node@v2 - with: - node-version: "16" - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - # step 3: set workflow variables - - name: Initialize workflow variables - id: vars - run: | - echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT - echo "BUILDDATE=$(date '+%F-%T')" >> $GITHUB_OUTPUT - echo "COMMIT=$(git rev-parse --verify HEAD)" >> $GITHUB_OUTPUT - echo "APP_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_OUTPUT - echo "REPO=$(echo 'github.com/${{ github.repository }}')" >> $GITHUB_OUTPUT - echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT - - if [ ! -z $DOCKER_TOKEN ]; then echo "HAS_DOCKER_TOKEN=${HAS_DOCKER_TOKEN}" >> $GITHUB_OUTPUT; fi - env: - DOCKER_TOKEN: "${{ secrets.DOCKER_TOKEN }}" - - # step 4: generate build files - - name: build frontend - run: cd ./view && npm install && npm run build - - name: Generate build files - uses: crazy-max/ghaction-xgo@v2 - with: - xgo_version: latest - go_version: ${{ matrix.go_version }} - dest: build - prefix: ${{steps.vars.outputs.APP_NAME}} - targets: linux/amd64,linux/arm64 - v: true - x: false - ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/version.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/version.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/version.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/mode.Mode=production - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{steps.vars.outputs.APP_NAME}} - path: build - - # step 7.2: push to GitHub Container Registry - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ghcr.io/${{ github.repository }} - - name: Build and push Docker images to ghci - uses: docker/build-push-action@v4 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ghcr.io/${{ github.repository }}:beta - labels: ${{ steps.meta.outputs.labels }} - build-args: | - APP_NAME=${{steps.vars.outputs.APP_NAME}} - VERSION=${{steps.vars.outputs.VERSION}} - BUILDDATE=${{steps.vars.outputs.BUILDDATE}} - COMMIT=${{steps.vars.outputs.COMMIT}} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7635c92..d66197c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,22 +11,23 @@ jobs: build: strategy: matrix: - go_version: [1.18.x] + go_version: [1.21.x] runs-on: ubuntu-latest steps: - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go_version }} - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: "16" - - uses: actions/checkout@v3 + node-version: "20" + - uses: actions/checkout@v4 - name: build frontend - run: cd ./view && npm install && npm run build - - uses: golangci/golangci-lint-action@v3 + run: cd ./web/static && npm install && npm run build + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 with: - version: v1.53.2 - args: --out-format=colored-line-number --timeout=5m + version: v1.58 + args: --timeout=5m - run: go mod download - # - run: go test -coverprofile=coverage.txt -covermode=atomic ./... + - run: go test -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/.github/workflows/clean_package.yml b/.github/workflows/clean_package.yml index c3d4c71..509dba6 100644 --- a/.github/workflows/clean_package.yml +++ b/.github/workflows/clean_package.yml @@ -5,8 +5,8 @@ name: "🗑️ Clean Package" # Controls when the workflow will run on: - schedule: - - cron: "0 0 1 * *" # the first day of the month + # schedule: + # - cron: "0 0 * * 1" # the first day of the week # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -28,8 +28,8 @@ jobs: uses: snok/container-retention-policy@v2 with: image-names: ${{steps.vars.outputs.APP_NAME}} - cut-off: A week ago UTC + cut-off: A month ago UTC keep-at-least: 10 - skip-tags: latest + skip-tags: latest, beta, alpha, main, master account-type: personal token: ${{ secrets.PAT }} diff --git a/.github/workflows/issue_close_inactive.yml b/.github/workflows/issue_close_inactive.yml index 4bb86ce..41767f4 100644 --- a/.github/workflows/issue_close_inactive.yml +++ b/.github/workflows/issue_close_inactive.yml @@ -1,8 +1,8 @@ name: "🚫 Close Inactive" on: - schedule: - - cron: "0 0 * * 1" + # schedule: + # - cron: "0 0 * * 1" # the first day of the week workflow_dispatch: jobs: @@ -20,4 +20,4 @@ jobs: days-before-stale: 30 days-before-close: 0 days-before-pr-stale: -1 - days-before-pr-close: -1 \ No newline at end of file + days-before-pr-close: -1 diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml new file mode 100644 index 0000000..6f9fd34 --- /dev/null +++ b/.github/workflows/release-nightly.yml @@ -0,0 +1,192 @@ +name: "🚀 Release Nightly" + +# on events +on: + # schedule: + # - cron: '0 0 * * *' # runs daily at 00:00 + workflow_dispatch: + # push: + # branches: [main, master, release/v*] + +# jobs +jobs: + check: + name: Check has new commits today + permissions: + contents: write + runs-on: ubuntu-latest + outputs: + new_commit_count: ${{steps.commit_check.outputs.new_commit_count}} + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Check for new commits + id: commit_check + run: echo "new_commit_count=$(git log --oneline --since '24 hours ago' | wc -l)" >> $GITHUB_OUTPUT + + build: + name: Generate cross-platform builds + if: ${{needs.check.outputs.new_commit_count > 0}} + needs: [check] + permissions: + contents: write + strategy: + matrix: + go_version: [1.21.x] + runs-on: ubuntu-latest + outputs: + VERSION: ${{steps.vars.outputs.VERSION}} + BUILDDATE: ${{steps.vars.outputs.BUILDDATE}} + COMMIT: ${{steps.vars.outputs.COMMIT}} + APP_NAME: ${{steps.vars.outputs.APP_NAME}} + PUSH_DOCKERHUB: ${{steps.vars.outputs.PUSH_DOCKERHUB}} + steps: + # step 1: checkout repository code + - name: Checkout the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # step 2: setup build envirement + - uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go_version }} + - uses: actions/setup-node@v4 + with: + node-version: "20" + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + # step 3: set workflow variables + - id: metadata + uses: ahmadnassri/action-metadata@v2 + - name: Initialize workflow environments variables + id: vars + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "BUILDDATE=$(date '+%F-%T')" >> $GITHUB_OUTPUT + echo "COMMIT=$(git rev-parse --verify HEAD)" >> $GITHUB_OUTPUT + echo "APP_NAME=${{ steps.metadata.outputs.repository_name }}" >> $GITHUB_OUTPUT + echo "REPO=$(echo 'github.com/${{ github.repository }}')" >> $GITHUB_OUTPUT + echo "BRANCH=${{ steps.metadata.outputs.repository_default_branch }}" >> $GITHUB_OUTPUT + + if [ ! -z $DOCKER_TOKEN ]; then echo "PUSH_DOCKERHUB=1" >> $GITHUB_OUTPUT; fi + env: + DOCKER_TOKEN: "${{ secrets.DOCKER_TOKEN }}" + + # step 4: generate build files + - name: build frontend + run: cd ./web/static && npm install && npm run build + - name: Generate build files + uses: crazy-max/ghaction-xgo@v2 + env: + CGO_ENABLED: "0" + with: + xgo_version: latest + go_version: ${{ matrix.go_version }} + dest: build + prefix: ${{steps.vars.outputs.APP_NAME}} + targets: windows/386,windows/amd64,linux/386,linux/amd64,darwin/386,darwin/amd64,linux/386,linux/arm64 + v: true + x: false + ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/app/build.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Mode=production + + # step 5: Upload binary to artifact + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{steps.vars.outputs.APP_NAME}} + path: build + retention-days: 1 + + dockerhub: + name: Push to DockerHub + if: ${{needs.build.outputs.PUSH_DOCKERHUB}} + needs: [build] + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{needs.build.outputs.APP_NAME}} + path: build + - name: Login to DockerHub + if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN }} + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }} + - name: Build and push Docker images to DockerHub + if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN }} + uses: docker/build-push-action@v5 + with: + context: . + push: true + provenance: false + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + APP_NAME=${{needs.build.outputs.APP_NAME}} + VERSION=${{needs.build.outputs.VERSION}} + BUILDDATE=${{needs.build.outputs.BUILDDATE}} + COMMIT=${{needs.build.outputs.COMMIT}} + + ghcr: + name: Push to GitHub Container Registry + needs: [build] + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{needs.build.outputs.APP_NAME}} + path: build + - name: Display structure of downloaded files + run: ls -R + working-directory: build + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + - name: Build and push Docker images to ghci + uses: docker/build-push-action@v5 + with: + context: . + push: true + provenance: false + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + APP_NAME=${{needs.build.outputs.APP_NAME}} + VERSION=${{needs.build.outputs.VERSION}} + BUILDDATE=${{needs.build.outputs.BUILDDATE}} + COMMIT=${{needs.build.outputs.COMMIT}} + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72b6e0f..e7a03e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,3 @@ -# Attention: -# - Need goto [Settings -> Actions -> general] -# - Set [workflow permissions] to "Read and write permissions" name: "🚀 Release" # on events @@ -10,17 +7,24 @@ on: # jobs jobs: - # generate build cross-platform build files - release: + build: name: Generate cross-platform builds + permissions: + contents: write strategy: matrix: - go_version: [1.18.x] + go_version: [1.21.x] runs-on: ubuntu-latest + outputs: + VERSION: ${{steps.vars.outputs.VERSION}} + BUILDDATE: ${{steps.vars.outputs.BUILDDATE}} + COMMIT: ${{steps.vars.outputs.COMMIT}} + APP_NAME: ${{steps.vars.outputs.APP_NAME}} + PUSH_DOCKERHUB: ${{steps.vars.outputs.PUSH_DOCKERHUB}} steps: # step 1: checkout repository code - name: Checkout the repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -28,63 +32,88 @@ jobs: - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go_version }} - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: "16" + node-version: "20" - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # step 3: set workflow variables - - name: Initialize workflow variables + - id: metadata + uses: ahmadnassri/action-metadata@v2 + - name: Initialize workflow environments variables id: vars run: | - echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_OUTPUT echo "BUILDDATE=$(date '+%F-%T')" >> $GITHUB_OUTPUT echo "COMMIT=$(git rev-parse --verify HEAD)" >> $GITHUB_OUTPUT - echo "APP_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_OUTPUT + echo "APP_NAME=${{ steps.metadata.outputs.repository_name }}" >> $GITHUB_OUTPUT echo "REPO=$(echo 'github.com/${{ github.repository }}')" >> $GITHUB_OUTPUT - echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT + echo "BRANCH=${{ steps.metadata.outputs.repository_default_branch }}" >> $GITHUB_OUTPUT - if [ ! -z $DOCKER_TOKEN ]; then echo "HAS_DOCKER_TOKEN=${HAS_DOCKER_TOKEN}" >> $GITHUB_OUTPUT; fi + if [ ! -z $DOCKER_TOKEN ]; then echo "PUSH_DOCKERHUB=1" >> $GITHUB_OUTPUT; fi env: DOCKER_TOKEN: "${{ secrets.DOCKER_TOKEN }}" # step 4: generate build files - name: build frontend - run: cd ./view && npm install && npm run build + run: cd ./web/static && npm install && npm run build - name: Generate build files uses: crazy-max/ghaction-xgo@v2 + env: + CGO_ENABLED: "0" with: xgo_version: latest go_version: ${{ matrix.go_version }} dest: build prefix: ${{steps.vars.outputs.APP_NAME}} - targets: linux/amd64,linux/arm64 + targets: windows/386,windows/amd64,linux/386,linux/amd64,darwin/386,darwin/amd64,linux/386,linux/arm64 v: true x: false - ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/version.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/version.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/version.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/mode.Mode=production + ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/app/build.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Mode=production - # step 5: compress build files - - name: Compress build files - run: cd ./build && for i in *; do tar -czf $i.tar.gz $i; done && cd .. + # step 5: Upload binary to artifact + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{steps.vars.outputs.APP_NAME}} + path: build + retention-days: 1 - # step 6: Upload binary to GitHub Release + # step 6: Generate Changelog - name: Generate Changelog id: changelog uses: bitxeno/changelogithub-action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }} + # output-file: ./docs/CHANGELOG.md types: | feat fix perf refactor - tweak + chore + docs + # build + # test + # style + # ci + # - name: Git commit changelog + # uses: EndBug/add-and-commit@v9 + # with: + # default_author: github_actions + # add: "docs/" + # message: "docs: release notes for ${{ github.ref_name }}" + # push: "origin HEAD:${{ steps.vars.outputs.BRANCH }}" + + # step 7: Upload binary to GitHub Release + - name: Compress build files + run: cd ./build && for i in *; do tar -czf $i.tar.gz $i; done && cd .. - name: Upload binary to GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: "startsWith(github.ref, 'refs/tags/')" with: files: | @@ -93,28 +122,67 @@ jobs: body: ${{ steps.changelog.outputs.changelog }} fail_on_unmatched_files: true - # step 7.1: push to DockerHub + dockerhub: + name: Push to DockerHub + if: ${{needs.build.outputs.PUSH_DOCKERHUB}} + needs: [build] + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{needs.build.outputs.APP_NAME}} + path: build - name: Login to DockerHub - if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN == 'true' }} - uses: docker/login-action@v1 + if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN }} + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }} - name: Build and push Docker images to DockerHub - if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN == 'true' }} - uses: docker/build-push-action@v2 + if: ${{ steps.vars.outputs.HAS_DOCKER_TOKEN }} + uses: docker/build-push-action@v5 with: context: . push: true + provenance: false platforms: linux/amd64,linux/arm64 - tags: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:latest + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} build-args: | - APP_NAME=${{steps.vars.outputs.APP_NAME}} - VERSION=${{steps.vars.outputs.VERSION}} - BUILDDATE=${{steps.vars.outputs.BUILDDATE}} - COMMIT=${{steps.vars.outputs.COMMIT}} + APP_NAME=${{needs.build.outputs.APP_NAME}} + VERSION=${{needs.build.outputs.VERSION}} + BUILDDATE=${{needs.build.outputs.BUILDDATE}} + COMMIT=${{needs.build.outputs.COMMIT}} - # step 7.2: push to GitHub Container Registry + ghcr: + name: Push to GitHub Container Registry + needs: [build] + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{needs.build.outputs.APP_NAME}} + path: build + - name: Display structure of downloaded files + run: ls -R + working-directory: build - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: @@ -123,19 +191,34 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} - name: Build and push Docker images to ghci - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: true + provenance: false platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | - APP_NAME=${{steps.vars.outputs.APP_NAME}} - VERSION=${{steps.vars.outputs.VERSION}} - BUILDDATE=${{steps.vars.outputs.BUILDDATE}} - COMMIT=${{steps.vars.outputs.COMMIT}} + APP_NAME=${{needs.build.outputs.APP_NAME}} + VERSION=${{needs.build.outputs.VERSION}} + BUILDDATE=${{needs.build.outputs.BUILDDATE}} + COMMIT=${{needs.build.outputs.COMMIT}} + + clean: + name: Delete temp artifacts + # ignore dockerhub job skipped + if: always() && (needs.dockerhub.result == 'success' || needs.dockerhub.result == 'skipped') && (needs.ghcr.result == 'success') + needs: [build, dockerhub, ghcr] + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: geekyeggo/delete-artifact@v5 + with: + name: ${{needs.build.outputs.APP_NAME}} + failOnError: false diff --git a/Dockerfile b/Dockerfile index 75e21dd..8af30ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ RUN case ${TARGETARCH} in \ "arm64") PKG_ARCH=aarch64 ;; \ esac \ && cd /tmp \ - && wget https://github.com/bitxeno/usbmuxd2/releases/download/v0.0.2/usbmuxd2-ubuntu-${PKG_ARCH}.tar.gz \ + && wget https://github.com/bitxeno/usbmuxd2/releases/download/v0.0.3/usbmuxd2-ubuntu-${PKG_ARCH}.tar.gz \ && tar zxf usbmuxd2-ubuntu-${PKG_ARCH}.tar.gz \ && dpkg -i ./libusb_1.0.26-1_${PKG_ARCH}.deb \ && dpkg -i ./libgeneral_1.0.0-1_${PKG_ARCH}.deb \ diff --git a/go.mod b/go.mod index d27256b..c6e3368 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/rs/zerolog v1.29.1 github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a - github.com/shirou/gopsutil/v4 v4.24.5 github.com/silenceper/wechat/v2 v2.1.5 github.com/urfave/cli/v2 v2.3.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -47,16 +46,15 @@ require ( github.com/fasthttp/websocket v1.5.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc // indirect github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/iamacarpet/go-winpty v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/kr/pretty v0.3.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -69,22 +67,18 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/philhofer/fwd v1.1.2 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/tinylib/msgp v1.1.8 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.10.0 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sync v0.3.0 // indirect diff --git a/go.sum b/go.sum index d0a01c7..190ad24 100644 --- a/go.sum +++ b/go.sum @@ -146,8 +146,6 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M= github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ= @@ -202,6 +200,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= @@ -328,8 +327,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -450,8 +447,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -511,11 +506,6 @@ github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJv github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM= -github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shogo82148/androidbinary v1.0.2/go.mod h1:c3BBft4TLXkvqry+EEN8z9ZtyY09r7jLMvFB/L/KrfI= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/silenceper/wechat/v2 v2.1.5 h1:eIlv61v2bAFBG9ZE75zuRC0ALHEZEUq8JlJ9tfKvatg= @@ -547,6 +537,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -555,10 +546,6 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -577,8 +564,6 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= -github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= @@ -691,7 +676,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -709,7 +693,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -729,8 +712,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/internal/app/config.go b/internal/app/config.go index 8232cfb..e822397 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -30,7 +30,7 @@ type Configuration struct { Server struct { ListenAddr string `koanf:"listen_addr" default:"0.0.0.0"` - Port int `koanf:"port" default:"9000"` + Port int `koanf:"port" default:"9400"` DataDir string `koanf:"data_dir"` } `koanf:"app" json:"app"` diff --git a/internal/task/task.go b/internal/task/task.go index a3ed3b3..38ef677 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -14,7 +14,6 @@ import ( "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/log" - "github.com/bitxeno/atvloadly/internal/manager" "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/notify" "github.com/bitxeno/atvloadly/internal/service" @@ -175,18 +174,18 @@ func (t *Task) runInternal(v model.InstalledApp) error { } // 检查developer disk image是否已mounted - imageInfo, err := manager.GetDeviceMountImageInfo(v.UDID) - if err != nil { - log.Err(err).Msg("Check DeveloperDiskImage mounted error: ") - return err - } - - if !imageInfo.ImageMounted { - log.Error("DeveloperDiskImage not mounted.") - return err - } - - cmd := exec.Command("sideloader", "install", "--quiet", "--udid", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) + // imageInfo, err := manager.GetDeviceMountImageInfo(v.UDID) + // if err != nil { + // log.Err(err).Msg("Check DeveloperDiskImage mounted error: ") + // return err + // } + + // if !imageInfo.ImageMounted { + // log.Error("DeveloperDiskImage not mounted.") + // return err + // } + + cmd := exec.Command("sideloader", "install", "--quiet", "--nocolor", "--udid", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) cmd.Dir = app.Config.Server.DataDir cmd.Env = []string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()} stdin, err := cmd.StdinPipe() @@ -199,21 +198,36 @@ func (t *Task) runInternal(v model.InstalledApp) error { log.Err(err).Msg("Error obtaining stdout: ") return err } + stderr, err := cmd.StderrPipe() + if err != nil { + log.Err(err).Msg("Error obtaining stdout: ") + return err + } var output strings.Builder reader := bufio.NewReader(stdout) + readerErr := bufio.NewReader(stderr) go func(reader io.Reader) { defer stdin.Close() scanner := bufio.NewScanner(reader) for scanner.Scan() { lineText := scanner.Text() + _, _ = output.WriteString(lineText) + _, _ = output.WriteString("\n") - // 忽略 Signing 消息,日志太多 - if strings.Contains(lineText, "Signing Progress") { - continue + // 处理中途需要输入才能继续的,如 Installing AltStore with Multiple AltServers Not Supported 消息 + if strings.Contains(lineText, "Press any key to continue") { + _, _ = stdin.Write([]byte("\n")) } + } + }(reader) + go func(reader io.Reader) { + defer stdin.Close() + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + lineText := scanner.Text() _, _ = output.WriteString(lineText) _, _ = output.WriteString("\n") @@ -222,14 +236,18 @@ func (t *Task) runInternal(v model.InstalledApp) error { _, _ = stdin.Write([]byte("\n")) } } - }(reader) + }(readerErr) if err := cmd.Start(); nil != err { + data := []byte(output.String()) + t.writeLog(v, data) log.Err(err).Msg("执行安装脚本出错") return err } err = cmd.Wait() if err != nil { + data := []byte(output.String()) + t.writeLog(v, data) log.Err(err).Msg("执行安装脚本出错") return err } diff --git a/web/static/src/page/install/index.vue b/web/static/src/page/install/index.vue index 5c28aaa..a6905c6 100644 --- a/web/static/src/page/install/index.vue +++ b/web/static/src/page/install/index.vue @@ -183,18 +183,7 @@ export default { _this.log.output = ""; _this.log.show = true; - // 挂载DeveloperDiskImage - _this.log.output += "Prepare to mount DeveloperDiskImage...\n"; - let data = await api.mountDeviceImageAsync(_this.id); - if (data != "success") { - _this.log.output += data; - _this.cmd.output = ""; - _this.loading = false; - toast.error(this.$t("install.toast.install_failed")); - return; - } - _this.log.output += "DeveloperDiskImage has mounted.\n"; - + let formData = new FormData(); for (let i = 0; i < _this.files.length; i++) { let file = _this.files[i]; @@ -207,7 +196,7 @@ export default { let ipa = res.data[0]; _this.ipa = ipa; _this.websocketsend( - `sideloader --udid ${_this.device.udid} -a '${_this.form.account}' -p '${_this.form.password}' '${ipa.path}'` + `sideloader install --nocolor --udid ${_this.device.udid} -a '${_this.form.account}' -p '${_this.form.password}' '${ipa.path}'` ); }) .catch((error) => { @@ -281,16 +270,17 @@ export default { ); _this.cmd.line = _this.cmd.line.replace(_this.form.password, "******"); - // if ( - // _this.cmd.line.indexOf("slideloader") === -1 - // ) { + // ignore slideloader command output + if ( + _this.cmd.line.indexOf("sideloader") === -1 + ) { _this.log.output += _this.cmd.line; // The textbox follows the scroll to the bottom _this.$nextTick(() => { const textarea = document.querySelector("#log"); textarea.scrollTop = textarea.scrollHeight; }); - // } + } _this.cmd.line = ""; } diff --git a/web/static/yarn.lock b/web/static/yarn.lock index d5269be..3478a1b 100644 --- a/web/static/yarn.lock +++ b/web/static/yarn.lock @@ -513,11 +513,6 @@ entities@^4.2.0: resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -esbuild-darwin-64@0.14.54: - version "0.14.54" - resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz" - integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== - esbuild@^0.14.27: version "0.14.54" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz" @@ -609,11 +604,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" diff --git a/web/web.go b/web/web.go index 657b4bb..ca1c175 100644 --- a/web/web.go +++ b/web/web.go @@ -2,6 +2,7 @@ package web import ( "fmt" + "math" "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/log" @@ -10,7 +11,9 @@ import ( ) func Run(addr string, port int) error { - server := fiber.New() + server := fiber.New(fiber.Config{ + BodyLimit: math.MaxInt, + }) route(server) // set fiber web server access log From 6ad503d375f6baf849348141ed4ea7c2bcae06ef Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 06:59:11 +0000 Subject: [PATCH 03/35] ci: fix linter error --- internal/task/task.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/task/task.go b/internal/task/task.go index 38ef677..cc5a0b4 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -8,7 +8,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strings" "time" @@ -22,10 +21,6 @@ import ( var instance = new() -var ( - regValidName = regexp.MustCompile("(?i)[^0-9a-zA-Z]+") -) - type Task struct { c *cron.Cron Running bool `json:"running"` From 635839ca5d760aaebf36b4e8b529416c2995607a Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:01:42 +0000 Subject: [PATCH 04/35] ci: fix build error --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d66197c..6f2af9f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,5 +29,5 @@ jobs: with: version: v1.58 args: --timeout=5m - - run: go mod download - - run: go test -coverprofile=coverage.txt -covermode=atomic ./... + # - run: go mod download + # - run: go test -coverprofile=coverage.txt -covermode=atomic ./... From a4cde476add55bf85ca8cb386e51b2054733663d Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:05:19 +0000 Subject: [PATCH 05/35] ci: update github aciton --- .github/workflows/release-nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 6f9fd34..6dc1a65 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -5,8 +5,8 @@ on: # schedule: # - cron: '0 0 * * *' # runs daily at 00:00 workflow_dispatch: - # push: - # branches: [main, master, release/v*] + push: + branches: [main, master, release/v*, tvos17] # jobs jobs: From d4a59148d33234562a982f2ce332dd265484c145 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:08:48 +0000 Subject: [PATCH 06/35] fix: compatible with old configuration. --- internal/app/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/config.go b/internal/app/config.go index e822397..c77a3a5 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -31,7 +31,7 @@ type Configuration struct { Server struct { ListenAddr string `koanf:"listen_addr" default:"0.0.0.0"` Port int `koanf:"port" default:"9400"` - DataDir string `koanf:"data_dir"` + DataDir string `koanf:"work_dir"` } `koanf:"app" json:"app"` Db db.Config `koanf:"db" json:"db"` From c390676e9917e8b9123d8e405463ca4cb252c29a Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:11:43 +0000 Subject: [PATCH 07/35] fix: compatible with old configuration. --- internal/app/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/app/config.go b/internal/app/config.go index c77a3a5..bd7efc5 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -19,7 +19,7 @@ type Configuration struct { ImageSource string `koanf:"image_source" json:"image_source" default:"https://github.com/haikieu/xcode-developer-disk-image-all-platforms/raw/master/DiskImages/AppleTVOS.platform/DeviceSupport/{0}.zip"` CNProxy string `koanf:"cn_proxy" json:"cn_proxy" default:"https://mirror.ghproxy.com"` } `koanf:"developer_disk_image" json:"developer_disk_image"` - } + } `koanf:"app" json:"app"` Log struct { Level string `koanf:"level" default:"info"` @@ -30,9 +30,9 @@ type Configuration struct { Server struct { ListenAddr string `koanf:"listen_addr" default:"0.0.0.0"` - Port int `koanf:"port" default:"9400"` + Port int `koanf:"port" default:"9000"` DataDir string `koanf:"work_dir"` - } `koanf:"app" json:"app"` + } `koanf:"server" json:"server"` Db db.Config `koanf:"db" json:"db"` } From 2dbd5a67eda8c4de9ba927465dcee82effa377d3 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:22:30 +0000 Subject: [PATCH 08/35] fix: fix github action --- .github/workflows/release-nightly.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 6dc1a65..7a7630c 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -57,9 +57,9 @@ jobs: with: node-version: "20" - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 # step 3: set workflow variables - id: metadata @@ -137,7 +137,7 @@ jobs: context: . push: true provenance: false - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | @@ -181,7 +181,7 @@ jobs: context: . push: true provenance: false - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | From b50d20fa15ef58045b0270cba8812b855b71d3a8 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 07:57:28 +0000 Subject: [PATCH 09/35] fix: compatible with old configuration. --- internal/cfg/cfg.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go index b5a08ae..3790818 100644 --- a/internal/cfg/cfg.go +++ b/internal/cfg/cfg.go @@ -37,7 +37,6 @@ func (c *configuration) load(path string) error { } // read config from file - ko := koanf.New(".") if !utils.Exists(c.path) { fmt.Printf("[WARN] Config file not exists. path: %s\n", c.path) return nil @@ -46,17 +45,17 @@ func (c *configuration) load(path string) error { fmt.Printf("Load config from path: %s\n", c.path) ext := filepath.Ext(c.path) if ext == ".yaml" || ext == ".yml" { - if err := ko.Load(file.Provider(c.path), yaml.Parser()); err != nil { + if err := c.ko.Load(file.Provider(c.path), yaml.Parser()); err != nil { log.Panicf("Yaml config file read failed. error: %s \n", err) } } if ext == ".json" { - if err := ko.Load(file.Provider(c.path), json.Parser()); err != nil { + if err := c.ko.Load(file.Provider(c.path), json.Parser()); err != nil { log.Panicf("Json config file read failed. error: %s \n", err) } } - return ko.Unmarshal("", &c) + return nil } func (c *configuration) BindStruct(dst any) error { From 12c95c327bf2ffd31969d7a4b8cbcf950731a36e Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 09:52:33 +0000 Subject: [PATCH 10/35] fix: disable log http cache --- internal/task/task.go | 1 + web/router.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/internal/task/task.go b/internal/task/task.go index cc5a0b4..40c8839 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -180,6 +180,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { // return err // } + // sideloader 会处理特殊字符"$",对于带有这特殊字符的,需要加单引号包括 cmd := exec.Command("sideloader", "install", "--quiet", "--nocolor", "--udid", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) cmd.Dir = app.Config.Server.DataDir cmd.Env = []string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()} diff --git a/web/router.go b/web/router.go index 2a58f67..078799c 100644 --- a/web/router.go +++ b/web/router.go @@ -65,6 +65,8 @@ func route(fi *fiber.App) { id := utils.MustParseInt(c.Params("id")) path := filepath.Join(app.Config.Server.DataDir, "log", fmt.Sprintf("task_%d.log", id)) + c.Set("Cache-Control", "no-cache, no-store, must-revalidate;") + c.Set("pragma", "no-cache") return c.Status(http.StatusOK).SendFile(path, false) }) From 938fa0172a9716c99c4187c482506984fce98eea Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:17:54 +0000 Subject: [PATCH 11/35] fix: add more timeout to upload ipa --- web/static/src/api/api.js | 2 +- web/static/src/page/install/index.vue | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/web/static/src/api/api.js b/web/static/src/api/api.js index 6bca2fd..77b35b6 100644 --- a/web/static/src/api/api.js +++ b/web/static/src/api/api.js @@ -55,7 +55,7 @@ export default { return request({ url: "/api/upload", method: "post", - timeout: 30000, + timeout: 1200000, headers: { "Content-Type": "multipart/form-data", }, diff --git a/web/static/src/page/install/index.vue b/web/static/src/page/install/index.vue index a6905c6..b0b6b08 100644 --- a/web/static/src/page/install/index.vue +++ b/web/static/src/page/install/index.vue @@ -202,6 +202,9 @@ export default { .catch((error) => { console.log(error); _this.log.output += error; + _this.loading = false; + toast.error(this.$t("install.toast.install_failed")); + return; }); }, reset() { @@ -294,11 +297,7 @@ export default { // pairing error - if ( - _this.cmd.output.indexOf("Could not install") !== -1 || - _this.cmd.output.indexOf("Error:") !== -1 || - _this.cmd.output.indexOf("command not found") !== -1 - ) { + if (_this.cmd.output.indexOf("ERROR") !== -1) { _this.cmd.output = ""; _this.loading = false; toast.error(this.$t("install.toast.install_failed")); From ccf5796b3e771bac9c01454c7b164f7fd9d125d2 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:36:06 +0000 Subject: [PATCH 12/35] fix: send notify not error log --- internal/task/task.go | 14 +++++++------- web/static/src/api/api.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/task/task.go b/internal/task/task.go index 40c8839..5f21d74 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -201,6 +201,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { } var output strings.Builder + var outputErr strings.Builder reader := bufio.NewReader(stdout) readerErr := bufio.NewReader(stderr) go func(reader io.Reader) { @@ -227,6 +228,9 @@ func (t *Task) runInternal(v model.InstalledApp) error { _, _ = output.WriteString(lineText) _, _ = output.WriteString("\n") + _, _ = outputErr.WriteString(lineText) + _, _ = outputErr.WriteString("\n") + // 处理中途需要输入才能继续的,如 Installing AltStore with Multiple AltServers Not Supported 消息 if strings.Contains(lineText, "Press any key to continue") { _, _ = stdin.Write([]byte("\n")) @@ -237,7 +241,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { data := []byte(output.String()) t.writeLog(v, data) log.Err(err).Msg("执行安装脚本出错") - return err + return fmt.Errorf(outputErr.String(), err) } err = cmd.Wait() @@ -245,7 +249,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { data := []byte(output.String()) t.writeLog(v, data) log.Err(err).Msg("执行安装脚本出错") - return err + return fmt.Errorf(outputErr.String(), err) } data := []byte(output.String()) @@ -254,12 +258,8 @@ func (t *Task) runInternal(v model.InstalledApp) error { log.Info("执行安装脚本成功") return nil } else { - if len(data) > 200 { - data = data[len(data)-200:] - } - log.Info("执行安装脚本失败") - return fmt.Errorf(string(data)) + return fmt.Errorf(outputErr.String()) } } diff --git a/web/static/src/api/api.js b/web/static/src/api/api.js index 77b35b6..09463ec 100644 --- a/web/static/src/api/api.js +++ b/web/static/src/api/api.js @@ -55,7 +55,7 @@ export default { return request({ url: "/api/upload", method: "post", - timeout: 1200000, + timeout: 300000, headers: { "Content-Type": "multipart/form-data", }, From cfece62fe07d7dcf5e7b1d00d650beaddaba059c Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:47:49 +0000 Subject: [PATCH 13/35] fix: install failed by usbmuxd error --- internal/app/bootstrap.go | 8 ++++++- internal/log/log.go | 2 ++ internal/manager/manager.go | 4 ++++ internal/task/task.go | 44 +++++++++++++++++++------------------ 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/internal/app/bootstrap.go b/internal/app/bootstrap.go index 8038567..2457e0d 100644 --- a/internal/app/bootstrap.go +++ b/internal/app/bootstrap.go @@ -68,7 +68,13 @@ func InitSettings(conf *Configuration, debug bool) error { } func InitLogger(conf *Configuration) error { - // set normal log + if conf.Log.LogFile == "" { + if conf.Server.DataDir != "" { + conf.Log.LogFile = filepath.Join(conf.Server.DataDir, "app.log") + } else { + conf.Log.LogFile = filepath.Join(cfg.DefaultConfigDir(), "app.log") + } + } log.AddFileOutput(conf.Log.LogFile) if conf.Log.Level == "debug" { log.SetDebugLevel() diff --git a/internal/log/log.go b/internal/log/log.go index d32cc17..1c5171c 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -1,6 +1,7 @@ package log import ( + "fmt" "io" "os" "path" @@ -24,6 +25,7 @@ func AddFileOutput(logPath string) { return } + fmt.Printf("Log file path: %s\n", logPath) consoleWriter := newConsoleWriter(os.Stderr) // output log to file logFileWriter := CreateRollingLogFile(logPath) diff --git a/internal/manager/manager.go b/internal/manager/manager.go index 1cbfeae..3ccff02 100644 --- a/internal/manager/manager.go +++ b/internal/manager/manager.go @@ -25,3 +25,7 @@ func ReloadDevices() { func ScanDevices() { deviceManager.Scan() } + +func RestartUsbmuxd() error { + return deviceManager.RestartUsbmuxd() +} diff --git a/internal/task/task.go b/internal/task/task.go index 5f21d74..35692f9 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -13,6 +13,7 @@ import ( "github.com/bitxeno/atvloadly/internal/app" "github.com/bitxeno/atvloadly/internal/log" + "github.com/bitxeno/atvloadly/internal/manager" "github.com/bitxeno/atvloadly/internal/model" "github.com/bitxeno/atvloadly/internal/notify" "github.com/bitxeno/atvloadly/internal/service" @@ -81,29 +82,30 @@ func (t *Task) Run() { } log.Infof("开始执行安装ipa:%s", v.IpaName) - tryTimes := 1 - for i := 0; i < tryTimes; i++ { - err := t.runInternal(v) - if err == nil { - v.RefreshedDate = &now - v.RefreshedResult = true - _ = service.UpdateAppRefreshResult(v) - break - } - - if i == (tryTimes - 1) { - now := time.Now() - v.RefreshedDate = &now - v.RefreshedResult = false - _ = service.UpdateAppRefreshResult(v) - - failedList = append(failedList, v) - failedMsg += fmt.Sprintf("app: %s\n 错误日志:%s\n\n", v.IpaName, err.Error()) - } else { - log.Infof("1分钟后再次重新尝试执行") - time.Sleep(1 * time.Minute) + if err := t.runInternal(v); err != nil { + // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix + if strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") { + log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) + if err = manager.RestartUsbmuxd(); err == nil { + log.Infof("usbmuxd 重启完成,再次尝试安装ipa:%s", v.IpaName) + time.Sleep(5 * time.Second) + err = t.runInternal(v) + } } } + if err != nil { + now := time.Now() + v.RefreshedDate = &now + v.RefreshedResult = false + _ = service.UpdateAppRefreshResult(v) + + failedList = append(failedList, v) + failedMsg += fmt.Sprintf("app: %s\n 错误日志:%s\n\n", v.IpaName, err.Error()) + } else { + v.RefreshedDate = &now + v.RefreshedResult = true + _ = service.UpdateAppRefreshResult(v) + } log.Infof("安装ipa执行完成.任务:%s", v.IpaName) // 下一个执行延迟10秒 From bdac8f877811bb0a401da2eb81ed02bab0612c58 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:51:18 +0000 Subject: [PATCH 14/35] fix: err not handle --- internal/task/task.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/task/task.go b/internal/task/task.go index 35692f9..0170549 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -82,7 +82,8 @@ func (t *Task) Run() { } log.Infof("开始执行安装ipa:%s", v.IpaName) - if err := t.runInternal(v); err != nil { + var err error + if err = t.runInternal(v); err != nil { // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix if strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") { log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) From 054df4344ab30408a74c5a86446af3f961791a90 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:15:21 +0000 Subject: [PATCH 15/35] fix: install failed by usbmuxd error --- internal/task/task.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/internal/task/task.go b/internal/task/task.go index 0170549..065c346 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -82,18 +82,7 @@ func (t *Task) Run() { } log.Infof("开始执行安装ipa:%s", v.IpaName) - var err error - if err = t.runInternal(v); err != nil { - // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix - if strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") { - log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) - if err = manager.RestartUsbmuxd(); err == nil { - log.Infof("usbmuxd 重启完成,再次尝试安装ipa:%s", v.IpaName) - time.Sleep(5 * time.Second) - err = t.runInternal(v) - } - } - } + err := t.runInternalRetry(v) if err != nil { now := time.Now() v.RefreshedDate = &now @@ -148,7 +137,7 @@ func (t *Task) RunImmediately(v model.InstalledApp) { defer t.completedState() now := time.Now() - err := t.runInternal(v) + err := t.runInternalRetry(v) if err == nil { v.RefreshedDate = &now v.RefreshedResult = true @@ -163,6 +152,20 @@ func (t *Task) RunImmediately(v model.InstalledApp) { } } +func (t *Task) runInternalRetry(v model.InstalledApp) error { + err := t.runInternal(v) + // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix + if err != nil && strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") { + log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) + if err = manager.RestartUsbmuxd(); err == nil { + log.Infof("usbmuxd 重启完成,再次尝试安装ipa:%s", v.IpaName) + time.Sleep(5 * time.Second) + err = t.runInternal(v) + } + } + return err +} + func (t *Task) runInternal(v model.InstalledApp) error { t.InstallingApp = &v @@ -243,16 +246,16 @@ func (t *Task) runInternal(v model.InstalledApp) error { if err := cmd.Start(); nil != err { data := []byte(output.String()) t.writeLog(v, data) - log.Err(err).Msg("执行安装脚本出错") - return fmt.Errorf(outputErr.String(), err) + log.Err(err).Msgf("执行安装脚本出错. %s", outputErr.String()) + return fmt.Errorf("%s %v", outputErr.String(), err) } err = cmd.Wait() if err != nil { data := []byte(output.String()) t.writeLog(v, data) - log.Err(err).Msg("执行安装脚本出错") - return fmt.Errorf(outputErr.String(), err) + log.Err(err).Msgf("执行安装脚本出错. %s", outputErr.String()) + return fmt.Errorf("%s %v", outputErr.String(), err) } data := []byte(output.String()) From 5aaa8a7a2dc9682df92caa295689fa174f21eb82 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Sun, 23 Jun 2024 07:07:02 +0000 Subject: [PATCH 16/35] fix: install failed after reboot appletv --- internal/task/task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/task/task.go b/internal/task/task.go index 065c346..da54df2 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -155,7 +155,7 @@ func (t *Task) RunImmediately(v model.InstalledApp) { func (t *Task) runInternalRetry(v model.InstalledApp) error { err := t.runInternal(v) // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix - if err != nil && strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") { + if err != nil && (strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") || strings.Contains(err.Error(), "AFC_E_MUX_ERROR")) { log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) if err = manager.RestartUsbmuxd(); err == nil { log.Infof("usbmuxd 重启完成,再次尝试安装ipa:%s", v.IpaName) From a5cd45490f5769578218861b42a0ba065d1ed8aa Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:58:28 +0800 Subject: [PATCH 17/35] feat: add afc check before install --- internal/manager/device_manager.go | 20 ++++++++++++++++++++ internal/manager/manager.go | 4 ++++ internal/service/service.go | 14 ++++++++++++++ web/router.go | 10 ++++++++++ web/static/src/api/api.js | 15 +++++++++++++++ web/static/src/page/install/index.vue | 11 +++++++++++ 6 files changed, 74 insertions(+) diff --git a/internal/manager/device_manager.go b/internal/manager/device_manager.go index 34b17a7..339da22 100644 --- a/internal/manager/device_manager.go +++ b/internal/manager/device_manager.go @@ -158,6 +158,26 @@ func (dm *DeviceManager) CheckHasMountImage(udid string) (bool, error) { return strings.Contains(output, "ImageSignature") && !strings.Contains(output, "ImageSignature[0]"), nil } +func (dm *DeviceManager) CheckAfcServiceStatus(udid string) error { + cmd := exec.Command("sideloader", "check", "afc", "--nocolor", "--udid", udid) + + data, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s%s", string(data), err.Error()) + } + + output := string(data) + if strings.Contains(output, "ERROR") { + return fmt.Errorf("%s", output) + } + + if !strings.Contains(output, "SUCCESS") { + return fmt.Errorf("%s", output) + } + + return nil +} + func (dm *DeviceManager) RestartUsbmuxd() error { cmd := exec.Command("/etc/init.d/usbmuxd", "restart") data, err := cmd.CombinedOutput() diff --git a/internal/manager/manager.go b/internal/manager/manager.go index 3ccff02..2178b32 100644 --- a/internal/manager/manager.go +++ b/internal/manager/manager.go @@ -26,6 +26,10 @@ func ScanDevices() { deviceManager.Scan() } +func CheckAfcServiceStatus(udid string) error { + return deviceManager.CheckAfcServiceStatus(udid) +} + func RestartUsbmuxd() error { return deviceManager.RestartUsbmuxd() } diff --git a/internal/service/service.go b/internal/service/service.go index 83c09ae..742a292 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -9,6 +9,7 @@ import ( "regexp" "runtime" "strings" + "time" "github.com/artdarek/go-unzip/pkg/unzip" "github.com/bitxeno/atvloadly/internal/app" @@ -95,6 +96,19 @@ func MountDeveloperDiskImage(ctx context.Context, id string) error { return nil } +func CheckAfcService(ctx context.Context, id string) error { + var err error + if err = manager.CheckAfcServiceStatus(id); err != nil { + // try restart usbmuxd to fix afc connect issue + if err = manager.RestartUsbmuxd(); err == nil { + time.Sleep(5 * time.Second) + err = manager.CheckAfcServiceStatus(id) + } + } + + return err +} + func downloadDeveloperDiskImage(imageInfo *model.UsbmuxdImage) (dmg string, signature string, reterr error) { // download current version DeveloperDiskImage err := downloadDeveloperDiskImageByVersion(imageInfo.DeveloperDiskImageUrl, imageInfo.DeveloperDiskImageVersion) diff --git a/web/router.go b/web/router.go index 078799c..c8981ad 100644 --- a/web/router.go +++ b/web/router.go @@ -130,6 +130,16 @@ func route(fi *fiber.App) { } }) + api.Post("/devices/:id/check/afc", func(c *fiber.Ctx) error { + id := c.Params("id") + + if err := service.CheckAfcService(c.Context(), id); err != nil { + return c.Status(http.StatusOK).JSON(apiSuccess(err.Error())) + } else { + return c.Status(http.StatusOK).JSON(apiSuccess("success")) + } + }) + api.Get("/scan", func(c *fiber.Ctx) error { manager.ScanDevices() diff --git a/web/static/src/api/api.js b/web/static/src/api/api.js index 09463ec..5b56eb8 100644 --- a/web/static/src/api/api.js +++ b/web/static/src/api/api.js @@ -22,6 +22,21 @@ export default { }); }); }, + checkAfcService: (id) => { + return new Promise((resolve, reject) => { + request({ + url: `/api/devices/${id}/check/afc`, + timeout: 60000, + method: "Post", + }) + .then((res) => { + resolve(res.data); + }) + .catch((err) => { + reject(err); + }); + }); + }, getDevices: (params) => { return request({ url: "/api/devices", diff --git a/web/static/src/page/install/index.vue b/web/static/src/page/install/index.vue index b0b6b08..a8b99c0 100644 --- a/web/static/src/page/install/index.vue +++ b/web/static/src/page/install/index.vue @@ -183,6 +183,17 @@ export default { _this.log.output = ""; _this.log.show = true; + _this.log.output += "Checking afc service status...\n"; + let data = await api.checkAfcService(_this.id); + if (data != "success") { + _this.log.output += data; + _this.cmd.output = ""; + _this.loading = false; + toast.error(this.$t("install.toast.install_failed")); + return; + } + _this.log.output += "afc service OK!\n"; + let formData = new FormData(); for (let i = 0; i < _this.files.length; i++) { From 06d8798da40fe0931b995a1e2a703b13421b8197 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:26:44 +0800 Subject: [PATCH 18/35] ci: update github action --- .github/workflows/clean_package.yml | 10 ++++------ .github/workflows/release-nightly.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/clean_package.yml b/.github/workflows/clean_package.yml index 509dba6..dcea1a0 100644 --- a/.github/workflows/clean_package.yml +++ b/.github/workflows/clean_package.yml @@ -25,11 +25,9 @@ jobs: run: | echo "APP_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_OUTPUT - name: Delete old images - uses: snok/container-retention-policy@v2 + uses: dataaxiom/ghcr-cleanup-action@v1 with: - image-names: ${{steps.vars.outputs.APP_NAME}} - cut-off: A month ago UTC - keep-at-least: 10 - skip-tags: latest, beta, alpha, main, master - account-type: personal + exclude-tags: 'v*,dev,latest,main,master' + keep-n-tagged: 10 + package: ${{steps.vars.outputs.APP_NAME}} token: ${{ secrets.PAT }} diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 7a7630c..98d68c1 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -90,7 +90,7 @@ jobs: go_version: ${{ matrix.go_version }} dest: build prefix: ${{steps.vars.outputs.APP_NAME}} - targets: windows/386,windows/amd64,linux/386,linux/amd64,darwin/386,darwin/amd64,linux/386,linux/arm64 + targets: windows/amd64,linux/amd64,darwin/amd64,linux/arm64 v: true x: false ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/app/build.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Mode=production diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7a03e9..9bfd6de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: go_version: ${{ matrix.go_version }} dest: build prefix: ${{steps.vars.outputs.APP_NAME}} - targets: windows/386,windows/amd64,linux/386,linux/amd64,darwin/386,darwin/amd64,linux/386,linux/arm64 + targets: windows/amd64,linux/amd64,darwin/amd64,linux/arm64 v: true x: false ldflags: -w -s -X ${{steps.vars.outputs.REPO}}/internal/app/build.Version=${{steps.vars.outputs.VERSION}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.BuildDate=${{steps.vars.outputs.BUILDDATE}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Commit=${{steps.vars.outputs.COMMIT}} -X ${{steps.vars.outputs.REPO}}/internal/app/build.Mode=production From c578146ae3423dfbbd71621192502487cec71cf2 Mon Sep 17 00:00:00 2001 From: bitxeno <137328844+bitxeno@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:25:30 +0800 Subject: [PATCH 19/35] feat: task add i18n support --- .air.toml | 2 +- go.mod | 2 + go.sum | 7 +++- internal/i18n/i18n.go | 56 +++++++++++++++++++++++++++ internal/i18n/locales/en.json | 23 +++++++++++ internal/i18n/locales/zh_cn.json | 23 +++++++++++ internal/service/db.go | 2 +- internal/task/task.go | 66 ++++++++++++++------------------ web/fs_dev.go | 2 +- web/router.go | 13 ++++++- web/static/src/api/api.js | 7 ++++ web/static/src/page/layout.vue | 3 ++ 12 files changed, 162 insertions(+), 44 deletions(-) create mode 100644 internal/i18n/i18n.go create mode 100644 internal/i18n/locales/en.json create mode 100644 internal/i18n/locales/zh_cn.json diff --git a/.air.toml b/.air.toml index 2154641..9e2161c 100644 --- a/.air.toml +++ b/.air.toml @@ -7,7 +7,7 @@ tmp_dir = "tmp" [build] # Just plain old shell command. You could use `make` as well. -cmd = "go build -o ./tmp/main ." +cmd = "go build -tags dev -o ./tmp/main ." # Binary file yields from `cmd`. bin = "tmp/main" # Customize binary. diff --git a/go.mod b/go.mod index c6e3368..1ec6cb4 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/knadh/koanf v1.5.0 github.com/mitchellh/go-ps v1.0.0 + github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/nikoksr/notify v0.41.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 @@ -28,6 +29,7 @@ require ( github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a github.com/silenceper/wechat/v2 v2.1.5 github.com/urfave/cli/v2 v2.3.0 + golang.org/x/text v0.14.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gorm.io/gorm v1.25.7 howett.net/plist v1.0.0 diff --git a/go.sum b/go.sum index 190ad24..7df8af9 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -393,6 +393,8 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= +github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ= github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= @@ -725,7 +727,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go new file mode 100644 index 0000000..0a74daa --- /dev/null +++ b/internal/i18n/i18n.go @@ -0,0 +1,56 @@ +package i18n + +import ( + "embed" + "encoding/json" + "path/filepath" + + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" +) + +//go:embed all:locales/* +var localesFs embed.FS +var i18nBundle *i18n.Bundle +var i18nLocalizer *i18n.Localizer +var currentLang = "en" + +func init() { + i18nBundle = i18n.NewBundle(language.English) + i18nBundle.RegisterUnmarshalFunc("json", json.Unmarshal) + + root := "locales" + files, err := localesFs.ReadDir(root) + if err != nil { + panic(err) + } + for _, v := range files { + path := filepath.Join(root, v.Name()) + i18nBundle.LoadMessageFileFS(localesFs, path) + } + + i18nLocalizer = i18n.NewLocalizer(i18nBundle, currentLang) +} + +func SetLanguage(lang string) { + if lang == "" { + return + } + if currentLang != lang { + currentLang = lang + i18nLocalizer = i18n.NewLocalizer(i18nBundle, currentLang) + } +} + +func Localize(key string) string { + return i18nLocalizer.MustLocalize(&i18n.LocalizeConfig{ + MessageID: key, + }) +} + +func LocalizeF(key string, data map[string]interface{}) string { + return i18nLocalizer.MustLocalize(&i18n.LocalizeConfig{ + MessageID: key, + TemplateData: data, + }) +} diff --git a/internal/i18n/locales/en.json b/internal/i18n/locales/en.json new file mode 100644 index 0000000..5f9f6fc --- /dev/null +++ b/internal/i18n/locales/en.json @@ -0,0 +1,23 @@ +{ + "language": "en", + "notify": { + "title": "[{{.name}}] Refresh task execution failed.", + "content": "Account: {{.account}}\nError log: {{.error}}", + "batch_title": "atvloadly Refresh Task Execution Failed", + "batch_content": "app: {{.name}}\n Error Log: {{.error}}\n\n" + }, + "task": { + "started": "Start executing installation task...", + "completed": "Installation task completed.", + "app_install_started": "Start installing ipa: {{.name}}", + "app_install_completed": "Ipa installation completed.{{.name}}", + "task_disabled": "App refresh scheduled task is not enabled", + "task_started": "App refresh scheduled task has started, time: {{.time}}", + "install_failed": "Error executing installation script.{{.error}}", + "error_get_app_list": "Failed to get the installation list", + "error_invalid_arguments": "Task account, password, UDID are empty", + "error_invalid_crod_time_format": "Failed to start app refresh scheduled task due to incorrect timing format: {{.time}}", + "try_restart_usbmuxd": "Try restarting usbmuxd to fix LOCKDOWN_E_MUX_ERROR error. {{.error}}", + "try_restart_usbmuxd_success": "usbmuxd restart complete, try installing ipa again: {{.name}}" + } +} \ No newline at end of file diff --git a/internal/i18n/locales/zh_cn.json b/internal/i18n/locales/zh_cn.json new file mode 100644 index 0000000..7bd33dd --- /dev/null +++ b/internal/i18n/locales/zh_cn.json @@ -0,0 +1,23 @@ +{ + "language": "zh_cn", + "notify": { + "title": "[{{.name}}]刷新任务执行失败", + "content": "帐号:{{.account}}\n错误日志:{{.error}}", + "batch_title": "atvloadly 刷新任务执行失败", + "batch_content": "app: {{.name}}\n 错误日志:{{.error}}\n\n" + }, + "task": { + "started": "开始执行安装任务...", + "completed": "安装任务执行完成。", + "app_install_started": "开始执行安装ipa:{{.name}}", + "app_install_completed": "安装ipa执行完成.{{.name}}", + "task_disabled": "app刷新定时任务未启用", + "task_started": "app刷新定时任务已启动,时间: {{.time}}", + "install_failed": "执行安装脚本出错。{{.error}}", + "error_get_app_list": "获取安装列表失败", + "error_invalid_arguments": "任务帐号,密码,UDID 为空", + "error_invalid_crod_time_format": "app刷新定时任务启动失败,定时格式错误:{{.time}}", + "try_restart_usbmuxd": "尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. {{.error}}", + "try_restart_usbmuxd_success": "usbmuxd 重启完成,再次尝试安装ipa:{{.name}}" + } +} \ No newline at end of file diff --git a/internal/service/db.go b/internal/service/db.go index de8b439..7dac986 100644 --- a/internal/service/db.go +++ b/internal/service/db.go @@ -45,7 +45,7 @@ func SaveApp(app model.InstalledApp) (*model.InstalledApp, error) { var cur model.InstalledApp result := db.Store().Where("udid=? and bundle_identifier=? and account=?", app.UDID, app.BundleIdentifier, app.Account).First(&cur) if result.Error != nil && result.Error != gorm.ErrRecordNotFound { - log.Err(result.Error).Msg("保存安装记录时出错.") + log.Err(result.Error).Msg("SaveApp error.") return nil, result.Error } diff --git a/internal/task/task.go b/internal/task/task.go index da54df2..495e56b 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -12,6 +12,7 @@ import ( "time" "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/i18n" "github.com/bitxeno/atvloadly/internal/log" "github.com/bitxeno/atvloadly/internal/manager" "github.com/bitxeno/atvloadly/internal/model" @@ -39,18 +40,18 @@ func (t *Task) RunSchedule() error { } if !app.Settings.Task.Enabled { - log.Info("app刷新定时任务未启用") + log.Info(i18n.Localize("task.task_disabled")) return nil } t.c = cron.New() if _, err := t.c.AddFunc(app.Settings.Task.CrodTime, t.Run); err != nil { - log.Err(err).Msgf("app刷新定时任务启动失败,定时格式错误:%s", app.Settings.Task.CrodTime) + log.Err(err).Msg(i18n.LocalizeF("task.error_invalid_crod_time_format", map[string]interface{}{"time": app.Settings.Task.CrodTime})) t.c = nil return err } - log.Infof("app刷新定时任务已启动,时间: %s", app.Settings.Task.CrodTime) + log.Info(i18n.LocalizeF("task.task_started", map[string]interface{}{"time": app.Settings.Task.CrodTime})) t.c.Start() return nil @@ -69,7 +70,7 @@ func (t *Task) Run() { installedApps, err := service.GetEnableAppList() if err != nil { - log.Err(err).Msg("获取安装列表失败") + log.Err(err).Msg(i18n.Localize("task.error_get_app_list")) return } @@ -81,7 +82,7 @@ func (t *Task) Run() { continue } - log.Infof("开始执行安装ipa:%s", v.IpaName) + log.Info(i18n.LocalizeF("task.app_install_started", map[string]interface{}{"name": v.IpaName})) err := t.runInternalRetry(v) if err != nil { now := time.Now() @@ -90,21 +91,22 @@ func (t *Task) Run() { _ = service.UpdateAppRefreshResult(v) failedList = append(failedList, v) - failedMsg += fmt.Sprintf("app: %s\n 错误日志:%s\n\n", v.IpaName, err.Error()) + failedMsg += i18n.LocalizeF("notify.batch_content", map[string]interface{}{"name": v.IpaName, "error": err.Error()}) } else { v.RefreshedDate = &now v.RefreshedResult = true _ = service.UpdateAppRefreshResult(v) } - log.Infof("安装ipa执行完成.任务:%s", v.IpaName) + log.Info(i18n.LocalizeF("task.app_install_completed", map[string]interface{}{"name": v.IpaName})) - // 下一个执行延迟10秒 + // Next execution delayed by 10 seconds. time.Sleep(10 * time.Second) } - // 发送安装失败通知 + // Send installation failure notification. if len(failedList) > 0 { - _ = notify.Send("atvloadly 自动刷新任务执行失败", failedMsg) + title := i18n.LocalizeF("notify.title", map[string]interface{}{"name": "atvloadly"}) + _ = notify.Send(title, failedMsg) } } @@ -147,8 +149,10 @@ func (t *Task) RunImmediately(v model.InstalledApp) { v.RefreshedResult = false _ = service.UpdateAppRefreshResult(v) - // 发送安装失败通知 - _ = notify.Send(fmt.Sprintf("[%s]刷新任务执行失败", v.IpaName), fmt.Sprintf("帐号:%s\n错误日志:%s", v.Account, err.Error())) + // Send installation failure notification + title := i18n.LocalizeF("notify.title", map[string]interface{}{"name": v.IpaName}) + message := i18n.LocalizeF("notify.content", map[string]interface{}{"account": v.Account, "error": err.Error()}) + _ = notify.Send(title, message) } } @@ -156,9 +160,9 @@ func (t *Task) runInternalRetry(v model.InstalledApp) error { err := t.runInternal(v) // AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix if err != nil && (strings.Contains(err.Error(), "LOCKDOWN_E_MUX_ERROR") || strings.Contains(err.Error(), "AFC_E_MUX_ERROR")) { - log.Infof("尝试重启 usbmuxd 修复 LOCKDOWN_E_MUX_ERROR 错误. %s", v.IpaName) + log.Info(i18n.LocalizeF("task.try_restart_usbmuxd", map[string]interface{}{"name": v.IpaName})) if err = manager.RestartUsbmuxd(); err == nil { - log.Infof("usbmuxd 重启完成,再次尝试安装ipa:%s", v.IpaName) + log.Info(i18n.LocalizeF("task.try_restart_usbmuxd_success", map[string]interface{}{"name": v.IpaName})) time.Sleep(5 * time.Second) err = t.runInternal(v) } @@ -170,23 +174,11 @@ func (t *Task) runInternal(v model.InstalledApp) error { t.InstallingApp = &v if v.Account == "" || v.Password == "" || v.UDID == "" { - log.Info("任务帐号,密码,UDID为空") - return fmt.Errorf("任务帐号,密码,UDID为空") + log.Info(i18n.Localize("task.error_invalid_arguments")) + return fmt.Errorf(i18n.Localize("task.error_invalid_arguments")) } - // 检查developer disk image是否已mounted - // imageInfo, err := manager.GetDeviceMountImageInfo(v.UDID) - // if err != nil { - // log.Err(err).Msg("Check DeveloperDiskImage mounted error: ") - // return err - // } - - // if !imageInfo.ImageMounted { - // log.Error("DeveloperDiskImage not mounted.") - // return err - // } - - // sideloader 会处理特殊字符"$",对于带有这特殊字符的,需要加单引号包括 + // The sideloader will handle special character "$". For those with this special character, it needs to be enclosed in single quotation marks. cmd := exec.Command("sideloader", "install", "--quiet", "--nocolor", "--udid", v.UDID, "-a", v.Account, "-p", v.Password, v.IpaPath) cmd.Dir = app.Config.Server.DataDir cmd.Env = []string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()} @@ -219,7 +211,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { _, _ = output.WriteString(lineText) _, _ = output.WriteString("\n") - // 处理中途需要输入才能继续的,如 Installing AltStore with Multiple AltServers Not Supported 消息 + // Processing interaction to continue, such as [the Installing AltStore with Multiple AltServers the Not Supported] message. if strings.Contains(lineText, "Press any key to continue") { _, _ = stdin.Write([]byte("\n")) } @@ -237,7 +229,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { _, _ = outputErr.WriteString(lineText) _, _ = outputErr.WriteString("\n") - // 处理中途需要输入才能继续的,如 Installing AltStore with Multiple AltServers Not Supported 消息 + // Processing interaction to continue, such as [the Installing AltStore with Multiple AltServers the Not Supported] message. if strings.Contains(lineText, "Press any key to continue") { _, _ = stdin.Write([]byte("\n")) } @@ -246,7 +238,7 @@ func (t *Task) runInternal(v model.InstalledApp) error { if err := cmd.Start(); nil != err { data := []byte(output.String()) t.writeLog(v, data) - log.Err(err).Msgf("执行安装脚本出错. %s", outputErr.String()) + log.Err(err).Msg(i18n.LocalizeF("install_failed", map[string]interface{}{"error": outputErr.String()})) return fmt.Errorf("%s %v", outputErr.String(), err) } @@ -254,23 +246,21 @@ func (t *Task) runInternal(v model.InstalledApp) error { if err != nil { data := []byte(output.String()) t.writeLog(v, data) - log.Err(err).Msgf("执行安装脚本出错. %s", outputErr.String()) + log.Err(err).Msg(i18n.LocalizeF("install_failed", map[string]interface{}{"error": outputErr.String()})) return fmt.Errorf("%s %v", outputErr.String(), err) } data := []byte(output.String()) t.writeLog(v, data) if strings.Contains(string(data), "Installation Succeeded") { - log.Info("执行安装脚本成功") return nil } else { - log.Info("执行安装脚本失败") return fmt.Errorf(outputErr.String()) } } func (t *Task) writeLog(v model.InstalledApp, data []byte) { - // 打码密码字符串 + // Hide log password string data = bytes.Replace(data, []byte(v.Password), []byte("******"), -1) saveDir := filepath.Join(app.Config.Server.DataDir, "log") @@ -288,13 +278,13 @@ func (t *Task) writeLog(v model.InstalledApp, data []byte) { func (t *Task) startedState() { t.Running = true - log.Info("开始执行定时任务...") + log.Info(i18n.Localize("task.started")) } func (t *Task) completedState() { t.Running = false t.InstallingApp = nil - log.Warn("定时任务执行完成") + log.Info(i18n.Localize("task.completed")) } func ScheduleRefreshApps() error { diff --git a/web/fs_dev.go b/web/fs_dev.go index 5c13a2a..3ba532c 100644 --- a/web/fs_dev.go +++ b/web/fs_dev.go @@ -8,5 +8,5 @@ import ( ) func StaticAssets() fs.FS { - return os.DirFS("static/dist") + return os.DirFS("web/static/dist") } diff --git a/web/router.go b/web/router.go index c8981ad..2ac774b 100644 --- a/web/router.go +++ b/web/router.go @@ -9,6 +9,7 @@ import ( "time" "github.com/bitxeno/atvloadly/internal/app" + "github.com/bitxeno/atvloadly/internal/i18n" "github.com/bitxeno/atvloadly/internal/ipa" "github.com/bitxeno/atvloadly/internal/manager" "github.com/bitxeno/atvloadly/internal/model" @@ -71,11 +72,21 @@ func route(fi *fiber.App) { }) - // api路由 + // API route api := fi.Group("/api") api.Get("/hello", func(c *fiber.Ctx) error { return c.SendString("hello world.") }) + api.Post("/lang/sync", func(c *fiber.Ctx) error { + lang := c.Params("lang") + accept := c.Get("Accept-Language") + if lang != "" { + i18n.SetLanguage(lang) + } else { + i18n.SetLanguage(accept) + } + return c.Status(http.StatusOK).JSON(apiSuccess(i18n.Localize("language"))) + }) api.Get("/settings", func(c *fiber.Ctx) error { return c.Status(http.StatusOK).JSON(apiSuccess(app.Settings)) }) diff --git a/web/static/src/api/api.js b/web/static/src/api/api.js index 5b56eb8..75a0b87 100644 --- a/web/static/src/api/api.js +++ b/web/static/src/api/api.js @@ -1,6 +1,13 @@ import request from "@/utils/request"; export default { + syncLang: (params) => { + return request({ + url: '/api/lang/sync', + method: "post", + params + }); + }, getDevice: (id) => { return request({ url: `/api/devices/${id}`, diff --git a/web/static/src/page/layout.vue b/web/static/src/page/layout.vue index 3d578c1..045d58b 100644 --- a/web/static/src/page/layout.vue +++ b/web/static/src/page/layout.vue @@ -57,6 +57,7 @@