From 8be57753133e9c316aea4b8d3bf3e435d33201ff Mon Sep 17 00:00:00 2001 From: Artem Chernyak Date: Tue, 31 Dec 2024 02:22:56 -0600 Subject: [PATCH 01/10] docs(kubernetes): add tmp volume (#1040) --- docs/Installation.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/Installation.md b/docs/Installation.md index c537bc324..46f5b8b82 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -93,6 +93,9 @@ spec: - name: app hostPath: path: /path/to/data/dir + - name: tmp + emptyDir: + medium: Memory containers: - name: shiori image: ghcr.io/go-shiori/shiori:latest @@ -103,6 +106,8 @@ spec: volumeMounts: - mountPath: /srv/shiori name: app + - mountPath: /tmp + name: tmp env: - name: SHIORI_DIR value: /srv/shiori @@ -112,7 +117,10 @@ spec: Here we are using a local directory to persist Shiori's data. You will need to replace `/path/to/data/dir` with the path to the directory where you want -to keep your data. Since we haven't configured a database in particular, +to keep your data. We are also mounting an `EmptyDir` volume for `/tmp` so +we can successfully generate ebooks. + +Since we haven't configured a database in particular, Shiori will use SQLite. I don't think Postgres or MySQL is worth it for such an app, but that's up to you. If you decide to use SQLite, I strongly suggest to keep `replicas` set to 1 since SQLite usually allows at most From f23c982ee330364f987a734056bf9eb4ab3be38c Mon Sep 17 00:00:00 2001 From: Ed Summers Date: Tue, 31 Dec 2024 03:27:31 -0500 Subject: [PATCH 02/10] fix: webroot not working in archive view (#1043) Without this the archive page fails to render. --- internal/view/archive.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/view/archive.html b/internal/view/archive.html index 5a263c750..4a53f594c 100644 --- a/internal/view/archive.html +++ b/internal/view/archive.html @@ -2,10 +2,11 @@ + $$.Book.Title$$ - + @@ -14,10 +15,10 @@
View Original $$if .Book.HasContent$$ - View Readable + View Readable $$end$$ - + From 0b745c170bb3c7a6d70d1f69333e5ce43c36b620 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:24:29 +0100 Subject: [PATCH 03/10] chore(deps): bump the all group with 2 updates (#1036) Bumps the all group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `actions/upload-artifact` from 4.4.3 to 4.5.0 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882...6f51ac03b9356f520e9adb1b1b7802705f340c2b) Updates `codecov/codecov-action` from 5.1.1 to 5.1.2 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/7f8b4b4bde536c465e797be725718b88c5d95e0e...1e68e06f1dbfde0e4cefc87efeba9e4643565303) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_gorelease.yml | 2 +- .github/workflows/_test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_gorelease.yml b/.github/workflows/_gorelease.yml index 6127740d6..dd3dc1571 100644 --- a/.github/workflows/_gorelease.yml +++ b/.github/workflows/_gorelease.yml @@ -27,7 +27,7 @@ jobs: args: release --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 4.4.3 + - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # 4.5.0 with: name: dist path: ./dist/* diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 1a4e9c52a..47a888acb 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -65,7 +65,7 @@ jobs: - run: go build -tags osusergo,netgo -ldflags="-s -w -X main.version=$(git describe --tags) -X main.date=$(date --iso-8601=seconds)" - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # 5.1.1 + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # 5.1.2 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From e1e58289a2fbfce93678d056d386b938a33dd43b Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:35:50 +0100 Subject: [PATCH 04/10] fix: login redirect not working properly after component refactor (#1044) * feat: Add secure destination URL handling for login redirect * fix: rootpath on redirect to login * fixed tests --- internal/http/response/shortcuts.go | 12 ++++++-- internal/http/routes/bookmark.go | 2 +- internal/http/routes/bookmark_test.go | 3 +- internal/view/assets/js/component/login.js | 33 ++++++++++++++++++++++ internal/view/index.html | 12 ++++---- 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/internal/http/response/shortcuts.go b/internal/http/response/shortcuts.go index dec01c9fa..d94936147 100644 --- a/internal/http/response/shortcuts.go +++ b/internal/http/response/shortcuts.go @@ -2,6 +2,7 @@ package response import ( "net/http" + "net/url" "github.com/gin-gonic/gin" ) @@ -35,10 +36,17 @@ func SendInternalServerError(ctx *gin.Context) { } // SendNotFound directly sends a not found response -func RedirectToLogin(ctx *gin.Context, dst string) { - ctx.Redirect(http.StatusFound, "/login?dst="+dst) +func RedirectToLogin(ctx *gin.Context, webroot, dst string) { + url := url.URL{ + Path: webroot, + RawQuery: url.Values{ + "dst": []string{dst}, + }.Encode(), + } + ctx.Redirect(http.StatusFound, url.String()) } +// NotFound directly sends a not found response func NotFound(ctx *gin.Context) { ctx.AbortWithStatus(http.StatusNotFound) } diff --git a/internal/http/routes/bookmark.go b/internal/http/routes/bookmark.go index e0073dcdd..7a2c7e8a8 100644 --- a/internal/http/routes/bookmark.go +++ b/internal/http/routes/bookmark.go @@ -64,7 +64,7 @@ func (r *BookmarkRoutes) getBookmark(c *context.Context) (*model.BookmarkDTO, er } if bookmark.Public != 1 && !c.UserIsLogged() { - response.RedirectToLogin(c.Context, c.Request.URL.String()) + response.RedirectToLogin(c.Context, r.deps.Config.Http.RootPath, c.Request.URL.String()) return nil, model.ErrUnauthorized } diff --git a/internal/http/routes/bookmark_test.go b/internal/http/routes/bookmark_test.go index 400dd95b2..9ed14b506 100644 --- a/internal/http/routes/bookmark_test.go +++ b/internal/http/routes/bookmark_test.go @@ -4,6 +4,7 @@ import ( "context" "net/http" "net/http/httptest" + "net/url" "strconv" "testing" @@ -122,7 +123,7 @@ func TestBookmarkContentHandler(t *testing.T) { req, _ := http.NewRequest("GET", path, nil) g.ServeHTTP(w, req) require.Equal(t, http.StatusFound, w.Code) - require.Equal(t, "/login?dst="+path, w.Header().Get("Location")) + require.Equal(t, "/?dst="+url.QueryEscape(path), w.Header().Get("Location")) }) t.Run("get existing bookmark content", func(t *testing.T) { diff --git a/internal/view/assets/js/component/login.js b/internal/view/assets/js/component/login.js index 4e74ba25b..e75925556 100644 --- a/internal/view/assets/js/component/login.js +++ b/internal/view/assets/js/component/login.js @@ -39,10 +39,35 @@ export default { username: "", password: "", remember: false, + destination: "/", // Default destination }; }, emits: ["login-success"], methods: { + sanitizeDestination(dst) { + try { + // Remove any leading/trailing whitespace + dst = dst.trim(); + + // Decode the URL to handle any encoded characters + dst = decodeURIComponent(dst); + + // Create a URL object to parse the destination + const url = new URL(dst, window.location.origin); + + // Only allow paths from the same origin + if (url.origin !== window.location.origin) { + return "/"; + } + + // Only return the pathname and search params + return url.pathname + url.search + url.hash; + } catch (e) { + // If any error occurs during parsing, return root + return "/"; + } + }, + async getErrorMessage(err) { switch (err.constructor) { case Error: @@ -119,6 +144,9 @@ export default { this.visible = false; this.$emit("login-success"); + + // Redirect to sanitized destination + if (this.destination !== "/") window.location.href = this.destination; }) .catch((err) => { this.loading = false; @@ -129,6 +157,11 @@ export default { }, }, async mounted() { + // Get and sanitize destination from URL parameters + const urlParams = new URLSearchParams(window.location.search); + const dst = urlParams.get("dst"); + this.destination = dst ? this.sanitizeDestination(dst) : "/"; + // Check if there's a valid session first const token = localStorage.getItem("shiori-token"); if (token) { diff --git a/internal/view/index.html b/internal/view/index.html index 177cc542f..c23f677a0 100644 --- a/internal/view/index.html +++ b/internal/view/index.html @@ -105,6 +105,8 @@ document.cookie = `token=; Path=${new URL(document.baseURI).pathname}; Expires=Thu, 01 Jan 1970 00:00:00 GMT;`; this.isLoggedIn = false; this.loginRequired = true; + this.dialog.loading = false; + this.dialog.visible = false; }).catch(err => { this.dialog.loading = false; this.getErrorMessage(err).then(msg => { @@ -155,7 +157,7 @@ owner: owner, }; }, - + onLoginSuccess() { this.loadSetting(); this.loadAccount(); @@ -165,7 +167,7 @@ async validateSession() { const token = localStorage.getItem("shiori-token"); const account = localStorage.getItem("shiori-account"); - + if (!(token && account)) { return false; } @@ -176,11 +178,11 @@ "Authorization": `Bearer ${token}` } }); - + if (!response.ok) { throw new Error('Invalid session'); } - + return true; } catch (err) { // Clear invalid session data @@ -195,7 +197,7 @@ async checkLoginStatus() { const isValid = await this.validateSession(); this.isLoggedIn = isValid; - + if (isValid) { this.loadSetting(); this.loadAccount(); From d75de897012d932f2c9c2a200a190d6b7cb5f6bb Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:12:07 +0100 Subject: [PATCH 05/10] fix: use mysql json default as expression (#1048) --- internal/database/migrations/mysql/0001_initial_account.up.sql | 2 +- internal/database/mysql.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/database/migrations/mysql/0001_initial_account.up.sql b/internal/database/migrations/mysql/0001_initial_account.up.sql index e4c996a94..014a0dc49 100644 --- a/internal/database/migrations/mysql/0001_initial_account.up.sql +++ b/internal/database/migrations/mysql/0001_initial_account.up.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS account( username VARCHAR(250) NOT NULL, password BINARY(80) NOT NULL, owner TINYINT(1) NOT NULL DEFAULT '0', - config JSON NOT NULL DEFAULT '{}', + config JSON NOT NULL DEFAULT ('{}'), PRIMARY KEY (id), UNIQUE KEY account_username_UNIQUE (username)) CHARACTER SET utf8mb4; diff --git a/internal/database/mysql.go b/internal/database/mysql.go index 27db774d5..a23816b81 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -48,7 +48,7 @@ var mysqlMigrations = []migration{ } defer tx.Rollback() - _, err = tx.Exec(`ALTER TABLE account ADD COLUMN config JSON NOT NULL DEFAULT '{}'`) + _, err = tx.Exec(`ALTER TABLE account ADD COLUMN config JSON NOT NULL DEFAULT ('{}')`) if err != nil && strings.Contains(err.Error(), `Duplicate column name`) { tx.Rollback() } else if err != nil { From 45bd4d693f5e3f3cd3afdf25538bcb7e39fbf2f7 Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:22:30 +0100 Subject: [PATCH 06/10] feat: fixes to webroot feature and improvements to development using it (#1046) * feat: Add nginx service to test shiori webroot configuration chore: Update nginx configuration to resolve 502 gateway error fix: Update SHIORI_WEBROOT to SHIORI_HTTP_ROOT_PATH in docker-compose feat: Add debug log level flag to shiori service refactor: Update docker-compose with simplified command and log configuration fix: Change nginx port mapping from 80 to 8081 feat: Add volume for Go module cache in docker-compose style: Add type attribute to script tags in index.html feat: Update import statements to use RootPath variable in index.html * docs: Update contribution guide with server and docker instructions * docs: Add Docker and nginx documentation for local development * test: IsValid() --- Makefile | 5 ----- docker-compose.yaml | 23 +++++++++++++++++--- docs/Contribute.md | 38 +++++++++++++++++++++++++++------- internal/cmd/root.go | 4 ++++ internal/config/config.go | 16 ++++++++++++++ internal/config/config_test.go | 16 ++++++++++++++ internal/view/index.html | 30 +++++++++++++-------------- testdata/nginx.conf | 24 +++++++++++++++++++++ 8 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 testdata/nginx.conf diff --git a/Makefile b/Makefile index bbac69fae..0588088a4 100644 --- a/Makefile +++ b/Makefile @@ -57,11 +57,6 @@ help: clean: rm -rf dist -## Runs the legacy http API for local development -.PHONY: serve -serve: - SHIORI_DEVELOPMENT=$(SHIORI_DEVELOPMENT) SHIORI_DIR=$(SHIORI_DIR) go run main.go serve - ## Runs server for local development .PHONY: run-server run-server: diff --git a/docker-compose.yaml b/docker-compose.yaml index 96fd9d73d..444ca2789 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,25 +1,39 @@ # Docker compose for development purposes only. # Edit it to fit your current development needs. -version: "3" services: shiori: build: context: . dockerfile: Dockerfile.compose container_name: shiori + command: + - "server" + - "--log-level" + - "debug" ports: - "8080:8080" volumes: - "./dev-data:/srv/shiori" - ".:/src/shiori" + - "go-mod-cache:/go/pkg/mod" restart: unless-stopped links: - "postgres" - "mariadb" environment: SHIORI_DIR: /srv/shiori - #SHIORI_DATABASE_URL: mysql://shiori:shiori@(mariadb)/shiori?charset=utf8mb4 - SHIORI_DATABASE_URL: postgres://shiori:shiori@postgres/shiori?sslmode=disable + # SHIORI_HTTP_ROOT_PATH: /shiori/ + # SHIORI_DATABASE_URL: mysql://shiori:shiori@(mariadb)/shiori?charset=utf8mb4 + # SHIORI_DATABASE_URL: postgres://shiori:shiori@postgres/shiori?sslmode=disable + + nginx: + image: nginx:alpine + ports: + - "8081:8081" + volumes: + - "./testdata/nginx.conf:/etc/nginx/nginx.conf:ro" + depends_on: + - shiori postgres: image: postgres:15 @@ -38,3 +52,6 @@ services: MYSQL_PASSWORD: shiori ports: - "3306:3306" + +volumes: + go-mod-cache: diff --git a/docs/Contribute.md b/docs/Contribute.md index ca0d7c65b..6cef5a55a 100644 --- a/docs/Contribute.md +++ b/docs/Contribute.md @@ -9,18 +9,10 @@ To run the current development server with the defaults you can run the following command: -```bash -make serve -``` - -If you want to run the refactored server, you can run the following command: - ```bash make run-server ``` -> **ℹ️ Note:** For more information into what the _refactored server_ means, please check this issue: https://github.com/go-shiori/shiori/issues/640 - ## Updating the API documentation > **ℹ️ Note:** This only applies for the Rest API documentation under the `internal/http` folder, **not** the one under `internal/webserver`. @@ -94,3 +86,33 @@ mkdocs serve This will start a local server at `http://127.0.0.1:8000` where you can preview your changes in real-time. Documentation for production is generated automatically on every release and published using github pages. + +## Running the server with docker + +To run the development server using Docker, you can use the provided `docker-compose.yaml` file which includes both PostgreSQL and MariaDB databases: + +```bash +docker compose up shiori +``` + +This will start the Shiori server on port 8080 with hot-reload enabled. Any changes you make to the code will automatically rebuild and restart the server. + +By default, it uses SQLite mounting the local `dev-data` folder in the source code path. To use MariaDB or PostgreSQL instead, uncomment the `SHIORI_DATABASE_URL` line for the appropriate engine in the `docker-compose.yaml` file. + +## Running the server using an nginx reverse proxy and a custom webroot + +To test Shiori behind an nginx reverse proxy with a custom webroot (e.g., `/shiori/`), you can use the provided nginx configuration: + +1. First, ensure the `SHIORI_HTTP_ROOT_PATH` environment variable is uncommented in `docker-compose.yaml`: + ```yaml + SHIORI_HTTP_ROOT_PATH: /shiori/ + ``` + +2. Then start both Shiori and nginx services: + ```bash + docker compose up shiori nginx + ``` + +This will start the shiori service along with nginx. You can access Shiori using [http://localhost:8081/shiori](http://localhost:8081/shiori). + +The nginx configuration in `testdata/nginx.conf` handles all the necessary configuration. diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 88b2dde48..48586c9f6 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -80,6 +80,10 @@ func initShiori(ctx context.Context, cmd *cobra.Command) (*config.Config, *depen cfg.SetDefaults(logger, portableMode) + if err := cfg.IsValid(); err != nil { + logger.WithError(err).Fatal("invalid configuration detected") + } + err := os.MkdirAll(cfg.Storage.DataDir, model.DataDirPerm) if err != nil { logger.WithError(err).Fatal("error creating data directory") diff --git a/internal/config/config.go b/internal/config/config.go index 5b7aa96f8..f62769410 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -79,6 +79,14 @@ func (c *HttpConfig) SetDefaults(logger *logrus.Logger) { } } +func (c *HttpConfig) IsValid() error { + if !strings.HasSuffix(c.RootPath, "/") { + return fmt.Errorf("root path should end with a slash") + } + + return nil +} + type DatabaseConfig struct { DBMS string `env:"DBMS"` // Deprecated // DBMS requires more environment variables. Check the database package for more information. @@ -140,6 +148,14 @@ func (c *Config) DebugConfiguration(logger *logrus.Logger) { logger.Debugf(" SHIORI_HTTP_DISABLE_PARSE_MULTIPART_FORM: %t", c.Http.DisablePreParseMultipartForm) } +func (c *Config) IsValid() error { + if err := c.Http.IsValid(); err != nil { + return fmt.Errorf("http configuration is invalid: %w", err) + } + + return nil +} + // ParseServerConfiguration parses the configuration from the enabled lookupers func ParseServerConfiguration(ctx context.Context, logger *logrus.Logger) *Config { var cfg Config diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 36f22717a..4c18e2f79 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -101,3 +101,19 @@ func TestConfigSetDefaults(t *testing.T) { require.NotEmpty(t, cfg.Storage.DataDir) require.NotEmpty(t, cfg.Database.URL) } + +func TestConfigIsValid(t *testing.T) { + log := logrus.New() + + t.Run("valid configuration", func(t *testing.T) { + cfg := ParseServerConfiguration(context.TODO(), log) + cfg.SetDefaults(log, false) + require.NoError(t, cfg.IsValid()) + }) + + t.Run("invalid http root path", func(t *testing.T) { + cfg := ParseServerConfiguration(context.TODO(), log) + cfg.Http.RootPath = "/invalid" + require.Error(t, cfg.IsValid()) + }) +} diff --git a/internal/view/index.html b/internal/view/index.html index c23f677a0..aba8a7ffb 100644 --- a/internal/view/index.html +++ b/internal/view/index.html @@ -16,27 +16,27 @@ - - + +
-
- - - -
- - - -
- - - - +
+ + + +
+ + + +
+ + + +
diff --git a/testdata/nginx.conf b/testdata/nginx.conf new file mode 100644 index 000000000..bff0cdabd --- /dev/null +++ b/testdata/nginx.conf @@ -0,0 +1,24 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server { + listen 8081; + server_name localhost; + + location /shiori/ { + proxy_pass http://shiori:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_send_timeout 300; + } + } +} From 98b6b3f1838911e93a7e42b8bae0931dffc43dcc Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:46:19 +0100 Subject: [PATCH 07/10] deps: upgrade golang dependencies (#1047) --- go.mod | 130 +++++++++++++++++++++++--------------------- go.sum | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 61 deletions(-) diff --git a/go.mod b/go.mod index 2c4d66d2a..c6064d7e1 100644 --- a/go.mod +++ b/go.mod @@ -1,86 +1,90 @@ module github.com/go-shiori/shiori // +heroku goVersion go1.22 -go 1.22.3 +go 1.23 + +toolchain go1.23.4 require ( git.sr.ht/~emersion/go-sqlite3-fts5 v0.0.0-20240124102820-f3a72e8b79b1 - github.com/PuerkitoBio/goquery v1.9.2 + github.com/PuerkitoBio/goquery v1.10.1 github.com/blang/semver v3.5.1+incompatible github.com/disintegration/imaging v1.6.2 - github.com/fatih/color v1.17.0 - github.com/gin-contrib/requestid v1.0.2 - github.com/gin-contrib/static v1.1.2 + github.com/fatih/color v1.18.0 + github.com/gin-contrib/requestid v1.0.4 + github.com/gin-contrib/static v1.1.3 github.com/gin-gonic/gin v1.10.0 github.com/go-shiori/go-epub v1.2.2-0.20240211121944-dc6435eac436 - github.com/go-shiori/go-readability v0.0.0-20240518065252-ec24db06a99d + github.com/go-shiori/go-readability v0.0.0-20241012063810-92284fa8a71f github.com/go-shiori/warc v0.0.0-20200621032813-359908319d1d github.com/go-sql-driver/mysql v1.8.1 - github.com/gofrs/uuid/v5 v5.2.0 + github.com/gofrs/uuid/v5 v5.3.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/jmoiron/sqlx v1.4.0 github.com/julienschmidt/httprouter v1.3.0 github.com/lib/pq v1.10.9 - github.com/mattn/go-sqlite3 v1.14.22 + github.com/mattn/go-sqlite3 v1.14.24 github.com/muesli/go-app-paths v0.2.2 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/sethvargo/go-envconfig v1.0.2 + github.com/sethvargo/go-envconfig v1.1.0 github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.11.0 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 - github.com/swaggo/swag v1.16.3 - github.com/testcontainers/testcontainers-go v0.31.0 + github.com/swaggo/swag v1.16.4 + github.com/testcontainers/testcontainers-go v0.34.0 github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f golang.org/x/crypto v0.31.0 - golang.org/x/image v0.18.0 - golang.org/x/net v0.25.0 + golang.org/x/image v0.23.0 + golang.org/x/net v0.33.0 golang.org/x/term v0.27.0 - modernc.org/sqlite v1.29.9 + modernc.org/sqlite v1.34.4 ) require ( - dario.cat/mergo v1.0.0 // indirect + dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Microsoft/hcsshim v0.12.9 // indirect + github.com/andybalholm/cascadia v1.3.3 // indirect + github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect + github.com/bytedance/sonic v1.12.6 // indirect + github.com/bytedance/sonic/loader v0.2.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/containerd/containerd v1.7.15 // indirect + github.com/containerd/containerd v1.7.24 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/docker v25.0.6+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.4.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/gin-contrib/sse v1.0.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -89,18 +93,20 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect + github.com/magiconair/properties v1.8.9 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -108,39 +114,41 @@ require ( github.com/ncruces/go-strftime v0.1.9 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shirou/gopsutil/v3 v3.24.5 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/tdewolff/parse v2.3.4+incompatible // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.9.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.etcd.io/bbolt v1.3.10 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/mod v0.17.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.etcd.io/bbolt v1.3.11 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + golang.org/x/arch v0.12.0 // indirect + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/mod v0.22.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/tools v0.28.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def // indirect + google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.36.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect - modernc.org/libc v1.50.6 // indirect - modernc.org/mathutil v1.6.0 // indirect + modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d // indirect + modernc.org/libc v1.61.6 // indirect + modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.8.0 // indirect - modernc.org/strutil v1.2.0 // indirect + modernc.org/strutil v1.2.1 // indirect modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index 76c9dfc32..b27af8b5b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.sr.ht/~emersion/go-sqlite3-fts5 v0.0.0-20240124102820-f3a72e8b79b1 h1:0j/o1v+RUUN5OqE1ms4+ptMYGaUj+QnndNsHsX+0SnY= @@ -12,31 +14,54 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg= +github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y= github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= +github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= +github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk= +github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= +github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= +github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= +github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -46,8 +71,12 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= +github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -56,27 +85,41 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= +github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/requestid v1.0.2 h1:MRJqVwmpHAbkkF3ENgtDWU41l5ICmmVy01q2ZDYI1BE= github.com/gin-contrib/requestid v1.0.2/go.mod h1:GZWwfwmwZKfuxjnByRCrf+ugr65OW+425m5HiryD37s= +github.com/gin-contrib/requestid v1.0.4 h1:h9u+YSCMgrDcn2QlHn9c6P/Zwy4WdXqZLFTmlIAJWpA= +github.com/gin-contrib/requestid v1.0.4/go.mod h1:2/3cAmLKQ9E2Pr1IrSPR7K8AWiJORo0hLvs0keKsMJw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= +github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= github.com/gin-contrib/static v1.1.2 h1:c3kT4bFkUJn2aoRU3s6XnMjJT8J6nNWJkR0NglqmlZ4= github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw= +github.com/gin-contrib/static v1.1.3 h1:WLOpkBtMDJ3gATFZgNJyVibFMio/UHonnueqJsQ0w4U= +github.com/gin-contrib/static v1.1.3/go.mod h1:zejpJ/YWp8cZj/6EpiL5f/+skv5daQTNwRx1E8Pci30= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 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-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -93,6 +136,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-shiori/dom v0.0.0-20190930082056-9d974a4f8b25/go.mod h1:360KoNl36ftFYhjLHuEty78kWUGw8i1opEicvIDLfRk= github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziHZixGO5ZBS73cKqVzZipfrLmO1w= github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM= @@ -100,14 +145,20 @@ github.com/go-shiori/go-epub v1.2.2-0.20240211121944-dc6435eac436 h1:eLPGGYvm3KF github.com/go-shiori/go-epub v1.2.2-0.20240211121944-dc6435eac436/go.mod h1:3rCTODnigEgy2j3ksndClrGT9h/dcz3js9q4yPX7hf8= github.com/go-shiori/go-readability v0.0.0-20240518065252-ec24db06a99d h1:gLIyppByW8mGuj7ihkflmQYs34mf9YSaJkCQbbCXLEY= github.com/go-shiori/go-readability v0.0.0-20240518065252-ec24db06a99d/go.mod h1:2DpZlTJO/ycxp/vsc/C11oUyveStOgIXB88SYV1lncI= +github.com/go-shiori/go-readability v0.0.0-20241012063810-92284fa8a71f h1:cypj7SJh+47G9J3VCPdMzT3uWcXWAWDJA54ErTfOigI= +github.com/go-shiori/go-readability v0.0.0-20241012063810-92284fa8a71f/go.mod h1:YWa00ashoPZMAOElrSn4E1cJErhDVU6PWAll4Hxzn+w= github.com/go-shiori/warc v0.0.0-20200621032813-359908319d1d h1:+SEf4hYDaAt2eyq8Xu3YyWCpnMsK8sZfbYsDRFCUgBM= github.com/go-shiori/warc v0.0.0-20200621032813-359908319d1d/go.mod h1:uaK5DAxFig7atOzy+aqLzhs6qJacMDfs8NxHV5+shzc= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= @@ -143,9 +194,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -159,25 +214,42 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 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/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -197,25 +269,36 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sethvargo/go-envconfig v1.0.2 h1:BAQnzBLK/mPN3R3pC0d46MLN0htc64YZBVrz/sZfAX4= github.com/sethvargo/go-envconfig v1.0.2/go.mod h1:OKZ02xFaD3MvWBBmEW45fQr08sJEsonGrrOdicvQmQA= +github.com/sethvargo/go-envconfig v1.1.0 h1:cWZiJxeTm7AlCvzGXrEXaSTCNgip5oJepekh/BOQuog= +github.com/sethvargo/go-envconfig v1.1.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 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= @@ -231,6 +314,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -247,22 +332,32 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= +github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38= github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= github.com/tdewolff/test v1.0.0 h1:jOwzqCXr5ePXEPGJaq2ivoR6HOCi+D5TPfpoyg8yvmU= github.com/tdewolff/test v1.0.0/go.mod h1:DiQUlutnqlEvdvhSn2LPGy4TFwRauAaYDsL+683RNX4= github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= 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/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo= +github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI= github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f h1:oqdnd6OGlOUu1InG37hWcCB3a+Jy3fwjylyVboaNMwY= github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f/go.mod h1:X3Dd1SB8Gt1V968NTzpKFjMM6O8ccta2NPC6MprOxZQ= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -276,43 +371,71 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= +golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190926025831-c00fd9afed17/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -323,13 +446,21 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -346,18 +477,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -365,6 +505,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -375,21 +518,32 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def h1:4P81qv5JXI/sDNae2ClVx88cgDDA6DPilADkG9tYKz8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def/go.mod h1:bdAgzvd4kFrpykc5/AC2eLUiegK9T/qxZHD4hXYf/ho= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -398,20 +552,30 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk= modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/cc/v4 v4.24.2 h1:uektamHbSXU7egelXcyVpMaaAsrRH4/+uMKUQAQUdOw= modernc.org/ccgo/v4 v4.17.7 h1:+MG+Np7uYtsuPvtoH3KtZ1+pqNiJAOqqqVIxggE1iIo= modernc.org/ccgo/v4 v4.17.7/go.mod h1:x87xuLLXuJv3Nn5ULTUqJn/HsTMMMiT1Eavo6rz1NiY= +modernc.org/ccgo/v4 v4.23.5 h1:6uAwu8u3pnla3l/+UVUrDDO1HIGxHTYmFH6w+X9nsyw= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v2 v2.6.0 h1:Tiw3pezQj7PfV8k4Dzyu/vhRHR2e92kOXtTFU8pbCl4= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8= modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d h1:d0JExN5U5FjUVHCP6L9DIlLJBZveR6KUM4AvfDUL3+k= +modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d/go.mod h1:qBSLm/exCqouT2hrfyTKikWKG9IPq8EoX5fS00l3jqk= modernc.org/libc v1.50.6 h1:72NPEFMyKP01RJrKXS2eLXv35UklKqlJZ1b9P7gSo6I= modernc.org/libc v1.50.6/go.mod h1:8lr2m1THY5Z3ikGyUc3JhLEQg1oaIBz/AQixw8/eksQ= +modernc.org/libc v1.61.6 h1:L2jW0wxHPCyHK0YSHaGaVlY0WxjpG/TTVdg6gRJOPqw= +modernc.org/libc v1.61.6/go.mod h1:G+DzuaCcReUYYg4nNSfigIfTDCENdj9EByglvaRx53A= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= @@ -420,8 +584,12 @@ modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= modernc.org/sqlite v1.29.9 h1:9RhNMklxJs+1596GNuAX+O/6040bvOwacTxuFcRuQow= modernc.org/sqlite v1.29.9/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA= +modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= +modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= From c58c35509bea7c2c99c0755cc12dcbbd4ae8b843 Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:46:39 +0100 Subject: [PATCH 08/10] feat: improve SQLite performance (#1024) * refactor: Improve SQLite performance with connection pooling and retry logic * feat: Add withTx and withTxRetry methods to SQLiteDatabase for handling database locks * refactor: add Init command to all databases * refactor: Improve transaction handling with retry and error management * refactor: Remove panic/recover pattern in transaction handling * refactor: Replace `errors.WithStack` with `fmt.Errorf` in transaction methods * docs: Add docstrings to `withTx` and `withTxRetry` methods in SQLite database implementation * feat: use new withTxRetry in SaveBookmarks * feat: sqlite inmmediate transactions by default * refactor: Split SQLiteDatabase into separate writer and reader dbbase instances * refactor: Update Init method to configure both reader and writer database connections * feat: use writer/reader sqlite databases * refactor: Replace all read calls to use the `reader` database instance * refactor: Replace errors.WithStack with fmt.Errorf and add nil checks refactor: Replace errors.WithStack with fmt.Errorf and add proper error handling fix: Handle potential database connection errors with improved error wrapping refactor: Replace errors.WithStack with fmt.Errorf and improve error handling refactor: Replace error handling with fmt.Errorf and proper nil checks refactor: Replace errors.WithStack with fmt.Errorf and add nil error checks refactor: Replace errors.WithStack with fmt.Errorf and add nil checks in sqlite.go refactor: Replace errors.WithStack with fmt.Errorf and add nil checks refactor: Replace errors.WithStack with fmt.Errorf and improve error handling refactor: Replace remaining errors.WithStack with fmt.Errorf in sqlite.go * refactor: Use withTxRetry for SetDatabaseSchemaVersion method * fix: Simplify error handling in GetBookmark and GetAccount methods * refactor: Remove duplicated non-nil error checks in sqlite.go fix: duplicated non-nil checks * tests: use testutil instead of a manual in memory sqlite db * fix: openbsd sqlite connection --- internal/config/config.go | 2 +- internal/database/database.go | 3 + internal/database/mysql.go | 5 + internal/database/pg.go | 5 + internal/database/sqlite.go | 258 ++++++++++++++++++++-------- internal/database/sqlite_noncgo.go | 21 ++- internal/database/sqlite_openbsd.go | 21 ++- internal/domains/bookmarks_test.go | 16 +- 8 files changed, 234 insertions(+), 97 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index f62769410..e70f00284 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -120,7 +120,7 @@ func (c Config) SetDefaults(logger *logrus.Logger, portableMode bool) { // Set default database url if not set if c.Database.DBMS == "" && c.Database.URL == "" { - c.Database.URL = fmt.Sprintf("sqlite:///%s", filepath.Join(c.Storage.DataDir, "shiori.db")) + c.Database.URL = fmt.Sprintf("sqlite:///%s?_txlock=immediate", filepath.Join(c.Storage.DataDir, "shiori.db")) } c.Http.SetDefaults(logger) diff --git a/internal/database/database.go b/internal/database/database.go index 211a91919..298fb81d2 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -67,6 +67,9 @@ type DB interface { // DBx is the underlying sqlx.DB DBx() *sqlx.DB + // Init initializes the database + Init(ctx context.Context) error + // Migrate runs migrations for this database Migrate(ctx context.Context) error diff --git a/internal/database/mysql.go b/internal/database/mysql.go index a23816b81..668db7ae2 100644 --- a/internal/database/mysql.go +++ b/internal/database/mysql.go @@ -95,6 +95,11 @@ func (db *MySQLDatabase) DBx() *sqlx.DB { return db.DB } +// Init initializes the database +func (db *MySQLDatabase) Init(ctx context.Context) error { + return nil +} + // Migrate runs migrations for this database engine func (db *MySQLDatabase) Migrate(ctx context.Context) error { if err := runMigrations(ctx, db, mysqlMigrations); err != nil { diff --git a/internal/database/pg.go b/internal/database/pg.go index a4403ff8b..05e1116c2 100644 --- a/internal/database/pg.go +++ b/internal/database/pg.go @@ -96,6 +96,11 @@ func (db *PGDatabase) DBx() *sqlx.DB { return db.DB } +// Init initializes the database +func (db *PGDatabase) Init(ctx context.Context) error { + return nil +} + // Migrate runs migrations for this database engine func (db *PGDatabase) Migrate(ctx context.Context) error { if err := runMigrations(ctx, db, postgresMigrations); err != nil { diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 0675db6aa..c9e663f37 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "log" + "runtime" "strings" "time" @@ -66,7 +67,98 @@ var sqliteMigrations = []migration{ // SQLiteDatabase is implementation of Database interface // for connecting to SQLite3 database. type SQLiteDatabase struct { - dbbase + writer *dbbase + reader *dbbase +} + +// withTx executes the given function within a transaction. +// If the function returns an error, the transaction is rolled back. +// Otherwise, the transaction is committed. +func (db *SQLiteDatabase) withTx(ctx context.Context, fn func(tx *sqlx.Tx) error) error { + tx, err := db.writer.BeginTxx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + + err = fn(tx) + if err != nil { + rbErr := tx.Rollback() + if rbErr != nil { + return fmt.Errorf("error rolling back: %v (original error: %w)", rbErr, err) + } + return fmt.Errorf("transaction failed: %w", err) + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil +} + +// withTxRetry executes the given function within a transaction with retry logic. +// It will retry up to 3 times if the database is locked, with exponential backoff. +// For other errors, it returns immediately. +func (db *SQLiteDatabase) withTxRetry(ctx context.Context, fn func(tx *sqlx.Tx) error) error { + maxRetries := 3 + var lastErr error + + for i := 0; i < maxRetries; i++ { + err := db.withTx(ctx, fn) + if err == nil { + return nil + } + + if strings.Contains(err.Error(), "database is locked") { + lastErr = err + time.Sleep(time.Duration(i+1) * 100 * time.Millisecond) + continue + } + + return fmt.Errorf("transaction failed after retry: %w", err) + } + + return fmt.Errorf("transaction failed after max retries, last error: %w", lastErr) +} + +// Init sets up the SQLite database with optimal settings for both reader and writer connections +func (db *SQLiteDatabase) Init(ctx context.Context) error { + // Initialize both connections with appropriate settings + for _, conn := range []*dbbase{db.writer, db.reader} { + // Reuse connections for up to one hour + conn.SetConnMaxLifetime(time.Hour) + + // Enable WAL mode for better concurrency + if _, err := conn.ExecContext(ctx, `PRAGMA journal_mode=WAL`); err != nil { + return fmt.Errorf("failed to set journal mode: %w", err) + } + + // Set busy timeout to avoid "database is locked" errors + if _, err := conn.ExecContext(ctx, `PRAGMA busy_timeout=5000`); err != nil { + return fmt.Errorf("failed to set busy timeout: %w", err) + } + + // Other performance and reliability settings + pragmas := []string{ + `PRAGMA synchronous=NORMAL`, + `PRAGMA cache_size=-2000`, // Use 2MB of memory for cache + `PRAGMA foreign_keys=ON`, + } + + for _, pragma := range pragmas { + if _, err := conn.ExecContext(ctx, pragma); err != nil { + return fmt.Errorf("failed to set pragma %s: %w", pragma, err) + } + } + } + + // Use a single connection on the writer to avoid database is locked errors + db.writer.SetMaxOpenConns(1) + + // Set maximum idle connections for the reader to number of CPUs (maxing at 4) + db.reader.SetMaxIdleConns(max(4, runtime.NumCPU())) + + return nil } type bookmarkContent struct { @@ -80,15 +172,20 @@ type tagContent struct { model.Tag } -// DBX returns the underlying sqlx.DB object +// DBX returns the underlying sqlx.DB object for writes func (db *SQLiteDatabase) DBx() *sqlx.DB { - return db.DB + return db.writer.DB +} + +// ReaderDBx returns the underlying sqlx.DB object for reading +func (db *SQLiteDatabase) ReaderDBx() *sqlx.DB { + return db.reader.DB } // Migrate runs migrations for this database engine func (db *SQLiteDatabase) Migrate(ctx context.Context) error { if err := runMigrations(ctx, db, sqliteMigrations); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to run migrations: %w", err) } return nil @@ -98,9 +195,9 @@ func (db *SQLiteDatabase) Migrate(ctx context.Context) error { func (db *SQLiteDatabase) GetDatabaseSchemaVersion(ctx context.Context) (string, error) { var version string - err := db.GetContext(ctx, &version, "SELECT database_schema_version FROM shiori_system") + err := db.reader.GetContext(ctx, &version, "SELECT database_schema_version FROM shiori_system") if err != nil { - return "", errors.WithStack(err) + return "", fmt.Errorf("failed to get database schema version: %w", err) } return version, nil @@ -108,15 +205,17 @@ func (db *SQLiteDatabase) GetDatabaseSchemaVersion(ctx context.Context) (string, // SetDatabaseSchemaVersion sets the current migrations version of the database func (db *SQLiteDatabase) SetDatabaseSchemaVersion(ctx context.Context, version string) error { - tx := db.MustBegin() - defer tx.Rollback() - - _, err := tx.Exec("UPDATE shiori_system SET database_schema_version = ?", version) - if err != nil { - return errors.WithStack(err) + if err := db.withTxRetry(ctx, func(tx *sqlx.Tx) error { + _, err := tx.ExecContext(ctx, "UPDATE shiori_system SET database_schema_version = ?", version) + if err != nil { + return err + } + return nil + }); err != nil { + return fmt.Errorf("failed to set database schema version: %w", err) } - return tx.Commit() + return nil } // SaveBookmarks saves new or updated bookmarks to database. @@ -124,14 +223,14 @@ func (db *SQLiteDatabase) SetDatabaseSchemaVersion(ctx context.Context, version func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookmarks ...model.BookmarkDTO) ([]model.BookmarkDTO, error) { var result []model.BookmarkDTO - if err := db.withTx(ctx, func(tx *sqlx.Tx) error { + if err := db.withTxRetry(ctx, func(tx *sqlx.Tx) error { // Prepare statement stmtInsertBook, err := tx.PreparexContext(ctx, `INSERT INTO bookmark (url, title, excerpt, author, public, modified_at, has_content, created_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?) RETURNING id`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare insert book statement: %w", err) } stmtUpdateBook, err := tx.PreparexContext(ctx, `UPDATE bookmark SET @@ -139,43 +238,43 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma public = ?, modified_at = ?, has_content = ? WHERE id = ?`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare update book statement: %w", err) } stmtInsertBookContent, err := tx.PreparexContext(ctx, `INSERT OR REPLACE INTO bookmark_content (docid, title, content, html) VALUES (?, ?, ?, ?)`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare insert book content statement: %w", err) } stmtUpdateBookContent, err := tx.PreparexContext(ctx, `UPDATE bookmark_content SET title = ?, content = ?, html = ? WHERE docid = ?`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare update book content statement: %w", err) } stmtGetTag, err := tx.PreparexContext(ctx, `SELECT id FROM tag WHERE name = ?`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare get tag statement: %w", err) } stmtInsertTag, err := tx.PreparexContext(ctx, `INSERT INTO tag (name) VALUES (?)`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare insert tag statement: %w", err) } stmtInsertBookTag, err := tx.PreparexContext(ctx, `INSERT OR IGNORE INTO bookmark_tag (tag_id, bookmark_id) VALUES (?, ?)`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare insert book tag statement: %w", err) } stmtDeleteBookTag, err := tx.PreparexContext(ctx, `DELETE FROM bookmark_tag WHERE bookmark_id = ? AND tag_id = ?`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute delete statement: %w", err) } // Prepare modified time @@ -211,25 +310,25 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma book.URL, book.Title, book.Excerpt, book.Author, book.Public, book.ModifiedAt, hasContent, book.ID) } if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark content: %w", err) } // Try to update it first to check for existence, we can't do an UPSERT here because // bookmant_content is a virtual table res, err := stmtUpdateBookContent.ExecContext(ctx, book.Title, book.Content, book.HTML, book.ID) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark tag: %w", err) } rows, err := res.RowsAffected() if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark: %w", err) } if rows == 0 { _, err = stmtInsertBookContent.ExecContext(ctx, book.ID, book.Title, book.Content, book.HTML) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute delete bookmark tag statement: %w", err) } } @@ -240,7 +339,7 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma if tag.Deleted { _, err = stmtDeleteBookTag.ExecContext(ctx, book.ID, tag.ID) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute delete bookmark statement: %w", err) } continue } @@ -252,26 +351,26 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma // If tag doesn't have any ID, fetch it from database if tag.ID == 0 { if err := stmtGetTag.GetContext(ctx, &tag.ID, tagName); err != nil && err != sql.ErrNoRows { - return errors.WithStack(err) + return fmt.Errorf("failed to get tag ID: %w", err) } // If tag doesn't exist in database, save it if tag.ID == 0 { res, err := stmtInsertTag.ExecContext(ctx, tagName) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to get last insert ID for tag: %w", err) } tagID64, err := res.LastInsertId() if err != nil && err != sql.ErrNoRows { - return errors.WithStack(err) + return fmt.Errorf("failed to insert bookmark tag: %w", err) } tag.ID = int(tagID64) } if _, err := stmtInsertBookTag.ExecContext(ctx, tag.ID, book.ID); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute bookmark tag statement: %w", err) } } @@ -284,7 +383,7 @@ func (db *SQLiteDatabase) SaveBookmarks(ctx context.Context, create bool, bookma return nil }); err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to execute select query for bookmark content: %w", err) } return result, nil @@ -405,14 +504,14 @@ func (db *SQLiteDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpt // Expand query, because some of the args might be an array query, args, err := sqlx.In(query, args...) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to execute select query for tags: %w", err) } // Fetch bookmarks bookmarks := []model.BookmarkDTO{} - err = db.SelectContext(ctx, &bookmarks, query, args...) + err = db.reader.SelectContext(ctx, &bookmarks, query, args...) if err != nil && err != sql.ErrNoRows { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to fetch accounts: %w", err) } // store bookmark IDs for further enrichment @@ -432,14 +531,14 @@ func (db *SQLiteDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpt contentMap := make(map[int]bookmarkContent, len(bookmarks)) contentQuery, args, err := sqlx.In(`SELECT docid, content, html FROM bookmark_content WHERE docid IN (?)`, bookmarkIds) - contentQuery = db.Rebind(contentQuery) + contentQuery = db.reader.Rebind(contentQuery) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to expand tags query with IN clause: %w", err) } - err = db.Select(&contents, contentQuery, args...) + err = db.reader.Select(&contents, contentQuery, args...) if err != nil && err != sql.ErrNoRows { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to get tags: %w", err) } for _, content := range contents { contentMap[content.ID] = content @@ -465,14 +564,14 @@ func (db *SQLiteDatabase) GetBookmarks(ctx context.Context, opts GetBookmarksOpt LEFT JOIN tag t ON bt.tag_id = t.id WHERE bt.bookmark_id IN (?) ORDER BY t.name`, bookmarkIds) - tagsQuery = db.Rebind(tagsQuery) + tagsQuery = db.reader.Rebind(tagsQuery) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to delete bookmark and related records: %w", err) } - err = db.Select(&tags, tagsQuery, tagArgs...) + err = db.reader.Select(&tags, tagsQuery, tagArgs...) if err != nil && err != sql.ErrNoRows { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to get tags: %w", err) } for _, fetchedTag := range tags { if tags, found := tagsMap[fetchedTag.ID]; found { @@ -584,14 +683,14 @@ func (db *SQLiteDatabase) GetBookmarksCount(ctx context.Context, opts GetBookmar // Expand query, because some of the args might be an array query, args, err := sqlx.In(query, args...) if err != nil { - return 0, errors.WithStack(err) + return 0, fmt.Errorf("failed to expand query with IN clause: %w", err) } // Fetch count var nBookmarks int - err = db.GetContext(ctx, &nBookmarks, query, args...) + err = db.reader.GetContext(ctx, &nBookmarks, query, args...) if err != nil && err != sql.ErrNoRows { - return 0, errors.WithStack(err) + return 0, fmt.Errorf("failed to get bookmark count: %w", err) } return nBookmarks, nil @@ -609,17 +708,17 @@ func (db *SQLiteDatabase) DeleteBookmarks(ctx context.Context, ids ...int) error if len(ids) == 0 { _, err := tx.ExecContext(ctx, delBookmarkContent) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare delete statement: %w", err) } _, err = tx.ExecContext(ctx, delBookmarkTag) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute delete account statement: %w", err) } _, err = tx.ExecContext(ctx, delBookmark) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to execute delete bookmark statement: %w", err) } } else { delBookmark += ` WHERE id = ?` @@ -628,40 +727,40 @@ func (db *SQLiteDatabase) DeleteBookmarks(ctx context.Context, ids ...int) error stmtDelBookmark, err := tx.Preparex(delBookmark) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to get bookmark: %w", err) } stmtDelBookmarkTag, err := tx.Preparex(delBookmarkTag) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to expand query with IN clause: %w", err) } stmtDelBookmarkContent, err := tx.Preparex(delBookmarkContent) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark content: %w", err) } for _, id := range ids { _, err = stmtDelBookmarkContent.ExecContext(ctx, id) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark: %w", err) } _, err = stmtDelBookmarkTag.ExecContext(ctx, id) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark tag: %w", err) } _, err = stmtDelBookmark.ExecContext(ctx, id) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark: %w", err) } } } return nil }); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to update database schema version: %w", err) } return nil @@ -684,8 +783,8 @@ func (db *SQLiteDatabase) GetBookmark(ctx context.Context, id int, url string) ( } book := model.BookmarkDTO{} - if err := db.GetContext(ctx, &book, query, args...); err != nil && err != sql.ErrNoRows { - return book, false, errors.WithStack(err) + if err := db.reader.GetContext(ctx, &book, query, args...); err != nil && err != sql.ErrNoRows { + return book, false, fmt.Errorf("failed to get bookmark: %w", err) } return book, book.ID != 0, nil @@ -707,9 +806,12 @@ func (db *SQLiteDatabase) SaveAccount(ctx context.Context, account model.Account password = ?, owner = ?`, account.Username, hashedPassword, account.Owner, account.Config, hashedPassword, account.Owner, account.Config) - return errors.WithStack(err) + if err != nil { + return fmt.Errorf("failed to hash password: %w", err) + } + return nil }); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to insert/update account: %w", err) } return nil @@ -723,9 +825,12 @@ func (db *SQLiteDatabase) SaveAccountSettings(ctx context.Context, account model SET config = ? WHERE username = ?`, account.Config, account.Username) - return errors.WithStack(err) + if err != nil { + return fmt.Errorf("failed to update account settings: %w", err) + } + return nil }); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare delete book tag statement: %w", err) } return nil @@ -750,9 +855,9 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio // Fetch list account accounts := []model.Account{} - err := db.SelectContext(ctx, &accounts, query, args...) + err := db.reader.SelectContext(ctx, &accounts, query, args...) if err != nil && err != sql.ErrNoRows { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to execute select query: %w", err) } return accounts, nil @@ -762,11 +867,14 @@ func (db *SQLiteDatabase) GetAccounts(ctx context.Context, opts GetAccountsOptio // Returns the account and boolean whether it's exist or not. func (db *SQLiteDatabase) GetAccount(ctx context.Context, username string) (model.Account, bool, error) { account := model.Account{} - if err := db.GetContext(ctx, &account, `SELECT + if err := db.reader.GetContext(ctx, &account, `SELECT id, username, password, owner, config FROM account WHERE username = ?`, username, ); err != nil { - return account, false, errors.WithStack(err) + if err != sql.ErrNoRows { + return account, false, fmt.Errorf("account does not exist %w", err) + } + return account, false, fmt.Errorf("failed to get account: %w", err) } return account, account.ID != 0, nil @@ -778,19 +886,19 @@ func (db *SQLiteDatabase) DeleteAccounts(ctx context.Context, usernames ...strin // Delete account stmtDelete, err := tx.Preparex(`DELETE FROM account WHERE username = ?`) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to insert tag: %w", err) } for _, username := range usernames { _, err := stmtDelete.ExecContext(ctx, username) if err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to delete bookmark tag: %w", err) } } return nil }); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to prepare statement: %w", err) } return nil @@ -810,17 +918,17 @@ func (db *SQLiteDatabase) CreateTags(ctx context.Context, tags ...model.Tag) err if err := db.withTx(ctx, func(tx *sqlx.Tx) error { stmt, err := tx.Preparex(query) if err != nil { - return errors.Wrap(errors.WithStack(err), "error preparing query") + return fmt.Errorf("failed to prepare tag creation query: %w", err) } _, err = stmt.ExecContext(ctx, values...) if err != nil { - return errors.Wrap(errors.WithStack(err), "error executing query") + return fmt.Errorf("failed to execute tag creation query: %w", err) } return nil }); err != nil { - return errors.Wrap(errors.WithStack(err), "error running transaction") + return fmt.Errorf("failed to run tag creation transaction: %w", err) } return nil @@ -834,9 +942,9 @@ func (db *SQLiteDatabase) GetTags(ctx context.Context) ([]model.Tag, error) { LEFT JOIN tag t ON bt.tag_id = t.id GROUP BY bt.tag_id ORDER BY t.name` - err := db.SelectContext(ctx, &tags, query) + err := db.reader.SelectContext(ctx, &tags, query) if err != nil && err != sql.ErrNoRows { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("failed to prepare delete bookmark content statement: %w", err) } return tags, nil @@ -848,7 +956,7 @@ func (db *SQLiteDatabase) RenameTag(ctx context.Context, id int, newName string) _, err := tx.ExecContext(ctx, `UPDATE tag SET name = ? WHERE id = ?`, newName, id) return err }); err != nil { - return errors.WithStack(err) + return fmt.Errorf("failed to rename tag: %w", err) } return nil diff --git a/internal/database/sqlite_noncgo.go b/internal/database/sqlite_noncgo.go index f2ee6b9c1..d25ec0ad1 100644 --- a/internal/database/sqlite_noncgo.go +++ b/internal/database/sqlite_noncgo.go @@ -5,9 +5,9 @@ package database import ( "context" + "fmt" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" _ "modernc.org/sqlite" ) @@ -15,11 +15,24 @@ import ( // OpenSQLiteDatabase creates and open connection to new SQLite3 database. func OpenSQLiteDatabase(ctx context.Context, databasePath string) (sqliteDB *SQLiteDatabase, err error) { // Open database - db, err := sqlx.ConnectContext(ctx, "sqlite", databasePath) + rwDB, err := sqlx.ConnectContext(ctx, "sqlite", databasePath) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("error opening writer database: %w", err) + } + + rDB, err := sqlx.ConnectContext(ctx, "sqlite", databasePath) + if err != nil { + return nil, fmt.Errorf("error opening reader database: %w", err) + } + + sqliteDB = &SQLiteDatabase{ + writer: &dbbase{rwDB}, + reader: &dbbase{rDB}, + } + + if err := sqliteDB.Init(ctx); err != nil { + return nil, fmt.Errorf("error initializing database: %w", err) } - sqliteDB = &SQLiteDatabase{dbbase: dbbase{db}} return sqliteDB, nil } diff --git a/internal/database/sqlite_openbsd.go b/internal/database/sqlite_openbsd.go index 64d9c7d00..ad0919906 100644 --- a/internal/database/sqlite_openbsd.go +++ b/internal/database/sqlite_openbsd.go @@ -5,9 +5,9 @@ package database import ( "context" + "fmt" "github.com/jmoiron/sqlx" - "github.com/pkg/errors" _ "git.sr.ht/~emersion/go-sqlite3-fts5" _ "github.com/mattn/go-sqlite3" @@ -16,11 +16,24 @@ import ( // OpenSQLiteDatabase creates and open connection to new SQLite3 database. func OpenSQLiteDatabase(ctx context.Context, databasePath string) (sqliteDB *SQLiteDatabase, err error) { // Open database - db, err := sqlx.ConnectContext(ctx, "sqlite3", databasePath) + rwDB, err := sqlx.ConnectContext(ctx, "sqlite", databasePath) if err != nil { - return nil, errors.WithStack(err) + return nil, fmt.Errorf("error opening writer database: %w", err) + } + + rDB, err := sqlx.ConnectContext(ctx, "sqlite", databasePath) + if err != nil { + return nil, fmt.Errorf("error opening reader database: %w", err) + } + + sqliteDB = &SQLiteDatabase{ + writer: &dbbase{rwDB}, + reader: &dbbase{rDB}, + } + + if err := sqliteDB.Init(ctx); err != nil { + return nil, fmt.Errorf("error initializing database: %w", err) } - sqliteDB = &SQLiteDatabase{dbbase: dbbase{db}} return sqliteDB, nil } diff --git a/internal/domains/bookmarks_test.go b/internal/domains/bookmarks_test.go index c02f16960..ba0e9d315 100644 --- a/internal/domains/bookmarks_test.go +++ b/internal/domains/bookmarks_test.go @@ -4,9 +4,6 @@ import ( "context" "testing" - "github.com/go-shiori/shiori/internal/config" - "github.com/go-shiori/shiori/internal/database" - "github.com/go-shiori/shiori/internal/dependencies" "github.com/go-shiori/shiori/internal/domains" "github.com/go-shiori/shiori/internal/model" "github.com/go-shiori/shiori/internal/testutil" @@ -17,17 +14,10 @@ import ( func TestBookmarkDomain(t *testing.T) { fs := afero.NewMemMapFs() + ctx := context.Background() + logger := logrus.New() + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) - db, err := database.OpenSQLiteDatabase(context.TODO(), ":memory:") - require.NoError(t, err) - require.NoError(t, db.Migrate(context.TODO())) - - deps := &dependencies.Dependencies{ - Database: db, - Config: config.ParseServerConfiguration(context.TODO(), logrus.New()), - Log: logrus.New(), - Domains: &dependencies.Domains{}, - } deps.Domains.Storage = domains.NewStorageDomain(deps, fs) fs.MkdirAll("thumb", 0755) From b9db27081633f2b1bb36d577f3554335ce5fcf08 Mon Sep 17 00:00:00 2001 From: Pr0m1x Date: Fri, 3 Jan 2025 16:16:52 +0800 Subject: [PATCH 09/10] fix: incorrectly set cookie's expires value in login.js (#1049) --- internal/view/assets/js/component/login.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/view/assets/js/component/login.js b/internal/view/assets/js/component/login.js index e75925556..f8ddf7bf8 100644 --- a/internal/view/assets/js/component/login.js +++ b/internal/view/assets/js/component/login.js @@ -130,10 +130,10 @@ export default { // Save session id document.cookie = `session-id=${json.message.session}; Path=${ new URL(document.baseURI).pathname - }; Expires=${json.message.expires}`; + }; Expires=${new Date(json.message.expires * 1000).toUTCString()}`; document.cookie = `token=${json.message.token}; Path=${ new URL(document.baseURI).pathname - }; Expires=${json.message.expires}`; + }; Expires=${new Date(json.message.expires * 1000).toUTCString()}`; // Save account data localStorage.setItem("shiori-token", json.message.token); From a9a45cc2cefe635cfad75a7a0ab38085a9b80b7f Mon Sep 17 00:00:00 2001 From: Felipe Martin <812088+fmartingr@users.noreply.github.com> Date: Sun, 5 Jan 2025 09:07:24 +0100 Subject: [PATCH 10/10] test: test all supported engines on their oldest supported releases (#1050) * test: test all supported engines on their oldest supported releases * update pre-hook file * update mysql container options --- .githooks/pre-commit | 11 ++++-- .github/workflows/_test.yml | 19 +++++++++-- docker-compose.yaml | 14 ++++++-- docs/Contribute.md | 9 +++-- internal/database/mysql_test.go | 60 ++++++++++++++++++--------------- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 3039f0fdc..62924ffed 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,4 +1,11 @@ #!/bin/zsh -e -golint -set_exit_status ./... -go mod tidy -v +docker compose up -d mysql mariadb postgres + +export SHIORI_TEST_PG_URL="postgres://shiori:shiori@localhost:5432/shiori?sslmode=disable" +export SHIORI_TEST_MYSQL_URL="shiori:shiori@(localhost:3306)/shiori" +export SHIORI_TEST_MARIADB_URL="shiori:shiori@(localhost:3307)/shiori" + +make lint + +make unittest GOTESTFMT_FLAGS="-hide all" diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 47a888acb..fead9e2dd 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres:15 + image: postgres:13.18 env: POSTGRES_PASSWORD: shiori POSTGRES_USER: shiori @@ -23,7 +23,7 @@ jobs: ports: - 5432:5432 mariadb: - image: mariadb:11 + image: mariadb:10.5.27 env: MYSQL_USER: shiori MYSQL_PASSWORD: shiori @@ -33,6 +33,20 @@ jobs: --health-cmd="/usr/local/bin/healthcheck.sh --connect" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 3306:3306 + mysql: + image: mysql:8.0.40 + env: + MYSQL_USER: shiori + MYSQL_PASSWORD: shiori + MYSQL_DATABASE: shiori + MYSQL_ROOT_PASSWORD: shiori + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + ports: + - 3307:3306 name: Go unit tests (ubuntu-latest) steps: @@ -60,6 +74,7 @@ jobs: env: SHIORI_TEST_PG_URL: "postgres://shiori:shiori@localhost:5432/shiori?sslmode=disable" SHIORI_TEST_MYSQL_URL: "shiori:shiori@(localhost:3306)/shiori" + SHIORI_TEST_MARIADB_URL: "shiori:shiori@(localhost:3307)/shiori" CGO_ENABLED: 1 # go test -race requires cgo - run: go build -tags osusergo,netgo -ldflags="-s -w -X main.version=$(git describe --tags) -X main.date=$(date --iso-8601=seconds)" diff --git a/docker-compose.yaml b/docker-compose.yaml index 444ca2789..dd59903af 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -36,7 +36,7 @@ services: - shiori postgres: - image: postgres:15 + image: postgres:13.18 environment: POSTGRES_PASSWORD: shiori POSTGRES_USER: shiori @@ -44,7 +44,7 @@ services: - "5432:5432" mariadb: - image: mariadb:11 + image: mariadb:10.5.27 environment: MYSQL_ROOT_PASSWORD: toor MYSQL_DATABASE: shiori @@ -53,5 +53,15 @@ services: ports: - "3306:3306" + mysql: + image: mysql:8.0.40 + environment: + MYSQL_ROOT_PASSWORD: toor + MYSQL_DATABASE: shiori + MYSQL_USER: shiori + MYSQL_PASSWORD: shiori + ports: + - "3307:3306" + volumes: go-mod-cache: diff --git a/docs/Contribute.md b/docs/Contribute.md index 6cef5a55a..023f30a52 100644 --- a/docs/Contribute.md +++ b/docs/Contribute.md @@ -57,14 +57,19 @@ In order to run the test suite, you need to have running a local instance of Mar If you have docker, you can do this by running the following command with the compose file provided: ```bash -docker-compose up -d mariadb postgres +docker-compose up -d mariadb mysql postgres ``` -After that, provide the `SHIORI_TEST_PG_URL` and `SHIORI_TEST_MYSQL_URL` environment variables with the connection string to the databases: +After that, provide the environment variables for the unitest to connect to the database engines: + +- `SHIORI_TEST_MYSQL_URL` for MySQL +- `SHIORI_TEST_MARIADB_URL` for MariaDB +- `SHIORI_TEST_PG_URL` for PostgreSQL ``` SHIORI_TEST_PG_URL=postgres://shiori:shiori@127.0.0.1:5432/shiori?sslmode=disable SHIORI_TEST_MYSQL_URL=shiori:shiori@tcp(127.0.0.1:3306)/shiori +SHIORI_TEST_MARIADB_URL=shiori:shiori@tcp(127.0.0.1:3307)/shiori ``` Finally, run the tests with the following command: diff --git a/internal/database/mysql_test.go b/internal/database/mysql_test.go index 5ee4e3587..dd1daf780 100644 --- a/internal/database/mysql_test.go +++ b/internal/database/mysql_test.go @@ -19,43 +19,49 @@ func init() { } } -func mysqlTestDatabaseFactory(_ *testing.T, ctx context.Context) (DB, error) { - connString := os.Getenv("SHIORI_TEST_MYSQL_URL") - db, err := OpenMySQLDatabase(ctx, connString) - if err != nil { - return nil, err - } - - var dbname string - err = db.withTx(ctx, func(tx *sqlx.Tx) error { - err := tx.QueryRow("SELECT DATABASE()").Scan(&dbname) +func mysqlTestDatabaseFactory(envKey string) testDatabaseFactory { + return func(_ *testing.T, ctx context.Context) (DB, error) { + connString := os.Getenv(envKey) + db, err := OpenMySQLDatabase(ctx, connString) if err != nil { - return err + return nil, err } - _, err = tx.ExecContext(ctx, "DROP DATABASE IF EXISTS "+dbname) - if err != nil { + var dbname string + err = db.withTx(ctx, func(tx *sqlx.Tx) error { + err := tx.QueryRow("SELECT DATABASE()").Scan(&dbname) + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, "DROP DATABASE IF EXISTS "+dbname) + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, "CREATE DATABASE "+dbname) return err + }) + if err != nil { + return nil, err } - _, err = tx.ExecContext(ctx, "CREATE DATABASE "+dbname) - return err - }) - if err != nil { - return nil, err - } + if _, err := db.Exec("USE " + dbname); err != nil { + return nil, err + } - if _, err := db.Exec("USE " + dbname); err != nil { - return nil, err - } + if err = db.Migrate(context.TODO()); err != nil { + return nil, err + } - if err = db.Migrate(context.TODO()); err != nil { - return nil, err + return db, err } - - return db, err } func TestMysqlsDatabase(t *testing.T) { - testDatabase(t, mysqlTestDatabaseFactory) + testDatabase(t, mysqlTestDatabaseFactory("SHIORI_TEST_MYSQL_URL")) +} + +func TestMariaDBDatabase(t *testing.T) { + testDatabase(t, mysqlTestDatabaseFactory("SHIORI_TEST_MARIADB_URL")) }