Compare commits

...

537 commits

Author SHA1 Message Date
Ravinou
0a10003b43 chore: 🧹 remove old package-lock.yml (pnpm migration) 2026-03-01 10:46:44 +01:00
Ravinou
3316be7b3c feat: default select first repo in wizard 2026-03-01 10:46:44 +01:00
Ravinou
3772949fdb chore: 🧹 update packages 2026-03-01 10:15:49 +01:00
Ravinou
80b440e2b9 chore: 🧹 update packages 2026-03-01 09:31:36 +01:00
dependabot[bot]
d7487fbc4a chore(deps-dev): bump @types/node from 25.0.6 to 25.0.9
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.0.6 to 25.0.9.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.0.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-24 13:34:02 +01:00
dependabot[bot]
4b76223097 chore(deps): bump next from 16.1.1 to 16.1.3
Bumps [next](https://github.com/vercel/next.js) from 16.1.1 to 16.1.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.1.1...v16.1.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-24 13:33:51 +01:00
dependabot[bot]
735a043858 chore(deps-dev): bump eslint-config-next from 16.1.1 to 16.1.3
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 16.1.1 to 16.1.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v16.1.3/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 16.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-24 12:50:42 +01:00
Ravinou
0f2a4a716f chore: 🧹 update packages 2026-01-18 13:01:24 +01:00
Ravinou
29ca7dfeec feat: switches borg serve restriction to use --restrict-to-repository 2026-01-18 12:27:38 +01:00
dependabot[bot]
1ddaa0f648 chore(deps): bump nodemailer and @types/nodemailer
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) and [@types/nodemailer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/nodemailer). These dependencies needed to be updated together.

Updates `nodemailer` from 7.0.11 to 7.0.12
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.11...v7.0.12)

Updates `@types/nodemailer` from 7.0.4 to 7.0.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/nodemailer)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 7.0.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/nodemailer"
  dependency-version: 7.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-18 11:21:27 +01:00
Ravinou
577fdbcd68 docker: 🐳 improve logs 2026-01-18 10:14:14 +01:00
dependabot[bot]
836450e8db chore(deps-dev): bump @types/react from 19.2.7 to 19.2.8
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.7 to 19.2.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-18 10:02:19 +01:00
dependabot[bot]
efd7083463 chore(deps): bump react-hook-form from 7.68.0 to 7.71.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.68.0 to 7.71.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.68.0...v7.71.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.71.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-18 10:01:45 +01:00
dependabot[bot]
187f9839f3 chore(deps-dev): bump vitest from 4.0.16 to 4.0.17
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.0.16 to 4.0.17.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.17/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.0.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-18 10:01:17 +01:00
dependabot[bot]
afa5c9bb1f chore(deps-dev): bump @types/node from 25.0.3 to 25.0.6
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.0.3 to 25.0.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.0.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-18 09:58:52 +01:00
Ravinou
a1bd3deb1d fix: 🐛 trim ssh public key value in textarea #599 2026-01-09 13:21:36 +01:00
dependabot[bot]
8f7be1d14e chore(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 20.2.0 to 20.3.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.3.1/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 20.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 12:53:46 +01:00
dependabot[bot]
42cca2763f chore(deps-dev): bump vitest from 4.0.15 to 4.0.16
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.0.15 to 4.0.16.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.0.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 12:51:33 +01:00
dependabot[bot]
916b562f73 chore(deps-dev): bump eslint-config-next from 16.0.10 to 16.1.1
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 16.0.10 to 16.1.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v16.1.1/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 16.1.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 12:51:21 +01:00
dependabot[bot]
e34037ebce chore(deps-dev): bump @commitlint/cli from 20.3.0 to 20.3.1
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 20.3.0 to 20.3.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.3.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 12:51:09 +01:00
dependabot[bot]
c819521b05 chore(deps): bump swr from 2.3.7 to 2.3.8
Bumps [swr](https://github.com/vercel/swr) from 2.3.7 to 2.3.8.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.3.7...v2.3.8)

---
updated-dependencies:
- dependency-name: swr
  dependency-version: 2.3.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 12:50:59 +01:00
Ravinou
c5e99cabe5 chore: 🧹 pipeline improvements 2026-01-08 15:34:17 +01:00
Ravinou
079bf35d87 chore: 🧹 migrate from npm to pnpm package manager 2026-01-08 13:58:08 +01:00
Hobbabobba
14dc526177 typo 2026-01-08 13:01:58 +01:00
Hobbabobba
9d6211b0aa adjusted to new version of borgmatic
example configuration of borgmatic adjusted
2026-01-08 13:01:58 +01:00
Ravinou
3fcd39cf40
Merge pull request #602 from Ravinou/dependabot/npm_and_yarn/types/bcryptjs-3.0.0
chore(deps-dev): bump @types/bcryptjs from 2.4.6 to 3.0.0
2026-01-08 12:56:08 +01:00
Ravinou
85b9ccaf04
🥇 Thanking a new sponsor 🥇
Thank you very much @daschmidt1994 for sponsoring ! This commit is dedicated to thanking you. You give me strength to continue the work! ❤️
2026-01-08 12:54:56 +01:00
dependabot[bot]
d944a70497
chore(deps-dev): bump @types/bcryptjs from 2.4.6 to 3.0.0
Bumps [@types/bcryptjs](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/bcryptjs) from 2.4.6 to 3.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/bcryptjs)

---
updated-dependencies:
- dependency-name: "@types/bcryptjs"
  dependency-version: 3.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-08 11:44:03 +00:00
Ravinou
961d30354d
Merge pull request #612 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-20.3.0
chore(deps-dev): bump @commitlint/cli from 20.2.0 to 20.3.0
2026-01-08 12:36:38 +01:00
Ravinou
ccfb21a790
Merge pull request #611 from Ravinou/dependabot/npm_and_yarn/types/node-25.0.3
chore(deps-dev): bump @types/node from 24.10.2 to 25.0.3
2026-01-08 12:36:07 +01:00
Ravinou
3925c8ab8c
Merge pull request #613 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.36.1
chore(deps): bump @tabler/icons-react from 3.35.0 to 3.36.1
2026-01-08 12:35:42 +01:00
Ravinou
1faa710105
Merge pull request #614 from Ravinou/dependabot/npm_and_yarn/next-16.1.1
chore(deps): bump next from 16.0.7 to 16.1.1
2026-01-08 12:35:18 +01:00
dependabot[bot]
2db5e65f9a
chore(deps): bump next from 16.0.7 to 16.1.1
Bumps [next](https://github.com/vercel/next.js) from 16.0.7 to 16.1.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.0.7...v16.1.1)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-02 19:07:46 +00:00
dependabot[bot]
5bb148459c
chore(deps): bump @tabler/icons-react from 3.35.0 to 3.36.1
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.35.0 to 3.36.1.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.36.1/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-version: 3.36.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-02 19:07:29 +00:00
dependabot[bot]
9981b81cf1
chore(deps-dev): bump @commitlint/cli from 20.2.0 to 20.3.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 20.2.0 to 20.3.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.3.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-02 19:07:21 +00:00
dependabot[bot]
a240f66e90
chore(deps-dev): bump @types/node from 24.10.2 to 25.0.3
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.2 to 25.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-02 19:07:09 +00:00
Ravinou
12be382b75
Merge pull request #606 from Ravinou/dependabot/npm_and_yarn/eslint-9.39.2
chore(deps-dev): bump eslint from 9.39.1 to 9.39.2
2026-01-02 11:59:31 +01:00
Ravinou
311fb04c34
Merge pull request #610 from Ravinou/dependabot/npm_and_yarn/react-dom-19.2.3
chore(deps): bump react-dom from 19.2.1 to 19.2.3
2026-01-02 11:59:07 +01:00
Ravinou
abe1fd74e0
Merge pull request #603 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-20.2.0
chore(deps-dev): bump @commitlint/cli from 20.1.0 to 20.2.0
2026-01-02 11:58:45 +01:00
dependabot[bot]
4d3336ff97
chore(deps): bump react-dom from 19.2.1 to 19.2.3
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 19.2.1 to 19.2.3.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.3/packages/react-dom)

---
updated-dependencies:
- dependency-name: react-dom
  dependency-version: 19.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 19:07:05 +00:00
dependabot[bot]
9e6df1e5b3
chore(deps-dev): bump @commitlint/cli from 20.1.0 to 20.2.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 20.1.0 to 20.2.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.2.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 17:57:55 +00:00
dependabot[bot]
897152d7fc
chore(deps-dev): bump eslint from 9.39.1 to 9.39.2
Bumps [eslint](https://github.com/eslint/eslint) from 9.39.1 to 9.39.2.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.1...v9.39.2)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 17:57:09 +00:00
Ravinou
3ccf5abc3d
Merge pull request #601 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-20.2.0
chore(deps-dev): bump @commitlint/config-conventional from 20.0.0 to 20.2.0
2025-12-29 18:56:38 +01:00
Ravinou
02cd028c40
Merge pull request #607 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-16.0.10
chore(deps-dev): bump eslint-config-next from 16.0.7 to 16.0.10
2025-12-29 18:55:54 +01:00
Ravinou
5636142104
Merge pull request #598 from Ravinou/dependabot/github_actions/actions/checkout-6
chore(deps): bump actions/checkout from 4 to 6
2025-12-29 18:55:41 +01:00
Ravinou
7c1790eb8c
Merge pull request #581 from Ravinou/dependabot/github_actions/actions/setup-node-6
chore(deps): bump actions/setup-node from 4 to 6
2025-12-29 18:55:19 +01:00
dependabot[bot]
904fa5928b
chore(deps-dev): bump eslint-config-next from 16.0.7 to 16.0.10
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 16.0.7 to 16.0.10.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v16.0.10/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 16.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 19:09:11 +00:00
dependabot[bot]
446e75d7aa
chore(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 20.0.0 to 20.2.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v20.2.0/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 20.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 12:15:18 +00:00
Ravinou
cf7b43625c
Merge pull request #605 from Ravinou/dependabot/npm_and_yarn/types/node-24.10.2
chore(deps-dev): bump @types/node from 24.10.1 to 24.10.2
2025-12-15 13:14:11 +01:00
dependabot[bot]
d201b290e9
chore(deps-dev): bump @types/node from 24.10.1 to 24.10.2
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.1 to 24.10.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.10.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-09 19:09:28 +00:00
dependabot[bot]
7b8c4d6017
chore(deps): bump actions/setup-node from 4 to 6
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-07 16:38:16 +00:00
dependabot[bot]
13872ed29e
chore(deps): bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-07 16:37:41 +00:00
Ravinou
90506c0827
🥇 Thanking a new sponsor 🥇
Thank you very much @shrippen for sponsoring ! This commit is dedicated to thanking you. You give me strength to continue the work! ❤️
2025-12-04 22:14:06 +01:00
Ravinou
9c267e75ec
publish: 📦 version 3.1.2 2025-12-04 21:59:16 +01:00
Ravinou
d9a8ecf70b
Merge pull request #600 from Ravinou/updates
chore: 🧹 update dependencies
2025-12-04 21:56:07 +01:00
Ravinou
2ce7232849
refactor: migrates to the new ESLint configuration 2025-12-04 21:47:29 +01:00
Ravinou
3ff2ace3bb
chore: 🧹 update dependencies 2025-12-04 20:47:39 +01:00
Ravinou
d5e8064348
Merge pull request #573 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.63.0
chore(deps): bump react-hook-form from 7.60.0 to 7.63.0
2025-10-02 20:05:06 +02:00
dependabot[bot]
dbe7b4081e
chore(deps): bump react-hook-form from 7.60.0 to 7.63.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.60.0 to 7.63.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.60.0...v7.63.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.63.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 19:10:02 +00:00
Ravinou
6a3625ef98
Merge pull request #534 from Ravinou/dependabot/npm_and_yarn/react-select-5.10.2
chore(deps): bump react-select from 5.10.1 to 5.10.2
2025-09-20 13:23:29 +02:00
Ravinou
f1731d769d
Merge pull request #563 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.5.3
chore(deps-dev): bump eslint-config-next from 15.3.4 to 15.5.3
2025-09-20 13:22:45 +02:00
dependabot[bot]
63216622a6
chore(deps-dev): bump eslint-config-next from 15.3.4 to 15.5.3
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.3.4 to 15.5.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.5.3/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 15.5.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-11 19:09:26 +00:00
dependabot[bot]
f5af821d47
chore(deps): bump react-select from 5.10.1 to 5.10.2
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.10.1 to 5.10.2.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.10.1...react-select@5.10.2)

---
updated-dependencies:
- dependency-name: react-select
  dependency-version: 5.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 21:58:11 +00:00
Ravinou
340f186a37
Merge pull request #525 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.60.0
chore(deps): bump react-hook-form from 7.59.0 to 7.60.0
2025-07-13 09:09:51 +02:00
Ravinou
53b29ea6f9
Merge pull request #526 from Ravinou/dependabot/npm_and_yarn/swr-2.3.4
chore(deps): bump swr from 2.3.3 to 2.3.4
2025-07-13 09:09:40 +02:00
dependabot[bot]
665974a15a
chore(deps): bump swr from 2.3.3 to 2.3.4
Bumps [swr](https://github.com/vercel/swr) from 2.3.3 to 2.3.4.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.3.3...v2.3.4)

---
updated-dependencies:
- dependency-name: swr
  dependency-version: 2.3.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 23:03:00 +00:00
dependabot[bot]
ac3c4c7b1d
chore(deps): bump react-hook-form from 7.59.0 to 7.60.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.59.0 to 7.60.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.59.0...v7.60.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 22:14:32 +00:00
Ravinou
2d094cfed9
Merge pull request #518 from Ravinou/dependabot/npm_and_yarn/prettier-3.6.2
chore(deps-dev): bump prettier from 3.5.3 to 3.6.2
2025-07-06 20:46:19 +02:00
Ravinou
aeb299ab3b
🥇 Thanking a new sponsor 🥇
Thank you very much @MacH59-cos for sponsoring ! This commit is dedicated to thanking you. You give me strength to continue the work! ❤️
2025-07-05 22:34:48 +02:00
Ravinou
53079bebce
Merge pull request #523 from Ravinou/3.1.1
3.1.1
2025-07-05 22:10:17 +02:00
Ravinou
5a96fb88b6
publish: 📦 version 3.1.1 2025-07-05 21:55:15 +02:00
Ravinou
b988b81089
chore: 🧹 update dependencies 2025-07-05 21:52:01 +02:00
Ravinou
b3d517d791
fix: 🐛 follows symbolic links when calculating storage #478 2025-07-05 21:50:14 +02:00
Ravinou
5ee55d18df
ui: 🎨 improve some label #497 2025-07-05 21:44:26 +02:00
Ravinou
eed58c129e
Merge pull request #520 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.59.0
chore(deps): bump react-hook-form from 7.58.1 to 7.59.0
2025-07-05 19:50:08 +02:00
dependabot[bot]
d5a440448e
chore(deps): bump react-hook-form from 7.58.1 to 7.59.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.58.1 to 7.59.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.58.1...v7.59.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 01:24:45 +00:00
dependabot[bot]
b6cc2c351e
chore(deps-dev): bump prettier from 3.5.3 to 3.6.2
Bumps [prettier](https://github.com/prettier/prettier) from 3.5.3 to 3.6.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.5.3...3.6.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.6.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-30 22:43:40 +00:00
Ravinou
b38ffd7be7
Merge pull request #513 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.3.4
chore(deps-dev): bump eslint-config-next from 15.3.3 to 15.3.4
2025-06-29 14:52:08 +02:00
Ravinou
4009b0cf8b
Merge pull request #514 from Ravinou/dependabot/npm_and_yarn/next-15.3.4
chore(deps): bump next from 15.3.3 to 15.3.4
2025-06-29 14:51:55 +02:00
dependabot[bot]
599fe35ddc
chore(deps): bump next from 15.3.3 to 15.3.4
Bumps [next](https://github.com/vercel/next.js) from 15.3.3 to 15.3.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.3.3...v15.3.4)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.3.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-23 21:10:01 +00:00
dependabot[bot]
97b31e6bae
chore(deps-dev): bump eslint-config-next from 15.3.3 to 15.3.4
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.3.3 to 15.3.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.3.4/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 15.3.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-23 20:31:29 +00:00
Ravinou
0c6e24f599
Merge pull request #505 from Ravinou/dependabot/npm_and_yarn/types/react-19.1.8
chore(deps-dev): bump @types/react from 19.1.5 to 19.1.8
2025-06-22 10:13:55 +02:00
Ravinou
980399d238
Merge pull request #511 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.58.1
chore(deps): bump react-hook-form from 7.57.0 to 7.58.1
2025-06-22 10:13:38 +02:00
dependabot[bot]
4189cc34ee
chore(deps): bump react-hook-form from 7.57.0 to 7.58.1
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.57.0 to 7.58.1.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.57.0...v7.58.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.58.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-17 19:59:40 +00:00
dependabot[bot]
6565442042
chore(deps-dev): bump @types/react from 19.1.5 to 19.1.8
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.5 to 19.1.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-11 19:45:10 +00:00
Ravinou
4e7b880624
Merge pull request #490 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.34.0
chore(deps): bump @tabler/icons-react from 3.33.0 to 3.34.0
2025-06-08 10:33:02 +02:00
Ravinou
c2912253df
Merge pull request #493 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.57.0
chore(deps): bump react-hook-form from 7.56.4 to 7.57.0
2025-06-08 10:32:47 +02:00
Ravinou
4f011f6c48
Merge pull request #496 from Ravinou/dependabot/npm_and_yarn/types/node-22.15.30
chore(deps-dev): bump @types/node from 22.15.21 to 22.15.30
2025-06-08 10:32:01 +02:00
dependabot[bot]
8202bcd2ad
chore(deps-dev): bump @types/node from 22.15.21 to 22.15.30
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.21 to 22.15.30.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.30
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-05 19:46:39 +00:00
dependabot[bot]
1ea2174f9f
chore(deps): bump react-hook-form from 7.56.4 to 7.57.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.56.4 to 7.57.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.56.4...v7.57.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.57.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 19:34:44 +00:00
dependabot[bot]
e3a973c09c
chore(deps): bump @tabler/icons-react from 3.33.0 to 3.34.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.33.0 to 3.34.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.34.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-version: 3.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 19:14:25 +00:00
Ravinou
57e4550e42
publish: 📦 3.1.0 2025-06-01 13:23:31 +02:00
Ravinou
ceeb7a3e6a
Merge pull request #483 from Ravinou/dependabot/npm_and_yarn/vitest-3.1.4
chore(deps-dev): bump vitest from 3.1.3 to 3.1.4
2025-06-01 13:17:43 +02:00
Ravinou
3df990f217
Merge pull request #484 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.56.4
chore(deps): bump react-hook-form from 7.56.3 to 7.56.4
2025-06-01 13:17:30 +02:00
Ravinou
ca9097a2aa
Merge pull request #486 from Ravinou/dependabot/npm_and_yarn/next-15.3.3
chore(deps): bump next from 15.3.1 to 15.3.3
2025-06-01 13:17:18 +02:00
Ravinou
aa8e493e37
Merge pull request #487 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.3.3
chore(deps-dev): bump eslint-config-next from 15.3.1 to 15.3.3
2025-06-01 13:17:01 +02:00
Ravinou
b6652a80f5
Merge pull request #489 from Ravinou/develop
Develop
2025-06-01 12:48:41 +02:00
Ravinou
88c8f920d9
fix: 🐛 handle empty SSH_SERVER_PORT with quick command button #470 2025-06-01 12:28:04 +02:00
Ravinou
3c1ff79add
feat: new toolbar and searching on repository list 2025-06-01 12:19:00 +02:00
Ravinou
6acfdbbfa1
refactor: username is now allowed from 1 to 40 char. #479 2025-06-01 11:02:31 +02:00
Ravinou
0263edd44f
ui: 🎨 adapting ui to allow alias with 100 char #485 2025-06-01 10:51:52 +02:00
Ravinou
05ba852371
Merge pull request #488 from Ravinou/develop
fix: 🐛 prevent shell injection by replacing exec with exeFile
2025-05-31 17:30:12 +02:00
dependabot[bot]
f15fbbcb6d
chore(deps-dev): bump eslint-config-next from 15.3.1 to 15.3.3
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.3.1 to 15.3.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.3.3/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 15.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-29 19:57:06 +00:00
dependabot[bot]
3f47efc572
chore(deps): bump next from 15.3.1 to 15.3.3
Bumps [next](https://github.com/vercel/next.js) from 15.3.1 to 15.3.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.3.1...v15.3.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-29 19:56:39 +00:00
Ravinou
1feb666c4c
fix: 🐛 (security) prevent shell injection by replacing exec with execFile 2025-05-29 11:40:45 +02:00
dependabot[bot]
9921d9d40d
chore(deps): bump react-hook-form from 7.56.3 to 7.56.4
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.56.3 to 7.56.4.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.56.3...v7.56.4)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.56.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 19:45:13 +00:00
dependabot[bot]
64fcafdfa0
chore(deps-dev): bump vitest from 3.1.3 to 3.1.4
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v3.1.4/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 3.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 19:44:25 +00:00
Ravinou
5e420d04ca
Merge pull request #471 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.33.0
chore(deps): bump @tabler/icons-react from 3.31.0 to 3.33.0
2025-05-25 18:54:04 +02:00
Ravinou
220f88bd6d
Merge pull request #472 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.8.1
chore(deps-dev): bump @commitlint/config-conventional from 19.8.0 to 19.8.1
2025-05-25 18:53:51 +02:00
Ravinou
cb64164b01
Merge pull request #476 from Ravinou/dependabot/npm_and_yarn/types/node-22.15.21
chore(deps-dev): bump @types/node from 22.15.18 to 22.15.21
2025-05-25 18:53:39 +02:00
Ravinou
6680f48253
Merge pull request #477 from Ravinou/dependabot/npm_and_yarn/types/react-19.1.5
chore(deps-dev): bump @types/react from 19.1.3 to 19.1.5
2025-05-25 18:53:27 +02:00
dependabot[bot]
d808f464ad
chore(deps-dev): bump @types/react from 19.1.3 to 19.1.5
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.3 to 19.1.5.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:51:25 +00:00
dependabot[bot]
3337e9b97a
chore(deps-dev): bump @types/node from 22.15.18 to 22.15.21
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.18 to 22.15.21.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.21
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:51:04 +00:00
dependabot[bot]
7edfc75379
chore(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.8.0 to 19.8.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.8.1/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 19.8.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 19:56:26 +00:00
dependabot[bot]
e2c74d067b
chore(deps): bump @tabler/icons-react from 3.31.0 to 3.33.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.31.0 to 3.33.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.33.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-version: 3.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 19:56:18 +00:00
Ravinou
250bf4ef0c
Merge pull request #455 from Ravinou/dependabot/npm_and_yarn/node-mocks-http-1.17.2
chore(deps-dev): bump node-mocks-http from 1.17.1 to 1.17.2
2025-05-18 12:08:16 +02:00
Ravinou
26c784900a
Merge pull request #462 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.8.1
chore(deps-dev): bump @commitlint/cli from 19.8.0 to 19.8.1
2025-05-18 12:08:00 +02:00
Ravinou
092a1d8d3d
Merge pull request #469 from Ravinou/dependabot/npm_and_yarn/types/node-22.15.18
chore(deps-dev): bump @types/node from 22.15.3 to 22.15.18
2025-05-18 12:07:49 +02:00
Ravinou
4514d6b7f2
Merge pull request #460 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.56.3
chore(deps): bump react-hook-form from 7.56.1 to 7.56.3
2025-05-18 12:07:36 +02:00
Ravinou
754eef3b41
Merge pull request #464 from Forceu/betterDates
Changed "last changes" to a more human-readable format
2025-05-18 12:06:56 +02:00
Marc Bulling
db4749479f
Merge branch 'main' into betterDates 2025-05-18 11:40:23 +02:00
Marc Ole Bulling
515535a5b3
ui: 🎨 changed "last changes" to a more human-readable format 2025-05-18 11:31:41 +02:00
Ravinou
aa85cee260
Merge pull request #465 from Forceu/noIds
Move ID from own row in overview to repo name row
2025-05-18 11:15:06 +02:00
Ravinou
62aedfa1e1
ui: 🎨 replace front-end "id" information with repositoryName 2025-05-18 11:02:25 +02:00
Marc Ole Bulling
0f03b26a63
Move ID to repository name 2025-05-15 23:13:40 +02:00
dependabot[bot]
b4c62818e0
chore(deps-dev): bump @types/node from 22.15.3 to 22.15.18
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.3 to 22.15.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 19:58:50 +00:00
dependabot[bot]
964f011a8a
chore(deps-dev): bump @commitlint/cli from 19.8.0 to 19.8.1
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.8.0 to 19.8.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.8.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 19.8.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-09 19:13:47 +00:00
dependabot[bot]
bcd7d25cd2
chore(deps-dev): bump node-mocks-http from 1.17.1 to 1.17.2
Bumps [node-mocks-http](https://github.com/eugef/node-mocks-http) from 1.17.1 to 1.17.2.
- [Release notes](https://github.com/eugef/node-mocks-http/releases)
- [Changelog](https://github.com/eugef/node-mocks-http/blob/master/HISTORY.md)
- [Commits](https://github.com/eugef/node-mocks-http/compare/v1.17.1...v1.17.2)

---
updated-dependencies:
- dependency-name: node-mocks-http
  dependency-version: 1.17.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 20:12:00 +00:00
Ravinou
f792e65b88
Merge pull request #457 from Ravinou/dependabot/npm_and_yarn/vitest-3.1.3
chore(deps-dev): bump vitest from 3.1.2 to 3.1.3
2025-05-08 22:11:06 +02:00
Ravinou
a01980a323
Merge pull request #458 from Ravinou/dependabot/npm_and_yarn/types/react-19.1.3
chore(deps-dev): bump @types/react from 19.1.2 to 19.1.3
2025-05-08 22:10:46 +02:00
dependabot[bot]
4b1c8c5930
chore(deps): bump react-hook-form from 7.56.1 to 7.56.3
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.56.1 to 7.56.3.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.56.1...v7.56.3)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.56.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 20:00:51 +00:00
dependabot[bot]
29e748e0d6
chore(deps-dev): bump @types/react from 19.1.2 to 19.1.3
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.2 to 19.1.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-06 19:30:42 +00:00
dependabot[bot]
680e826a4f
chore(deps-dev): bump vitest from 3.1.2 to 3.1.3
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v3.1.3/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 3.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 19:42:08 +00:00
Ravinou
fd0ba8baaa
Merge pull request #453 from Ravinou/dependabot/npm_and_yarn/node-mocks-http-1.17.1
chore(deps-dev): bump node-mocks-http from 1.17.0 to 1.17.1
2025-05-03 18:03:37 +02:00
dependabot[bot]
c65b8a9e5e
chore(deps-dev): bump node-mocks-http from 1.17.0 to 1.17.1
Bumps [node-mocks-http](https://github.com/eugef/node-mocks-http) from 1.17.0 to 1.17.1.
- [Release notes](https://github.com/eugef/node-mocks-http/releases)
- [Changelog](https://github.com/eugef/node-mocks-http/blob/master/HISTORY.md)
- [Commits](https://github.com/eugef/node-mocks-http/compare/v1.17.0...v1.17.1)

---
updated-dependencies:
- dependency-name: node-mocks-http
  dependency-version: 1.17.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-02 19:27:51 +00:00
Ravinou
73ecb4bf72
Merge pull request #452 from Ravinou/dependabot/npm_and_yarn/types/node-22.15.3
chore(deps-dev): bump @types/node from 22.15.2 to 22.15.3
2025-05-02 11:09:19 +02:00
Ravinou
720328db0c
Merge pull request #451 from Ravinou/dependabot/npm_and_yarn/node-mocks-http-1.17.0
chore(deps-dev): bump node-mocks-http from 1.16.2 to 1.17.0
2025-05-02 11:09:00 +02:00
dependabot[bot]
cb6e1413d7
chore(deps-dev): bump @types/node from 22.15.2 to 22.15.3
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.2 to 22.15.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 20:41:39 +00:00
dependabot[bot]
10e13763fa
chore(deps-dev): bump node-mocks-http from 1.16.2 to 1.17.0
Bumps [node-mocks-http](https://github.com/eugef/node-mocks-http) from 1.16.2 to 1.17.0.
- [Release notes](https://github.com/eugef/node-mocks-http/releases)
- [Changelog](https://github.com/eugef/node-mocks-http/blob/master/HISTORY.md)
- [Commits](https://github.com/eugef/node-mocks-http/compare/v1.16.2...v1.17.0)

---
updated-dependencies:
- dependency-name: node-mocks-http
  dependency-version: 1.17.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 20:20:56 +00:00
Ravinou
49e1f8c2a2
publish: 📦 version 3.0.0 2025-04-27 13:41:32 +02:00
Ravinou
d2acc5bba0
Update issue templates 2025-04-27 11:32:18 +02:00
Ravinou
05f532c10d
config: github issue template 2025-04-27 11:27:33 +02:00
Ravinou
8be42035e7
config: github issue template
Update issue templates
2025-04-27 11:18:05 +02:00
Ravinou
33a0de52e3
Update issue templates 2025-04-27 11:16:53 +02:00
Ravinou
18925266f8
Merge pull request #448 from Ravinou/dependabot/npm_and_yarn/types/node-22.15.2
chore(deps-dev): bump @types/node from 22.14.1 to 22.15.2
2025-04-27 09:03:33 +02:00
Ravinou
933f5931e2
Merge pull request #447 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.56.1
chore(deps): bump react-hook-form from 7.56.0 to 7.56.1
2025-04-27 09:03:16 +02:00
dependabot[bot]
d50750033e
chore(deps-dev): bump @types/node from 22.14.1 to 22.15.2
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.14.1 to 22.15.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-25 19:09:28 +00:00
dependabot[bot]
f0783a3027
chore(deps): bump react-hook-form from 7.56.0 to 7.56.1
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.56.0 to 7.56.1.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.56.0...v7.56.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.56.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-23 19:39:52 +00:00
Ravinou
939f56137b
doc: 📚 update README 2025-04-21 16:23:19 +02:00
Ravinou
05a76a5f2a
Merge pull request #446 from Ravinou/develop
React v19 + new loader on app
2025-04-21 15:50:51 +02:00
Ravinou
e4f694d383
refactor: watcher on login form is not used anymore 2025-04-21 13:55:32 +02:00
Ravinou
25022778b9
fix: 🐛 little css warning for flex-start 2025-04-21 13:53:21 +02:00
Ravinou
b1cee4486b
chore: 🧹 update toastify to latest major 2025-04-21 13:48:08 +02:00
Ravinou
e6310a5412
chore: 🧹 react to v19 + update dependencies 2025-04-21 13:43:31 +02:00
Ravinou
a0924a8ba9
chore: 🧹 remove spinners-react lib 2025-04-21 13:36:10 +02:00
Ravinou
daa199dfb0
refactor: remove spinners for nprogress 2025-04-21 13:34:41 +02:00
Ravinou
799bf03cd5
feat: add a new progress bar layout 2025-04-21 10:54:10 +02:00
Ravinou
d601c6dadf
release 3.0.0 - breaking changes 2025-04-20 23:25:12 +02:00
Ravinou
d6245d65c4
chore: 🧹 update dependencies 2025-04-20 23:08:43 +02:00
Ravinou
56ff17853a
refactor: improves repo deletion error handling 2025-04-20 23:08:43 +02:00
Ravinou
1e4a34edce
refactor: improves User Settings component 2025-04-20 23:08:42 +02:00
Ravinou
afe828fc1a
refactor: update cron message to be consistent 2025-04-20 23:08:42 +02:00
Ravinou
5567cddfdb
refactor: improves error handling in repository management 2025-04-20 23:08:41 +02:00
Ravinou
766a63d524
refactor: email template attachement path for typescript build 2025-04-20 23:08:40 +02:00
Ravinou
a62e55b42a
refactor: repoManage component now use repositoryName API 2025-04-20 23:08:40 +02:00
Ravinou
a3d156bdbf
refactor: next-auth does not support api versioning 2025-04-20 23:08:39 +02:00
Ravinou
144bea3947
config: 🔧 add ESlint to github action CI 2025-04-20 23:08:39 +02:00
Ravinou
6f24a63077
chore: 🧹 eslint fixes 2025-04-20 23:08:38 +02:00
Ravinou
58f55fa9fc
refactor: update new api url to components 2025-04-20 23:08:37 +02:00
Ravinou
fb61846bbb
test: add test to lanCommandOption util 2025-04-20 23:08:37 +02:00
Ravinou
4de1884de8
!breaking: 💣 repositoryName is now used instead of id #343
For every repository actions, to be idempotent
2025-04-20 23:08:36 +02:00
Ravinou
c5e206a818
refactor: versioning API 2025-04-20 23:08:36 +02:00
Ravinou
f7faada494
refactor: repositories API rest compliant 2025-04-20 23:08:35 +02:00
Ravinou
3d66ff18e6
refactor: cron API rest compliant 2025-04-20 23:08:34 +02:00
Ravinou
03e4b175df
refactor: account API rest compliant 2025-04-20 23:08:34 +02:00
Ravinou
0ee771f64a
refactor: email API rest compliant 2025-04-20 23:08:33 +02:00
Ravinou
533bfce0d0
refactor: apprise API rest compliant 2025-04-20 23:08:33 +02:00
Ravinou
fec9ba21ad
feat: prevent the cronjob from being executed multiple times 2025-04-20 23:08:32 +02:00
Ravinou
a9dadb9a53
fix: 🐛 handle Apprise to fail gracefully on check status 2025-04-20 23:08:31 +02:00
Ravinou
ae27636dac
ui: 🎨 repo manage dialog 2025-04-20 23:08:31 +02:00
Ravinou
86133a64b0
ui: 🎨 log more info through toast 2025-04-20 23:08:30 +02:00
Ravinou
db36c806b6
fix: 🐛 wizardEnv fetching 2025-04-20 23:08:30 +02:00
Ravinou
ff25907bb3
ui: 🎨 new borgwarehouse logo and favicon 2025-04-20 23:08:29 +02:00
Ravinou
e939b704ef
docker: 🐳 typescript migration 2025-04-20 23:08:28 +02:00
Ravinou
90816bd705
config: 🔧 add docker to husky 2025-04-20 23:08:28 +02:00
Ravinou
c6911e77d2
config: 🔧 add shellcheck to develop PR 2025-04-20 23:08:27 +02:00
Ravinou
26f8864ebf
config: 🔧 add vitest to CI/CD 2025-04-20 23:08:27 +02:00
Ravinou
8237b428bc
chore(deps): 🧹 package rebase with main 2025-04-20 23:08:26 +02:00
Ravinou
73842a8d62
chore: 🧹 clean up some imports 2025-04-20 23:08:25 +02:00
Ravinou
7266ea464e
test: auth service 2025-04-20 23:08:25 +02:00
Ravinou
837b5f01f9
refactor: notif service 2025-04-20 23:08:24 +02:00
Ravinou
8b16a713a5
refactor: auth service 2025-04-20 23:08:23 +02:00
Ravinou
3815109958
refactor: config service 2025-04-20 23:08:23 +02:00
Ravinou
785413eec7
refactor: shell service 2025-04-20 23:08:22 +02:00
Ravinou
e1f234d54b
test: adding some API tests 2025-04-20 23:08:22 +02:00
Ravinou
52d8bca2ad
test: migration from Jest to Vitest 2025-04-20 23:08:21 +02:00
Ravinou
9e2ae9f0fa
refactor: create config service 2025-04-20 23:08:20 +02:00
Ravinou
201f5b41a1
feat: new config service with lowdb and mutex 2025-04-20 23:08:20 +02:00
Ravinou
2463a61943
config: 🔧 add lowdb and async-mutex 2025-04-20 23:08:19 +02:00
Ravinou
ca8199ca33
refactor: cleaning up the centralisation of json reading 2025-04-20 23:08:19 +02:00
Ravinou
7ec99a75c7
config: 🔧 jest and next to typescript 2025-04-20 23:08:18 +02:00
Ravinou
3105963b11
fix: 🐛 wizardEnv data fetch 2025-04-20 23:08:17 +02:00
Ravinou
11aa62a548
refactor: tokenManager API 2025-04-20 23:08:17 +02:00
Ravinou
e4dc585fe5
test: add repo API 2025-04-20 23:08:16 +02:00
Ravinou
49cfbf44e0
refactor: add repo API 2025-04-20 23:08:16 +02:00
Ravinou
ddbb629d75
test: repoList index API 2025-04-20 23:08:15 +02:00
Ravinou
fb68c4331b
refactor: repoList index 2025-04-20 23:08:14 +02:00
Ravinou
8b4ca5d7bc
refactor: create repo.json if not exist 2025-04-20 23:08:14 +02:00
Ravinou
2316fb573e
test: clean some test and add one for repo index 2025-04-20 23:08:13 +02:00
Ravinou
cb2032f309
refactor: repository index 2025-04-20 23:08:13 +02:00
Ravinou
1ae96c8f9a
test: edit API 2025-04-20 23:08:12 +02:00
Ravinou
b7d3aec3b1
refactor: edit API 2025-04-20 23:08:11 +02:00
Ravinou
93000d4406
test: delete API 2025-04-20 23:08:11 +02:00
Ravinou
149fad13ec
refactor: delete API 2025-04-20 23:08:10 +02:00
Ravinou
35ad73fd23
refactor: storage API 2025-04-20 23:08:10 +02:00
Ravinou
dac0c41df4
refactor: status API + misc 2025-04-20 23:08:10 +02:00
Ravinou
da60d50dcb
test: wizardEnv API 2025-04-20 23:08:09 +02:00
Ravinou
d753df49a0
test: apprise, email and wizard API 2025-04-20 23:08:09 +02:00
Ravinou
b40c7d7343
refactor: version API and some pages index 2025-04-20 23:08:08 +02:00
Ravinou
73c8350442
refactor: user settings API 2025-04-20 23:08:08 +02:00
Ravinou
6a661a4f6a
refactor: updateAppriseServices API 2025-04-20 23:08:07 +02:00
Ravinou
c6111329de
refactor: updateAppriseMode API 2025-04-20 23:08:07 +02:00
Ravinou
8a0a69b7dc
refactor: updateAppriseAlert API 2025-04-20 23:08:06 +02:00
Ravinou
5ce6e2c19c
test: adding supertest (jest) to test API 2025-04-20 23:08:06 +02:00
Ravinou
e1cd8e1642
refactor: nodemailer types 2025-04-20 23:08:05 +02:00
Ravinou
f9856e5689
refactor: auth.ts type 2025-04-20 23:08:05 +02:00
Ravinou
70eaa38f1f
refactor: improve type for nexauth jwt 2025-04-20 23:08:04 +02:00
Ravinou
acdaaffc16
refactor: sendTestEmail API 2025-04-20 23:08:04 +02:00
Ravinou
b266787295
refactor: sendTestApprise API 2025-04-20 23:08:03 +02:00
Ravinou
7625e5af02
refactor: getEmailAlert API 2025-04-20 23:08:03 +02:00
Ravinou
73e35295dc
chore: 🧹 add date-fns lib 2025-04-20 23:08:03 +02:00
Ravinou
313a2f30f9
refactor: getAppriseServices API 2025-04-20 23:07:29 +02:00
Ravinou
21330fa672
refactor: getAppriseMode API 2025-04-20 23:07:29 +02:00
Ravinou
7aa47195f1
refactor: getAppriseAlert API 2025-04-20 23:07:28 +02:00
Ravinou
086ae6dad3
fix: 🐛 all notifications methods can be undefined (old versions) 2025-04-20 23:07:28 +02:00
Ravinou
67861260f8
refactor: index for account and monitoring page 2025-04-20 23:07:27 +02:00
Ravinou
d7bd79b5b4
refactor: _app, 404 and index 2025-04-20 23:07:27 +02:00
Ravinou
0c4d5a898b
refactor: login page 2025-04-20 23:07:26 +02:00
Ravinou
46b923da77
refactor: password and username components 2025-04-20 23:07:26 +02:00
Ravinou
80277dbe75
refactor: integrations form 2025-04-20 23:07:25 +02:00
Ravinou
448781c3c3
refactor: email setting 2025-04-20 23:07:25 +02:00
Ravinou
c4f59c905b
refactor: user settings 2025-04-20 23:07:24 +02:00
Ravinou
8a64fe16da
refactor: email alert settings 2025-04-20 23:07:24 +02:00
Ravinou
940367e6b2
refactor: apprise alert and switch component 2025-04-20 23:07:24 +02:00
Ravinou
233b621bc7
refactor: apprise services form 2025-04-20 23:07:23 +02:00
Ravinou
0d3377baa6
refactor: apprise mode component 2025-04-20 23:07:23 +02:00
Ravinou
d66e7a2263
refactor: component repoList 2025-04-20 23:07:22 +02:00
Ravinou
b32318ccc7
refactor: storage chart 2025-04-20 23:07:22 +02:00
Ravinou
349275b908
refactor: switch and storagebar ui 2025-04-20 23:07:21 +02:00
Ravinou
12de337017
refactor: layout 2025-04-20 23:07:21 +02:00
Ravinou
d245e30af7
refactor: wizard 2025-04-20 23:07:20 +02:00
Ravinou
7b6d1a2785
refactor: info component 2025-04-20 23:07:20 +02:00
Ravinou
f228117720
refactor: error component 2025-04-20 23:07:19 +02:00
Ravinou
d9500df622
refactor: copybutton component 2025-04-20 23:07:19 +02:00
Ravinou
4f175114ff
refactor: components Repo and auth config 2025-04-20 23:07:19 +02:00
Ravinou
e323cdd9b8
refactor: component Repo in TS 2025-04-20 23:07:18 +02:00
Ravinou
5e66de2f64
config: 🔧 components and containers to TS 2025-04-20 23:07:17 +02:00
Ravinou
e485d6f394
config: 🔧 typescript init 2025-04-20 23:07:17 +02:00
Ravinou
cd24d4479f
Merge pull request #442 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.8.0
build(deps-dev): bump @commitlint/cli from 19.7.1 to 19.8.0
2025-04-20 22:57:27 +02:00
Ravinou
d84215df4b
Merge pull request #444 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.3.1
build(deps-dev): bump eslint-config-next from 15.1.6 to 15.3.1
2025-04-20 22:57:14 +02:00
dependabot[bot]
a5f3530431
build(deps-dev): bump eslint-config-next from 15.1.6 to 15.3.1
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.1.6 to 15.3.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.3.1/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-version: 15.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-17 19:15:35 +00:00
dependabot[bot]
5da8e61b8f
build(deps-dev): bump @commitlint/cli from 19.7.1 to 19.8.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.7.1 to 19.8.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.8.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 19.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-10 19:17:52 +00:00
Ravinou
82aa9015c8
Merge pull request #437 from Ravinou/dependabot/npm_and_yarn/bcryptjs-3.0.2
build(deps): bump bcryptjs from 2.4.3 to 3.0.2
2025-04-10 10:53:24 +02:00
Ravinou
42a6f0f551
Merge pull request #440 from Ravinou/dependabot/npm_and_yarn/next-15.2.5
build(deps): bump next from 15.2.3 to 15.2.5
2025-04-10 10:52:38 +02:00
dependabot[bot]
e984dcf17b
build(deps): bump next from 15.2.3 to 15.2.5
Bumps [next](https://github.com/vercel/next.js) from 15.2.3 to 15.2.5.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.2.3...v15.2.5)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-09 19:51:29 +00:00
dependabot[bot]
4e78e65d2d
build(deps): bump bcryptjs from 2.4.3 to 3.0.2
Bumps [bcryptjs](https://github.com/dcodeIO/bcrypt.js) from 2.4.3 to 3.0.2.
- [Release notes](https://github.com/dcodeIO/bcrypt.js/releases)
- [Commits](https://github.com/dcodeIO/bcrypt.js/compare/2.4.3...v3.0.2)

---
updated-dependencies:
- dependency-name: bcryptjs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 20:34:28 +00:00
Ravinou
e652a95a0b
Merge pull request #419 from Ravinou/dependabot/npm_and_yarn/react-select-5.10.1
build(deps): bump react-select from 5.10.0 to 5.10.1
2025-03-30 15:42:15 +02:00
Ravinou
ff4a676f32
Merge pull request #430 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.8.0
build(deps-dev): bump @commitlint/config-conventional from 19.7.1 to 19.8.0
2025-03-30 15:42:05 +02:00
Ravinou
1ba028ad14
Merge pull request #431 from Ravinou/dependabot/npm_and_yarn/prettier-3.5.3
build(deps-dev): bump prettier from 3.5.1 to 3.5.3
2025-03-30 15:41:43 +02:00
dependabot[bot]
b501cbe93c
build(deps-dev): bump prettier from 3.5.1 to 3.5.3
Bumps [prettier](https://github.com/prettier/prettier) from 3.5.1 to 3.5.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.5.1...3.5.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-24 20:11:15 +00:00
dependabot[bot]
85eb0891c6
build(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.7.1 to 19.8.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.8.0/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-24 20:10:18 +00:00
dependabot[bot]
b29c6ba7b3
build(deps): bump react-select from 5.10.0 to 5.10.1
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.10.0 to 5.10.1.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.10.0...react-select@5.10.1)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-22 09:44:59 +00:00
Ravinou
3afeeb7134
Merge pull request #421 from Ravinou/dependabot/npm_and_yarn/swr-2.3.3
build(deps): bump swr from 2.3.0 to 2.3.3
2025-03-22 10:44:02 +01:00
Ravinou
70faeba69a
Merge pull request #429 from Ravinou/dependabot/npm_and_yarn/next-15.2.3
build(deps): bump next from 15.1.6 to 15.2.3
2025-03-22 10:43:50 +01:00
Ravinou
2a862e23bd
🥇 Thanking a new sponsor 🥇
Thank you very much @fphammerle for sponsoring ! This commit is dedicated to thanking you. You give me strength to continue the work! ❤️
2025-03-19 21:01:17 +01:00
dependabot[bot]
7477dcfdbd
build(deps): bump next from 15.1.6 to 15.2.3
Bumps [next](https://github.com/vercel/next.js) from 15.1.6 to 15.2.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.1.6...v15.2.3)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-18 19:57:35 +00:00
Ravinou
fa3e2067c9
Merge pull request #423 from Ravinou/doc/readme-update
doc: 📚 update readme with new logo
2025-03-08 11:22:30 +01:00
Ravinou
2664f2b3d7
doc: 📚 update readme with new logo 2025-03-08 11:17:59 +01:00
dependabot[bot]
08bb4ebe70
build(deps): bump swr from 2.3.0 to 2.3.3
Bumps [swr](https://github.com/vercel/swr) from 2.3.0 to 2.3.3.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.3.0...v2.3.3)

---
updated-dependencies:
- dependency-name: swr
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 19:43:07 +00:00
Ravinou
edbaf23f3f
Merge pull request #413 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.30.0
build(deps): bump @tabler/icons-react from 3.29.0 to 3.30.0
2025-03-02 14:07:18 +01:00
Ravinou
53d749e529
Merge pull request #414 from Ravinou/dependabot/npm_and_yarn/prettier-3.5.1
build(deps-dev): bump prettier from 3.4.2 to 3.5.1
2025-02-15 19:43:39 +01:00
dependabot[bot]
2bab3f1180
build(deps-dev): bump prettier from 3.4.2 to 3.5.1
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.2 to 3.5.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.2...3.5.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-13 20:02:33 +00:00
dependabot[bot]
d003d2173b
build(deps): bump @tabler/icons-react from 3.29.0 to 3.30.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.29.0 to 3.30.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.30.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-11 20:01:29 +00:00
Ravinou
4e0dc479bd
Merge pull request #407 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.7.1
build(deps-dev): bump @commitlint/cli from 19.6.1 to 19.7.1
2025-02-08 16:56:32 +01:00
dependabot[bot]
e1263867f8
build(deps-dev): bump @commitlint/cli from 19.6.1 to 19.7.1
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.6.1 to 19.7.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.7.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-08 10:34:15 +00:00
Ravinou
f7b7f62069
Merge pull request #408 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.7.1
build(deps-dev): bump @commitlint/config-conventional from 19.6.0 to 19.7.1
2025-02-08 11:33:10 +01:00
dependabot[bot]
31a1ad9769
build(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.6.0 to 19.7.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.7.1/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 19:15:36 +00:00
Ravinou
09f2b9b599
Merge pull request #405 from Ravinou/dependabot/npm_and_yarn/nodemailer-6.10.0
build(deps): bump nodemailer from 6.9.16 to 6.10.0
2025-02-01 14:53:12 +01:00
Ravinou
6eedd52a70
Merge pull request #406 from Ravinou/dependabot/npm_and_yarn/react-select-5.10.0
build(deps): bump react-select from 5.9.0 to 5.10.0
2025-01-31 21:57:56 +01:00
dependabot[bot]
b22dcdaa4d
build(deps): bump react-select from 5.9.0 to 5.10.0
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.9.0 to 5.10.0.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.9.0...react-select@5.10.0)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 19:19:07 +00:00
dependabot[bot]
1b6c0ae4be
build(deps): bump nodemailer from 6.9.16 to 6.10.0
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.16 to 6.10.0.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.16...v6.10.0)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 19:18:57 +00:00
Ravinou
39a8335787
Merge pull request #402 from Ravinou/dependabot/npm_and_yarn/next-15.1.6
build(deps): bump next from 15.1.4 to 15.1.6
2025-01-26 11:36:40 +01:00
Ravinou
28ae1ff42c
Merge pull request #404 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.1.6
build(deps-dev): bump eslint-config-next from 15.1.4 to 15.1.6
2025-01-26 11:36:31 +01:00
dependabot[bot]
36bc28d85a
build(deps): bump next from 15.1.4 to 15.1.6
Bumps [next](https://github.com/vercel/next.js) from 15.1.4 to 15.1.6.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.1.4...v15.1.6)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-25 14:58:10 +00:00
Ravinou
ed247c592f
Merge pull request #403 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.29.0
build(deps): bump @tabler/icons-react from 3.28.1 to 3.29.0
2025-01-25 15:56:57 +01:00
dependabot[bot]
5150257441
build(deps-dev): bump eslint-config-next from 15.1.4 to 15.1.6
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.1.4 to 15.1.6.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.1.6/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-22 19:28:15 +00:00
dependabot[bot]
22779c590e
build(deps): bump @tabler/icons-react from 3.28.1 to 3.29.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.28.1 to 3.29.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.29.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-22 19:28:04 +00:00
Ravinou
c3d1469d5a
Merge pull request #394 from Ravinou/dependabot/npm_and_yarn/next-15.1.4
build(deps): bump next from 15.1.3 to 15.1.4
2025-01-17 20:05:27 +01:00
Ravinou
8e80bfa49c
Merge pull request #395 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.1.4
build(deps-dev): bump eslint-config-next from 15.1.3 to 15.1.4
2025-01-17 20:05:16 +01:00
dependabot[bot]
5af9e0e2e3
build(deps-dev): bump eslint-config-next from 15.1.3 to 15.1.4
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.1.3 to 15.1.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.1.4/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 19:51:55 +00:00
dependabot[bot]
2ac4fa3b2a
build(deps): bump next from 15.1.3 to 15.1.4
Bumps [next](https://github.com/vercel/next.js) from 15.1.3 to 15.1.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.1.3...v15.1.4)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 19:51:42 +00:00
Ravinou
a19bb130f0
Merge pull request #392 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.28.1
build(deps): bump @tabler/icons-react from 3.26.0 to 3.28.1
2025-01-12 19:19:11 +01:00
Ravinou
307b2a5676
Merge pull request #386 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.6.1
build(deps-dev): bump @commitlint/cli from 19.6.0 to 19.6.1
2025-01-11 11:16:17 +01:00
Ravinou
b8b20ebc7c
Merge pull request #393 from Ravinou/dependabot/npm_and_yarn/uuid-11.0.5
build(deps): bump uuid from 11.0.3 to 11.0.5
2025-01-11 11:15:55 +01:00
dependabot[bot]
1dca17b50f
build(deps): bump uuid from 11.0.3 to 11.0.5
Bumps [uuid](https://github.com/uuidjs/uuid) from 11.0.3 to 11.0.5.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v11.0.3...v11.0.5)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-10 20:02:10 +00:00
dependabot[bot]
7415039a2b
build(deps): bump @tabler/icons-react from 3.26.0 to 3.28.1
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.26.0 to 3.28.1.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.28.1/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-08 19:46:09 +00:00
Ravinou
333d7fa119
publish: 📦 release 2.4.4 2025-01-05 13:41:24 +01:00
Ravinou
7c3632a131
Merge pull request #389 from Ravinou/develop
fix: 🐛 allow smtp settings without credentials #364
2025-01-05 13:35:31 +01:00
Ravinou
4d32f1e002
fix: 🐛 allow smtp settings without credentials #364 2025-01-05 11:37:57 +01:00
Ravinou
4f9234e325
Merge pull request #388 from Ravinou/dependabot/npm_and_yarn/react-chartjs-2-5.3.0
build(deps): bump react-chartjs-2 from 5.2.0 to 5.3.0
2025-01-03 17:19:53 +01:00
dependabot[bot]
9a33efc121
build(deps): bump react-chartjs-2 from 5.2.0 to 5.3.0
Bumps [react-chartjs-2](https://github.com/reactchartjs/react-chartjs-2) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/reactchartjs/react-chartjs-2/releases)
- [Changelog](https://github.com/reactchartjs/react-chartjs-2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reactchartjs/react-chartjs-2/compare/v5.2.0...v5.3.0)

---
updated-dependencies:
- dependency-name: react-chartjs-2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-01 19:15:17 +00:00
dependabot[bot]
f5bbae9c0d
build(deps-dev): bump @commitlint/cli from 19.6.0 to 19.6.1
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.6.0 to 19.6.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.6.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-01 17:30:42 +00:00
Ravinou
2d2446ea51
Merge pull request #387 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.1.3
build(deps-dev): bump eslint-config-next from 15.1.2 to 15.1.3
2025-01-01 18:29:42 +01:00
Ravinou
97d909aaed
Merge pull request #382 from Ravinou/dependabot/npm_and_yarn/next-15.1.3
build(deps): bump next from 15.1.2 to 15.1.3
2025-01-01 18:29:30 +01:00
dependabot[bot]
0198051eb0
build(deps-dev): bump eslint-config-next from 15.1.2 to 15.1.3
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.1.2 to 15.1.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.1.3/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 19:13:42 +00:00
dependabot[bot]
28c7e60481
build(deps): bump next from 15.1.2 to 15.1.3
Bumps [next](https://github.com/vercel/next.js) from 15.1.2 to 15.1.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.1.2...v15.1.3)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-29 11:25:01 +00:00
Ravinou
cedcdb2418
Merge pull request #383 from Ravinou/dependabot/npm_and_yarn/swr-2.3.0
build(deps): bump swr from 2.2.5 to 2.3.0
2024-12-29 12:24:21 +01:00
Ravinou
55fd807e8e
Merge pull request #384 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.26.0
build(deps): bump @tabler/icons-react from 3.24.0 to 3.26.0
2024-12-29 12:23:50 +01:00
dependabot[bot]
c546bc7bc2
build(deps): bump @tabler/icons-react from 3.24.0 to 3.26.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.24.0 to 3.26.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.26.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 19:53:22 +00:00
dependabot[bot]
c8dbaac92e
build(deps): bump swr from 2.2.5 to 2.3.0
Bumps [swr](https://github.com/vercel/swr) from 2.2.5 to 2.3.0.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.2.5...v2.3.0)

---
updated-dependencies:
- dependency-name: swr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 19:53:13 +00:00
Ravinou
0c407cd732
publish: 📦 release 2.4.3 2024-12-26 17:51:25 +01:00
Ravinou
1e08e64ce6
config: 🔧 prepare husky to v10 2024-12-26 17:51:08 +01:00
Ravinou
8e9fa1eb56
Merge pull request #380 from Ravinou/develop
release 2.4.3
2024-12-26 16:24:41 +01:00
Ravinou
66047df78a
config: 🔧 update CI/CD to include commit version on develop version 2024-12-26 15:48:13 +01:00
Ravinou
96ae9b8f65
feat: include 'id' and 'repositoryName' in 'add' API response #342 2024-12-26 14:49:20 +01:00
Ravinou
88a80f42e8
fix: 🐛 allow self-signed cert and no-auth for smtp settings #367 #364 2024-12-26 14:19:13 +01:00
Ravinou
0d645fc461
Merge pull request #373 from Ravinou/dependabot/npm_and_yarn/react-select-5.9.0
build(deps): bump react-select from 5.8.3 to 5.9.0
2024-12-26 10:37:13 +01:00
dependabot[bot]
bdc0104c9a
build(deps): bump react-select from 5.8.3 to 5.9.0
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.8.3 to 5.9.0.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.8.3...react-select@5.9.0)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 09:26:15 +00:00
Ravinou
0935409d08
Merge pull request #379 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.54.2
build(deps): bump react-hook-form from 7.54.0 to 7.54.2
2024-12-26 10:25:11 +01:00
Ravinou
a70e39bd6d
Merge pull request #377 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-15.1.2
build(deps-dev): bump eslint-config-next from 15.0.4 to 15.1.2
2024-12-26 10:24:55 +01:00
Ravinou
904eb1db60
Merge pull request #378 from Ravinou/dependabot/npm_and_yarn/next-15.1.2
build(deps): bump next from 15.0.4 to 15.1.2
2024-12-26 10:24:40 +01:00
dependabot[bot]
94ce693c54
build(deps): bump react-hook-form from 7.54.0 to 7.54.2
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.54.0 to 7.54.2.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.54.0...v7.54.2)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 19:26:31 +00:00
dependabot[bot]
14ba99028e
build(deps): bump next from 15.0.4 to 15.1.2
Bumps [next](https://github.com/vercel/next.js) from 15.0.4 to 15.1.2.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.0.4...v15.1.2)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 19:15:01 +00:00
dependabot[bot]
b2c9d6d9ae
build(deps-dev): bump eslint-config-next from 15.0.4 to 15.1.2
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 15.0.4 to 15.1.2.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.1.2/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 19:14:29 +00:00
Ravinou
bfe7deb9c4
publish: 📦 release 2.4.2 2024-12-08 20:21:35 +01:00
Ravinou
1ae44441be
Merge pull request #368 from Ravinou/develop
Release v2.4.2
2024-12-08 20:19:06 +01:00
Ravinou
14883f24c7
chore(deps): 🧹 update npm deps 2024-12-08 20:05:08 +01:00
Ravinou
c36f63e54a
config: 🔧 prepare husky v10 2024-12-08 19:59:05 +01:00
Ravinou
73ac5339f4
config: 🔧 upgrade Docker base image to Node.js LTS 22 2024-12-08 19:57:41 +01:00
Ravinou
cef8263641
fix: 🐛 add unique key to React Fragment in repo list rendering 2024-12-08 11:20:50 +01:00
Ravinou
8620fb2e02
chore(deps): 🧹 update NextJS 14 to 15 2024-12-08 11:17:50 +01:00
Ravinou
fc3f57e24c
fix: 🐛 improves storage calculation accuracy 2024-12-08 11:02:43 +01:00
nezu
b0fae4f59d
fix: remove hardcoded HOSTNAME in Docker
Instead of hardcoding it in supervisord.conf, we simply set it to an
empty value in the Dockerfile. This prevents docker from setting it's
own value while also allowing the user to override it.

In next JS an empty HOSTNAME is equivalent to not setting it at all.
2024-12-08 11:02:43 +01:00
dumbasPL
72d3fecb33
fix: listen on all interfaced in Docker
By default, the `HOSTNAME` environment variable will have the
container's hostname. NodeJS `server.listen` when used with a
hostname that resolved to multiple addresses will only use the
first one. By setting it to 0.0.0.0 we are restoring the default
behavior of Next.js and listening on all interfaces.
2024-12-08 11:02:42 +01:00
Ravinou
0c6220f533
fix: 🐛 ignore lost+found directories on storage retrieval
#267
2024-12-08 11:02:41 +01:00
Ravinou
bac544e552
Merge pull request #359 from Ravinou/dependabot/npm_and_yarn/chart.js-4.4.7
build(deps): bump chart.js from 4.4.6 to 4.4.7
2024-12-08 10:58:32 +01:00
dependabot[bot]
28dbe55417
build(deps): bump chart.js from 4.4.6 to 4.4.7
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.6 to 4.4.7.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.6...v4.4.7)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-07 16:45:57 +00:00
Ravinou
26d822f426
Merge pull request #360 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.24.0
build(deps): bump @tabler/icons-react from 3.23.0 to 3.24.0
2024-12-07 17:44:45 +01:00
Ravinou
d627671483
Merge pull request #361 from Ravinou/dependabot/npm_and_yarn/prettier-3.4.2
build(deps-dev): bump prettier from 3.4.1 to 3.4.2
2024-12-07 17:44:29 +01:00
dependabot[bot]
fa2d0fa4ca
build(deps-dev): bump prettier from 3.4.1 to 3.4.2
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.1 to 3.4.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.1...3.4.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-04 20:04:04 +00:00
dependabot[bot]
6fa2b8d2e2
build(deps): bump @tabler/icons-react from 3.23.0 to 3.24.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.23.0 to 3.24.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.24.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-03 20:05:27 +00:00
Ravinou
3a5a301cb9
Merge pull request #355 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.6.0
build(deps-dev): bump @commitlint/config-conventional from 19.5.0 to 19.6.0
2024-12-01 21:18:32 +01:00
Ravinou
a1c19396c2
Merge pull request #356 from Ravinou/dependabot/npm_and_yarn/prettier-3.4.1
build(deps-dev): bump prettier from 3.3.3 to 3.4.1
2024-12-01 21:17:57 +01:00
dependabot[bot]
43ec07bafb
build(deps-dev): bump prettier from 3.3.3 to 3.4.1
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.3 to 3.4.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.3...3.4.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-30 09:59:37 +00:00
Ravinou
56502311db
Merge pull request #358 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.23.0
build(deps): bump @tabler/icons-react from 3.22.0 to 3.23.0
2024-11-30 10:58:26 +01:00
dependabot[bot]
cd8f625173
build(deps): bump @tabler/icons-react from 3.22.0 to 3.23.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.22.0 to 3.23.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.23.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 19:59:11 +00:00
dependabot[bot]
3f65b27d1a
build(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.5.0 to 19.6.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.6.0/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-25 19:37:38 +00:00
Ravinou
5bee3d8b1e
Merge pull request #348 from Ravinou/dependabot/npm_and_yarn/react-select-5.8.3
build(deps): bump react-select from 5.8.2 to 5.8.3
2024-11-24 14:03:40 +01:00
Ravinou
d5ddfa1809
Merge pull request #353 from Ravinou/dependabot/npm_and_yarn/husky-9.1.7
build(deps-dev): bump husky from 9.1.6 to 9.1.7
2024-11-23 14:08:19 +01:00
Ravinou
ac16be3c8c
Merge pull request #354 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.6.0
build(deps-dev): bump @commitlint/cli from 19.5.0 to 19.6.0
2024-11-23 14:07:58 +01:00
dependabot[bot]
7ab8c48326
build(deps-dev): bump @commitlint/cli from 19.5.0 to 19.6.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.5.0 to 19.6.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.6.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-19 19:40:03 +00:00
dependabot[bot]
a26f4a4a4c
build(deps-dev): bump husky from 9.1.6 to 9.1.7
Bumps [husky](https://github.com/typicode/husky) from 9.1.6 to 9.1.7.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.1.6...v9.1.7)

---
updated-dependencies:
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 20:11:21 +00:00
dependabot[bot]
4308f37126
build(deps): bump react-select from 5.8.2 to 5.8.3
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.8.2 to 5.8.3.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.8.2...react-select@5.8.3)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-17 12:04:45 +00:00
Ravinou
7f952e7211
Merge pull request #349 from Ravinou/dependabot/npm_and_yarn/spinners-react-1.0.10
build(deps): bump spinners-react from 1.0.7 to 1.0.10
2024-11-17 13:03:30 +01:00
Ravinou
3c5ff142a3
Merge pull request #352 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.22.0
build(deps): bump @tabler/icons-react from 3.21.0 to 3.22.0
2024-11-16 11:52:41 +01:00
dependabot[bot]
7e1d4cfe2c
build(deps): bump @tabler/icons-react from 3.21.0 to 3.22.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.21.0 to 3.22.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.22.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 19:31:16 +00:00
dependabot[bot]
9ebd1608c8
build(deps): bump spinners-react from 1.0.7 to 1.0.10
Bumps [spinners-react](https://github.com/adexin/spinners-react) from 1.0.7 to 1.0.10.
- [Changelog](https://github.com/adexin/spinners-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/adexin/spinners-react/compare/v1.0.7...v1.0.10)

---
updated-dependencies:
- dependency-name: spinners-react
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 14:05:47 +00:00
Ravinou
545d26c066
Merge pull request #351 from Ravinou/dependabot/npm_and_yarn/uuid-11.0.3
build(deps): bump uuid from 11.0.2 to 11.0.3
2024-11-14 15:04:35 +01:00
dependabot[bot]
972a83733e
build(deps): bump uuid from 11.0.2 to 11.0.3
Bumps [uuid](https://github.com/uuidjs/uuid) from 11.0.2 to 11.0.3.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v11.0.2...v11.0.3)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-12 19:12:54 +00:00
Ravinou
4f066af82e
Merge pull request #350 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.53.2
build(deps): bump react-hook-form from 7.53.1 to 7.53.2
2024-11-12 15:03:06 +01:00
dependabot[bot]
75aa177a11
build(deps): bump react-hook-form from 7.53.1 to 7.53.2
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.53.1 to 7.53.2.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.53.1...v7.53.2)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 19:53:47 +00:00
Ravinou
2b1f405d96
Merge pull request #341 from Ravinou/dependabot/npm_and_yarn/react-select-5.8.2
build(deps): bump react-select from 5.8.1 to 5.8.2
2024-11-03 11:32:19 +01:00
Ravinou
5bc4f9aa52
Merge pull request #340 from Ravinou/dependabot/npm_and_yarn/nodemailer-6.9.16
build(deps): bump nodemailer from 6.9.15 to 6.9.16
2024-11-03 11:31:42 +01:00
dependabot[bot]
52ae3bc9d8
build(deps): bump react-select from 5.8.1 to 5.8.2
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.8.1 to 5.8.2.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.8.1...react-select@5.8.2)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-01 19:56:44 +00:00
dependabot[bot]
05d5297898
build(deps): bump nodemailer from 6.9.15 to 6.9.16
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.15 to 6.9.16.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.15...v6.9.16)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-01 19:56:38 +00:00
Ravinou
0298e71892
Merge pull request #339 from Ravinou/develop
Integrate unit tests with Bats for shell scripts
2024-11-01 17:45:02 +01:00
Ravinou
7f61e464eb
config: 🔧 automate bats tests via github action ci/cd 2024-11-01 17:07:14 +01:00
Ravinou
893c86dc75
test: bats versus getStorageUsed.sh 2024-11-01 17:07:14 +01:00
Ravinou
027ccd7c28
refactor: remove jc package dependency 2024-11-01 17:07:13 +01:00
Ravinou
a8161f2ea7
test: bats versus getLastSave.sh 2024-11-01 17:07:13 +01:00
Ravinou
fab8f17c77
test: bats versus updateRepo.sh 2024-11-01 17:07:12 +01:00
Ravinou
da4b5b2cec
refactor: improve control on repository's name 2024-11-01 17:07:11 +01:00
Ravinou
4d62b9988a
test: bats versus deleteRepo.sh 2024-11-01 17:07:11 +01:00
Ravinou
f0206076ab
config: 🔧 docker-compose file for bats 2024-11-01 17:07:10 +01:00
Ravinou
fdc2cd186c
refactor: improve Quota argument validation 2024-11-01 17:07:09 +01:00
Ravinou
e704e5283b
test: bats versus createRepo.sh 2024-11-01 17:07:08 +01:00
Ravinou
6a6bf3fa93
Merge pull request #334 from Ravinou/dependabot/npm_and_yarn/uuid-11.0.2
build(deps): bump uuid from 10.0.0 to 11.0.2
2024-11-01 17:06:08 +01:00
Ravinou
fc00745140
Merge pull request #335 from Ravinou/dependabot/npm_and_yarn/chart.js-4.4.6
build(deps): bump chart.js from 4.4.5 to 4.4.6
2024-11-01 17:00:49 +01:00
Ravinou
e977638af2
Merge pull request #336 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.21.0
build(deps): bump @tabler/icons-react from 3.20.0 to 3.21.0
2024-11-01 17:00:39 +01:00
dependabot[bot]
bd836e0937
build(deps): bump @tabler/icons-react from 3.20.0 to 3.21.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.20.0 to 3.21.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.21.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 19:20:31 +00:00
dependabot[bot]
ef204563dc
build(deps): bump chart.js from 4.4.5 to 4.4.6
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.5...v4.4.6)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 19:20:24 +00:00
dependabot[bot]
6ee8ab288a
build(deps): bump uuid from 10.0.0 to 11.0.2
Bumps [uuid](https://github.com/uuidjs/uuid) from 10.0.0 to 11.0.2.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v10.0.0...v11.0.2)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 19:20:15 +00:00
Ravinou
d47e54abee
Merge pull request #332 from Ravinou/dependabot/npm_and_yarn/next-auth-4.24.10
build(deps): bump next-auth from 4.24.8 to 4.24.10
2024-10-26 11:49:20 +02:00
Ravinou
19d6b99807
Merge pull request #328 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.53.1
build(deps): bump react-hook-form from 7.53.0 to 7.53.1
2024-10-26 11:49:06 +02:00
Ravinou
12afb2ac55
Merge pull request #329 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.20.0
build(deps): bump @tabler/icons-react from 3.19.0 to 3.20.0
2024-10-26 11:48:54 +02:00
dependabot[bot]
02798de614
build(deps): bump next-auth from 4.24.8 to 4.24.10
Bumps [next-auth](https://github.com/nextauthjs/next-auth) from 4.24.8 to 4.24.10.
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@4.24.8...next-auth@4.24.10)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-24 19:09:41 +00:00
dependabot[bot]
a8e7074064
build(deps): bump @tabler/icons-react from 3.19.0 to 3.20.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.19.0 to 3.20.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.20.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-23 19:39:20 +00:00
dependabot[bot]
cbb6d36fb3
build(deps): bump react-hook-form from 7.53.0 to 7.53.1
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.53.0 to 7.53.1.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.53.0...v7.53.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 19:45:14 +00:00
Ravinou
18a79c04c0
Merge pull request #324 from Ravinou/dependabot/npm_and_yarn/chart.js-4.4.5
build(deps): bump chart.js from 4.4.4 to 4.4.5
2024-10-19 18:51:05 +02:00
dependabot[bot]
52778b39a6
build(deps): bump chart.js from 4.4.4 to 4.4.5
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 19:24:42 +00:00
Ravinou
bff91d065b
Merge pull request #323 from Ravinou/dependabot/npm_and_yarn/react-toastify-10.0.6
build(deps): bump react-toastify from 10.0.5 to 10.0.6
2024-10-14 21:49:50 +02:00
dependabot[bot]
42bac2e038
build(deps): bump react-toastify from 10.0.5 to 10.0.6
Bumps [react-toastify](https://github.com/fkhadra/react-toastify) from 10.0.5 to 10.0.6.
- [Release notes](https://github.com/fkhadra/react-toastify/releases)
- [Commits](https://github.com/fkhadra/react-toastify/compare/v10.0.5...v10.0.6)

---
updated-dependencies:
- dependency-name: react-toastify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 19:23:46 +00:00
Ravinou
2497e87313
Merge pull request #319 from Ravinou/dependabot/npm_and_yarn/next-14.2.15
build(deps): bump next from 14.2.14 to 14.2.15
2024-10-13 17:49:55 +02:00
Ravinou
93b6d27ddb
Merge pull request #320 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.15
build(deps-dev): bump eslint-config-next from 14.2.14 to 14.2.15
2024-10-13 17:49:46 +02:00
dependabot[bot]
dd563b3d6e
build(deps-dev): bump eslint-config-next from 14.2.14 to 14.2.15
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.14 to 14.2.15.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.15/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 19:40:01 +00:00
dependabot[bot]
551c591599
build(deps): bump next from 14.2.14 to 14.2.15
Bumps [next](https://github.com/vercel/next.js) from 14.2.14 to 14.2.15.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.14...v14.2.15)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 19:39:52 +00:00
Ravinou
8b8a95ebba
Merge pull request #315 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.19.0
build(deps): bump @tabler/icons-react from 3.18.0 to 3.19.0
2024-10-05 15:31:17 +02:00
Ravinou
518939aa8c
Merge pull request #316 from Ravinou/dependabot/npm_and_yarn/next-14.2.14
build(deps): bump next from 14.2.13 to 14.2.14
2024-10-05 15:31:05 +02:00
Ravinou
635ed848e1
Merge pull request #317 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.14
build(deps-dev): bump eslint-config-next from 14.2.13 to 14.2.14
2024-10-05 15:30:53 +02:00
dependabot[bot]
9590cfb717
build(deps-dev): bump eslint-config-next from 14.2.13 to 14.2.14
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.13 to 14.2.14.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.14/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 19:46:15 +00:00
dependabot[bot]
5739ae67c2
build(deps): bump next from 14.2.13 to 14.2.14
Bumps [next](https://github.com/vercel/next.js) from 14.2.13 to 14.2.14.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.13...v14.2.14)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 19:46:01 +00:00
dependabot[bot]
85d934c112
build(deps): bump @tabler/icons-react from 3.18.0 to 3.19.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.18.0 to 3.19.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.19.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 19:25:59 +00:00
Ravinou
35ef56d15b
Merge pull request #311 from Ravinou/dependabot/npm_and_yarn/react-select-5.8.1
build(deps): bump react-select from 5.8.0 to 5.8.1
2024-09-29 16:49:47 +02:00
Ravinou
3c6af174c8
Merge pull request #312 from Ravinou/dependabot/npm_and_yarn/next-auth-4.24.8
build(deps): bump next-auth from 4.24.7 to 4.24.8
2024-09-29 16:49:22 +02:00
Ravinou
e9c6014a56
Merge pull request #314 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.18.0
build(deps): bump @tabler/icons-react from 3.17.0 to 3.18.0
2024-09-29 16:49:01 +02:00
dependabot[bot]
a9da4b980a
build(deps): bump @tabler/icons-react from 3.17.0 to 3.18.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.17.0 to 3.18.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.18.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-27 19:19:30 +00:00
dependabot[bot]
f0f7b880d4
build(deps): bump next-auth from 4.24.7 to 4.24.8
Bumps [next-auth](https://github.com/nextauthjs/next-auth) from 4.24.7 to 4.24.8.
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@4.24.7...next-auth@4.24.8)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 19:32:40 +00:00
dependabot[bot]
82f947bf38
build(deps): bump react-select from 5.8.0 to 5.8.1
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.8.0 to 5.8.1.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.8.0...react-select@5.8.1)

---
updated-dependencies:
- dependency-name: react-select
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 20:04:00 +00:00
Ravinou
5fa0cf0735
Merge pull request #305 from Ravinou/dependabot/npm_and_yarn/husky-9.1.6
build(deps-dev): bump husky from 9.1.5 to 9.1.6
2024-09-21 14:01:00 +02:00
dependabot[bot]
24cd77dd00
build(deps-dev): bump husky from 9.1.5 to 9.1.6
Bumps [husky](https://github.com/typicode/husky) from 9.1.5 to 9.1.6.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.1.5...v9.1.6)

---
updated-dependencies:
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-21 11:59:12 +00:00
Ravinou
53584899b3
Merge pull request #309 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.13
build(deps-dev): bump eslint-config-next from 14.2.8 to 14.2.13
2024-09-21 13:58:05 +02:00
dependabot[bot]
faa5fdbe52
build(deps-dev): bump eslint-config-next from 14.2.8 to 14.2.13
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.8 to 14.2.13.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.13/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-21 11:56:35 +00:00
Ravinou
2eacabef56
Merge pull request #304 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.5.0
build(deps-dev): bump @commitlint/config-conventional from 19.4.1 to 19.5.0
2024-09-21 13:55:40 +02:00
Ravinou
be3cac0a63
Merge pull request #306 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.17.0
build(deps): bump @tabler/icons-react from 3.16.0 to 3.17.0
2024-09-21 13:55:23 +02:00
Ravinou
b98951c83d
Merge pull request #310 from Ravinou/dependabot/npm_and_yarn/next-14.2.13
build(deps): bump next from 14.2.8 to 14.2.13
2024-09-21 13:55:12 +02:00
dependabot[bot]
38013ca100
build(deps): bump next from 14.2.8 to 14.2.13
Bumps [next](https://github.com/vercel/next.js) from 14.2.8 to 14.2.13.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.8...v14.2.13)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-20 20:05:49 +00:00
dependabot[bot]
02c8e7cb05
build(deps): bump @tabler/icons-react from 3.16.0 to 3.17.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.16.0 to 3.17.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.17.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 19:56:35 +00:00
dependabot[bot]
c0bb4b8928
build(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.4.1 to 19.5.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.5.0/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-13 19:53:37 +00:00
Ravinou
2e75250203
Merge pull request #291 from Ravinou/dependabot/npm_and_yarn/nodemailer-6.9.15
build(deps): bump nodemailer from 6.9.14 to 6.9.15
2024-09-13 21:41:04 +02:00
Ravinou
728704b6c9
Merge pull request #297 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.16.0
build(deps): bump @tabler/icons-react from 3.14.0 to 3.16.0
2024-09-13 21:40:24 +02:00
Ravinou
482fd448a0
Merge pull request #298 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.5.0
build(deps-dev): bump @commitlint/cli from 19.4.1 to 19.5.0
2024-09-13 21:37:22 +02:00
dependabot[bot]
52f6708a0f
build(deps-dev): bump @commitlint/cli from 19.4.1 to 19.5.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.4.1 to 19.5.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.5.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 19:08:20 +00:00
dependabot[bot]
5df908b64e
build(deps): bump @tabler/icons-react from 3.14.0 to 3.16.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.14.0 to 3.16.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.16.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 19:21:13 +00:00
dependabot[bot]
8e7b96ef44
build(deps): bump nodemailer from 6.9.14 to 6.9.15
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.14 to 6.9.15.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.14...v6.9.15)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-07 19:27:26 +00:00
Ravinou
0c511cb948
Merge pull request #292 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.8
build(deps-dev): bump eslint-config-next from 14.2.7 to 14.2.8
2024-09-07 21:26:35 +02:00
Ravinou
d03aa3a0a8
Merge pull request #293 from Ravinou/dependabot/npm_and_yarn/next-14.2.8
build(deps): bump next from 14.2.7 to 14.2.8
2024-09-07 21:26:18 +02:00
dependabot[bot]
54ecf4cebf
build(deps): bump next from 14.2.7 to 14.2.8
Bumps [next](https://github.com/vercel/next.js) from 14.2.7 to 14.2.8.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.7...v14.2.8)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-05 19:49:34 +00:00
dependabot[bot]
b814ee54ba
build(deps-dev): bump eslint-config-next from 14.2.7 to 14.2.8
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.7 to 14.2.8.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.8/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-05 19:49:22 +00:00
Ravinou
1d5b2943c3
publish: 📦 release 2.4.1 2024-09-01 14:58:57 +02:00
Ravinou
77edb8c941
Merge pull request #289 from Ravinou/develop
hotfix: 🚑 compatibility with installations behind .htaccess/.htpasswd…
2024-09-01 14:57:30 +02:00
Ravinou
e87e6d9760
hotfix: 🚑 compatibility with installations behind .htaccess/.htpasswd that use an Authorization header 2024-09-01 14:00:22 +02:00
Ravinou
f38822ff14
publish: 📦 release 2.4.0 2024-09-01 11:37:11 +02:00
Ravinou
54d2b245a9
chore: 🧹 update dependencies 2024-09-01 11:32:19 +02:00
Ravinou
07d8c0bb3d
Merge pull request #288 from Ravinou/develop
Release 2.4.0
2024-09-01 11:23:21 +02:00
Ravinou
026497303d
ui: 🎨 integration of Pika Backup in the wizard 2024-08-31 22:47:46 +02:00
Ravinou
e2cd515ee6
feat: add a link to documentation in new "Integrations" menu 2024-08-31 22:30:12 +02:00
Ravinou
0c3f136cb7
chore: 🧹 improve auth log with timestamp 2024-08-31 21:01:08 +02:00
Ravinou
5320b1c8db
doc: 📚 "version" in docker-compose is now deprecated 2024-08-31 21:01:07 +02:00
Ravinou
57124899a2
refactor: improve HTTP status code 2024-08-31 21:01:07 +02:00
Ravinou
dd51c23aaf
fix: 🐛 usage of new environment variables with Docker 2024-08-31 21:01:06 +02:00
Ravinou
3587942d31
feat: new env variable to hide the SSH port in quickcommands and wizard
#237
2024-08-31 21:01:06 +02:00
Ravinou
0c352dce5e
refactor: add and edit endpoints 2024-08-31 21:01:05 +02:00
Ravinou
9d03010278
fix: 🐛 prevents an alert from being sent after a new repo has been created 2024-08-31 21:01:05 +02:00
Ravinou
c3b782f434
fix: 🐛 overflow that was causing a 1px scroll on all pages 2024-08-31 21:01:05 +02:00
Ravinou
2f509f7903
ui: 🎨 improve delete token loading 2024-08-31 21:01:04 +02:00
Ravinou
a795831e3b
fix: 🐛 type error when no token has ever been created 2024-08-31 21:01:04 +02:00
Ravinou
00c98f842d
feat: add the ability to globally disable API integrations with env var 2024-08-31 21:01:03 +02:00
Ravinou
f092d73679
feat: added log for API authentication 2024-08-31 21:01:03 +02:00
Ravinou
ba11e0ab04
feat: full granularity for CRUD API permissions 2024-08-31 21:01:02 +02:00
Ravinou
aa57a19ff1
feat: CRUD repoList with API token auth 2024-08-31 21:01:02 +02:00
Ravinou
83fe9a5355
config: 🔧 update prettier format 2024-08-31 21:01:01 +02:00
Ravinou
e4d9484759
feat: generate and manage access tokens in account settings 2024-08-31 20:57:34 +02:00
Ravinou
202e7dcef9
feat: add the ability to use FQDN without SSH port in wizards #237 2024-08-31 20:57:34 +02:00
Ravinou
85951c4cd0
ui: 🎨 improve warning message when delete mode is disable 2024-08-31 20:57:34 +02:00
Ravinou
bfe1bd3433
doc: 📚 add IPv6 setting in the sample .env 2024-08-31 20:57:33 +02:00
Marc Ole Bulling
3ea38e6648
Add option to disable deletion 2024-08-31 20:57:33 +02:00
Ravinou
76d20b699e
Merge pull request #283 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.7
build(deps-dev): bump eslint-config-next from 14.2.6 to 14.2.7
2024-08-31 19:04:53 +02:00
dependabot[bot]
9980dea4e2
build(deps-dev): bump eslint-config-next from 14.2.6 to 14.2.7
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.6 to 14.2.7.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.7/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-31 17:03:33 +00:00
Ravinou
9252df2e87
Merge pull request #286 from Ravinou/dependabot/npm_and_yarn/commitlint/config-conventional-19.4.1
build(deps-dev): bump @commitlint/config-conventional from 19.2.2 to 19.4.1
2024-08-31 19:02:23 +02:00
dependabot[bot]
00b047aa70
build(deps-dev): bump @commitlint/config-conventional
Bumps [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) from 19.2.2 to 19.4.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.4.1/@commitlint/config-conventional)

---
updated-dependencies:
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-31 16:35:51 +00:00
Ravinou
9e74d09418
Merge pull request #284 from Ravinou/dependabot/npm_and_yarn/next-14.2.7
build(deps): bump next from 14.2.6 to 14.2.7
2024-08-31 18:35:05 +02:00
Ravinou
bba618aa69
Merge pull request #276 from Ravinou/dependabot/npm_and_yarn/husky-9.1.5
build(deps-dev): bump husky from 9.1.4 to 9.1.5
2024-08-31 18:34:41 +02:00
Ravinou
2fd3329e7d
Merge pull request #282 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.14.0
build(deps): bump @tabler/icons-react from 3.13.0 to 3.14.0
2024-08-31 18:34:13 +02:00
dependabot[bot]
0b564d10f8
build(deps): bump next from 14.2.6 to 14.2.7
Bumps [next](https://github.com/vercel/next.js) from 14.2.6 to 14.2.7.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.6...v14.2.7)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-27 19:13:03 +00:00
dependabot[bot]
b6104d2c3e
build(deps-dev): bump husky from 9.1.4 to 9.1.5
Bumps [husky](https://github.com/typicode/husky) from 9.1.4 to 9.1.5.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.1.4...v9.1.5)

---
updated-dependencies:
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-27 19:12:45 +00:00
dependabot[bot]
3d0bc52fc3
build(deps): bump @tabler/icons-react from 3.13.0 to 3.14.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.13.0 to 3.14.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.14.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-27 19:12:39 +00:00
Ravinou
c8f5ae92db
Merge pull request #280 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.53.0
build(deps): bump react-hook-form from 7.52.2 to 7.53.0
2024-08-27 21:11:35 +02:00
Ravinou
e8038e7467
Merge pull request #281 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.13.0
build(deps): bump @tabler/icons-react from 3.11.0 to 3.13.0
2024-08-27 21:11:18 +02:00
dependabot[bot]
00e76b37a6
build(deps): bump @tabler/icons-react from 3.11.0 to 3.13.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.11.0 to 3.13.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.13.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 19:20:35 +00:00
dependabot[bot]
3a5f6fb6a2
build(deps): bump react-hook-form from 7.52.2 to 7.53.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.52.2 to 7.53.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.52.2...v7.53.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 19:20:28 +00:00
Ravinou
fbfa81fd07
Merge pull request #277 from Ravinou/dependabot/npm_and_yarn/chart.js-4.4.4
build(deps): bump chart.js from 4.4.3 to 4.4.4
2024-08-24 15:55:30 +02:00
dependabot[bot]
cbd6e92b3e
build(deps): bump chart.js from 4.4.3 to 4.4.4
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.3 to 4.4.4.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.3...v4.4.4)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-23 20:42:01 +00:00
Ravinou
49291f0fd4
Merge pull request #278 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.6
build(deps-dev): bump eslint-config-next from 14.2.5 to 14.2.6
2024-08-23 22:41:00 +02:00
Ravinou
727c56e1f0
Merge pull request #279 from Ravinou/dependabot/npm_and_yarn/next-14.2.6
build(deps): bump next from 14.2.5 to 14.2.6
2024-08-23 22:40:48 +02:00
dependabot[bot]
91aae7fdb9
build(deps): bump next from 14.2.5 to 14.2.6
Bumps [next](https://github.com/vercel/next.js) from 14.2.5 to 14.2.6.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.5...v14.2.6)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-22 19:53:32 +00:00
dependabot[bot]
b8259dd4ae
build(deps-dev): bump eslint-config-next from 14.2.5 to 14.2.6
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.5 to 14.2.6.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.6/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-22 19:53:20 +00:00
Ravinou
20048ba80c
Merge pull request #271 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.52.2
build(deps): bump react-hook-form from 7.52.1 to 7.52.2
2024-08-09 17:51:04 +02:00
Ravinou
b781f47ae2
Merge pull request #269 from Ravinou/dependabot/npm_and_yarn/husky-9.1.4
build(deps-dev): bump husky from 9.1.1 to 9.1.4
2024-08-09 17:50:52 +02:00
Ravinou
0fb445d998
Merge pull request #272 from Ravinou/dependabot/npm_and_yarn/commitlint/cli-19.4.0
build(deps-dev): bump @commitlint/cli from 19.3.0 to 19.4.0
2024-08-09 17:50:13 +02:00
dependabot[bot]
9a9f7305ed
build(deps-dev): bump @commitlint/cli from 19.3.0 to 19.4.0
Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.3.0 to 19.4.0.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.4.0/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 19:17:04 +00:00
dependabot[bot]
37167a7d6e
build(deps): bump react-hook-form from 7.52.1 to 7.52.2
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.52.1 to 7.52.2.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.52.1...v7.52.2)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 19:29:09 +00:00
dependabot[bot]
61042e6cdb
build(deps-dev): bump husky from 9.1.1 to 9.1.4
Bumps [husky](https://github.com/typicode/husky) from 9.1.1 to 9.1.4.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.1.1...v9.1.4)

---
updated-dependencies:
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 19:41:25 +00:00
Ravinou
d14262bce2
Merge pull request #263 from Ravinou/dependabot/npm_and_yarn/husky-9.1.1
build(deps-dev): bump husky from 9.0.11 to 9.1.1
2024-07-20 20:12:32 +02:00
dependabot[bot]
d413d8c732
build(deps-dev): bump husky from 9.0.11 to 9.1.1
Bumps [husky](https://github.com/typicode/husky) from 9.0.11 to 9.1.1.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.0.11...v9.1.1)

---
updated-dependencies:
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-18 20:05:14 +00:00
Ravinou
aa3e4b90f4
Merge pull request #261 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.11.0
build(deps): bump @tabler/icons-react from 3.10.0 to 3.11.0
2024-07-17 20:49:59 +02:00
dependabot[bot]
d2fab3eeea
build(deps): bump @tabler/icons-react from 3.10.0 to 3.11.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.10.0 to 3.11.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.11.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-16 19:14:32 +00:00
Ravinou
5c74509d8b
Merge pull request #260 from Ravinou/dependabot/npm_and_yarn/prettier-3.3.3
build(deps-dev): bump prettier from 3.3.2 to 3.3.3
2024-07-15 21:43:24 +02:00
dependabot[bot]
32483c23b6
build(deps-dev): bump prettier from 3.3.2 to 3.3.3
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.2 to 3.3.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.2...3.3.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-15 19:09:03 +00:00
Ravinou
5bea6c1aa6
Merge pull request #259 from Ravinou/develop
config: 🔧 add husky and commitlint
2024-07-14 22:24:27 +02:00
Ravinou
3f57e310d2
config: 🔧 add husky and commitlint
Thank you to @Benjmain for his contribution 🙏
2024-07-14 22:16:42 +02:00
Ravinou
ee9a4f00c9
Merge pull request #255 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.10.0
build(deps): bump @tabler/icons-react from 3.9.0 to 3.10.0
2024-07-14 20:31:50 +02:00
dependabot[bot]
2d452e101c
build(deps): bump @tabler/icons-react from 3.9.0 to 3.10.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.10.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-14 18:31:17 +00:00
Ravinou
a85c457603
Merge pull request #256 from Ravinou/dependabot/npm_and_yarn/next-14.2.5
build(deps): bump next from 14.2.4 to 14.2.5
2024-07-14 20:30:37 +02:00
Ravinou
56b50826d8
Merge pull request #257 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.5
build(deps-dev): bump eslint-config-next from 14.2.4 to 14.2.5
2024-07-14 20:30:11 +02:00
dependabot[bot]
8140962395
build(deps-dev): bump eslint-config-next from 14.2.4 to 14.2.5
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.4 to 14.2.5.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.5/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 19:56:50 +00:00
dependabot[bot]
125c0317cf
build(deps): bump next from 14.2.4 to 14.2.5
Bumps [next](https://github.com/vercel/next.js) from 14.2.4 to 14.2.5.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.4...v14.2.5)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-10 19:56:37 +00:00
Ravinou
9a493f6b18
Merge pull request #250 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.52.1
build(deps): bump react-hook-form from 7.52.0 to 7.52.1
2024-07-08 22:07:21 +02:00
Ravinou
370f207d9e
Merge pull request #253 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.9.0
build(deps): bump @tabler/icons-react from 3.7.0 to 3.9.0
2024-07-08 22:07:00 +02:00
dependabot[bot]
65d0848376
build(deps): bump @tabler/icons-react from 3.7.0 to 3.9.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.7.0 to 3.9.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.9.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-05 19:16:47 +00:00
dependabot[bot]
51bca3098d
build(deps): bump react-hook-form from 7.52.0 to 7.52.1
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.52.0 to 7.52.1.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.52.0...v7.52.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 19:25:46 +00:00
Ravinou
43e3b708cd
Merge pull request #238 from Ravinou/dependabot/github_actions/docker/build-push-action-6
build(deps): bump docker/build-push-action from 5 to 6
2024-06-30 10:51:15 +02:00
Ravinou
26530d6bdc
Merge pull request #248 from Ravinou/develop
fix: adapt email template to improve GMAIL compatibility #242
2024-06-27 21:46:14 +02:00
Ravinou
13aa4055ff
fix: adapt email template to improve GMAIL compatibility #242 2024-06-27 21:21:33 +02:00
Ravinou
6763cac795
Merge pull request #239 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.52.0
build(deps): bump react-hook-form from 7.51.5 to 7.52.0
2024-06-26 22:17:11 +02:00
Ravinou
2085a7bb67
Merge pull request #240 from Ravinou/dependabot/npm_and_yarn/nodemailer-6.9.14
build(deps): bump nodemailer from 6.9.13 to 6.9.14
2024-06-26 22:16:38 +02:00
Ravinou
b7ec34588f
Merge pull request #246 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.7.0
build(deps): bump @tabler/icons-react from 3.6.0 to 3.7.0
2024-06-26 22:16:09 +02:00
dependabot[bot]
f7de065ee2
build(deps): bump @tabler/icons-react from 3.6.0 to 3.7.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.7.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-25 19:50:59 +00:00
dependabot[bot]
b620cd27b8
build(deps): bump nodemailer from 6.9.13 to 6.9.14
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.13 to 6.9.14.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.13...v6.9.14)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-19 19:42:23 +00:00
dependabot[bot]
d51103c4f5
build(deps): bump react-hook-form from 7.51.5 to 7.52.0
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.51.5 to 7.52.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.51.5...v7.52.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 19:21:44 +00:00
dependabot[bot]
b37bfa37df
build(deps): bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 19:12:16 +00:00
Ravinou
de3fa3f3af
Merge pull request #233 from Ravinou/dependabot/npm_and_yarn/eslint-config-next-14.2.4
build(deps-dev): bump eslint-config-next from 14.2.3 to 14.2.4
2024-06-16 22:40:38 +02:00
Ravinou
f8b6c46d80
Merge pull request #235 from Forceu/Forceu-patch-1
Make ARGs compliant with Docker specs
2024-06-16 22:33:54 +02:00
dependabot[bot]
8a7e30d008
build(deps-dev): bump eslint-config-next from 14.2.3 to 14.2.4
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 14.2.3 to 14.2.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v14.2.4/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: eslint-config-next
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-16 20:11:29 +00:00
Ravinou
7dbe181629
Merge pull request #231 from Ravinou/dependabot/npm_and_yarn/prettier-3.3.2
build(deps-dev): bump prettier from 3.2.5 to 3.3.2
2024-06-16 22:10:50 +02:00
Ravinou
36753d0aa0
Merge pull request #232 from Ravinou/dependabot/npm_and_yarn/next-14.2.4
build(deps): bump next from 14.2.3 to 14.2.4
2024-06-16 22:10:22 +02:00
Ravinou
67d959f9fb
Merge pull request #234 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.6.0
build(deps): bump @tabler/icons-react from 3.5.0 to 3.6.0
2024-06-16 22:09:39 +02:00
Marc Ole Bulling
81ed817045
Make ARGs complient with Docker specs 2024-06-15 16:41:19 +02:00
dependabot[bot]
02efa8ad94
build(deps): bump @tabler/icons-react from 3.5.0 to 3.6.0
Bumps [@tabler/icons-react](https://github.com/tabler/tabler-icons/tree/HEAD/packages/icons-react) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/tabler/tabler-icons/releases)
- [Commits](https://github.com/tabler/tabler-icons/commits/v3.6.0/packages/icons-react)

---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-14 19:13:53 +00:00
dependabot[bot]
3f8fbb77bb
build(deps): bump next from 14.2.3 to 14.2.4
Bumps [next](https://github.com/vercel/next.js) from 14.2.3 to 14.2.4.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.3...v14.2.4)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-12 19:47:27 +00:00
dependabot[bot]
52ed551200
build(deps-dev): bump prettier from 3.2.5 to 3.3.2
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 19:11:41 +00:00
Ravinou
ea5d565567
Merge pull request #224 from Ravinou/dependabot/npm_and_yarn/chart.js-4.4.3
build(deps): bump chart.js from 4.4.2 to 4.4.3
2024-05-26 11:15:56 +02:00
Ravinou
fa218b0522
Merge pull request #226 from Ravinou/dependabot/npm_and_yarn/react-hook-form-7.51.5
build(deps): bump react-hook-form from 7.51.4 to 7.51.5
2024-05-25 00:01:28 +02:00
dependabot[bot]
56098d3f8d
build(deps): bump chart.js from 4.4.2 to 4.4.3
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.2...v4.4.3)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-24 21:25:28 +00:00
Ravinou
08e6b0e6a6
Merge pull request #227 from Ravinou/dependabot/npm_and_yarn/tabler/icons-react-3.5.0
build(deps): bump @tabler/icons-react from 3.3.0 to 3.5.0
2024-05-24 23:24:02 +02:00
dependabot[bot]
a79a91ecb0
---
updated-dependencies:
- dependency-name: "@tabler/icons-react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 19:57:00 +00:00
dependabot[bot]
779cceacf2
---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 19:56:46 +00:00
Ravinou
94fd0c6188
🥇 Thanking a new sponsor 🥇
Thank you very much @royalmoose for sponsoring ! This commit is dedicated to thanking you. You give me strength to continue the work!
2024-05-13 22:24:53 +02:00
250 changed files with 18667 additions and 13164 deletions

30
.commitlintrc.mjs Normal file
View file

@ -0,0 +1,30 @@
const config = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'build',
'chore',
'config',
'doc',
'feat',
'fix',
'hotfix',
'i18n',
'refactor',
'revert',
'test',
'ui',
'wip',
'publish',
'docker',
'WIP',
],
],
},
ignores: [(message) => message.includes('WIP'), (message) => message.includes('wip')],
};
export default config;

View file

@ -23,8 +23,6 @@ CONFIG_PATH=./config
SSH_PATH=./ssh
SSH_HOST=./ssh_host
BORG_REPOSITORY_PATH=./repos
TMP_PATH=./tmp
LOGS_PATH=./logs
## Optional variables section ##
@ -32,10 +30,22 @@ LOGS_PATH=./logs
FQDN_LAN=
SSH_SERVER_PORT_LAN=
# Disable the DELETE feature
#DISABLE_DELETE_REPO=true
# Disable the integrations (API tokens to CRUD repositories)
#DISABLE_INTEGRATIONS=true
# Hide the SSH port in the UI : quickcommands & wizard
#HIDE_SSH_PORT=true
# SMTP server settings
MAIL_SMTP_FROM=
MAIL_SMTP_HOST=
MAIL_SMTP_PORT=
MAIL_SMTP_LOGIN=
MAIL_SMTP_PWD=
MAIL_REJECT_SELFSIGNED_TLS=
MAIL_REJECT_SELFSIGNED_TLS=
# Force app to start on IPv6
#HOSTNAME=::

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report a bug
title: ''
labels: ''
assignees: ''
---
**BorgWarehouse version :**
**Installation type :**
- [ ] Docker
- [ ] Baremetal (Debian/Ubuntu)
- [ ] Other environment :
-------
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.
**Please, [BorgWarehouse's documentation](https://borgwarehouse.com/)
is up to date and comprehensive, so take the time to look for answers. You can also look for answers in the project's historical [github issues](https://github.com/Ravinou/borgwarehouse/issues?q=is%3Aissue%20state%3Aclosed). I take time to answer each issue, but it's always less time for BorgWarehouse development. Thanks in advance.**

21
.github/ISSUE_TEMPLATE/i-need-help.md vendored Normal file
View file

@ -0,0 +1,21 @@
---
name: I need help
about: You need help about installation, usage, or specific cases.
title: ''
labels: help wanted
assignees: ''
---
**BorgWarehouse version :**
**Installation type :**
- [ ] Docker
- [ ] Baremetal (Debian/Ubuntu)
- [ ] Other environment :
-------
Describe your problem here.
**Please, [BorgWarehouse's documentation](https://borgwarehouse.com/)
is up to date and comprehensive, so take the time to look for answers. You can also look for answers in the project's historical [github issues](https://github.com/Ravinou/borgwarehouse/issues?q=is%3Aissue%20state%3Aclosed). I take time to answer each issue, but it's always less time for BorgWarehouse development. Thanks in advance.**

View file

@ -1,16 +1,18 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
- package-ecosystem: 'docker'
directory: '/'
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/"
interval: 'daily'
# Note: Dependabot uses "npm" ecosystem but automatically detects pnpm-lock.yaml
# Make sure package-lock.json is gitignored to prevent confusion
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: "daily"
interval: 'daily'
# Maintain dependencies for GitHub Actions
# src: https://github.com/marketplace/actions/build-and-push-docker-images#keep-up-to-date-with-github-dependabot
- package-ecosystem: "github-actions"
directory: "/"
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: "daily"
interval: 'daily'

29
.github/workflows/bats.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Bats
permissions:
contents: read
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
jobs:
bats-test:
name: Run bats tests against shells
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build container & run bats tests
run: |
docker compose -f tests/bats/docker-compose.yml up --abort-on-container-exit --build

View file

@ -1,29 +1,38 @@
name: Build and Push Docker Image for Develop Branch
on:
push:
branches:
- 'develop'
push:
branches:
- 'develop'
permissions:
contents: read
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64 # linux/arm/v7 arm32 is not supported by node20 https://github.com/nodejs/docker-node/issues/1946
tags: borgwarehouse/borgwarehouse:develop
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Update package.json version
run: |
COMMIT=$(git rev-parse --short HEAD)
echo "Current Commit: $COMMIT"
jq '.version = "develop-'$COMMIT'"' package.json > package.tmp.json
mv package.tmp.json package.json
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64 # linux/arm/v7 arm32 is not supported by node20 https://github.com/nodejs/docker-node/issues/1946
tags: borgwarehouse/borgwarehouse:develop

View file

@ -1,4 +1,6 @@
name: Build and Push Docker Image
permissions:
contents: read
on:
push:
@ -10,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@ -21,7 +23,7 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
push: true

View file

@ -5,12 +5,15 @@ on:
types:
- published
permissions:
contents: read
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@ -24,7 +27,7 @@ jobs:
id: get_release_tag
run: echo "::set-output name=TAG::${{ github.event.release.tag_name }}"
- name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
push: true

View file

@ -1,21 +1,24 @@
name: Test Docker Container Build on Pull Request
name: Test to build docker container on Pull Request
permissions:
contents: read
on:
pull_request:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
jobs:
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Container
run: |
docker buildx build --platform linux/amd64,linux/arm64 -t borgwarehouse:pr-${{ github.event.pull_request.number }} .
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build BorgWarehouse Container
run: |
docker buildx build --platform linux/amd64,linux/arm64 -t borgwarehouse:pr-${{ github.event.pull_request.number }} .

View file

@ -4,19 +4,21 @@ on:
- main
- develop
pull_request:
branches: main
branches:
- main
- develop
name: "Shellcheck"
name: 'Shellcheck'
permissions: {}
jobs:
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:

63
.github/workflows/vitest.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Vitest & ESLint CI
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
permissions:
contents: read
jobs:
test:
name: Run Vitest
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Vitest
run: pnpm run test
lint:
name: Run ESLint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run ESLint
run: pnpm exec eslint

13
.gitignore vendored
View file

@ -50,6 +50,14 @@ typings/
# Optional npm cache directory
.npm
# pnpm
.pnpm-store/
pnpm-debug.log*
# Lock files (pnpm-lock.yaml is used)
package-lock.json
yarn.lock
# Optional eslint cache
.eslintcache
@ -111,4 +119,7 @@ config/repo.json
config/users.json
# docker files
docker-compose.yml
docker-compose.yml
# Commit tests docker-compose
!tests/bats/docker-compose.yml

92
.husky/append-icon.sh Executable file
View file

@ -0,0 +1,92 @@
#!/bin/bash
# define log prefix
prefix="pre-commit:"
# store message file, first and only param of hook
commitMessageFile="$1"
# breaking change icon !
boomIcon=':boom:'
# check for breaking change in file content
# find any line starting with 'BREAKING CHANGE'
function checkBreakingChangeInBody() {
breakingChange='BREAKING CHANGE'
while read -r line; do
if [[ "$line" == "$breakingChange"* ]]; then
echo "$prefix found $breakingChange in message body"
return 0
fi
done < "$1"
return 1
}
function findTypeIcon() {
message="$1"
if [[ "$message" =~ ^.*!:\ .* ]]; then
echo "$boomIcon"
return 0
fi
declare -A icons=(
[build]='🤖'
[chore]='🧹'
["chore(deps)"]='🧹'
[config]='🔧'
[deploy]='🚀'
[doc]='📚'
[feat]='✨'
[fix]='🐛'
[hotfix]='🚑'
[i18n]='💬'
[publish]='📦'
[refactor]='⚡'
[revert]='⏪'
[test]='✅'
[ui]='🎨'
[wip]='🚧'
[WIP]='🚧'
[docker]='🐳'
)
commit_type="${message%%:*}"
icon="${icons[$commit_type]}"
if [[ -n "$icon" ]]; then
echo "$icon"
return 0
else
return 1
fi
}
# extract original message from the first line of file
message=$(head -n 1 <"$commitMessageFile")
echo "$prefix commit subject: '$message'"
if checkBreakingChangeInBody "$commitMessageFile"; then
echo 'setting breaking change icon'
icon=$boomIcon
else
icon=$(findTypeIcon "$message")
if [ $? -eq 1 ]; then
echo "$prefix ❌ unable to find icon corresponding to commit type. Make sure your commit-lint config (.commitlintrc.js) and append-msg script (append-msg.sh) types match"
exit 1
fi
fi
# check if icon has been appended before
if [[ "$message" == *"$icon"* ]]; then
echo "⏭️ skipping icon append as it's been added before"
exit 0
fi
# otherwise append icon
updatedMessage="${message/:/: $icon}"
# replace first line of file with updated message
sed -i "1s/.*/$updatedMessage/" "$commitMessageFile"
echo "$prefix ✅ appended icon $icon to commit message subject"

5
.husky/commit-msg Executable file
View file

@ -0,0 +1,5 @@
# run commit lint
npx commitlint --edit "$1"
# run script to prepend message with icon
./.husky/append-icon.sh "$1"

5
.husky/prepare-commit-msg Executable file
View file

@ -0,0 +1,5 @@
# Check if it's an amend commit
if [ "$2" = "commit" ]; then
echo "Amendment detected, appending icon..."
./.husky/append-icon.sh "$1"
fi

7
.npmrc Normal file
View file

@ -0,0 +1,7 @@
# Configuration pnpm
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=false
# Force pnpm usage (prevent npm/yarn)
package-manager=pnpm

View file

@ -1,21 +1,20 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"singleAttributePerLine": false,
"bracketSameLine": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": true,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"useTabs": false,
"embeddedLanguageFormatting": "auto"
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"singleAttributePerLine": false,
"bracketSameLine": false,
"jsxSingleQuote": true,
"printWidth": 100,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"useTabs": false,
"embeddedLanguageFormatting": "auto"
}

View file

@ -1,70 +0,0 @@
//Lib
import React from 'react';
import { useState } from 'react';
import classes from './QuickCommands.module.css';
import { IconSettingsAutomation, IconCopy } from '@tabler/icons-react';
export default function QuickCommands(props) {
////Vars
const wizardEnv = props.wizardEnv;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
let FQDN;
let SSH_SERVER_PORT;
if (
props.lanCommand &&
wizardEnv.FQDN_LAN &&
wizardEnv.SSH_SERVER_PORT_LAN
) {
FQDN = wizardEnv.FQDN_LAN;
SSH_SERVER_PORT = wizardEnv.SSH_SERVER_PORT_LAN;
} else {
FQDN = wizardEnv.FQDN;
SSH_SERVER_PORT = wizardEnv.SSH_SERVER_PORT;
}
//State
const [isCopied, setIsCopied] = useState(false);
//Functions
const handleCopy = async () => {
// Asynchronously call copy to clipboard
navigator.clipboard
.writeText(
`ssh://${wizardEnv.UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.repositoryName}`
)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<div className={classes.container}>
{isCopied ? (
<div className={classes.copyValid}>Copied !</div>
) : (
<div className={classes.tooltip}>
ssh://{wizardEnv.UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.repositoryName}
</div>
)}
{props.lanCommand && <div className={classes.lanBadge}>LAN</div>}
<div className={classes.icons}>
<button onClick={handleCopy} className={classes.copyButton}>
<IconCopy color='#65748b' stroke={1.25} />
</button>
<div className={classes.quickSetting}>
<IconSettingsAutomation color='#65748b' stroke={1.25} />
</div>
</div>
</div>
);
}

View file

@ -1,116 +1,117 @@
.container {
display: flex;
align-items: center;
align-self: flex-start;
margin: auto 47px auto auto;
display: flex;
align-items: center;
align-self: flex-start;
margin: auto 25px auto auto;
}
.icons {
position: relative;
bottom: 13px;
position: relative;
bottom: 13px;
}
.quickSetting {
position: absolute;
visibility: visible;
opacity: 1;
position: absolute;
visibility: visible;
opacity: 1;
}
.lanBadge {
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
padding: 2px 5px;
margin-right: 8px;
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
padding: 2px 5px;
margin-right: 8px;
}
.tooltip {
visibility: hidden;
opacity: 0;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
visibility: hidden;
opacity: 0;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #fafafa;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
}
.copyButton {
position: absolute;
visibility: hidden;
opacity: 0;
border: none;
background-color: none;
position: absolute;
visibility: hidden;
opacity: 0;
border: none;
background-color: none;
}
.copyValid {
margin: auto 8px auto auto;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
margin: auto 8px auto auto;
padding: 6px 6px;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@keyframes scale-in-center {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
/* On Hover */
.container:hover .tooltip {
visibility: visible;
opacity: 1;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
visibility: visible;
opacity: 1;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #fafafa;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
}
.container:hover .copyButton {
position: absolute;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
position: absolute;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
}
.container:hover .quickSetting {
position: absolute;
visibility: hidden;
opacity: 0;
position: absolute;
visibility: hidden;
opacity: 0;
}
.container:hover .lanBadge {
visibility: hidden;
opacity: 0;
width: 0;
height: 0;
margin: 0;
padding: 0;
visibility: hidden;
opacity: 0;
width: 0;
height: 0;
margin: 0;
padding: 0;
}
@media all and (max-width: 1000px) {
.container {
display: none;
}
.container {
display: none;
}
}

View file

@ -0,0 +1,62 @@
import React from 'react';
import { useState } from 'react';
import classes from './QuickCommands.module.css';
import { IconSettingsAutomation, IconCopy } from '@tabler/icons-react';
import { lanCommandOption } from '~/helpers/functions';
import { WizardEnvType } from '~/types/domain/config.types';
type QuickCommandsProps = {
repositoryName: string;
wizardEnv?: WizardEnvType;
lanCommand?: boolean;
};
export default function QuickCommands(props: QuickCommandsProps) {
const wizardEnv = props.wizardEnv;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.lanCommand);
const [isCopied, setIsCopied] = useState(false);
const handleCopy = async () => {
// Asynchronously call copy to clipboard
navigator.clipboard
.writeText(
`ssh://${wizardEnv?.UNIX_USER}@${FQDN}${SSH_SERVER_PORT ? SSH_SERVER_PORT : ''}/./${props.repositoryName}`
)
.then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<div className={classes.container}>
{isCopied ? (
<div className={classes.copyValid}>Copied !</div>
) : (
<div className={classes.tooltip}>
ssh://{wizardEnv?.UNIX_USER}@{FQDN}
{SSH_SERVER_PORT ? SSH_SERVER_PORT : ''}/./
{props.repositoryName}
</div>
)}
{props.lanCommand && <div className={classes.lanBadge}>LAN</div>}
<div className={classes.icons}>
<button onClick={handleCopy} className={classes.copyButton}>
<IconCopy color='#65748b' stroke={1.25} />
</button>
<div className={classes.quickSetting}>
<IconSettingsAutomation color='#65748b' stroke={1.25} />
</div>
</div>
</div>
);
}

View file

@ -1,217 +0,0 @@
//Lib
import { useState } from 'react';
import classes from './Repo.module.css';
import {
IconSettings,
IconInfoCircle,
IconChevronDown,
IconChevronUp,
IconBellOff,
IconLockPlus,
} from '@tabler/icons-react';
import timestampConverter from '../../helpers/functions/timestampConverter';
import StorageBar from '../UI/StorageBar/StorageBar';
import QuickCommands from './QuickCommands/QuickCommands';
export default function Repo(props) {
//Load displayDetails from LocalStorage
const displayDetailsFromLS = () => {
try {
if (
localStorage.getItem('displayDetailsRepo' + props.id) === null
) {
localStorage.setItem(
'displayDetailsRepo' + props.id,
JSON.stringify(true)
);
return true;
} else {
return JSON.parse(
localStorage.getItem('displayDetailsRepo' + props.id)
);
}
} catch (error) {
console.log(
'LocalStorage error, key',
'displayDetailsRepo' + props.id,
'will be removed. Try again.',
'Error message on this key : ',
error
);
localStorage.removeItem('displayDetailsRepo' + props.id);
}
};
//States
const [displayDetails, setDisplayDetails] = useState(displayDetailsFromLS);
//BUTTON : Display or not repo details for ONE repo
const displayDetailsForOneHandler = (boolean) => {
//Update localStorage
localStorage.setItem(
'displayDetailsRepo' + props.id,
JSON.stringify(boolean)
);
setDisplayDetails(boolean);
};
//Status indicator
const statusIndicator = () => {
return props.status
? classes.statusIndicatorGreen
: classes.statusIndicatorRed;
};
//Alert indicator
const alertIndicator = () => {
if (props.alert === 0) {
return (
<div className={classes.alertIcon}>
<IconBellOff size={16} color='grey' />
</div>
);
}
};
const appendOnlyModeIndicator = () => {
if (props.appendOnlyMode) {
return (
<div className={classes.appendOnlyModeIcon}>
<IconLockPlus size={16} color='grey' />
</div>
);
}
};
return (
<>
{displayDetails ? (
<>
<div className={classes.RepoOpen}>
<div className={classes.openFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='grey' />
<div className={classes.toolTip}>
{props.comment}
</div>
</div>
)}
<QuickCommands
repositoryName={props.repositoryName}
lanCommand={props.lanCommand}
wizardEnv={props.wizardEnv}
/>
</div>
<table className={classes.tabInfo}>
<thead>
<tr>
<th style={{ width: '15%' }}>Repository</th>
<th style={{ width: '10%' }}>
Storage Size
</th>
<th style={{ width: '30%' }}>
Storage Used
</th>
<th style={{ width: '15%' }}>
Last change
</th>
<th style={{ width: '5%' }}>ID</th>
<th style={{ width: '5%' }}>Edit</th>
</tr>
</thead>
<tbody>
<tr>
<th>{props.repositoryName}</th>
<th>{props.storageSize} GB</th>
<th style={{ padding: '0 4% 0 4%' }}>
<StorageBar
storageUsed={props.storageUsed}
storageSize={props.storageSize}
/>
</th>
<th>
<div className={classes.lastSave}>
{props.lastSave === 0
? '-'
: timestampConverter(
props.lastSave
)}
</div>
</th>
<th>#{props.id}</th>
<th>
<div className={classes.editButton}>
<IconSettings
width={24}
color='#6d4aff'
onClick={() =>
props.repoManageEditHandler()
}
/>
</div>
</th>
</tr>
</tbody>
</table>
</div>
</>
) : (
<>
<div className={classes.RepoClose}>
<div className={classes.closeFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='#637381' />
<div className={classes.toolTip}>
{props.comment}
</div>
</div>
)}
</div>
<div className={classes.lastSave}>
{props.lastSave === 0
? null
: timestampConverter(props.lastSave)}
<span
style={{ marginLeft: '20px', color: '#637381' }}
>
#{props.id}
</span>
</div>
</div>
</>
)}
{displayDetails ? (
<div className={classes.chevron}>
<IconChevronUp
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(false);
}}
/>
</div>
) : (
<div className={classes.chevron}>
<IconChevronDown
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(true);
}}
/>
</div>
)}
</>
);
}

View file

@ -1,250 +1,309 @@
/*Repo CLOSE*/
.RepoClose {
display: flex;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 65px;
margin: 20px 0px 0px 0px;
border-radius: 5px;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 65px;
margin: 20px 0px 0px 0px;
border-radius: 5px;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
background: #fff;
}
.closeFlex {
display: flex;
align-items: center;
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 15px;
gap: 10px;
}
.RepoClose .lastSave {
padding: 15px;
white-space: nowrap;
}
.RepoClose .leftGroup {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
gap: 10px;
}
.RepoClose .alias {
font-weight: bold;
color: #111827;
font-size: 1.05em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
flex: 1;
min-width: 0;
}
/* REPO OPEN */
.RepoOpen {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 200px;
margin: 20px 0px 0px 0px;
padding: 15px;
border-radius: 5px;
transition: max-height 0.1s linear;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
margin: 20px 0px 0px 0px;
padding: 15px;
border-radius: 5px;
transition: max-height 0.1s linear;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
background: #fff;
}
.openFlex {
display: flex;
align-items: center;
align-self: flex-start;
width: 100%;
display: block;
width: 100%;
}
.aliasFlex {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}
.indicatorsFlex {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
width: 100%;
gap: 15px;
}
.tabInfo {
width: 100%;
overflow-wrap: break-word;
border-collapse: collapse;
background: #fff;
border-radius: 10px;
overflow: hidden;
margin: 25px auto;
table-layout: fixed;
width: 100%;
overflow-wrap: break-word;
border-collapse: collapse;
background: #fff;
border-radius: 10px;
overflow: hidden;
margin: 15px auto;
table-layout: fixed;
}
.tabInfo thead tr {
height: 50px;
background: #111827;
color: #fff;
height: 50px;
background: #111827;
color: #fff;
}
.tabInfo thead th {
font-size: 1em;
color: #fff;
line-height: 1.2;
font-weight: normal;
font-size: 1em;
color: #fff;
line-height: 1.2;
font-weight: 500;
}
.tabInfo tbody tr {
background-color: #f3f4f6;
height: 50px;
background-color: #f3f4f6;
height: 50px;
}
.tabInfo tbody tr th {
color: #65748b;
font-size: 0.95rem;
font-weight: 400;
color: #65748b;
font-size: 0.95rem;
font-weight: 400;
}
/*STATUS*/
.statusIndicatorGreen {
background: rgb(9, 255, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
box-shadow: 0 0 0 0 rgb(9, 255, 0);
transform: scale(1);
animation: pulseGreen 5s infinite;
animation-delay: 1s;
.statusIndicatorGreen,
.statusIndicatorRed {
border-radius: 50%;
height: 16px;
width: 16px;
flex-shrink: 0;
animation: pulse 5s infinite;
}
@keyframes pulseGreen {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0.7);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(17, 255, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0);
}
.statusIndicatorGreen {
background: #00d26a;
box-shadow: 0 0 0 0 rgba(0, 210, 106, 0.7);
}
.statusIndicatorRed {
background: rgb(255, 0, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
box-shadow: 0 0 0 0 rgb(255, 0, 0);
transform: scale(1);
animation: pulseRed 5s infinite;
animation-delay: 0.5s;
background: #ff3d3d;
box-shadow: 0 0 0 0 rgba(255, 61, 61, 0.7);
animation-delay: 0.5s;
}
@keyframes pulseRed {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(255, 0, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
}
@keyframes pulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.4);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
}
/* Alert icon */
.alertIcon {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
}
.appendOnlyModeIcon {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
}
/* GENERAL */
.alias {
font-weight: bold;
color: #111827;
font-size: 1.05em;
font-weight: bold;
color: #111827;
font-size: 1.05em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.RepoOpen .alias {
margin-top: 5px;
}
.lastSave {
color: #65748b;
color: #65748b;
}
.editButton {
cursor: pointer;
cursor: pointer;
}
/* Comment */
.comment {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
}
.toolTip {
visibility: hidden;
width: auto;
height: auto;
max-width: 400px;
max-height: 250px;
background-color: #fff;
color: #637381;
text-align: center;
border-radius: 6px;
padding: 5px 5px;
position: absolute;
z-index: 1;
margin: 0px 0 0 20px;
opacity: 1;
transition: 0.5s opacity;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
overflow: auto;
visibility: hidden;
width: auto;
height: auto;
max-width: 400px;
max-height: 250px;
background-color: #fff;
color: #637381;
text-align: center;
border-radius: 6px;
padding: 5px 5px;
position: absolute;
z-index: 1;
margin: 0px 0 0 20px;
opacity: 1;
transition: 0.5s opacity;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
overflow: auto;
}
.comment:hover .toolTip,
.comment:active .toolTip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}
.chevron {
margin: auto;
margin: auto;
}
.chevron :focus,
.chevron :hover {
cursor: pointer;
filter: invert(27%) sepia(82%) saturate(2209%) hue-rotate(240deg)
brightness(99%) contrast(105%);
cursor: pointer;
filter: invert(27%) sepia(82%) saturate(2209%) hue-rotate(240deg) brightness(99%) contrast(105%);
}
/* MOBILE */
@media all and (max-width: 1000px) {
.tabInfo {
display: none;
}
.toolTip {
display: none;
}
.comment {
display: none;
}
.lastSave {
display: none;
}
.closeFlex {
margin: auto;
}
.openFlex {
margin: auto;
width: auto;
}
.openFlex,
.tabInfo,
.toolTip,
.comment,
.chevron {
display: none !important;
}
.RepoOpen,
.RepoClose {
display: flex !important;
flex-direction: row !important;
align-items: center !important;
justify-content: space-between !important;
max-height: 65px !important;
padding: 15px !important;
margin: 20px 0 0 0 !important;
}
.closeFlex {
display: flex !important;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0 !important;
margin: 0 !important;
}
.alias {
flex: 1;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.leftGroup {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
gap: 10px;
}
.rightGroup {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.lastSave {
display: block !important;
color: #65748b;
white-space: nowrap;
font-size: 0.85em;
flex-shrink: 0;
margin-left: 10px;
}
.appendOnlyModeIcon,
.alertIcon {
display: flex;
align-items: center;
}
}

261
Components/Repo/Repo.tsx Normal file
View file

@ -0,0 +1,261 @@
import { useState, useMemo } from 'react';
import classes from './Repo.module.css';
import {
IconSettings,
IconInfoCircle,
IconChevronDown,
IconChevronUp,
IconBellOff,
IconLockPlus,
} from '@tabler/icons-react';
import StorageBar from '../UI/StorageBar/StorageBar';
import QuickCommands from './QuickCommands/QuickCommands';
import { Repository, WizardEnvType, Optional } from '~/types';
import { fromUnixTime, formatDistanceStrict } from 'date-fns';
import useMedia from 'use-media';
type RepoProps = Omit<Repository, 'unixUser' | 'displayDetails'> & {
repoManageEditHandler: () => void;
wizardEnv: Optional<WizardEnvType>;
};
export default function Repo(props: RepoProps) {
const isMobile = useMedia({ maxWidth: 1000 });
const currentDate = useMemo(() => new Date(), []);
//Load displayDetails from LocalStorage
const displayDetailsFromLS = (): boolean => {
const key = `displayDetailsRepo${props.id}`;
try {
const storedValue = localStorage.getItem(key);
if (storedValue === null) {
const defaultValue = true;
localStorage.setItem(key, JSON.stringify(defaultValue));
return defaultValue;
}
const parsedValue = JSON.parse(storedValue);
if (typeof parsedValue === 'boolean') {
return parsedValue;
}
localStorage.removeItem(key);
return true;
} catch (error) {
localStorage.removeItem(key);
return true;
}
};
//States
const [displayDetails, setDisplayDetails] = useState(displayDetailsFromLS);
//BUTTON : Display or not repo details for ONE repo
const displayDetailsForOneHandler = (boolean: boolean) => {
//Update localStorage
localStorage.setItem('displayDetailsRepo' + props.id, JSON.stringify(boolean));
setDisplayDetails(boolean);
};
//Status indicator
const statusIndicator = () => {
return props.status ? classes.statusIndicatorGreen : classes.statusIndicatorRed;
};
//Alert indicator
const alertIndicator = () => {
if (props.alert === 0) {
return (
<div className={classes.alertIcon}>
<IconBellOff size={16} color='grey' />
</div>
);
}
};
const appendOnlyModeIndicator = () => {
if (props.appendOnlyMode) {
return (
<div className={classes.appendOnlyModeIcon}>
<IconLockPlus size={16} color='grey' />
</div>
);
}
};
const mobileView = () => {
return (
<>
<div className={classes.RepoClose}>
<div className={classes.closeFlex}>
<div className={classes.leftGroup}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='#637381' />
<div className={classes.toolTip}>{props.comment}</div>
</div>
)}
<div className={classes.lastSave}>
<span
title={
props.lastSave === 0 ? undefined : fromUnixTime(props.lastSave).toLocaleString()
}
>
{props.lastSave === 0
? '-'
: formatDistanceStrict(fromUnixTime(props.lastSave), currentDate, {
addSuffix: true,
})}
</span>
</div>
</div>
</div>
</>
);
};
if (isMobile) {
return mobileView();
} else {
return (
<>
{displayDetails ? (
<>
<div className={classes.RepoOpen}>
<div className={classes.indicatorsFlex}>
<div className={statusIndicator()} />
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='grey' />
<div className={classes.toolTip}>{props.comment}</div>
</div>
)}
{appendOnlyModeIndicator()}
{alertIndicator()}
<QuickCommands
repositoryName={props.repositoryName}
lanCommand={props.lanCommand}
wizardEnv={props.wizardEnv}
/>
</div>
<div className={classes.aliasFlex}>
<div className={classes.alias}>{props.alias}</div>
</div>
<table className={classes.tabInfo}>
<thead>
<tr>
<th style={{ width: '15%' }}>Repository</th>
<th style={{ width: '10%' }}>Storage Size</th>
<th style={{ width: '30%' }}>Storage Used</th>
<th style={{ width: '15%' }}>Last change</th>
<th style={{ width: '10%' }}>Edit</th>
</tr>
</thead>
<tbody>
<tr>
<th>{props.repositoryName}</th>
<th>{props.storageSize} GB</th>
<th style={{ padding: '0 4% 0 4%' }}>
<StorageBar storageUsed={props.storageUsed} storageSize={props.storageSize} />
</th>
<th>
<div
className={classes.lastSave}
title={
props.lastSave === 0
? undefined
: fromUnixTime(props.lastSave).toLocaleString()
}
>
{props.lastSave === 0
? '-'
: formatDistanceStrict(fromUnixTime(props.lastSave), currentDate, {
addSuffix: true,
})}
</div>
</th>
<th>
<div className={classes.editButton}>
<IconSettings
width={24}
color='#6d4aff'
onClick={() => props.repoManageEditHandler()}
/>
</div>
</th>
</tr>
</tbody>
</table>
</div>
</>
) : (
<>
<div className={classes.RepoClose}>
<div className={classes.closeFlex}>
<div className={classes.leftGroup}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='#637381' />
<div className={classes.toolTip}>{props.comment}</div>
</div>
)}
<div className={classes.lastSave}>
<span
title={
props.lastSave === 0
? undefined
: fromUnixTime(props.lastSave).toLocaleString()
}
>
{props.lastSave === 0
? '-'
: formatDistanceStrict(fromUnixTime(props.lastSave), currentDate, {
addSuffix: true,
})}
</span>
</div>
</div>
</div>
</>
)}
{displayDetails ? (
<div className={classes.chevron}>
<IconChevronUp
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(false);
}}
/>
</div>
) : (
<div className={classes.chevron}>
<IconChevronDown
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(true);
}}
/>
</div>
)}
</>
);
}
}

View file

@ -1,39 +0,0 @@
//Lib
import classes from './CopyButton.module.css';
import { useState } from 'react';
import { IconCopy } from '@tabler/icons-react';
export default function CopyButton(props) {
//State
const [isCopied, setIsCopied] = useState(false);
//Function
const handleCopy = async (data) => {
navigator.clipboard
.writeText(data)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<button
className={classes.copyButton}
onClick={() => handleCopy(props.dataToCopy)}
>
<IconCopy color='#65748b' stroke={1.25} size={props.size} />
</button>
{isCopied ? (
<span className={classes.copyValid}>Copied !</span>
) : null}
</>
);
}

View file

@ -1,26 +1,35 @@
.copyButton {
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
display: flex;
align-items: center;
}
.copyButton span {
font-size: 0.95rem;
color: #6d4aff;
margin-right: 5px;
user-select: text;
}
.copyValid {
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@keyframes scale-in-center {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}

View file

@ -0,0 +1,46 @@
import classes from './CopyButton.module.css';
import { useState, ReactNode } from 'react';
import { IconChecks, IconCopy } from '@tabler/icons-react';
type CopyButtonProps = {
dataToCopy: string;
children?: ReactNode;
displayIconConfirmation?: boolean;
size?: number;
stroke?: number;
};
export default function CopyButton(props: CopyButtonProps) {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = async (data: string) => {
navigator.clipboard
.writeText(data)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<button className={classes.copyButton} onClick={() => handleCopy(props.dataToCopy)}>
{props.children}
{isCopied && props.displayIconConfirmation ? (
<IconChecks color='#07bc0c' stroke={props.stroke || 1.25} size={props.size} />
) : (
<IconCopy color='#65748b' stroke={props.stroke || 1.25} size={props.size} />
)}
</button>
{isCopied
? !props.displayIconConfirmation && <span className={classes.copyValid}>Copied !</span>
: null}
</>
);
}

View file

@ -1,6 +0,0 @@
//Lib
import classes from './Error.module.css';
export default function Error(props) {
return <div className={classes.errorMessage}>{props.message}</div>;
}

View file

@ -1,18 +1,18 @@
.errorMessage {
margin: 15px 0px;
background-color: red;
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
margin: 15px 0px;
background-color: red;
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
}
@keyframes myAnim {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}

View file

@ -0,0 +1,9 @@
import classes from './Error.module.css';
type ErrorProps = {
message: string;
};
export default function Error(props: ErrorProps) {
return <div className={classes.errorMessage}>{props.message}</div>;
}

View file

@ -1,6 +0,0 @@
//Lib
import classes from './Info.module.css';
export default function Info(props) {
return <div className={classes.infoMessage}>{props.message}</div>;
}

View file

@ -1,18 +1,18 @@
.infoMessage {
margin: 15px 0px;
background-color: rgb(17, 147, 0);
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
margin: 15px 0px;
background-color: rgb(17, 147, 0);
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
}
@keyframes myAnim {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}

View file

@ -0,0 +1,17 @@
import { ReactNode } from 'react';
import classes from './Info.module.css';
type InfoProps = {
message: string;
color?: string;
children?: ReactNode;
};
export default function Info(props: InfoProps) {
return (
<div className={classes.infoMessage} style={{ backgroundColor: props.color }}>
{props.message}
{props.children}
</div>
);
}

View file

@ -1,24 +0,0 @@
//Lib
import classes from './Footer.module.css';
import packageInfo from '../../../../package.json';
function Footer() {
return (
<div className={classes.footer}>
<p>
About{' '}
<a
className={classes.site}
target='_blank'
href='https://borgwarehouse.com/'
rel='noreferrer'
>
BorgWarehouse
</a>{' '}
- v{packageInfo.version}
</p>
</div>
);
}
export default Footer;

View file

@ -1,26 +1,25 @@
.footer {
color: #494b7a;
text-align: center;
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
color: #494b7a;
text-align: center;
position: absolute;
bottom: 0;
width: 100%;
}
.footer p {
padding-left: 70px;
padding-left: 70px;
}
a.site {
color: #6d4aff;
text-decoration: none;
color: #6d4aff;
text-decoration: none;
}
@media all and (max-width: 1000px) {
.footer {
width: 100%;
}
.footer p {
padding-left: 0;
}
.footer {
width: 100%;
}
.footer p {
padding-left: 0;
}
}

View file

@ -0,0 +1,23 @@
import classes from './Footer.module.css';
import packageInfo from '~/package.json';
function Footer() {
return (
<div className={classes.footer}>
<p>
About{' '}
<a
className={classes.site}
target='_blank'
href='https://borgwarehouse.com/'
rel='noreferrer'
>
BorgWarehouse
</a>{' '}
- v{packageInfo.version}
</p>
</div>
);
}
export default Footer;

View file

@ -1,21 +0,0 @@
//Lib
import classes from './Header.module.css';
//Components
import Nav from './Nav/Nav';
function Header() {
return (
<header className={classes.Header}>
<div className={[classes.flex, 'container'].join(' ')}>
<div className={classes.logo}>BorgWarehouse</div>
<nav>
<Nav />
</nav>
</div>
</header>
);
}
export default Header;

View file

@ -1,31 +1,31 @@
.Header {
width: 100%;
background: #111827;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
height: 50px;
color: white;
display: flex;
align-items: center;
position: static;
top: 0;
z-index: 1000;
width: 100%;
background: #111827;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
height: 50px;
color: white;
display: flex;
align-items: center;
position: static;
top: 0;
z-index: 1000;
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1500px;
margin: auto;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1500px;
margin: auto;
}
.logo {
font-size: 1.5em;
font-weight: bold;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
margin-left: 20px;
font-size: 1.5em;
font-weight: bold;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
margin-left: 70px;
}

View file

@ -0,0 +1,28 @@
import Image from 'next/image';
import classes from './Header.module.css';
import Nav from './Nav/Nav';
function Header() {
return (
<header className={classes.Header}>
<div className={[classes.flex, 'container'].join(' ')}>
<div className={classes.logo}>
<Image
src='/borgwarehouse-logo-violet.svg'
alt='BorgWarehouse'
width={225}
height={40}
className={classes.logoImage}
priority
/>
</div>
<nav>
<Nav />
</nav>
</div>
</header>
);
}
export default Header;

View file

@ -1,57 +0,0 @@
//Lib
import classes from './Nav.module.css';
import { IconUser, IconLogout } from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useSession, signOut } from 'next-auth/react';
export default function Nav() {
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
const { status, data } = useSession();
//Function
const onLogoutClickedHandler = async () => {
//This bug is open : https://github.com/nextauthjs/next-auth/issues/1542
//I put redirect to false and redirect with router.
//The result on logout click is an ugly piece of page for a few milliseconds before returning to the login page.
//It's ugly if you are perfectionist but functional and invisible for most of users while waiting for a next-auth fix.
await signOut({ redirect: false });
router.replace('/login');
};
return (
<ul className={classes.Nav}>
<li
style={{ margin: '0px 15px 0px 0px' }}
className={classes.account}
>
<Link
href='/account'
className={
currentRoute === '/account' ? classes.active : null
}
>
<div className={classes.user}>
<div>
<IconUser size={28} />
</div>
<div className={classes.username}>
{status === 'authenticated' && data.user.name}
</div>
</div>
</Link>
</li>
<li>
<div className={classes.logout}>
<a onClick={onLogoutClickedHandler}>
<IconLogout size={28} />
</a>
</div>
</li>
</ul>
);
}

View file

@ -1,54 +1,54 @@
.Nav {
list-style-type: none;
margin: 0px 15px 0px 0px;
padding: 0;
display: flex;
list-style-type: none;
margin: 0px 15px 0px 0px;
padding: 0;
display: flex;
}
.user {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.username::first-letter {
text-transform: capitalize;
text-transform: capitalize;
}
.account {
background: none;
border: none;
cursor: pointer;
color: #494b7a;
background: none;
border: none;
cursor: pointer;
color: #494b7a;
}
.account a {
color: #494b7a;
text-decoration: none;
color: #494b7a;
text-decoration: none;
}
.account :focus,
.account .active,
.account :hover {
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
}
.logout {
background: none;
border: none;
cursor: pointer;
color: #494b7a;
background: none;
border: none;
cursor: pointer;
color: #494b7a;
}
.logout :focus,
.logout .active,
.logout :hover {
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
}
@media all and (max-width: 1000px) {
.account {
display: none;
}
.account {
display: none;
}
}

View file

@ -0,0 +1,43 @@
import classes from './Nav.module.css';
import { IconUser, IconLogout } from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useSession, signOut } from 'next-auth/react';
export default function Nav() {
const router = useRouter();
const currentRoute = router.pathname;
const { status, data } = useSession();
const onLogoutClickedHandler = async () => {
//This bug is open : https://github.com/nextauthjs/next-auth/issues/1542
//I put redirect to false and redirect with router.
//The result on logout click is an ugly piece of page for a few milliseconds before returning to the login page.
//It's ugly if you are perfectionist but functional and invisible for most of users while waiting for a next-auth fix.
await signOut({ redirect: false });
router.replace('/login');
};
return (
<ul className={classes.Nav}>
<li style={{ margin: '0px 15px 0px 0px' }} className={classes.account}>
<Link href='/account' className={currentRoute === '/account' ? classes.active : undefined}>
<div className={classes.user}>
<div>
<IconUser size={28} />
</div>
<div className={classes.username}>{status === 'authenticated' && data.user?.name}</div>
</div>
</Link>
</li>
<li>
<div className={classes.logout}>
<a onClick={onLogoutClickedHandler}>
<IconLogout size={28} />
</a>
</div>
</li>
</ul>
);
}

View file

@ -1,30 +0,0 @@
//Lib
import Footer from './Footer/Footer';
import Header from './Header/Header';
import NavSide from './NavSide/NavSide';
import classes from './Layout.module.css';
import { useSession } from 'next-auth/react';
function Layout(props) {
//Var
const { status } = useSession();
if (status === 'authenticated') {
return (
<>
<Header />
<NavSide />
<div className={classes.mainWrapper}>{props.children}</div>
<Footer />
</>
);
} else if (status === 'unauthenticated') {
return (
<>
<div className={classes.login}>{props.children}</div>
</>
);
}
}
export default Layout;

View file

@ -1,23 +1,23 @@
.mainWrapper {
margin: auto;
max-width: 1400px;
height: calc(100vh - 100px);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
/* to prevent main content under navside on little screen */
padding-left: 90px;
/* Disable scrollbar */
scrollbar-width: none;
margin: auto;
max-width: 1400px;
height: calc(100vh - 100px);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
/* to prevent main content under navside on little screen */
padding-left: 90px;
/* Disable scrollbar */
scrollbar-width: none;
}
.login {
background-color: #111827;
background-color: #111827;
}
@media all and (max-width: 1000px) {
.mainWrapper {
padding-left: 0px;
}
.mainWrapper {
padding-left: 0px;
}
}

View file

@ -0,0 +1,32 @@
import Footer from './Footer/Footer';
import Header from './Header/Header';
import NavSide from './NavSide/NavSide';
import classes from './Layout.module.css';
import { useSession } from 'next-auth/react';
type LayoutProps = {
children: React.ReactNode;
};
function Layout(props: LayoutProps) {
const { status } = useSession();
if (status === 'authenticated') {
return (
<>
<Header />
<NavSide />
<div className={classes.mainWrapper}>{props.children}</div>
<Footer />
</>
);
} else if (status === 'unauthenticated') {
return (
<>
<div className={classes.login}>{props.children}</div>
</>
);
}
}
export default Layout;

View file

@ -1,56 +0,0 @@
//Lib
import classes from './NavSide.module.css';
import {
IconServer,
IconSettingsAutomation,
IconActivityHeartbeat,
} from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
//Composants
export default function NavSide() {
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
return (
<ul className={classes.NavSide}>
<li className={classes.NavSideItem}>
<Link
href='/'
className={currentRoute === '/' ? classes.active : null}
>
<IconServer size={40} />
</Link>
<span className={classes.tooltip}>Repositories</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/setup-wizard/1'
className={
currentRoute === '/setup-wizard/[slug]'
? classes.active
: null
}
>
<IconSettingsAutomation size={40} />
</Link>
<span className={classes.tooltip}>Setup Wizard</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/monitoring'
className={
currentRoute === '/monitoring' ? classes.active : null
}
>
<IconActivityHeartbeat size={40} />
</Link>
<span className={classes.tooltip}>Monitoring</span>
</li>
</ul>
);
}

View file

@ -1,74 +1,74 @@
/* NAVSIDE */
.NavSide {
display: flex;
flex-direction: column;
justify-content: flex-start;
position: absolute;
top: 50px;
left: 0;
bottom: 0;
text-align: center;
/* border-right: 2px solid #e5e7eb; */
height: calc(100% - 50px);
width: 70px;
list-style-type: none;
/* background: #1b1340; */
background: #111827;
/* box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); */
display: flex;
flex-direction: column;
justify-content: flex-start;
position: absolute;
top: 50px;
left: 0;
bottom: 0;
text-align: center;
/* border-right: 2px solid #e5e7eb; */
height: calc(100% - 50px);
width: 70px;
list-style-type: none;
/* background: #1b1340; */
background: #111827;
/* box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); */
}
ul.NavSide {
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
}
/* NAV SIDE ITEMS */
.NavSideItem {
margin: 0px 0px 30px 0px;
margin: 0px 0px 30px 0px;
}
.NavSideItem a {
text-decoration: none;
color: #494b7a;
font-weight: bold;
text-decoration: none;
color: #494b7a;
font-weight: bold;
}
.NavSideItem a:hover,
.NavSideItem a:focus,
.NavSideItem a.active {
color: #6d4aff;
font-weight: bold;
/* border-bottom: 2px solid #6d4aff; */
/* padding-bottom: 15px; */
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
font-weight: bold;
/* border-bottom: 2px solid #6d4aff; */
/* padding-bottom: 15px; */
text-shadow: #6d4aff 0px 0px 18px;
}
.tooltip {
visibility: hidden;
width: 120px;
/* background-color: #1b1340; */
background-color: #111827;
color: #d1d5db;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
margin: 5px 0 0 20px;
opacity: 0;
transition: 0.5s opacity;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.251);
visibility: hidden;
width: 120px;
/* background-color: #1b1340; */
background-color: #111827;
color: #d1d5db;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
margin: 5px 0 0 20px;
opacity: 0;
transition: 0.5s opacity;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.251);
}
.NavSideItem:hover .tooltip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}
@media all and (max-width: 1000px) {
.NavSide {
display: none;
}
.NavSide {
display: none;
}
}

View file

@ -0,0 +1,38 @@
import classes from './NavSide.module.css';
import { IconServer, IconSettingsAutomation, IconActivityHeartbeat } from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function NavSide() {
const router = useRouter();
const currentRoute = router.pathname;
return (
<ul className={classes.NavSide}>
<li className={classes.NavSideItem}>
<Link href='/' className={currentRoute === '/' ? classes.active : undefined}>
<IconServer size={40} />
</Link>
<span className={classes.tooltip}>Repositories</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/setup-wizard/1'
className={currentRoute === '/setup-wizard/[slug]' ? classes.active : undefined}
>
<IconSettingsAutomation size={40} />
</Link>
<span className={classes.tooltip}>Setup Wizard</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/monitoring'
className={currentRoute === '/monitoring' ? classes.active : undefined}
>
<IconActivityHeartbeat size={40} />
</Link>
<span className={classes.tooltip}>Monitoring</span>
</li>
</ul>
);
}

View file

@ -1,19 +0,0 @@
//Lib
import classes from './ShimmerRepoList.module.css';
export default function ShimmerRepoList() {
return (
<div className={classes.container}>
<div className={classes.loadingButtonContainer}>
<div className={classes.buttonIsLoading} />
</div>
<div className={classes.loadingRepoContainer}>
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
</div>
</div>
);
}

View file

@ -1,50 +1,50 @@
.container {
display: flex;
width: 90%;
justify-content: center;
flex-direction: column;
display: flex;
width: 90%;
justify-content: center;
flex-direction: column;
}
.loadingButtonContainer {
display: flex;
flex-direction: column;
margin: 20px auto;
width: 90%;
justify-content: center;
display: flex;
flex-direction: column;
margin: 20px auto;
width: 90%;
justify-content: center;
}
.buttonIsLoading {
height: 62px;
width: 211px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
height: 62px;
width: 211px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
}
.loadingRepoContainer {
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
margin: auto;
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
margin: auto;
}
.repoIsLoading {
width: 100%;
height: 65px;
margin: 20px auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
width: 100%;
height: 65px;
margin: 20px auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
}
@keyframes shine {
to {
background-position-x: -200%;
}
to {
background-position-x: -200%;
}
}

View file

@ -0,0 +1,22 @@
import classes from './ShimmerRepoList.module.css';
const LOADING_REPO_COUNT = 5;
function ShimmerRepoItem() {
return <div className={classes.repoIsLoading} />;
}
export default function ShimmerRepoList() {
return (
<div className={classes.container}>
<div className={classes.loadingButtonContainer}>
<div className={classes.buttonIsLoading} />
</div>
<div className={classes.loadingRepoContainer}>
{Array.from({ length: LOADING_REPO_COUNT }, (_, i) => (
<ShimmerRepoItem key={i} />
))}
</div>
</div>
);
}

View file

@ -1,32 +0,0 @@
//Lib
import classes from './StorageBar.module.css';
export default function StorageBar(props) {
//Var
//storageUsed is in octet, storageSize is in GB. Round to 1 decimal for %.
const storageUsedPercent = (
((props.storageUsed / 1000000) * 100) /
props.storageSize
).toFixed(1);
return (
<div className={classes.barContainer}>
<div className={classes.barBackground}>
<div
style={{
maxWidth: '100%',
width: storageUsedPercent + '%',
transition: 'width 0.5s 0s ease',
}}
>
<div className={classes.progressionStyle} />
</div>
<div className={classes.tooltip}>
{storageUsedPercent}% (
{(props.storageUsed / 1000000).toFixed(1)} GB /{' '}
{props.storageSize} GB)
</div>
</div>
</div>
);
}

View file

@ -1,36 +1,36 @@
.barContainer {
margin: auto;
margin: auto;
}
.barBackground {
background-color: #704dff5e;
border-radius: 3px;
height: 19px;
width: 100%;
position: relative;
background-color: #704dff5e;
border-radius: 3px;
height: 19px;
width: 100%;
position: relative;
}
.progressionStyle {
background-color: #704dff;
border-radius: 3px;
height: 19px;
width: 100%;
background-color: #704dff;
border-radius: 3px;
height: 19px;
width: 100%;
}
.tooltip {
visibility: hidden;
width: auto;
height: auto;
color: #fff;
text-align: center;
opacity: 0;
transition: 0.5s opacity;
position: absolute;
left: calc(30%);
top: 0px;
visibility: hidden;
width: auto;
height: auto;
color: #fff;
text-align: center;
opacity: 0;
transition: 0.5s opacity;
position: absolute;
left: calc(30%);
top: 0px;
}
.barBackground:hover .tooltip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}

View file

@ -0,0 +1,33 @@
import classes from './StorageBar.module.css';
type StorageBarProps = {
storageUsed: number;
storageSize: number;
};
export default function StorageBar(props: StorageBarProps) {
//storageUsed is in kB, storageSize is in GB. Round to 1 decimal for %.
const storageUsedPercent = (((props.storageUsed / 1024 ** 2) * 100) / props.storageSize).toFixed(
1
);
return (
<div className={classes.barContainer}>
<div className={classes.barBackground}>
<div
style={{
maxWidth: '100%',
width: storageUsedPercent + '%',
transition: 'width 0.5s 0s ease',
}}
>
<div className={classes.progressionStyle} />
</div>
<div className={classes.tooltip}>
{storageUsedPercent}% ({(props.storageUsed / 1024 ** 2).toFixed(1)} GB /{' '}
{props.storageSize} GB)
</div>
</div>
</div>
);
}

View file

@ -1,26 +0,0 @@
//Lib
import classes from './Switch.module.css';
export default function Switch(props) {
return (
<>
<div className={classes.switchWrapper}>
<div className={classes.switch}>
<label className={classes.pureMaterialSwitch}>
<input
checked={props.checked}
disabled={props.disabled}
type='checkbox'
onChange={(e) => props.onChange(e.target.checked)}
/>
<span>{props.switchName}</span>
</label>
</div>
<div className={classes.switchDescription}>
<span>{props.switchDescription}</span>
</div>
</div>
</>
);
}

View file

@ -1,157 +1,84 @@
/* Wrapper styles */
.switchWrapper {
display: flex;
flex-direction: column;
margin-bottom: 20px;
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 20px;
}
/* Switch container */
.switch {
display: flex;
display: flex;
align-items: center;
gap: 10px;
}
.switchDescription {
display: flex;
margin: 8px 0px 0px 0px;
color: #6c737f;
font-size: 0.875rem;
}
.pureMaterialSwitch {
z-index: 0;
position: relative;
display: inline-block;
color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.87);
font-family: var(
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui,
-apple-system
);
font-size: 16px;
line-height: 1.5;
/* Label */
.switchLabel {
display: inline-flex;
align-items: center;
gap: 10px;
cursor: pointer;
position: relative;
user-select: none;
}
/* Input */
.pureMaterialSwitch > input {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
z-index: -1;
position: absolute;
right: 6px;
top: -8px;
display: block;
margin: 0;
border-radius: 50%;
width: 40px;
height: 40px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
outline: none;
opacity: 0;
transform: scale(1);
pointer-events: none;
transition:
opacity 0.3s 0.1s,
transform 0.2s 0.1s;
.switchLabel input {
display: none;
}
/* Span */
.pureMaterialSwitch > span {
display: inline-block;
width: 100%;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
color: #494b7a;
/* Slider */
.switchSlider {
position: relative;
width: 40px;
height: 20px;
background: #ccc;
border-radius: 12px;
transition: #ccc 0.3s ease;
}
/* Track */
.pureMaterialSwitch > span::before {
content: '';
float: right;
display: inline-block;
margin: 5px 0 5px 30px;
border-radius: 7px;
width: 36px;
height: 14px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
vertical-align: top;
transition:
background-color 0.2s,
opacity 0.2s;
.switchSlider::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
background: #fff;
border-radius: 50%;
transition: transform 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* Thumb */
.pureMaterialSwitch > span::after {
content: '';
position: absolute;
top: 2px;
right: 16px;
border-radius: 50%;
width: 20px;
height: 20px;
background-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255));
box-shadow:
0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
transition:
background-color 0.2s,
transform 0.2s;
/* Checked styles */
.switchLabel input:checked + .switchSlider {
background: #704dff;
}
/* Checked */
.pureMaterialSwitch > input:checked {
right: -10px;
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
.switchLabel input:checked + .switchSlider::after {
transform: translateX(20px);
}
.pureMaterialSwitch > input:checked + span::before {
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
/* Disabled styles */
.switchLabel input:disabled + .switchSlider {
background: #e0e0e0;
}
.pureMaterialSwitch > input:checked + span::after {
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
transform: translateX(16px);
.switchLabel input:disabled + .switchSlider::after {
background: #bdbdbd;
}
/* Active */
.pureMaterialSwitch > input:active {
opacity: 1;
transform: scale(0);
transition:
transform 0s,
opacity 0s;
/* Switch text */
.switchText {
font-size: 1rem;
color: #494b7a;
font-weight: 500;
}
.pureMaterialSwitch > input:active + span::before {
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
/* Description */
.switchDescription {
font-size: 0.875rem;
color: #6c737f;
margin-top: 4px;
}
.pureMaterialSwitch > input:checked:active + span::before {
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
}
/* Disabled */
.pureMaterialSwitch > input:disabled + span {
cursor: wait;
}
/* .pureMaterialSwitch > input:disabled {
opacity: 0;
}
.pureMaterialSwitch > input:disabled + span {
color: rgb(var(--pure-material-onsurface-rgb, 0, 0, 0));
opacity: 0.38;
cursor: default;
}
.pureMaterialSwitch > input:disabled + span::before {
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
}
.pureMaterialSwitch > input:checked:disabled + span::before {
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
} */

View file

@ -0,0 +1,45 @@
import { Optional } from '~/types';
import classes from './Switch.module.css';
import { useLoader } from '~/contexts/LoaderContext';
import { useEffect } from 'react';
type SwitchProps = {
switchName: string;
switchDescription: string;
checked: Optional<boolean>;
disabled: boolean;
loading?: boolean;
onChange: (checked: boolean) => void;
};
export default function Switch(props: SwitchProps) {
const { start, stop } = useLoader();
useEffect(() => {
if (props.loading) {
start();
} else {
stop();
}
}, [props.loading, start, stop]);
return (
<div className={classes.switchWrapper}>
<div className={classes.switch}>
<label className={classes.switchLabel}>
<>
<input
type='checkbox'
checked={props.checked || false}
disabled={props.disabled}
onChange={(e) => props.onChange(e.target.checked)}
/>
<span className={classes.switchSlider}></span>
</>
<span className={classes.switchText}>{props.switchName}</span>
</label>
</div>
<p className={classes.switchDescription}>{props.switchDescription}</p>
</div>
);
}

View file

@ -1,69 +0,0 @@
//Lib
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconDeviceDesktopAnalytics, IconTerminal2 } from '@tabler/icons-react';
function WizardStep1() {
return (
<div className={classes.container}>
<h1>
<IconTerminal2 className={classes.icon} />
Command Line Interface
</h1>
<div className={classes.description}>
We recommend using the official <b>BorgBackup</b> client which
is supported by most Linux distributions.
<br />
More information about installation can be{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/installation.html'
target='_blank'
rel='noreferrer'
>
found here
</a>
.<br />
To <b>automate your backup</b>, you can also use{' '}
<a
href='https://torsion.org/borgmatic/'
target='_blank'
rel='noreferrer'
>
Borgmatic
</a>{' '}
which is a{' '}
<a
href='https://packages.debian.org/buster/borgmatic'
target='_blank'
rel='noreferrer'
>
Debian package
</a>
. On the step 4, you will find a pattern of default config.
</div>
<div className={classes.separator}></div>
<h1>
<IconDeviceDesktopAnalytics className={classes.icon} />
Graphical User Interface
</h1>
<div className={classes.description}>
<b>Vorta</b> is an opensource (GPLv3) backup client for Borg
Backup.
<br />
It runs on Linux, MacOS and Windows (via Windows Linux
Subsystem (WSL)). Find the right way to install it{' '}
<a
href='https://vorta.borgbase.com/'
target='_blank'
rel='noreferrer'
>
just here
</a>
.
</div>
<img src='/vorta-demo.gif' alt='Vorta GIF' />
</div>
);
}
export default WizardStep1;

View file

@ -1,137 +1,137 @@
.container {
margin: 40px 20px 20px 5px;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
border-radius: 5px;
text-align: left;
padding: 30px 70px;
animation: animStep ease-in 0.3s 1 normal none;
margin: 40px 20px 20px 5px;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
border-radius: 5px;
text-align: left;
padding: 30px 70px;
animation: animStep ease-in 0.3s 1 normal none;
}
@keyframes animStep {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
50% {
opacity: 0.5;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.container h1 {
text-align: center;
font-size: 1.7em;
padding-bottom: 20px;
display: flex;
align-items: center;
text-align: center;
font-size: 1.7em;
padding-bottom: 20px;
display: flex;
align-items: center;
}
.container a {
color: #6d4aff;
color: #6d4aff;
}
.separator {
height: 15px;
height: 15px;
}
.container {
line-height: 1.8em;
font-size: 1.05em;
line-height: 1.8em;
font-size: 1.05em;
}
h1 .icon {
color: #6d4aff;
margin-right: 5px;
color: #6d4aff;
margin-right: 5px;
}
.container img {
max-width: 650px;
max-width: 650px;
}
.code {
background-color: #111827;
color: #f8f8f2;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
padding: 5px 15px;
border-radius: 5px;
display: inline-block;
margin: 5px 0px 10px 0px;
font-size: 0.8em;
white-space: pre;
line-height: 1em;
background-color: #111827;
color: #f8f8f2;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
padding: 5px 15px;
border-radius: 5px;
display: inline-block;
margin: 5px 0px 10px 0px;
font-size: 0.8em;
white-space: pre;
line-height: 1em;
}
.verifyOrange {
background-color: #ff7a1b;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
background-color: #ff7a1b;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
}
.verifyRed {
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 10px;
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 10px;
}
.verifyOrange li,
.container li {
list-style: disc;
margin-top: 1px;
margin-left: 30px;
list-style: disc;
margin-top: 1px;
margin-left: 30px;
}
.verifyOrange li .sshPublicKey {
background-color: #282a36;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
border-radius: 5px;
padding: 5px;
background-color: #282a36;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
border-radius: 5px;
padding: 5px;
}
.verifyRed .alert,
.verifyOrange .alert {
text-align: center;
font-size: 1.1em;
padding-bottom: 10px;
display: flex;
align-items: center;
text-align: center;
font-size: 1.1em;
padding-bottom: 10px;
display: flex;
align-items: center;
}
.iconAlert {
margin-right: 5px;
margin-right: 5px;
}
.note {
font-style: italic;
color: #494b7a4d;
font-size: 0.8em;
font-style: italic;
color: #494b7a4d;
font-size: 0.8em;
}
.note a {
color: #6d4aff73;
color: #6d4aff73;
}

View file

@ -0,0 +1,56 @@
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconDeviceDesktopAnalytics, IconTerminal2 } from '@tabler/icons-react';
function WizardStep1() {
return (
<div className={classes.container}>
<h1>
<IconTerminal2 className={classes.icon} />
Command Line Interface
</h1>
<div className={classes.description}>
We recommend using the official <b>BorgBackup</b> client which is supported by most Linux
distributions.
<br />
More information about installation can be{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/installation.html'
target='_blank'
rel='noreferrer'
>
found here
</a>
.<br />
To <b>automate your backup</b>, you can also use{' '}
<a href='https://torsion.org/borgmatic/' target='_blank' rel='noreferrer'>
Borgmatic
</a>{' '}
which is a{' '}
<a href='https://packages.debian.org/buster/borgmatic' target='_blank' rel='noreferrer'>
Debian package
</a>
. On the step 4, you will find a pattern of default config.
</div>
<div className={classes.separator}></div>
<h1>
<IconDeviceDesktopAnalytics className={classes.icon} />
Graphical User Interface
</h1>
<div className={classes.description}>
BorgWarehouse is <b>compatible with all BorgBackup graphical clients</b>, including the
well-known{' '}
<a href='https://apps.gnome.org/PikaBackup/' target='_blank' rel='noreferrer'>
Pika Backup
</a>{' '}
and{' '}
<a href='https://vorta.borgbase.com/' target='_blank' rel='noreferrer'>
Vorta
</a>
.
</div>
</div>
);
}
export default WizardStep1;

View file

@ -1,149 +0,0 @@
//Lib
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconTool, IconAlertCircle } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep2(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
return (
<div className={classes.container}>
<h1>
<IconTool className={classes.icon} />
Initialize a repository
</h1>
<div className={classes.description}>
To initialize your repository with borgbackup :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg init -e repokey-blake2 ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg init -e repokey-blake2 ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<div className={classes.note}>
The encryption mode is the one recommended by BorgBackup.
For more information,{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/init.html?highlight=init#more-encryption-modes'
rel='noreferrer'
target='_blank'
>
click here
</a>
.
</div>
</div>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic and have <b>already edited</b> the configuration file
(find a sample on the step 4) :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borgmatic init -e repokey-blake2
</div>
<CopyButton dataToCopy='borgmatic init -e repokey-blake2' />
</div>
</div>
<h2>Vorta</h2>
<div className={classes.description}>
To "Initialize a new repository" or "Add existing repository",
copy this into the field "Repository URL" of Vorta :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
For more information about the Vorta graphical client, please
refer to{' '}
<a
href='https://vorta.borgbase.com/usage/remote/'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<div className={classes.separator} />
<div className={classes.verifyOrange}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Check the fingerprint of server</b>
</div>
To check that you are talking to the right server, please make
sure to validate one of the following key's fingerprint when you
first connect :
<li>
<span className={classes.sshPublicKey}>
ECDSA : {wizardEnv.SSH_SERVER_FINGERPRINT_ECDSA}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
ED25519 : {wizardEnv.SSH_SERVER_FINGERPRINT_ED25519}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
RSA : {wizardEnv.SSH_SERVER_FINGERPRINT_RSA}
</span>
</li>
</div>
<div className={classes.verifyRed}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Save your passphrase</b>
</div>
Once again, the server cannot access your encrypted backup data
or the encryption passphrase. Remember to put your passphrase in
your password manager when you initialise your repository.
</div>
</div>
);
}
export default WizardStep2;

View file

@ -0,0 +1,132 @@
import { IconAlertCircle, IconTool } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import { WizardStepProps } from '~/types';
import classes from '../WizardStep1/WizardStep1.module.css';
import { lanCommandOption } from '~/helpers/functions';
function WizardStep2(props: WizardStepProps) {
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv?.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedRepo?.lanCommand);
return (
<div className={classes.container}>
<h1>
<IconTool className={classes.icon} />
Initialize a repository
</h1>
<div className={classes.description}>
To initialize your repository with borgbackup :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg init -e repokey-blake2 ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
</div>
<CopyButton
dataToCopy={`borg init -e repokey-blake2 ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}`}
/>
</div>
<div className={classes.note}>
The encryption mode is the one recommended by BorgBackup. For more information,{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/init.html?highlight=init#more-encryption-modes'
rel='noreferrer'
target='_blank'
>
click here
</a>
.
</div>
</div>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic and have <b>already edited</b> the configuration file (find a
sample on the step 4) :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>borgmatic init -e repokey-blake2</div>
<CopyButton dataToCopy='borgmatic init -e repokey-blake2' />
</div>
</div>
<h2>Pika, Vorta...</h2>
<div className={classes.description}>
To &quot;Initialize a new repository&quot; or &quot;Add existing repository&quot;, copy this
into the field &quot;Repository URL&quot; of your graphical client :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
</div>
<CopyButton
dataToCopy={`ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}`}
/>
</div>
</div>
<div className={classes.separator} />
<div className={classes.verifyOrange}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Check the fingerprint of server</b>
</div>
To check that you are talking to the right server, please make sure to validate one of the
following key&apos;s fingerprint when you first connect :
<li>
<span className={classes.sshPublicKey}>
ECDSA : {wizardEnv?.SSH_SERVER_FINGERPRINT_ECDSA}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
ED25519 : {wizardEnv?.SSH_SERVER_FINGERPRINT_ED25519}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
RSA : {wizardEnv?.SSH_SERVER_FINGERPRINT_RSA}
</span>
</li>
</div>
<div className={classes.verifyRed}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Save your passphrase</b>
</div>
Once again, the server cannot access your encrypted backup data or the encryption
passphrase. Remember to put your passphrase in your password manager when you initialise
your repository.
</div>
</div>
);
}
export default WizardStep2;

View file

@ -1,184 +0,0 @@
//Lib
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconChecks, IconPlayerPlay } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep3(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
return (
<div className={classes.container}>
<h1>
<IconPlayerPlay className={classes.icon} />
Launch a backup
</h1>
<div className={classes.description}>
To launch a backup with borgbackup :
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg create ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /your/pathToBackup
</div>
<CopyButton
dataToCopy={`borg create ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /your/pathToBackup`}
/>
</div>
</div>
<div className={classes.separator}></div>
<h1>
<IconChecks className={classes.icon} />
Check your backup{' '}
<span style={{ color: '#494b7a4d', fontWeight: 'normal' }}>
&nbsp;(always)
</span>
</h1>
<div className={classes.description}>
BorgWarehouse <b>only stores</b> your backups. They are
encrypted and <b>there is no way</b> for BorgWarehouse to know
if the backup is intact.
<br />
You should regularly test your backups and check that the data
is recoverable.{' '}
<b>
BorgWarehouse cannot do this for you and does not guarantee
anything.
</b>
<br />
</div>
<span className={classes.description}>
Based on the Borg documentation, you have multiple ways to check
that your backups are correct with your tools (tar, rsync, diff
or other tools).
<br />
<li>Check the integrity of a repository with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg check -v --progress ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg check -v --progress ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>List the remote archives with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg list ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg list ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>Download a remote archive with the following command :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg export-tar --tar-filter="gzip -9" ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 archive1.tar.gz
</div>
<CopyButton
dataToCopy={`borg export-tar --tar-filter="gzip -9" ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 archive1.tar.gz`}
/>
</div>
<li>
Mount an archive to compare or backup some files without
download all the archive :
</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg mount ssh://
{UNIX_USER}@{FQDN}:{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /tmp/yourMountPoint
</div>
<CopyButton
dataToCopy={`borg mount ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /tmp/yourMountPoint`}
/>
</div>
<br />
To verify the consistency of a repository and the corresponding
archives, please refer to{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/check.html'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
</span>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, please refer to{' '}
<a
href='https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#consistency-check-configuration'
rel='noreferrer'
target='_blank'
>
this documentation
</a>{' '}
for a consistency check.
</div>
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to{' '}
<a
href='https://vorta.borgbase.com/usage/'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
</div>
);
}
export default WizardStep3;

View file

@ -0,0 +1,174 @@
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconChecks, IconPlayerPlay } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import { WizardStepProps } from '~/types';
import { lanCommandOption } from '~/helpers/functions';
function WizardStep3(props: WizardStepProps) {
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv?.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedRepo?.lanCommand);
return (
<div className={classes.container}>
<h1>
<IconPlayerPlay className={classes.icon} />
Launch a backup
</h1>
<div className={classes.description}>
To launch a backup with borgbackup :
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg create ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
::archive1 /your/pathToBackup
</div>
<CopyButton
dataToCopy={`borg create ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}::archive1 /your/pathToBackup`}
/>
</div>
</div>
<div className={classes.separator}></div>
<h1>
<IconChecks className={classes.icon} />
Check your backup{' '}
<span style={{ color: '#494b7a4d', fontWeight: 'normal' }}>&nbsp;(always)</span>
</h1>
<div className={classes.description}>
BorgWarehouse <b>only stores</b> your backups. They are encrypted and <b>there is no way</b>{' '}
for BorgWarehouse to know if the backup is intact.
<br />
You should regularly test your backups and check that the data is recoverable.{' '}
<b>BorgWarehouse cannot do this for you and does not guarantee anything.</b>
<br />
</div>
<span className={classes.description}>
Based on the Borg documentation, you have multiple ways to check that your backups are
correct with your tools (tar, rsync, diff or other tools).
<br />
<li>Check the integrity of a repository with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg check -v --progress ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
</div>
<CopyButton
dataToCopy={`borg check -v --progress ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}`}
/>
</div>
<li>List the remote archives with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg list ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
</div>
<CopyButton
dataToCopy={`borg list ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}`}
/>
</div>
<li>Download a remote archive with the following command :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg export-tar --tar-filter=&quot;gzip -9&quot; ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
::archive1 archive1.tar.gz
</div>
<CopyButton
dataToCopy={`borg export-tar --tar-filter="gzip -9" ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}::archive1 archive1.tar.gz`}
/>
</div>
<li>Mount an archive to compare or backup some files without download all the archive :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg mount ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedRepo?.repositoryName}
::archive1 /tmp/yourMountPoint
</div>
<CopyButton
dataToCopy={`borg mount ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}::archive1 /tmp/yourMountPoint`}
/>
</div>
<br />
To verify the consistency of a repository and the corresponding archives, please refer to{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/check.html'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
</span>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, please refer to{' '}
<a
href='https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#consistency-check-configuration'
rel='noreferrer'
target='_blank'
>
this documentation
</a>{' '}
for a consistency check.
</div>
<h2>Pika, Vorta...</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to{' '}
<a href='https://vorta.borgbase.com/usage/' rel='noreferrer' target='_blank'>
this documentation
</a>
.<br />
If you are using Pika, please refer to{' '}
<a href='https://apps.gnome.org/PikaBackup/' rel='noreferrer' target='_blank'>
this documentation
</a>
.
</div>
</div>
);
}
export default WizardStep3;

View file

@ -1,125 +0,0 @@
//Lib
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconWand } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep4(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
const configBorgmatic = `location:
# List of source directories to backup.
source_directories:
- /your-repo-to-backup
- /another/repo-to-backup
repositories:
# Paths of local or remote repositories to backup to.
- ssh://${UNIX_USER}@${FQDN}:${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}
storage:
archive_name_format: '{FQDN}-documents-{now}'
encryption_passphrase: "YOUR PASSPHRASE"
retention:
# Retention policy for how many backups to keep.
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
consistency:
# List of checks to run to validate your backups.
checks:
- name: repository
- name: archives
frequency: 2 weeks
#hooks:
# Custom preparation scripts to run.
#before_backup:
# - prepare-for-backup.sh
# Databases to dump and include in backups.
#postgresql_databases:
# - name: users
# Third-party services to notify you if backups aren't happening.
#healthchecks: https://hc-ping.com/be067061-cf96-4412-8eae-62b0c50d6a8c`;
return (
<div className={classes.container}>
<h1>
<IconWand className={classes.icon} />
Automate your backup
</h1>
<div className={classes.description}>
The official borgbackup project provides a script in its
documentation&nbsp;
<a
href='https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups'
rel='noreferrer'
target='_blank'
>
right here
</a>
.
</div>
<div className={classes.separator} />
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer
to&nbsp;
<a
href='https://vorta.borgbase.com/usage/#scheduling-automatic-backups'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, you can check&nbsp;
<a
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
rel='noreferrer'
target='_blank'
>
this documentation&nbsp;
</a>
and <b>adapt</b> and use the following script :
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>{configBorgmatic}</div>
<div
style={{
margin: '15px 0 auto 0',
display: 'flex',
alignContent: 'center',
}}
>
<CopyButton dataToCopy={configBorgmatic} size={32} />
</div>
</div>
</div>
);
}
export default WizardStep4;

View file

@ -0,0 +1,125 @@
import React from 'react';
import classes from '../WizardStep1/WizardStep1.module.css';
import { IconWand } from '@tabler/icons-react';
import CopyButton from '../../UI/CopyButton/CopyButton';
import { WizardStepProps } from '~/types';
import { lanCommandOption } from '~/helpers/functions';
function WizardStep4(props: WizardStepProps) {
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv?.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedRepo?.lanCommand);
const configBorgmatic = `
# List of source directories to backup.
source_directories:
- /your-repo-to-backup
- /another/repo-to-backup
repositories:
# Paths of local or remote repositories to backup to.
- path: ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedRepo?.repositoryName}
archive_name_format: '{FQDN}-documents-{now}'
encryption_passphrase: "YOUR PASSPHRASE"
# Retention policy for how many backups to keep.
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
# List of checks to run to validate your backups.
checks:
- name: repository
- name: archives
frequency: 2 weeks
#hooks:
# Custom preparation scripts to run.
#before_backup:
# - prepare-for-backup.sh
# Databases to dump and include in backups.
#postgresql_databases:
# - name: users
# Third-party services to notify you if backups aren't happening.
#healthchecks: https://hc-ping.com/be067061-cf96-4412-8eae-62b0c50d6a8c`;
return (
<div className={classes.container}>
<h1>
<IconWand className={classes.icon} />
Automate your backup
</h1>
<div className={classes.description}>
The official borgbackup project provides a script in its documentation&nbsp;
<a
href='https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups'
rel='noreferrer'
target='_blank'
>
right here
</a>
.
</div>
<div className={classes.separator} />
<h2>Pika, Vorta...</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to&nbsp;
<a
href='https://vorta.borgbase.com/usage/#scheduling-automatic-backups'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.<br />
If you are using Pika Backup, please refer to&nbsp;
<a
href='https://world.pages.gitlab.gnome.org/pika-backup/help/C/feature-schedule.html'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, you can check&nbsp;
<a
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
rel='noreferrer'
target='_blank'
>
this documentation&nbsp;
</a>
and <b>adapt</b> and use the following script :
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>{configBorgmatic}</div>
<div
style={{
margin: '15px 0 auto 0',
display: 'flex',
alignContent: 'center',
}}
>
<CopyButton dataToCopy={configBorgmatic} size={32} />
</div>
</div>
</div>
);
}
export default WizardStep4;

View file

@ -1,103 +0,0 @@
//Lib
import React from 'react';
import classes from './WizardStepBar.module.css';
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
function WizardStepBar(props) {
////Functions
//Color onClick on a step
const colorHandler = (step) => {
if (step <= props.step) {
return classes.active;
} else {
return classes.inactive;
}
};
//Color onClick on next step button
const colorChevronNextStep = () => {
if (props.step < 4) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
//Color onClick on previous step button
const colorChevronPreviousStep = () => {
if (props.step > 1) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
return (
<div className={classes.stepBarContainer}>
<IconChevronLeft
size={32}
className={colorChevronPreviousStep()}
onClick={props.previousStepHandler}
/>
<ul>
<li
className={colorHandler(2)}
onClick={() => props.setStep(1)}
>
<div
className={[classes.number, colorHandler(1)].join(' ')}
>
1
</div>
<div className={[classes.text, colorHandler(1)].join(' ')}>
Client Setup
</div>
<div className={classes.line}></div>
</li>
<li
className={colorHandler(3)}
onClick={() => props.setStep(2)}
>
<div
className={[classes.number, colorHandler(2)].join(' ')}
>
2
</div>
<div className={[classes.text, colorHandler(2)].join(' ')}>
Init. repository
</div>
<div className={classes.line}></div>
</li>
<li
className={colorHandler(4)}
onClick={() => props.setStep(3)}
>
<div
className={[classes.number, colorHandler(3)].join(' ')}
>
3
</div>
<div className={[classes.text, colorHandler(3)].join(' ')}>
Launch & Verify
</div>
<div className={classes.line}></div>
</li>
<li onClick={() => props.setStep(4)}>
<div
className={[classes.number, colorHandler(4)].join(' ')}
>
4
</div>
<div className={[classes.text, colorHandler(4)].join(' ')}>
Automation
</div>
</li>
</ul>
<IconChevronRight
size={32}
className={colorChevronNextStep()}
onClick={props.nextStepHandler}
/>
</div>
);
}
export default WizardStepBar;

View file

@ -1,94 +1,94 @@
/* General */
.stepBarContainer {
text-transform: uppercase;
color: #494b7a;
font-size: 1em;
margin-top: 40px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
text-transform: uppercase;
color: #494b7a;
font-size: 1em;
margin-top: 40px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.stepBarContainer li {
display: inline-block;
position: relative;
margin: 0px 30px;
width: 180px;
cursor: pointer;
display: inline-block;
position: relative;
margin: 0px 30px;
width: 180px;
cursor: pointer;
}
.stepBarContainer ul {
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
}
/* Transition Active / Inactive */
.number {
display: inline-block;
position: relative;
color: #494b7a4d;
background: #fff;
border: 1px solid #494b7a4d;
border-radius: 50%;
width: 37px;
padding: 8px 5px;
margin-bottom: 4px;
font-weight: 700;
z-index: 1;
display: inline-block;
position: relative;
color: #494b7a4d;
background: #fff;
border: 1px solid #494b7a4d;
border-radius: 50%;
width: 37px;
padding: 8px 5px;
margin-bottom: 4px;
font-weight: 700;
z-index: 1;
}
.active.number {
box-shadow: 0 0 15px 5px rgba(110, 74, 255, 0.405);
color: #fff;
background: #6d4aff;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
box-shadow: 0 0 15px 5px rgba(110, 74, 255, 0.405);
color: #fff;
background: #6d4aff;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
}
.text {
padding: 8px 0 0 0;
color: #494b7a4d;
padding: 8px 0 0 0;
color: #494b7a4d;
}
.active.text {
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
color: #494b7a;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
color: #494b7a;
}
.line {
background: #6d4aff;
width: 0;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
transition: all 500ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
background: #6d4aff;
width: 0;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
transition: all 500ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
}
.active .line {
background: #6d4aff;
width: 204px;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
background: #6d4aff;
width: 204px;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
}
.activeChevron {
cursor: pointer;
cursor: pointer;
}
.activeChevron:hover {
color: #6d4aff;
color: #6d4aff;
}
.inactiveChevron {
color: #494b7a4d;
color: #494b7a4d;
}

View file

@ -0,0 +1,75 @@
import React from 'react';
import classes from './WizardStepBar.module.css';
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
type WizardStepBarProps = {
step: number;
setStep: (step: number) => void;
previousStepHandler: () => void;
nextStepHandler: () => void;
};
function WizardStepBar(props: WizardStepBarProps) {
//Color onClick on a step
const colorHandler = (step: number) => {
if (step <= props.step) {
return classes.active;
} else {
return classes.inactive;
}
};
//Color onClick on next step button
const colorChevronNextStep = () => {
if (props.step < 4) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
//Color onClick on previous step button
const colorChevronPreviousStep = () => {
if (props.step > 1) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
return (
<div className={classes.stepBarContainer}>
<IconChevronLeft
size={32}
className={colorChevronPreviousStep()}
onClick={props.previousStepHandler}
/>
<ul>
<li className={colorHandler(2)} onClick={() => props.setStep(1)}>
<div className={[classes.number, colorHandler(1)].join(' ')}>1</div>
<div className={[classes.text, colorHandler(1)].join(' ')}>Client Setup</div>
<div className={classes.line}></div>
</li>
<li className={colorHandler(3)} onClick={() => props.setStep(2)}>
<div className={[classes.number, colorHandler(2)].join(' ')}>2</div>
<div className={[classes.text, colorHandler(2)].join(' ')}>Init. repository</div>
<div className={classes.line}></div>
</li>
<li className={colorHandler(4)} onClick={() => props.setStep(3)}>
<div className={[classes.number, colorHandler(3)].join(' ')}>3</div>
<div className={[classes.text, colorHandler(3)].join(' ')}>Launch & Verify</div>
<div className={classes.line}></div>
</li>
<li onClick={() => props.setStep(4)}>
<div className={[classes.number, colorHandler(4)].join(' ')}>4</div>
<div className={[classes.text, colorHandler(4)].join(' ')}>Automation</div>
</li>
</ul>
<IconChevronRight
size={32}
className={colorChevronNextStep()}
onClick={props.nextStepHandler}
/>
</div>
);
}
export default WizardStepBar;

View file

@ -1,94 +0,0 @@
//Lib
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { useState, useEffect } from 'react';
export default function StorageUsedChartBar() {
//States
const [data, setData] = useState([]);
//LifeCycle
useEffect(() => {
const dataFetch = async () => {
try {
const response = await fetch('/api/repo', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setData((await response.json()).repoList);
} catch (error) {
console.log('Fetching datas error');
}
};
dataFetch();
}, []);
////Chart.js
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const options = {
responsive: true,
plugins: {
legend: {
position: 'bottom',
},
title: {
position: 'bottom',
display: true,
text: 'Storage used for each repository',
},
},
scales: {
y: {
max: 100,
min: 0,
ticks: {
// Include a dollar sign in the ticks
callback: function (value) {
return value + '%';
},
stepSize: 10,
},
},
},
};
const labels = data.map((repo) => repo.alias);
const dataChart = {
labels,
datasets: [
{
label: 'Storage used (%)',
//storageUsed is in octet, storageSize is in GB. Round to 1 decimal for %.
data: data.map((repo) =>
(
((repo.storageUsed / 1000000) * 100) /
repo.storageSize
).toFixed(1)
),
backgroundColor: '#704dff',
},
],
};
return <Bar options={options} data={dataChart} />;
}

View file

@ -0,0 +1,82 @@
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { useState, useEffect } from 'react';
import { Repository, Optional } from '~/types';
export default function StorageUsedChartBar() {
const [data, setData] = useState<Optional<Array<Repository>>>();
useEffect(() => {
const dataFetch = async () => {
try {
const response = await fetch('/api/v1/repositories', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setData((await response.json()).repoList);
} catch (error) {
console.log('Fetching datas error');
}
};
dataFetch();
}, []);
////Chart.js
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
const options = {
responsive: true,
plugins: {
legend: {
position: 'bottom' as const,
},
title: {
position: 'bottom' as const,
display: true,
text: 'Storage used for each repository',
},
},
scales: {
y: {
max: 100,
min: 0,
ticks: {
// Include a dollar sign in the ticks
callback: function (value: number | string) {
return value + '%';
},
stepSize: 10,
},
},
},
};
const labels = data?.map((repo) => repo.alias);
const dataChart = {
labels,
datasets: [
{
label: 'Storage used (%)',
//storageUsed is in kB, storageSize is in GB. Round to 1 decimal for %.
data: data?.map((repo) =>
(((repo.storageUsed / 1024 ** 2) * 100) / repo.storageSize).toFixed(1)
),
backgroundColor: '#704dff',
},
],
};
return <Bar options={options} data={dataChart} />;
}

View file

@ -1,171 +0,0 @@
//Lib
import classes from './RepoList.module.css';
import { useState, useEffect } from 'react';
import { IconPlus } from '@tabler/icons-react';
import { useRouter } from 'next/router';
import Link from 'next/link';
import useSWR, { useSWRConfig } from 'swr';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
//Composants
import Repo from '../../Components/Repo/Repo';
import RepoManage from '../RepoManage/RepoManage';
import ShimmerRepoList from '../../Components/UI/ShimmerRepoList/ShimmerRepoList';
export default function RepoList() {
////Var
const router = useRouter();
const { mutate } = useSWRConfig();
const toastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
////Datas
//Write a fetcher function to wrap the native fetch function and return the result of a call to url in json format
const fetcher = async (url) => await fetch(url).then((res) => res.json());
const { data, error } = useSWR('/api/repo', fetcher);
////LifeCycle
//Component did mount
useEffect(() => {
//If the route is home/manage-repo/add, open the RepoAdd box.
if (router.pathname === '/manage-repo/add') {
setDisplayRepoAdd(!displayRepoAdd);
}
//If the route is home/manage-repo/edit, open the RepoAdd box.
if (router.pathname.startsWith('/manage-repo/edit')) {
setDisplayRepoEdit(!displayRepoEdit);
}
//Fetch wizardEnv to hydrate Repo components
const fetchWizardEnv = async () => {
try {
const response = await fetch('/api/account/getWizardEnv', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setWizardEnv((await response.json()).wizardEnv);
} catch (error) {
console.log('Fetching datas error');
}
};
fetchWizardEnv();
}, []);
////States
const [displayRepoAdd, setDisplayRepoAdd] = useState(false);
const [displayRepoEdit, setDisplayRepoEdit] = useState(false);
const [wizardEnv, setWizardEnv] = useState({});
////Functions
//Firstly, check the availability of data and condition it.
if (!data) {
//Force mutate after login (force a API GET on /api/repo to load repoList)
mutate('/api/repo');
return <ShimmerRepoList />;
}
if (error) {
toast.error('An error has occurred.', toastOptions);
return <ToastContainer />;
}
if (data.status == 500) {
toast.error('API Error !', toastOptions);
return <ToastContainer />;
}
//BUTTON : Display RepoManage component box for ADD
const manageRepoAddHandler = () => {
router.replace('/manage-repo/add');
};
//BUTTON : Display RepoManage component box for EDIT
const repoManageEditHandler = (id) => {
router.replace('/manage-repo/edit/' + id);
};
//BUTTON : Close RepoManage component box (when cross is clicked)
const closeRepoManageBoxHandler = () => {
router.replace('/');
};
// UI EFFECT : Display blur when display add repo modale
const displayBlur = () => {
if (displayRepoAdd || displayRepoEdit) {
return classes.containerBlur;
} else {
return classes.container;
}
};
//Dynamic list of repositories (with a map of Repo components)
const renderRepoList = data.repoList.map((repo, index) => {
return (
<>
<Repo
key={repo.id}
id={repo.id}
alias={repo.alias}
status={repo.status}
lastSave={repo.lastSave}
alert={repo.alert}
repositoryName={repo.repositoryName}
storageSize={repo.storageSize}
storageUsed={repo.storageUsed}
sshPublicKey={repo.sshPublicKey}
comment={repo.comment}
lanCommand={repo.lanCommand}
appendOnlyMode={repo.appendOnlyMode}
repoManageEditHandler={() => repoManageEditHandler(repo.id)}
wizardEnv={wizardEnv}
></Repo>
</>
);
});
return (
<>
<div className={displayBlur()}>
<div className={classes.containerAddRepo}>
<Link
href='/manage-repo/add'
className={classes.newRepoButton}
onClick={manageRepoAddHandler}
>
<IconPlus
className={classes.plusIcon}
size={24}
stroke={2}
/>
<span>Add a repository</span>
</Link>
</div>
<div className={classes.containerRepoList}>
<div className={classes.RepoList}>{renderRepoList}</div>
</div>
</div>
{displayRepoAdd ? (
<RepoManage
mode='add'
repoList={data.repoList}
closeHandler={closeRepoManageBoxHandler}
/>
) : null}
{displayRepoEdit ? (
<RepoManage
mode='edit'
repoList={data.repoList}
closeHandler={closeRepoManageBoxHandler}
/>
) : null}
</>
);
}

View file

@ -1,125 +1,198 @@
.container {
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
}
.containerBlur {
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
filter: blur(3px);
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
filter: blur(3px);
}
.containerRepoList {
display: flex;
flex-direction: row;
display: flex;
flex-direction: row;
}
.containerAddRepo {
display: flex;
flex-direction: row;
margin: 20px auto;
width: 90%;
display: flex;
flex-direction: row;
margin: 20px auto;
width: 90%;
}
.newRepoButton {
position: relative;
margin: auto;
padding: 19px 22px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
text-decoration: none;
position: relative;
margin: auto;
padding: 19px 22px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
text-decoration: none;
}
.newRepoButton:before {
content: '';
position: absolute;
top: 6px;
left: 3px;
display: block;
border-radius: 28px;
background: #6d4aff;
width: 50px;
height: 50px;
transition: all 0.3s ease;
content: '';
position: absolute;
top: 6px;
left: 3px;
display: block;
border-radius: 28px;
background: #6d4aff;
width: 50px;
height: 50px;
transition: all 0.3s ease;
}
.newRepoButton span {
position: relative;
font-size: 16px;
line-height: 18px;
font-weight: 600;
vertical-align: middle;
padding-left: 15px;
color: #494b7a;
position: relative;
font-size: 16px;
line-height: 18px;
font-weight: 600;
vertical-align: middle;
padding-left: 15px;
color: #494b7a;
}
.newRepoButton .plusIcon {
position: relative;
top: 0;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #fff;
transform: translateX(-5px);
transition: all 0.3s ease;
position: relative;
top: 0;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #fff;
transform: translateX(-5px);
transition: all 0.3s ease;
}
.newRepoButton:hover:before {
width: 100%;
background: #6d4aff;
width: 100%;
background: #6d4aff;
}
.newRepoButton:hover span {
color: #fff;
color: #fff;
}
.newRepoButton:hover .plusIcon {
transform: translateX(0);
transform: translateX(0);
}
.newRepoButton:active {
transform: scale(0.96);
transform: scale(0.96);
}
.RepoList {
display: flex;
flex-direction: column;
width: 90%;
margin: 5px auto;
padding: 15px;
display: flex;
flex-direction: column;
width: 90%;
margin: 5px auto;
}
.unfoldButton {
cursor: pointer;
position: sticky;
color: #a6a6b8;
padding-top: 49px;
align-self: flex-start;
top: 0;
cursor: pointer;
position: sticky;
color: #a6a6b8;
padding-top: 49px;
align-self: flex-start;
top: 0;
}
.foldButton {
cursor: pointer;
position: sticky;
color: #a6a6b8;
padding-top: 49px;
align-self: flex-start;
top: 0;
cursor: pointer;
position: sticky;
color: #a6a6b8;
padding-top: 49px;
align-self: flex-start;
top: 0;
}
.unfoldButton:active,
.foldButton:active {
transform: scale(0.96);
transform: scale(0.96);
}
@media all and (max-width: 1000px) {
.newRepoButton {
display: none;
}
.chevron {
display: none;
}
.containerAddRepo {
display: none;
}
.newRepoButton {
display: none;
}
.chevron {
display: none;
}
.containerAddRepo {
display: none;
}
}
/* Toolbar */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
width: 90%;
margin: 20px auto 10px;
flex-wrap: wrap;
gap: 10px;
}
.searchInput {
padding: 10px 15px;
border: 1px solid #ccc;
border-radius: 8px;
font-size: 14px;
width: 100%;
max-width: 300px;
}
.sortIcons {
display: flex;
gap: 10px;
align-items: center;
}
.icon {
cursor: pointer;
color: #a6a6b8;
transition: transform 0.2s ease;
}
.icon:hover {
transform: scale(1.1);
color: #6d4aff;
}
.iconActive {
color: #6d4aff;
transform: scale(1.2);
}
.searchContainer {
position: relative;
display: flex;
align-items: center;
width: 100%;
max-width: 300px;
}
.searchInput {
width: 100%;
padding: 8px 32px 8px 12px;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 14px;
outline: none;
background-color: white;
}
.clearButton {
position: absolute;
right: 8px;
background: transparent;
border: none;
cursor: pointer;
color: #999;
}
.clearButton:hover {
color: #333;
}

View file

@ -0,0 +1,279 @@
import classes from './RepoList.module.css';
import React, { useState, useEffect } from 'react';
import {
IconPlus,
IconSortAscendingLetters,
IconSortDescendingLetters,
IconSortAscending2,
IconSortDescending2,
IconDatabase,
IconX,
IconClock,
IconCalendarUp,
IconCalendarDown,
IconSortAscendingSmallBig,
IconSortDescendingSmallBig,
IconSortDescending2Filled,
} from '@tabler/icons-react';
import { useRouter } from 'next/router';
import Link from 'next/link';
import useSWR, { useSWRConfig } from 'swr';
import { ToastContainer, ToastOptions, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Repo from '~/Components/Repo/Repo';
import RepoManage from '../RepoManage/RepoManage';
import ShimmerRepoList from '~/Components/UI/ShimmerRepoList/ShimmerRepoList';
import { Repository, WizardEnvType, Optional } from '~/types';
type SortOption =
| 'alias-asc'
| 'alias-desc'
| 'status-true'
| 'status-false'
| 'storage-used-asc'
| 'storage-used-desc'
| 'last-save-asc'
| 'last-save-desc';
export default function RepoList() {
const router = useRouter();
const { mutate } = useSWRConfig();
const [displayRepoAdd, setDisplayRepoAdd] = useState(false);
const [displayRepoEdit, setDisplayRepoEdit] = useState(false);
const [wizardEnv, setWizardEnv] = useState<Optional<WizardEnvType>>();
const [sortOption, setSortOption] = useState<SortOption>(() => {
const savedSort = localStorage.getItem('repoSort');
return (savedSort as SortOption) || 'alias-asc';
});
const [searchQuery, setSearchQuery] = useState(() => {
const savedSearch = localStorage.getItem('repoSearch');
return savedSearch || '';
});
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setDisplayRepoAdd(router.pathname === '/manage-repo/add');
setDisplayRepoEdit(router.pathname.startsWith('/manage-repo/edit'));
const fetchWizardEnv = async () => {
try {
const response = await fetch('/api/v1/account/wizard-env');
const data: WizardEnvType = await response.json();
setWizardEnv(data);
} catch (error) {
console.log('Fetching wizard env error');
}
};
fetchWizardEnv();
}, [router.pathname]);
const fetcher = async (url: string) => await fetch(url).then((res) => res.json());
const { data, error } = useSWR('/api/v1/repositories', fetcher);
if (!data) {
mutate('/api/v1/repositories');
return <ShimmerRepoList />;
}
if (error || data.status == 500) {
toast.error('Error loading repositories.', toastOptions);
return <ToastContainer />;
}
const handleSortChange = (option: SortOption) => {
setSortOption(option);
localStorage.setItem('repoSort', option);
};
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const query = e.target.value;
setSearchQuery(query);
localStorage.setItem('repoSearch', query);
};
const getSortedRepoList = () => {
let repoList = [...data.repoList];
// Filter
if (searchQuery) {
repoList = repoList.filter((repo) =>
`${repo.alias} ${repo.comment} ${repo.repositoryName}`
.toLowerCase()
.includes(searchQuery.toLowerCase())
);
}
// Sort
switch (sortOption) {
case 'alias-asc':
return repoList.sort((a, b) => a.alias.localeCompare(b.alias));
case 'alias-desc':
return repoList.sort((a, b) => b.alias.localeCompare(a.alias));
case 'status-true':
return repoList.sort((a, b) => Number(b.status) - Number(a.status));
case 'status-false':
return repoList.sort((a, b) => Number(a.status) - Number(b.status));
case 'storage-used-asc':
return repoList.sort((a, b) => {
const aRatio = a.storageSize ? a.storageUsed / a.storageSize : 0;
const bRatio = b.storageSize ? b.storageUsed / b.storageSize : 0;
return aRatio - bRatio;
});
case 'storage-used-desc':
return repoList.sort((a, b) => {
const aRatio = a.storageSize ? a.storageUsed / a.storageSize : 0;
const bRatio = b.storageSize ? b.storageUsed / b.storageSize : 0;
return bRatio - aRatio;
});
case 'last-save-asc':
return repoList.sort((a, b) => {
const aDate = a.lastSave ? new Date(a.lastSave).getTime() : 0;
const bDate = b.lastSave ? new Date(b.lastSave).getTime() : 0;
return aDate - bDate;
});
case 'last-save-desc':
return repoList.sort((a, b) => {
const aDate = a.lastSave ? new Date(a.lastSave).getTime() : 0;
const bDate = b.lastSave ? new Date(b.lastSave).getTime() : 0;
return bDate - aDate;
});
default:
return repoList;
}
};
const manageRepoAddHandler = () => router.replace('/manage-repo/add');
const manageRepoEditHandler = (id: number) => router.replace('/manage-repo/edit/' + id);
const closeRepoManageBoxHandler = () => router.replace('/');
const displayBlur = () =>
displayRepoAdd || displayRepoEdit ? classes.containerBlur : classes.container;
const renderRepoList = getSortedRepoList().map((repo: Repository) => (
<Repo
key={repo.id}
id={repo.id}
alias={repo.alias}
status={repo.status}
lastSave={repo.lastSave}
alert={repo.alert}
repositoryName={repo.repositoryName}
storageUsed={repo.storageUsed}
storageSize={repo.storageSize}
sshPublicKey={repo.sshPublicKey}
comment={repo.comment}
lanCommand={repo.lanCommand}
appendOnlyMode={repo.appendOnlyMode}
repoManageEditHandler={() => manageRepoEditHandler(repo.id)}
wizardEnv={wizardEnv}
/>
));
return (
<>
<div className={displayBlur()}>
<div className={classes.containerAddRepo}>
<Link
href='/manage-repo/add'
className={classes.newRepoButton}
onClick={manageRepoAddHandler}
>
<IconPlus className={classes.plusIcon} size={24} stroke={2} />
<span>Add a repository</span>
</Link>
</div>
<div className={classes.toolbar}>
<div className={classes.searchContainer}>
<input
type='text'
placeholder='Alias, comment, repository name...'
value={searchQuery}
onChange={handleSearchChange}
className={classes.searchInput}
/>
{searchQuery && (
<button
onClick={() =>
handleSearchChange({
target: { value: '' },
} as React.ChangeEvent<HTMLInputElement>)
}
className={classes.clearButton}
title='Clear search'
>
<IconX size={16} stroke={2} />
</button>
)}
</div>
<div className={classes.sortIcons}>
<IconSortAscendingLetters
className={sortOption === 'alias-asc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('alias-asc')}
title='Alias A-Z'
/>
<IconSortDescendingLetters
className={sortOption === 'alias-desc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('alias-desc')}
title='Alias Z-A'
/>
<IconSortDescending2Filled
className={sortOption === 'status-true' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('status-true')}
title='Status OK → KO'
/>
<IconSortDescending2
className={sortOption === 'status-false' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('status-false')}
title='Status KO → OK'
/>
<IconCalendarDown
className={sortOption === 'last-save-desc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('last-save-desc')}
title='Last save (recent → old)'
/>
<IconCalendarUp
className={sortOption === 'last-save-asc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('last-save-asc')}
title='Last save (old → recent)'
/>
<IconSortAscendingSmallBig
className={sortOption === 'storage-used-asc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('storage-used-asc')}
title='Storage usage % low → high'
/>
<IconSortDescendingSmallBig
className={sortOption === 'storage-used-desc' ? classes.iconActive : classes.icon}
onClick={() => handleSortChange('storage-used-desc')}
title='Storage usage % high → low'
/>
</div>
</div>
<div className={classes.containerRepoList}>
<div className={classes.RepoList}>{renderRepoList}</div>
</div>
</div>
{displayRepoAdd && (
<RepoManage mode='add' repoList={data.repoList} closeHandler={closeRepoManageBoxHandler} />
)}
{displayRepoEdit && (
<RepoManage mode='edit' repoList={data.repoList} closeHandler={closeRepoManageBoxHandler} />
)}
</>
);
}

View file

@ -1,571 +0,0 @@
//Lib
import classes from './RepoManage.module.css';
import { IconAlertCircle, IconX } from '@tabler/icons-react';
import { useState } from 'react';
import { useRouter } from 'next/router';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useForm, Controller } from 'react-hook-form';
import { SpinnerDotted } from 'spinners-react';
import Select from 'react-select';
import Link from 'next/link';
import { IconExternalLink } from '@tabler/icons-react';
export default function RepoManage(props) {
////Var
let targetRepo;
const router = useRouter();
const {
register,
handleSubmit,
control,
formState: { errors, isSubmitting, isValid },
} = useForm({ mode: 'onChange' });
//List of possible times for alerts
const alertOptions = [
{ value: 0, label: 'Disabled' },
{ value: 3600, label: '1 hour' },
{ value: 21600, label: '6 hours' },
{ value: 43200, label: '12 hours' },
{ value: 90000, label: '1 day' },
{ value: 172800, label: '2 days' },
{ value: 259200, label: '3 days' },
{ value: 345600, label: '4 days' },
{ value: 432000, label: '5 days' },
{ value: 518400, label: '6 days' },
{ value: 604800, label: '7 days' },
{ value: 864000, label: '10 days' },
{ value: 1209600, label: '14 days' },
{ value: 2592000, label: '30 days' },
];
const toastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
////State
const [deleteDialog, setDeleteDialog] = useState(false);
const [isLoading, setIsLoading] = useState(false);
////Functions
//router.query.slug is undefined for few milliseconds on first render for a direct URL access (https://github.com/vercel/next.js/discussions/11484).
//If I call repoManage with edit mode (props), i'm firstly waiting that router.query.slug being available before rendering.
if (!router.query.slug && props.mode == 'edit') {
return (
<SpinnerDotted
size={30}
thickness={100}
speed={180}
color='rgba(109, 74, 255, 1)'
/>
);
} else if (props.mode == 'edit') {
for (let element in props.repoList) {
if (props.repoList[element].id == router.query.slug) {
targetRepo = props.repoList[element];
}
}
//If the ID does not exist > 404
if (!targetRepo) {
router.push('/404');
return null;
}
}
//Delete a repo
const deleteHandler = async () => {
//API Call for delete
fetch('/api/repo/id/' + router.query.slug + '/delete', {
method: 'DELETE',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({ toDelete: true }),
})
.then((response) => {
if (response.ok) {
toast.success(
'🗑 The repository #' +
router.query.slug +
' has been successfully deleted',
toastOptions
);
router.replace('/');
} else {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log('Fail to delete');
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
});
};
//Verify that the SSH key is unique
const isSSHKeyUnique = async (sshPublicKey) => {
let isUnique = true;
// Extract the first two columns of the SSH key in the form
const publicKeyPrefix = sshPublicKey.split(' ').slice(0, 2).join(' ');
await fetch('/api/repo', { method: 'GET' })
.then((response) => response.json())
.then((data) => {
for (let element in data.repoList) {
// Extract the first two columns of the SSH key in the repoList
const repoPublicKeyPrefix = data.repoList[
element
].sshPublicKey
.split(' ')
.slice(0, 2)
.join(' ');
if (
repoPublicKeyPrefix === publicKeyPrefix && // Compare the first two columns of the SSH key
(!targetRepo ||
data.repoList[element].id != targetRepo.id)
) {
toast.error(
'The SSH key is already used in repository #' +
data.repoList[element].id +
'. Please use another key or delete the key from the other repository.',
toastOptions
);
isUnique = false;
break;
}
}
})
.catch((error) => {
console.log(error);
toast.error('An error has occurred', toastOptions);
isUnique = false;
});
return isUnique;
};
//Form submit Handler for ADD or EDIT a repo
const formSubmitHandler = async (dataForm) => {
//Loading button on submit to avoid multiple send.
setIsLoading(true);
//Verify that the SSH key is unique
if (!(await isSSHKeyUnique(dataForm.sshkey))) {
setIsLoading(false);
return;
}
//ADD a repo
if (props.mode == 'add') {
const newRepo = {
alias: dataForm.alias,
size: dataForm.size,
sshPublicKey: dataForm.sshkey,
comment: dataForm.comment,
alert: dataForm.alert.value,
lanCommand: dataForm.lanCommand,
appendOnlyMode: dataForm.appendOnlyMode,
};
//POST API to send new repo
await fetch('/api/repo/add', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(newRepo),
})
.then(async (response) => {
if (response.ok) {
toast.success(
'New repository added ! 🥳',
toastOptions
);
router.replace('/');
} else {
const errorMessage = await response.json();
toast.error(
`An error has occurred : ${errorMessage.message}`,
toastOptions
);
router.replace('/');
console.log(`Fail to ${props.mode}`);
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
});
//EDIT a repo
} else if (props.mode == 'edit') {
const dataEdited = {
alias: dataForm.alias,
size: dataForm.size,
sshPublicKey: dataForm.sshkey,
comment: dataForm.comment,
alert: dataForm.alert.value,
lanCommand: dataForm.lanCommand,
appendOnlyMode: dataForm.appendOnlyMode,
};
await fetch('/api/repo/id/' + router.query.slug + '/edit', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(dataEdited),
})
.then(async (response) => {
if (response.ok) {
toast.success(
'The repository #' +
targetRepo.id +
' has been successfully edited !',
toastOptions
);
router.replace('/');
} else {
const errorMessage = await response.json();
toast.error(
`An error has occurred : ${errorMessage.message}`,
toastOptions
);
router.replace('/');
console.log(`Fail to ${props.mode}`);
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
});
}
};
return (
<>
<div className={classes.modaleWrapper} />
<div className={classes.modale}>
<div onClick={props.closeHandler} className={classes.close}>
<IconX size={36} />
</div>
{deleteDialog ? (
<div className={classes.deleteDialogWrapper}>
<div>
<IconAlertCircle
size={80}
color='red'
style={{ margin: 'auto' }}
/>
<h1 style={{ textAlign: 'center' }}>
Delete the repository{' '}
<span
style={{
color: 'rgba(99, 115, 129, 0.38)',
}}
>
#{targetRepo.id}
</span>{' '}
?
</h1>
</div>
<div className={classes.deleteDialogMessage}>
<div style={{ marginBottom: '5px' }}>
You are about to permanently delete the
repository <b>#{targetRepo.id}</b> and all the
backups it contains.
</div>
<div>
The data will not be recoverable and it will not
be possible to go back.
</div>
</div>
<div className={classes.deleteDialogButtonWrapper}>
{isLoading ? (
<SpinnerDotted
size={30}
thickness={150}
speed={100}
color='#6d4aff'
/>
) : (
<>
<button
onClick={props.closeHandler}
className={classes.cancelButton}
>
Cancel
</button>
<button
onClick={() => {
deleteHandler();
setIsLoading(true);
}}
className={classes.deleteButton}
>
Yes, delete it !
</button>
</>
)}
</div>
</div>
) : (
<div className={classes.formWrapper}>
{props.mode == 'edit' && (
<h1>
Edit the repository{' '}
<span
style={{
color: 'rgba(99, 115, 129, 0.38)',
}}
>
#{targetRepo.id}
</span>
</h1>
)}
{props.mode == 'add' && <h1>Add a repository</h1>}
<form
className={classes.repoManageForm}
onSubmit={handleSubmit(formSubmitHandler)}
>
{/* ALIAS */}
<label htmlFor='alias'>Alias</label>
<input
className='form-control is-invalid'
placeholder='Alias for the repository, e.g."Server 1"'
type='text'
defaultValue={
props.mode == 'edit'
? targetRepo.alias
: null
}
{...register('alias', {
required: 'An alias is required.',
minLength: {
value: 2,
message: '2 characters min',
},
maxLength: {
value: 40,
message: '40 characters max',
},
})}
/>
{errors.alias && (
<span className={classes.errorMessage}>
{errors.alias.message}
</span>
)}
{/* SSH KEY */}
<label htmlFor='sshkey'>SSH public key</label>
<textarea
placeholder='Public key in OpenSSH format (rsa, ed25519, ed25519-sk)'
type='text'
defaultValue={
props.mode == 'edit'
? targetRepo.sshPublicKey
: null
}
{...register('sshkey', {
required: 'SSH public key is required.',
pattern: {
value: /^(ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t|ssh-rsa AAAAB3NzaC1yc2)[0-9A-Za-z+/]+[=]{0,3}(\s.*)?$/,
message:
'Invalid public key. The SSH key needs to be in OpenSSH format (rsa, ed25519, ed25519-sk)',
},
})}
/>
{errors.sshkey && (
<span className={classes.errorMessage}>
{errors.sshkey.message}
</span>
)}
{/* SIZE */}
<label htmlFor='size'>Storage Size (GB)</label>
<input
type='number'
min='1'
defaultValue={
props.mode == 'edit'
? targetRepo.storageSize
: null
}
{...register('size', {
required: 'A size is required.',
})}
/>
{errors.size && (
<span className={classes.errorMessage}>
{errors.size.message}
</span>
)}
{/* COMMENT */}
<label htmlFor='comment'>Comment</label>
<textarea
type='text'
placeholder='Little comment for your repository...'
defaultValue={
props.mode == 'edit'
? targetRepo.comment
: null
}
{...register('comment', {
required: false,
maxLength: {
value: 200,
message: '200 characters maximum.',
},
})}
/>
{errors.comment && (
<span className={classes.errorMessage}>
{errors.comment.message}
</span>
)}
{/* LAN COMMAND GENERATION */}
<div className={classes.optionCommandWrapper}>
<input
type='checkbox'
name='lanCommand'
defaultChecked={
props.mode == 'edit'
? targetRepo.lanCommand
: false
}
{...register('lanCommand')}
/>
<label htmlFor='lanCommand'>
Generates commands for use over LAN.
</label>
<Link
style={{
alignSelf: 'baseline',
marginLeft: '5px',
}}
href='https://borgwarehouse.com/docs/user-manual/repositories/#generates-commands-for-use-over-lan'
rel='noreferrer'
target='_blank'
>
<IconExternalLink
size={16}
color='#6c737f'
/>
</Link>
</div>
{/* APPEND-ONLY MODE */}
<div className={classes.optionCommandWrapper}>
<input
type='checkbox'
name='appendOnlyMode'
defaultChecked={
props.mode == 'edit'
? targetRepo.appendOnlyMode
: false
}
{...register('appendOnlyMode')}
/>
<label htmlFor='appendOnlyMode'>
Enable append-only mode.
</label>
<Link
style={{
alignSelf: 'baseline',
marginLeft: '5px',
}}
href='https://borgwarehouse.com/docs/user-manual/repositories/#append-only-mode'
rel='noreferrer'
target='_blank'
>
<IconExternalLink
size={16}
color='#6c737f'
/>
</Link>
</div>
{/* ALERT */}
<label
style={{ margin: '25px auto 10px auto' }}
htmlFor='alert'
>
Alert if there is no backup since :
</label>
<div className={classes.selectAlert}>
<Controller
name='alert'
defaultValue={
props.mode == 'edit'
? alertOptions.find(
(x) =>
x.value ===
targetRepo.alert
)
: alertOptions[4]
}
control={control}
render={({
field: { onChange, value },
}) => (
<Select
onChange={onChange}
value={value}
options={alertOptions}
isSearchable={false}
maxMenuHeight={150}
menuPlacement='top'
theme={(theme) => ({
...theme,
borderRadius: '5px',
colors: {
...theme.colors,
primary25: '#c3b6fa',
primary: '#6d4aff',
},
})}
/>
)}
/>
</div>
{isLoading ? (
<div
style={{
textAlign: 'center',
marginTop: '8px',
}}
>
<SpinnerDotted
size={30}
thickness={150}
speed={100}
color='#6d4aff'
/>
</div>
) : (
<button
type='submit'
className='defaultButton'
disabled={!isValid || isSubmitting}
>
{props.mode == 'edit' && 'Edit'}
{props.mode == 'add' && 'Add'}
</button>
)}
</form>
{props.mode == 'edit' ? (
<button
className={classes.littleDeleteButton}
onClick={() => setDeleteDialog(true)}
>
Delete this repository
</button>
) : null}
</div>
)}
</div>
</>
);
}

View file

@ -1,268 +1,296 @@
.modaleWrapper {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
margin: 50px 0px 0px 70px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
margin: 50px 0px 0px 70px;
}
.modale {
position: fixed;
top: 10%;
width: 1000px;
height: auto;
max-width: 75%;
max-height: 85%;
background: #fff;
padding: 20px 20px 20px;
overflow: auto;
border-radius: 10px;
box-shadow: 1px 2px 6px rgba(0, 0, 0, 0.4);
margin: 0 auto;
animation: append-animate 0.3s linear;
position: fixed;
top: 10%;
width: 800px;
height: auto;
max-width: 75%;
max-height: 85%;
background: #fff;
padding: 20px 20px 20px;
overflow: auto;
border-radius: 10px;
box-shadow: 1px 2px 6px rgba(0, 0, 0, 0.4);
margin: 0 auto;
animation: append-animate 0.3s linear;
}
.modale h2 {
margin-top: 0;
color: #374151;
}
@keyframes append-animate {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
.close {
cursor: pointer;
margin-left: 95%;
color: #494b7a;
cursor: pointer;
margin-left: 95%;
color: #494b7a;
}
.close :hover {
color: #aa60ff;
color: #aa60ff;
}
.repoManageForm {
margin: auto;
width: 80%;
padding: 15px 30px 30px 30px;
border-radius: 5px;
text-align: left;
margin: auto;
width: 100%;
max-width: 600px;
border-radius: 8px;
background-color: #ffffff;
font-family: Inter, sans-serif;
color: #1f2937;
}
.formWrapper {
text-align: center;
margin: auto;
width: 100%;
height: auto;
color: #494b7a;
text-align: center;
width: 100%;
margin: 0 auto;
color: inherit;
}
.repoManageForm label {
display: block;
margin-bottom: 8px;
text-align: center;
margin-top: 20px;
color: #494b7a;
display: block;
margin-top: 0.9rem;
margin-bottom: 0.5rem;
font-weight: 600;
font-size: 0.95rem;
color: #374151;
text-align: left;
}
.repoManageForm input,
.repoManageForm textarea,
.repoManageForm select {
border: 1px solid #6d4aff21;
font-size: 16px;
height: auto;
margin: 0;
margin-bottom: 0px;
outline: 0;
padding: 15px;
width: 100%;
background-color: #f5f5f5;
border-radius: 5px;
/* color: #1b1340; */
color: #494b7a;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03) inset;
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 0.9rem;
background-color: #f9fafb;
color: #111827;
font-family: Inter;
}
.repoManageForm textarea {
resize: vertical;
resize: vertical;
min-height: 80px;
}
.repoManageForm textarea:focus,
.repoManageForm input:focus,
.repoManageForm textarea:focus,
.repoManageForm select:focus {
outline: 1px solid #6d4aff;
box-shadow: 0 0 10px 3px rgba(110, 74, 255, 0.605);
border-color: #6d4aff;
background-color: #ffffff;
outline: none;
box-shadow: 0 0 0 2px rgba(109, 74, 255, 0.3);
}
.repoManageForm .invalid {
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
background-color: #fef2f2;
border-color: #ef4444;
}
.repoManageForm .invalid:focus {
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
box-shadow: 0 0 10px 3px rgba(255, 74, 74, 0.605);
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.3);
}
.repoManageForm button {
display: block;
margin: 15px auto;
display: block;
margin: 2rem auto 0 auto;
background-color: #6d4aff;
color: #ffffff;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease;
}
.repoManageForm button:hover {
display: block;
margin: 15px auto;
background-color: #5c3dff;
}
.errorMessage {
color: red;
display: block;
margin-top: 3px;
color: #dc2626;
font-size: 0.875rem;
margin-top: 0.3rem;
}
.optionCommandWrapper {
display: flex;
margin-top: 20px;
color: #494b7a;
display: flex;
gap: 0.5rem;
align-items: center;
margin-top: 1.5rem;
font-size: 0.95rem;
color: #374151;
}
.optionCommandWrapper label {
margin: 0;
margin: 0;
}
.optionCommandWrapper input[type='checkbox'] {
width: auto;
margin-right: 8px;
cursor: pointer;
accent-color: #6d4aff;
width: 18px;
height: 18px;
accent-color: #6d4aff;
cursor: pointer;
}
.optionCommandWrapper input[type='checkbox']:focus {
outline: 0;
box-shadow: none;
accent-color: #6d4aff;
outline: none;
box-shadow: 0 0 0 2px rgba(109, 74, 255, 0.4);
}
.selectAlert {
max-width: 160px;
}
.selectAlertWrapper label {
margin: 0;
}
.selectAlertWrapper {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
margin-top: 1.5rem;
gap: 0.5rem;
font-size: 0.95rem;
color: #374151;
}
/* DELETE DIALOG */
.deleteDialogWrapper {
text-align: center;
margin: auto;
width: 80%;
height: 100%;
max-height: 590px;
color: #111827;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
margin: auto;
width: 80%;
height: 100%;
max-height: 590px;
color: #111827;
display: flex;
flex-direction: column;
justify-content: center;
}
.deleteDialogMessage {
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 15px;
margin-bottom: 15px;
font-size: 1.1em;
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 15px;
margin-bottom: 15px;
font-size: 1.1em;
}
.cancelButton {
border: 0;
padding: 10px 15px;
background-color: #c1c1c1;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #c1c1c1;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.cancelButton:hover {
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.cancelButton:active {
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
}
.deleteButton {
border: 0;
padding: 10px 15px;
background-color: #ff0000;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #ff0000;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.deleteButton:hover {
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.deleteButton:active {
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
}
.littleDeleteButton {
border: none;
font-weight: 300;
color: red;
text-decoration: underline;
background: none;
cursor: pointer;
}
.selectAlert {
margin: auto auto 35px auto;
max-width: 160px;
margin-top: 10px;
border: none;
font-weight: 300;
color: red;
text-decoration: underline;
background: none;
cursor: pointer;
}

View file

@ -0,0 +1,496 @@
import { IconAlertCircle, IconExternalLink, IconX } from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import Select from 'react-select';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useLoader } from '~/contexts/LoaderContext';
import { alertOptions, Optional, Repository } from '~/types';
import classes from './RepoManage.module.css';
type RepoManageProps = {
mode: 'add' | 'edit';
repoList: Optional<Array<Repository>>;
closeHandler: () => void;
};
type DataForm = {
alias: string;
storageSize: string;
sshkey: string;
comment: string;
alert: { value: Optional<number>; label: string };
lanCommand: boolean;
appendOnlyMode: boolean;
};
export default function RepoManage(props: RepoManageProps) {
const router = useRouter();
const targetRepo =
props.mode === 'edit' && router.query.slug
? props.repoList?.find((repo) => repo.id.toString() === router.query.slug)
: undefined;
const {
register,
handleSubmit,
control,
formState: { errors, isSubmitting, isValid },
} = useForm<DataForm>({ mode: 'onChange' });
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const [deleteDialog, setDeleteDialog] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { start, stop } = useLoader();
//router.query.slug is undefined for few milliseconds on first render for a direct URL access (https://github.com/vercel/next.js/discussions/11484).
//If I call repoManage with edit mode (props), i'm firstly waiting that router.query.slug being available before rendering.
if (props.mode === 'edit') {
if (!router.query.slug) {
start();
return;
} else if (!targetRepo) {
stop();
router.push('/404');
}
}
//Delete a repo
const deleteHandler = async (repositoryName?: string) => {
start();
if (!repositoryName) {
stop();
toast.error('Repository name not found', toastOptions);
router.replace('/');
return;
}
//API Call for delete
await fetch('/api/v1/repositories/' + repositoryName, {
method: 'DELETE',
headers: {
'Content-type': 'application/json',
},
})
.then(async (response) => {
if (response.ok) {
toast.success(
'🗑 The repository ' + repositoryName + ' has been successfully deleted',
toastOptions
);
router.replace('/');
} else {
if (response.status == 403) {
toast.warning(
'🔒 The server is currently protected against repository deletion.',
toastOptions
);
setIsLoading(false);
router.replace('/');
} else {
const errorMessage = await response.json();
toast.error(`An error has occurred : ${errorMessage.message.stderr}`, toastOptions);
router.replace('/');
console.log('Fail to delete');
}
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
})
.finally(() => {
stop();
});
};
const isSSHKeyUnique = async (sshPublicKey: string): Promise<boolean> => {
try {
// Extract the first two columns of the SSH key in the form
const publicKeyPrefix = sshPublicKey.split(' ').slice(0, 2).join(' ');
const response = await fetch('/api/v1/repositories', { method: 'GET' });
const data: { repoList: Repository[] } = await response.json();
const conflictingRepo = data.repoList.find((repo: { sshPublicKey: string; id: number }) => {
const repoPublicKeyPrefix = repo.sshPublicKey.split(' ').slice(0, 2).join(' ');
return (
repoPublicKeyPrefix === publicKeyPrefix && (!targetRepo || repo.id !== targetRepo.id)
);
});
if (conflictingRepo) {
toast.error(
`The SSH key is already used in repository ${conflictingRepo.repositoryName}. Please use another key or delete the key from the other repository.`,
toastOptions
);
return false;
}
return true;
} catch (error) {
console.log(error);
toast.error('An error has occurred', toastOptions);
return false;
}
};
//Form submit Handler for ADD or EDIT a repo
const formSubmitHandler = async (dataForm: DataForm) => {
setIsLoading(true);
start();
// Clean SSH key by removing leading/trailing whitespace and line breaks
const cleanedSSHKey = dataForm.sshkey.trim();
//Verify that the SSH key is unique
if (!(await isSSHKeyUnique(cleanedSSHKey))) {
stop();
setIsLoading(false);
return;
}
//ADD a repo
if (props.mode == 'add') {
const newRepo = {
alias: dataForm.alias,
storageSize: parseInt(dataForm.storageSize),
sshPublicKey: cleanedSSHKey,
comment: dataForm.comment,
alert: dataForm.alert.value,
lanCommand: dataForm.lanCommand,
appendOnlyMode: dataForm.appendOnlyMode,
};
//POST API to send new repo
await fetch('/api/v1/repositories', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(newRepo),
})
.then(async (response) => {
if (response.ok) {
toast.success('New repository added ! 🥳', toastOptions);
router.replace('/');
} else {
const errorMessage = await response.json();
toast.error(`An error has occurred : ${errorMessage.message.stderr}`, toastOptions);
router.replace('/');
console.log(`Fail to ${props.mode}`);
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
})
.finally(() => {
stop();
setIsLoading(false);
});
//EDIT a repo
} else if (props.mode == 'edit') {
const dataEdited = {
alias: dataForm.alias,
storageSize: parseInt(dataForm.storageSize),
sshPublicKey: cleanedSSHKey,
comment: dataForm.comment,
alert: dataForm.alert.value,
lanCommand: dataForm.lanCommand,
appendOnlyMode: dataForm.appendOnlyMode,
};
await fetch('/api/v1/repositories/' + targetRepo?.repositoryName, {
method: 'PATCH',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(dataEdited),
})
.then(async (response) => {
if (response.ok) {
toast.success(
'The repository ' + targetRepo?.repositoryName + ' has been successfully edited !',
toastOptions
);
router.replace('/');
} else {
const errorMessage = await response.json();
toast.error(`An error has occurred : ${errorMessage.message.stderr}`, toastOptions);
router.replace('/');
console.log(`Fail to ${props.mode}`);
}
})
.catch((error) => {
toast.error('An error has occurred', toastOptions);
router.replace('/');
console.log(error);
})
.finally(() => {
stop();
setIsLoading(false);
});
}
};
return (
<>
<div className={classes.modaleWrapper} />
<div className={classes.modale}>
<div onClick={props.closeHandler} className={classes.close}>
<IconX size={36} />
</div>
{deleteDialog ? (
<div className={classes.deleteDialogWrapper}>
<div>
<IconAlertCircle size={80} color='red' style={{ margin: 'auto' }} />
<h1 style={{ textAlign: 'center' }}>
Delete the repository{' '}
<span
style={{
color: 'rgba(99, 115, 129, 0.38)',
}}
>
{targetRepo?.repositoryName}
</span>{' '}
?
</h1>
</div>
<div className={classes.deleteDialogMessage}>
<div style={{ marginBottom: '5px' }}>
You are about to permanently delete the repository{' '}
<b>{targetRepo?.repositoryName}</b> and all the backups it contains.
</div>
<div>The data will not be recoverable and it will not be possible to go back.</div>
</div>
<div className={classes.deleteDialogButtonWrapper}>
<>
<button
onClick={props.closeHandler}
disabled={isLoading}
className={classes.cancelButton}
>
Cancel
</button>
<button
onClick={() => {
deleteHandler(targetRepo?.repositoryName);
setIsLoading(true);
}}
className={classes.deleteButton}
>
Yes, delete it !
</button>
</>
</div>
</div>
) : (
<div className={classes.formWrapper}>
{props.mode == 'edit' && (
<h2>
Edit the repository{' '}
<span
style={{
color: '#6d4aff',
}}
>
{targetRepo?.repositoryName}
</span>
</h2>
)}
{props.mode == 'add' && <h2>Add a repository</h2>}
<form className={classes.repoManageForm} onSubmit={handleSubmit(formSubmitHandler)}>
{/* ALIAS */}
<label htmlFor='alias'>Alias</label>
<input
className='form-control is-invalid'
placeholder='Alias for the repository, e.g."Server 1"'
type='text'
defaultValue={props.mode == 'edit' ? targetRepo?.alias : undefined}
{...register('alias', {
required: 'An alias is required.',
minLength: {
value: 1,
message: '1 character min',
},
maxLength: {
value: 100,
message: '100 characters max',
},
})}
/>
{errors.alias && <span className={classes.errorMessage}>{errors.alias.message}</span>}
{/* SSH KEY */}
<label htmlFor='sshkey'>SSH public key</label>
<textarea
placeholder='Public key in OpenSSH format (rsa, ed25519, ed25519-sk)'
defaultValue={props.mode == 'edit' ? targetRepo?.sshPublicKey : undefined}
{...register('sshkey', {
required: 'SSH public key is required.',
validate: (value) => {
const trimmedValue = value.trim();
const pattern =
/^(ssh-ed25519 AAAAC3NzaC1lZDI1NTE5|sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t|ssh-rsa AAAAB3NzaC1yc2)[0-9A-Za-z+/]+[=]{0,3}(\s.*)?$/;
return (
pattern.test(trimmedValue) ||
'Invalid public key. The key needs to be in OpenSSH format (rsa, ed25519, ed25519-sk)'
);
},
})}
/>
{errors.sshkey && (
<span className={classes.errorMessage}>{errors.sshkey.message}</span>
)}
{/* storageSize */}
<label htmlFor='storageSize'>Storage Size (GB)</label>
<input
type='number'
placeholder='1000'
min='1'
defaultValue={props.mode == 'edit' ? targetRepo?.storageSize : undefined}
{...register('storageSize', {
required: 'A storage size is required.',
})}
/>
{errors.storageSize && (
<span className={classes.errorMessage}>{errors.storageSize.message}</span>
)}
{/* COMMENT */}
<label htmlFor='comment'>Comment</label>
<textarea
defaultValue={props.mode == 'edit' ? targetRepo?.comment : undefined}
{...register('comment', {
required: false,
maxLength: {
value: 500,
message: '500 characters maximum.',
},
})}
/>
{errors.comment && (
<span className={classes.errorMessage}>{errors.comment.message}</span>
)}
{/* LAN COMMAND GENERATION */}
<div className={classes.optionCommandWrapper}>
<input
type='checkbox'
defaultChecked={props.mode == 'edit' ? targetRepo?.lanCommand : false}
{...register('lanCommand')}
/>
<label htmlFor='lanCommand'>Generates commands for use over LAN</label>
<Link
href='https://borgwarehouse.com/docs/user-manual/repositories/#generates-commands-for-use-over-lan'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
{/* APPEND-ONLY MODE */}
<div className={classes.optionCommandWrapper}>
<input
type='checkbox'
defaultChecked={props.mode == 'edit' ? targetRepo?.appendOnlyMode : false}
{...register('appendOnlyMode')}
/>
<label htmlFor='appendOnlyMode'>Enable append-only mode</label>
<Link
href='https://borgwarehouse.com/docs/user-manual/repositories/#append-only-mode'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
{/* ALERT */}
<div className={classes.selectAlertWrapper}>
<label htmlFor='alert'>Alert if there is no backup since :</label>
<div className={classes.selectAlert}>
<Controller
name='alert'
defaultValue={
props.mode == 'edit'
? alertOptions.find((x) => x.value === targetRepo?.alert) || {
value: targetRepo?.alert,
label: `Custom value (${targetRepo?.alert} seconds)`,
}
: alertOptions[4]
}
control={control}
render={({ field: { onChange, value } }) => (
<Select
onChange={onChange}
value={value}
options={alertOptions}
isSearchable={false}
maxMenuHeight={300}
menuPlacement='top'
styles={{
control: (base) => ({
...base,
minHeight: '35px',
height: '35px',
}),
valueContainer: (base) => ({
...base,
height: '35px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
padding: '0 8px',
}),
input: (base) => ({
...base,
margin: 0,
}),
indicatorsContainer: (base) => ({
...base,
height: '35px',
}),
}}
theme={(theme) => ({
...theme,
borderRadius: 5,
colors: {
...theme.colors,
primary25: '#c3b6fa',
primary: '#6d4aff',
},
})}
/>
)}
/>
</div>
</div>
<button
type='submit'
className='defaultButton'
disabled={!isValid || isSubmitting || isLoading}
>
{props.mode == 'edit' && 'Save'}
{props.mode == 'add' && 'Add repository'}
</button>
</form>
{props.mode == 'edit' ? (
<button className={classes.littleDeleteButton} onClick={() => setDeleteDialog(true)}>
Delete this repository
</button>
) : null}
</div>
)}
</div>
</>
);
}

View file

@ -1,159 +0,0 @@
//Lib
import React from 'react';
import classes from './SetupWizard.module.css';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Select from 'react-select';
//Components
import WizardStepBar from '../../Components/WizardSteps/WizardStepBar/WizardStepBar';
import WizardStep1 from '../../Components/WizardSteps/WizardStep1/WizardStep1';
import WizardStep2 from '../../Components/WizardSteps/WizardStep2/WizardStep2';
import WizardStep3 from '../../Components/WizardSteps/WizardStep3/WizardStep3';
import WizardStep4 from '../../Components/WizardSteps/WizardStep4/WizardStep4';
function SetupWizard(props) {
////Var
const router = useRouter();
////States
const [list, setList] = useState([]);
const [listIsLoading, setListIsLoading] = useState(true);
const [step, setStep] = useState();
const [wizardEnv, setWizardEnv] = useState({});
const [selectedOption, setSelectedOption] = useState({
id: '#id',
repository: 'repo',
});
////LifeCycle
//ComponentDidMount
useEffect(() => {
//retrieve the repository list
const repoList = async () => {
try {
const response = await fetch('/api/repo', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setList((await response.json()).repoList);
setListIsLoading(false);
} catch (error) {
console.log('Fetching datas error');
}
};
repoList();
//Fetch wizardEnv to hydrate Wizard' steps
const fetchWizardEnv = async () => {
try {
const response = await fetch('/api/account/getWizardEnv', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setWizardEnv((await response.json()).wizardEnv);
} catch (error) {
console.log('Fetching datas error');
}
};
fetchWizardEnv();
}, []);
//Component did update
useEffect(() => {
//Go to the step in the URL param when URL change
setStep(props.step);
}, [props.step]);
////Functions
//Options for react-select
const options = list.map((repo) => ({
label: `${repo.alias} - #${repo.id}`,
value: `${repo.alias} - #${repo.id}`,
id: repo.id,
repositoryName: repo.repositoryName,
lanCommand: repo.lanCommand,
}));
//Step button (free selection of user)
const changeStepHandler = (x) => router.push('/setup-wizard/' + x);
//Next Step button
const nextStepHandler = () => {
if (step < 4) {
router.push('/setup-wizard/' + `${Number(step) + 1}`);
}
};
//Previous Step button
const previousStepHandler = () => {
if (step > 1) {
router.push('/setup-wizard/' + `${Number(step) - 1}`);
}
};
//Change Step with State
const wizardStep = (step) => {
if (step == 1) {
return <WizardStep1 />;
} else if (step == 2) {
return (
<WizardStep2
selectedOption={selectedOption}
wizardEnv={wizardEnv}
/>
);
} else if (step == 3) {
return (
<WizardStep3
selectedOption={selectedOption}
wizardEnv={wizardEnv}
/>
);
} else {
return (
<WizardStep4
selectedOption={selectedOption}
wizardEnv={wizardEnv}
/>
);
}
};
return (
<div className={classes.container}>
<WizardStepBar
setStep={(x) => changeStepHandler(x)}
step={step}
nextStepHandler={() => nextStepHandler()}
previousStepHandler={() => previousStepHandler()}
/>
<div className={classes.selectRepo}>
<Select
onChange={setSelectedOption}
isLoading={listIsLoading}
isDisabled={listIsLoading}
options={options}
isSearchable
placeholder='Select your repository...'
theme={(theme) => ({
...theme,
borderRadius: '5px',
colors: {
...theme.colors,
primary25: '#c3b6fa',
primary: '#6d4aff',
},
})}
/>
</div>
{wizardStep(step)}
</div>
);
}
export default SetupWizard;

View file

@ -1,28 +1,28 @@
.container {
display: flex;
flex-direction: column;
justify-content: center;
color: #494b7a;
display: flex;
flex-direction: column;
justify-content: center;
color: #494b7a;
}
.container a {
text-decoration: none;
text-decoration: none;
}
.wizardStepTransition-enter {
opacity: 0;
opacity: 0;
}
.wizardStepTransition-enter-active {
opacity: 1;
transition: opacity 200ms;
opacity: 1;
transition: opacity 200ms;
}
.wizardStepTransition-exit {
opacity: 1;
opacity: 1;
}
.wizardStepTransition-exit-active {
opacity: 0;
transition: opacity 200ms;
opacity: 0;
transition: opacity 200ms;
}
.selectRepo {
margin: 30px auto auto auto;
width: 300px;
margin: 30px auto auto auto;
width: 300px;
}

View file

@ -0,0 +1,170 @@
import { useRouter } from 'next/router';
import { useEffect, useMemo, useState } from 'react';
import Select, { SingleValue } from 'react-select';
import classes from './SetupWizard.module.css';
import { Optional, SelectedRepoWizard, Repository, WizardEnvType } from '~/types';
//Components
import WizardStep1 from '../../Components/WizardSteps/WizardStep1/WizardStep1';
import WizardStep2 from '../../Components/WizardSteps/WizardStep2/WizardStep2';
import WizardStep3 from '../../Components/WizardSteps/WizardStep3/WizardStep3';
import WizardStep4 from '../../Components/WizardSteps/WizardStep4/WizardStep4';
import WizardStepBar from '../../Components/WizardSteps/WizardStepBar/WizardStepBar';
type SetupWizardProps = {
step?: number;
};
function SetupWizard(props: SetupWizardProps) {
const router = useRouter();
const [repoList, setRepoList] = useState<Optional<Array<Repository>>>();
const [repoListIsLoading, setRepoListIsLoading] = useState<boolean>(true);
const [step, setStep] = useState<number>(1);
const [wizardEnv, setWizardEnv] = useState<Optional<WizardEnvType>>();
const [selectedItem, setSelectedItem] = useState<Optional<SelectedRepoWizard>>();
////LifeCycle
//ComponentDidMount
useEffect(() => {
//retrieve the repository list
const fetchRepoList = async () => {
try {
const response = await fetch('/api/v1/repositories', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data = await response.json();
const repos = data.repoList;
setRepoList(repos);
setRepoListIsLoading(false);
// Auto-select first repository if available
if (repos && repos.length > 0) {
setSelectedItem({
label: `${repos[0].alias} - ${repos[0].repositoryName}`,
value: `${repos[0].alias} - ${repos[0].repositoryName}`,
id: repos[0].id.toString(),
repositoryName: repos[0].repositoryName,
lanCommand: repos[0].lanCommand ? repos[0].lanCommand : false,
});
}
} catch (error) {
console.log('Fetching datas error');
}
};
fetchRepoList();
//Fetch wizardEnv to hydrate Wizard' steps
const fetchWizardEnv = async () => {
try {
const response = await fetch('/api/v1/account/wizard-env', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: WizardEnvType = await response.json();
setWizardEnv(data);
} catch (error) {
console.log('Fetching datas error');
}
};
fetchWizardEnv();
}, []);
//Component did update
useEffect(() => {
//Go to the step in the URL param when URL change
if (props.step) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setStep(props.step);
}
}, [props.step]);
//Options for react-select
const options: Optional<Array<SelectedRepoWizard>> = useMemo(
() =>
repoList?.map((repo) => ({
label: `${repo.alias} - ${repo.repositoryName}`,
value: `${repo.alias} - ${repo.repositoryName}`,
id: repo.id.toString(),
repositoryName: repo.repositoryName,
lanCommand: repo.lanCommand ? repo.lanCommand : false,
})),
[repoList]
);
//Step button (free selection of user)
const changeStepHandler = (x: number) => router.push('/setup-wizard/' + x.toString());
//Next Step button
const nextStepHandler = () => {
if (step && step < 4) {
router.push('/setup-wizard/' + `${step + 1}`);
}
};
//Previous Step button
const previousStepHandler = () => {
if (step && step > 1) {
router.push('/setup-wizard/' + `${step - 1}`);
}
};
const onChangeSelect = (option: SingleValue<SelectedRepoWizard>) => {
if (option) {
setSelectedItem(option);
} else {
setSelectedItem(undefined);
}
};
//Change Step with State
const wizardStep = (step?: number) => {
if (!step || step === 1) {
return <WizardStep1 />;
} else if (step === 2) {
return <WizardStep2 selectedRepo={selectedItem} wizardEnv={wizardEnv} />;
} else if (step === 3) {
return <WizardStep3 selectedRepo={selectedItem} wizardEnv={wizardEnv} />;
} else {
return <WizardStep4 selectedRepo={selectedItem} wizardEnv={wizardEnv} />;
}
};
return (
<div className={classes.container}>
<WizardStepBar
setStep={(x) => changeStepHandler(x)}
step={step}
nextStepHandler={() => nextStepHandler()}
previousStepHandler={() => previousStepHandler()}
/>
<div className={classes.selectRepo}>
<Select
onChange={(item) => onChangeSelect(item)}
isLoading={repoListIsLoading}
isDisabled={repoListIsLoading}
options={options}
isSearchable
value={selectedItem}
placeholder='Select your repository...'
theme={(theme) => ({
...theme,
borderRadius: 5,
colors: {
...theme.colors,
primary25: '#c3b6fa',
primary: '#6d4aff',
},
})}
/>
</div>
{wizardStep(step)}
</div>
);
}
export default SetupWizard;

View file

@ -1,220 +0,0 @@
//Lib
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
import { useState } from 'react';
import { SpinnerCircularFixed } from 'spinners-react';
import { IconExternalLink } from '@tabler/icons-react';
import Link from 'next/link';
//Components
import Error from '../../../Components/UI/Error/Error';
import Switch from '../../../Components/UI/Switch/Switch';
import AppriseURLs from './AppriseURLs/AppriseURLs';
import AppriseMode from './AppriseMode/AppriseMode';
export default function AppriseAlertSettings() {
//Var
const toastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
//Callback > re-enabled button after notification.
onClose: () => setDisabled(false),
};
////State
const [checkIsLoading, setCheckIsLoading] = useState(true);
const [error, setError] = useState();
const [disabled, setDisabled] = useState(false);
const [checked, setChecked] = useState();
const [testIsLoading, setTestIsLoading] = useState(false);
const [info, setInfo] = useState(false);
////LifeCycle
//Component did mount
useEffect(() => {
//Initial fetch to get the status of Apprise Alert
const getAppriseAlert = async () => {
try {
const response = await fetch('/api/account/getAppriseAlert', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setChecked((await response.json()).appriseAlert);
setCheckIsLoading(false);
} catch (error) {
setError(
'Fetching apprise alert setting failed. Contact your administrator.'
);
console.log('Fetching apprise alert setting failed.');
setCheckIsLoading(false);
}
};
getAppriseAlert();
}, []);
////Functions
//Switch to enable/disable Apprise notifications
const onChangeSwitchHandler = async (data) => {
//Remove old error
setError();
//Disabled button
setDisabled(true);
await fetch('/api/account/updateAppriseAlert', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => {
console.log(response);
if (response.ok) {
if (data.appriseAlert) {
setChecked(!checked);
toast.success(
'Apprise notifications enabled.',
toastOptions
);
} else {
setChecked(!checked);
toast.success(
'Apprise notifications disabled.',
toastOptions
);
}
} else {
setError('Update apprise alert setting failed.');
setTimeout(() => {
setError();
setDisabled(false);
}, 4000);
}
})
.catch((error) => {
console.log(error);
setError('Update Apprise failed. Contact your administrator.');
setTimeout(() => {
setError();
setDisabled(false);
}, 4000);
});
};
//Send Apprise test notification to services
const onSendTestAppriseHandler = async () => {
//Loading
setTestIsLoading(true);
//Remove old error
setError();
try {
const response = await fetch('/api/account/sendTestApprise', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({ sendTestApprise: true }),
});
const result = await response.json();
if (!response.ok) {
setTestIsLoading(false);
setError(result.message);
} else {
setTestIsLoading(false);
setInfo(true);
setTimeout(() => {
setInfo(false);
}, 4000);
}
} catch (error) {
setTestIsLoading(false);
console.log(error);
setError('Send notification failed. Contact your administrator.');
setTimeout(() => {
setError();
}, 4000);
}
};
return (
<>
{/* APPRISE ALERT */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2 style={{ alignSelf: 'baseline' }}>Apprise alert</h2>
<Link
style={{ alignSelf: 'baseline', marginLeft: '5px' }}
href='https://borgwarehouse.com/docs/user-manual/account/#apprise'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{/* NOTIFY SWITCH */}
{checkIsLoading ? (
<SpinnerCircularFixed
size={30}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
) : (
<Switch
checked={checked}
disabled={disabled}
switchName='Notify my Apprise services'
switchDescription='You will receive an alert on all your services every 24H if you have a down status.'
onChange={(e) =>
onChangeSwitchHandler({ appriseAlert: e })
}
/>
)}
{/* APPRISE SERVICES URLS */}
<AppriseURLs />
{/* APPRISE MODE SELECTION */}
<AppriseMode />
{/* APPRISE TEST BUTTON */}
{testIsLoading ? (
<SpinnerCircularFixed
style={{ marginTop: '20px' }}
size={30}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
) : (
<button
style={{ marginTop: '20px' }}
className='defaultButton'
onClick={() => onSendTestAppriseHandler()}
>
Send a test notification
</button>
)}
{info && (
<span
style={{ marginLeft: '10px', color: '#119300' }}
>
Notification successfully sent.
</span>
)}
{error && <Error message={error} />}
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,171 @@
import { IconExternalLink } from '@tabler/icons-react';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
//Components
import Switch from '~/Components/UI/Switch/Switch';
import { useLoader } from '~/contexts/LoaderContext';
import { Optional } from '~/types';
import AppriseMode from './AppriseMode/AppriseMode';
import AppriseURLs from './AppriseURLs/AppriseURLs';
type AppriseAlertDataForm = {
appriseAlert: boolean;
};
export default function AppriseAlertSettings() {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const { start, stop } = useLoader();
////State
const [isSendingTestNotification, setIsSendingTestNotification] = useState(false);
const [isSwitchDisabled, setIsSwitchDisabled] = useState(true);
const [isAlertEnabled, setIsAlertEnabled] = useState<Optional<boolean>>(undefined);
const [info, setInfo] = useState(false);
////LifeCycle
//Component did mount
useEffect(() => {
//Initial fetch to get the status of Apprise Alert
const getAppriseAlert = async () => {
try {
const response = await fetch('/api/v1/notif/apprise/alert', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: Optional<AppriseAlertDataForm> = await response.json();
setIsAlertEnabled(data?.appriseAlert ?? false);
setIsSwitchDisabled(false);
} catch (error) {
setIsSwitchDisabled(true);
setIsAlertEnabled(false);
toast.error('Fetching Apprise alert setting failed', toastOptions);
}
};
getAppriseAlert();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
////Functions
//Switch to enable/disable Apprise notifications
const onChangeSwitchHandler = async (data: AppriseAlertDataForm) => {
start();
setIsSwitchDisabled(true);
await fetch('/api/v1/notif/apprise/alert', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => {
if (response.ok && typeof data.appriseAlert === 'boolean') {
setIsAlertEnabled(data.appriseAlert);
toast.success(
data.appriseAlert ? 'Apprise notifications enabled' : 'Apprise notifications disabled',
toastOptions
);
} else {
toast.error('Update Apprise failed', toastOptions);
}
})
.catch(() => {
toast.error('Update Apprise failed', toastOptions);
})
.finally(() => {
stop();
setIsSwitchDisabled(false);
});
};
//Send Apprise test notification to services
const onSendTestAppriseHandler = async () => {
start();
setIsSendingTestNotification(true);
try {
const response = await fetch('/api/v1/notif/apprise/test', {
method: 'POST',
});
const result = await response.json();
if (!response.ok) {
toast.error(result.message, toastOptions);
} else {
setInfo(true);
setTimeout(() => {
setInfo(false);
}, 4000);
}
} catch (error) {
toast.error('Sending test notification failed', toastOptions);
} finally {
stop();
setIsSendingTestNotification(false);
}
};
return (
<>
{/* APPRISE ALERT */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2 style={{ alignSelf: 'baseline' }}>Apprise alert</h2>
<Link
style={{ alignSelf: 'baseline', marginLeft: '5px' }}
href='https://borgwarehouse.com/docs/user-manual/account/#apprise'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
<Switch
loading={isAlertEnabled === undefined}
checked={isAlertEnabled}
disabled={isSwitchDisabled}
switchName='Notify my Apprise services'
switchDescription='You will receive an alert on all your services every 24H if you have a down status.'
onChange={(e) => onChangeSwitchHandler({ appriseAlert: e })}
/>
{isAlertEnabled && (
<>
<AppriseURLs />
<AppriseMode />
<button
disabled={isSendingTestNotification}
style={{ marginTop: '20px' }}
className='defaultButton'
onClick={() => onSendTestAppriseHandler()}
>
Send a test notification
</button>
{info && (
<span style={{ marginLeft: '10px', color: '#119300' }}>
Notification successfully sent.
</span>
)}
</>
)}
</div>
</div>
</div>
</>
);
}

View file

@ -1,173 +0,0 @@
//Lib
import { useEffect } from 'react';
import classes from '../../UserSettings.module.css';
import { useState } from 'react';
import { SpinnerCircularFixed } from 'spinners-react';
import { useForm } from 'react-hook-form';
//Components
import Error from '../../../../Components/UI/Error/Error';
export default function AppriseMode() {
//Var
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onBlur' });
////State
const [formIsLoading, setFormIsLoading] = useState(false);
const [modeFormIsSaved, setModeFormIsSaved] = useState(false);
const [error, setError] = useState(false);
const [displayStatelessURL, setDisplayStatelessURL] = useState(false);
const [appriseMode, setAppriseMode] = useState('stateless');
const [appriseStatelessURL, setAppriseStatelessURL] = useState();
////LifeCycle
//Component did mount
useEffect(() => {
//Initial fetch to get Apprise Mode enabled
const getAppriseMode = async () => {
try {
const response = await fetch('/api/account/getAppriseMode', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const { appriseStatelessURL, appriseMode } =
await response.json();
setAppriseMode(appriseMode);
if (appriseMode == 'stateless') {
setAppriseStatelessURL(appriseStatelessURL);
setDisplayStatelessURL(true);
}
} catch (error) {
console.log('Fetching Apprise Mode failed.');
}
};
getAppriseMode();
}, []);
////Functions
//Form submit handler to modify Apprise Mode
const modeFormSubmitHandler = async (data) => {
//Remove old error
setError();
//Loading button on submit to avoid multiple send.
setFormIsLoading(true);
//POST API to update Apprise Mode
try {
const response = await fetch('/api/account/updateAppriseMode', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
setFormIsLoading(false);
setError(result.message);
setTimeout(() => setError(), 4000);
} else {
setFormIsLoading(false);
setModeFormIsSaved(true);
setTimeout(() => setModeFormIsSaved(false), 3000);
}
} catch (error) {
setFormIsLoading(false);
setError('Change mode failed. Contact your administrator.');
setTimeout(() => {
setError();
}, 4000);
}
};
return (
<>
{/* APPRISE MODE SELECTION */}
<div className={classes.headerFormAppriseUrls}>
<div style={{ margin: '0px 10px 0px 0px' }}>Apprise mode</div>
<div style={{ display: 'flex' }}>
{formIsLoading && (
<SpinnerCircularFixed
size={18}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
)}
{modeFormIsSaved && (
<div className={classes.formIsSavedMessage}>
Apprise mode has been saved.
</div>
)}
</div>
</div>
{error && <Error message={error} />}
<form
className={classes.bwForm}
onBlur={handleSubmit(modeFormSubmitHandler)}
>
<div className='radio-group'>
<label style={{ marginRight: '50px' }}>
<div style={{ display: 'flex' }}>
<input
{...register('appriseMode')}
type='radio'
value='package'
onClick={() => {
setDisplayStatelessURL(false);
setAppriseMode('package');
}}
checked={
appriseMode == 'package' ? true : false
}
/>
<span>Local package</span>
</div>
</label>
<label>
<div style={{ display: 'flex' }}>
<input
{...register('appriseMode')}
value='stateless'
type='radio'
onClick={() => {
setDisplayStatelessURL(true);
setAppriseMode('stateless');
}}
checked={
appriseMode == 'stateless' ? true : false
}
/>
<span>Stateless API server</span>
</div>
</label>
</div>
{displayStatelessURL && (
<input
type='text'
placeholder='http://localhost:8000'
defaultValue={appriseStatelessURL}
{...register('appriseStatelessURL', {
pattern: {
value: /^(http|https):\/\/.+/g,
message: 'Invalid URL format.',
},
})}
/>
)}
{errors.appriseStatelessURL && (
<small className={classes.errorMessage}>
{errors.appriseStatelessURL.message}
</small>
)}
</form>
</>
);
}

View file

@ -0,0 +1,147 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { AppriseModeDTO, AppriseModeEnum, Optional } from '~/types';
import classes from '../../UserSettings.module.css';
//Components
import Error from '~/Components/UI/Error/Error';
import { useLoader } from '~/contexts/LoaderContext';
import { useFormStatus } from '~/hooks';
type AppriseModeDataForm = {
appriseMode: string;
appriseStatelessURL: string;
};
export default function AppriseMode() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<AppriseModeDataForm>({ mode: 'onChange' });
const { error, setIsLoading, handleSuccess, handleError, clearError } = useFormStatus();
const { start, stop } = useLoader();
const [displayStatelessURL, setDisplayStatelessURL] = useState<boolean>(false);
const [appriseMode, setAppriseMode] = useState<Optional<AppriseModeEnum>>(
AppriseModeEnum.STATELESS
);
const [appriseStatelessURL, setAppriseStatelessURL] = useState<Optional<string>>();
//Component did mount
useEffect(() => {
//Initial fetch to get Apprise Mode enabled
const getAppriseMode = async () => {
try {
const response = await fetch('/api/v1/notif/apprise/mode', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: AppriseModeDTO = await response.json();
const { appriseStatelessURL, appriseMode } = data;
setAppriseMode(appriseMode);
if (appriseMode == AppriseModeEnum.STATELESS) {
setAppriseStatelessURL(appriseStatelessURL);
setDisplayStatelessURL(true);
}
} catch (error) {
console.log('Fetching Apprise Mode failed.');
}
};
getAppriseMode();
}, []);
////Functions
const modeFormSubmitHandler = async (data: AppriseModeDataForm) => {
clearError();
setIsLoading(true);
start();
try {
const response = await fetch('/api/v1/notif/apprise/mode', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
handleError(result.message);
} else {
handleSuccess();
}
} catch (error) {
handleError('The Apprise mode change has failed');
} finally {
stop();
setIsLoading(false);
}
};
return (
<>
{/* APPRISE MODE SELECTION */}
<div className={classes.headerFormAppriseUrls}>
<div style={{ margin: '0px 10px 0px 0px' }}>Apprise mode</div>
</div>
{error && <Error message={error} />}
<form className={classes.bwForm} onChange={handleSubmit(modeFormSubmitHandler)}>
<div className='radio-group'>
<label style={{ marginRight: '50px' }}>
<div style={{ display: 'flex' }}>
<input
{...register('appriseMode')}
type='radio'
value='package'
onClick={() => {
setDisplayStatelessURL(false);
setAppriseMode(AppriseModeEnum.PACKAGE);
}}
checked={appriseMode == 'package' ? true : false}
/>
<span>Local package</span>
</div>
</label>
<label>
<div style={{ display: 'flex' }}>
<input
{...register('appriseMode')}
value='stateless'
type='radio'
onClick={() => {
setDisplayStatelessURL(true);
setAppriseMode(AppriseModeEnum.STATELESS);
}}
checked={appriseMode == 'stateless' ? true : false}
/>
<span>Stateless API server</span>
</div>
</label>
</div>
{displayStatelessURL && (
<input
type='text'
placeholder='http://localhost:8000'
defaultValue={appriseStatelessURL}
{...register('appriseStatelessURL', {
pattern: {
value: /^(http|https):\/\/.+/g,
message: 'Invalid URL format.',
},
})}
/>
)}
{errors.appriseStatelessURL && (
<small className={classes.errorMessage}>{errors.appriseStatelessURL.message}</small>
)}
</form>
</>
);
}

View file

@ -1,163 +0,0 @@
//Lib
import { useEffect } from 'react';
import classes from '../../UserSettings.module.css';
import { useState } from 'react';
import { SpinnerCircularFixed } from 'spinners-react';
import { useForm } from 'react-hook-form';
//Components
import Error from '../../../../Components/UI/Error/Error';
export default function AppriseURLs() {
//Var
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onBlur' });
////State
const [formIsLoading, setFormIsLoading] = useState(false);
const [urlsFormIsSaved, setUrlsFormIsSaved] = useState(false);
const [appriseServicesList, setAppriseServicesList] = useState();
const [error, setError] = useState();
////LifeCycle
//Component did mount
useEffect(() => {
//Initial fetch to build the list of Apprise Services enabled
const getAppriseServices = async () => {
try {
const response = await fetch(
'/api/account/getAppriseServices',
{
method: 'GET',
headers: {
'Content-type': 'application/json',
},
}
);
let servicesArray = (await response.json()).appriseServices;
const AppriseServicesListToText = () => {
let list = '';
for (let service of servicesArray) {
list += service + '\n';
}
return list;
};
setAppriseServicesList(AppriseServicesListToText());
} catch (error) {
console.log('Fetching Apprise services list failed.');
}
};
getAppriseServices();
}, []);
////Functions
//Form submit handler to modify Apprise services
const urlsFormSubmitHandler = async (data) => {
//Remove old error
setError();
//Loading button on submit to avoid multiple send.
setFormIsLoading(true);
//POST API to update Apprise Services
try {
const response = await fetch('/api/account/updateAppriseServices', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
setFormIsLoading(false);
setError(result.message);
setTimeout(() => setError(), 4000);
} else {
setFormIsLoading(false);
setUrlsFormIsSaved(true);
setTimeout(() => setUrlsFormIsSaved(false), 3000);
}
} catch (error) {
setFormIsLoading(false);
setError(
'Failed to update your services. Contact your administrator.'
);
setTimeout(() => {
setError();
}, 4000);
}
};
return (
<>
{/* APPRISE SERVICES URLS */}
<div className={classes.headerFormAppriseUrls}>
<div style={{ marginRight: '10px' }}>Apprise URLs</div>
{error && <Error message={error} />}
<div style={{ display: 'flex' }}>
{formIsLoading && (
<SpinnerCircularFixed
size={18}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
)}
{urlsFormIsSaved && (
<div className={classes.formIsSavedMessage}>
Apprise configuration has been saved.
</div>
)}
</div>
</div>
<form
onBlur={handleSubmit(urlsFormSubmitHandler)}
className={classes.bwForm + ' ' + classes.currentSetting}
>
<textarea
style={{ height: '100px' }}
type='text'
placeholder={
'matrixs://{user}:{password}@{matrixhost}\ndiscord://{WebhookID}/{WebhookToken}\nmmosts://user@hostname/authkey'
}
defaultValue={appriseServicesList}
{...register('appriseURLs', {
pattern: {
value: /^.+:\/\/.+$/gm,
message: 'Invalid URLs format.',
},
})}
/>
{errors.appriseURLs && (
<small className={classes.errorMessage}>
{errors.appriseURLs.message}
</small>
)}
</form>
<div
style={{
color: '#6c737f',
fontSize: '0.875rem',
marginBottom: '20px',
}}
>
Use{' '}
<a
style={{
color: '#6d4aff',
textDecoration: 'none',
}}
href='https://github.com/caronc/apprise#supported-notifications'
rel='noreferrer'
>
Apprise URLs
</a>{' '}
to send a notification to any service. Only one URL per line.
</div>
</>
);
}

View file

@ -0,0 +1,142 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { AppriseServicesDTO, Optional } from '~/types';
import classes from '../../UserSettings.module.css';
//Components
import Error from '~/Components/UI/Error/Error';
import { useLoader } from '~/contexts/LoaderContext';
import { useFormStatus } from '~/hooks';
type AppriseURLsDataForm = {
appriseURLs: string;
};
export default function AppriseURLs() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<AppriseURLsDataForm>({ mode: 'onBlur' });
const { isSaved, error, handleSuccess, handleError, clearError } = useFormStatus();
const { start, stop } = useLoader();
const [appriseServicesList, setAppriseServicesList] = useState<Optional<string>>();
const [fetchError, setFetchError] = useState<Optional<boolean>>();
//Component did mount
useEffect(() => {
//Initial fetch to build the list of Apprise Services enabled
const getAppriseServices = async () => {
try {
const response = await fetch('/api/v1/notif/apprise/services', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: AppriseServicesDTO = await response.json();
const servicesText = data.appriseServices?.join('\n');
setAppriseServicesList(servicesText);
setFetchError(false);
} catch (error) {
setFetchError(true);
handleError('Fetching Apprise services list failed.');
}
};
getAppriseServices();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//Form submit handler to modify Apprise services
const urlsFormSubmitHandler = async (data: AppriseURLsDataForm) => {
clearError();
start();
if (fetchError) {
handleError('Cannot update Apprise services. Failed to fetch the initial list.');
stop();
return;
}
try {
const response = await fetch('/api/v1/notif/apprise/services', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
handleError(result.message);
} else {
handleSuccess();
}
} catch (error) {
handleError('Failed to update your Apprise services.');
} finally {
stop();
}
};
return (
<>
{/* APPRISE SERVICES URLS */}
<div className={classes.headerFormAppriseUrls}>
<div style={{ marginRight: '10px' }}>Apprise URLs</div>
<div style={{ display: 'flex' }}>
{isSaved && (
<div className={classes.formIsSavedMessage}>
Apprise configuration has been saved.
</div>
)}
</div>
</div>
<form
onBlur={handleSubmit(urlsFormSubmitHandler)}
className={classes.bwForm + ' ' + classes.currentSetting}
>
<textarea
style={{ height: '100px' }}
placeholder={
'matrixs://{user}:{password}@{matrixhost}\ndiscord://{WebhookID}/{WebhookToken}\nmmosts://user@hostname/authkey'
}
defaultValue={appriseServicesList}
{...register('appriseURLs', {
pattern: {
value: /^.+:\/\/.+$/gm,
message: 'Invalid URLs format.',
},
})}
/>
{errors.appriseURLs && (
<small className={classes.errorMessage}>{errors.appriseURLs.message}</small>
)}
</form>
<div
style={{
color: '#6c737f',
fontSize: '0.875rem',
marginBottom: '20px',
}}
>
Use{' '}
<a
style={{
color: '#6d4aff',
textDecoration: 'none',
}}
href='https://github.com/caronc/apprise#supported-notifications'
rel='noreferrer'
>
Apprise URLs
</a>{' '}
to send a notification to any service. Only one URL per line.
</div>
{error && <Error message={error} />}
</>
);
}

View file

@ -1,208 +0,0 @@
//Lib
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
import { useState } from 'react';
import { SpinnerCircularFixed } from 'spinners-react';
import { IconExternalLink } from '@tabler/icons-react';
import Link from 'next/link';
//Components
import Error from '../../../Components/UI/Error/Error';
import Switch from '../../../Components/UI/Switch/Switch';
export default function EmailAlertSettings() {
//Var
const toastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
//Callback > re-enabled button after notification.
onClose: () => setDisabled(false),
};
////State
const [isLoading, setIsLoading] = useState(true);
const [testIsLoading, setTestIsLoading] = useState(false);
const [error, setError] = useState();
const [disabled, setDisabled] = useState(false);
const [checked, setChecked] = useState();
const [info, setInfo] = useState(false);
////LifeCycle
//Component did mount
useEffect(() => {
const dataFetch = async () => {
try {
const response = await fetch('/api/account/getEmailAlert', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
setChecked((await response.json()).emailAlert);
setIsLoading(false);
} catch (error) {
setError(
'Fetching email alert setting failed. Contact your administrator.'
);
console.log('Fetching email alert setting failed.');
setIsLoading(false);
}
};
dataFetch();
}, []);
////Functions
//Switch to enable/disable Email notifications
const onChangeSwitchHandler = async (data) => {
//Remove old error
setError();
//Disabled button
setDisabled(true);
await fetch('/api/account/updateEmailAlert', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => {
console.log(response);
if (response.ok) {
if (data.emailAlert) {
setChecked(!checked);
toast.success(
'Email notification enabled !',
toastOptions
);
} else {
setChecked(!checked);
toast.success(
'Email notification disabled !',
toastOptions
);
}
} else {
setError('Update email alert setting failed.');
setTimeout(() => {
setError();
setDisabled(false);
}, 4000);
}
})
.catch((error) => {
console.log(error);
setError('Update failed. Contact your administrator.');
setTimeout(() => {
setError();
setDisabled(false);
}, 4000);
});
};
//Send a test notification by email
const onSendTestMailHandler = async () => {
//Loading
setTestIsLoading(true);
//Remove old error
setError();
await fetch('/api/account/sendTestEmail', {
method: 'POST',
})
.then((response) => {
if (!response.ok) {
setTestIsLoading(false);
setError('Failed to send the notification.');
setTimeout(() => {
setError();
}, 4000);
} else {
setTestIsLoading(false);
setInfo(true);
setTimeout(() => {
setInfo(false);
}, 4000);
}
})
.catch((error) => {
setTestIsLoading(false);
console.log(error);
setError('Send email failed. Contact your administrator.');
setTimeout(() => {
setError();
}, 4000);
});
};
return (
<>
{/* EMAIL ALERT */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2 style={{ alignSelf: 'baseline' }}>Email alert</h2>
<Link
style={{ alignSelf: 'baseline', marginLeft: '5px' }}
href='https://borgwarehouse.com/docs/user-manual/account/#alerting'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{isLoading ? (
<SpinnerCircularFixed
size={30}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
) : (
<Switch
checked={checked}
disabled={disabled}
switchName='Alert me by email'
switchDescription='You will receive an alert every 24H if you have a down status.'
onChange={(e) =>
onChangeSwitchHandler({ emailAlert: e })
}
/>
)}
{testIsLoading ? (
<SpinnerCircularFixed
size={30}
thickness={150}
speed={150}
color='#704dff'
secondaryColor='#c3b6fa'
/>
) : (
<button
className='defaultButton'
onClick={onSendTestMailHandler}
>
Send a test mail
</button>
)}
{info && (
<span
style={{ marginLeft: '10px', color: '#119300' }}
>
Mail successfully sent.
</span>
)}
{error && <Error message={error} />}
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,169 @@
import { IconExternalLink } from '@tabler/icons-react';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useLoader } from '~/contexts/LoaderContext';
import { EmailAlertDTO, Optional } from '~/types';
import classes from '../UserSettings.module.css';
//Components
import Error from '~/Components/UI/Error/Error';
import Switch from '~/Components/UI/Switch/Switch';
import { useFormStatus } from '~/hooks';
export default function EmailAlertSettings() {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
//Callback > re-enabled button after notification.
onClose: () => setIsSwitchDisabled(false),
};
const { error, handleError, clearError } = useFormStatus();
const { start, stop } = useLoader();
////State
const [isSendingTestNotification, setIsSendingTestNotification] = useState(false);
const [isSwitchDisabled, setIsSwitchDisabled] = useState(true);
const [isAlertEnabled, setIsAlertEnabled] = useState<Optional<boolean>>(undefined);
const [info, setInfo] = useState(false);
////LifeCycle
//Component did mount
useEffect(() => {
const dataFetch = async () => {
try {
const response = await fetch('/api/v1/notif/email/alert', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: Optional<EmailAlertDTO> = await response.json();
setIsAlertEnabled(data?.emailAlert ?? false);
setIsSwitchDisabled(false);
} catch (error) {
setIsSwitchDisabled(true);
setIsAlertEnabled(false);
handleError('Fetching email alert setting failed');
}
};
dataFetch();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
////Functions
//Switch to enable/disable Email notifications
const onChangeSwitchHandler = async (data: EmailAlertDTO) => {
clearError();
start();
setIsSwitchDisabled(true);
await fetch('/api/v1/notif/email/alert', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => {
if (response.ok && typeof data.emailAlert === 'boolean') {
setIsAlertEnabled(data.emailAlert);
toast.success(
data.emailAlert ? 'Email notification enabled !' : 'Email notification disabled !',
toastOptions
);
} else {
handleError('Update email alert setting failed.');
}
})
.catch(() => {
handleError('Update email alert setting failed.');
})
.finally(() => {
stop();
setIsSwitchDisabled(false);
});
};
//Send a test notification by email
const onSendTestMailHandler = async () => {
clearError();
start();
setIsSendingTestNotification(true);
try {
const response = await fetch('/api/v1/notif/email/test', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
});
const result = await response.json();
if (!response.ok) {
setIsSendingTestNotification(false);
handleError(result.message);
} else {
setIsSendingTestNotification(false);
setInfo(true);
setTimeout(() => {
setInfo(false);
}, 4000);
}
} catch (error) {
setIsSendingTestNotification(false);
handleError('Send notification failed');
} finally {
stop();
}
};
return (
<>
{/* EMAIL ALERT */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2 style={{ alignSelf: 'baseline' }}>Email alert</h2>
<Link
style={{ alignSelf: 'baseline', marginLeft: '5px' }}
href='https://borgwarehouse.com/docs/user-manual/account/#alerting'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
<Switch
loading={isAlertEnabled === undefined}
checked={isAlertEnabled}
disabled={isSwitchDisabled}
switchName='Alert me by email'
switchDescription='You will receive an alert every 24H if you have a down status.'
onChange={(e) => onChangeSwitchHandler({ emailAlert: e })}
/>
<button
className='defaultButton'
disabled={isSendingTestNotification}
onClick={onSendTestMailHandler}
>
Send a test mail
</button>
{info && (
<span style={{ marginLeft: '10px', color: '#119300' }}>Mail successfully sent.</span>
)}
{error && <Error message={error} />}
</div>
</div>
</div>
</>
);
}

View file

@ -1,138 +0,0 @@
//Lib
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { SpinnerDotted } from 'spinners-react';
//Components
import Error from '../../../Components/UI/Error/Error';
import Info from '../../../Components/UI/Info/Info';
export default function EmailSettings(props) {
//Var
const toastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting, isValid },
} = useForm({ mode: 'onChange' });
////State
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
const [info, setInfo] = useState(false);
////Functions
//Form submit Handler for ADD a repo
const formSubmitHandler = async (data) => {
//Remove old error
setError();
//Loading button on submit to avoid multiple send.
setIsLoading(true);
//POST API to send the new mail address
try {
const response = await fetch('/api/account/updateEmail', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
setIsLoading(false);
reset();
setError(result.message);
setTimeout(() => setError(), 4000);
} else {
reset();
setIsLoading(false);
setInfo(true);
toast.success('Email edited !', toastOptions);
}
} catch (error) {
reset();
setIsLoading(false);
setError("Can't update your email. Contact your administrator.");
setTimeout(() => setError(), 4000);
}
};
return (
<>
{/* EMAIL */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Email</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{info ? ( //For local JWTs (cookie) without an OAuth provider, Next-Auth does not allow
//at the time this code is written to refresh client-side session information
//without triggering a logout.
//I chose to inform the user to reconnect rather than force logout.
<Info message='Please, logout to update your session.' />
) : (
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={
classes.bwForm +
' ' +
classes.currentSetting
}
>
<p>
{error && <Error message={error} />}
<input
type='email'
placeholder={props.email}
{...register('email', {
required: true,
pattern: {
value: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
message:
'Your email is not valid.',
},
})}
/>
{errors.email && (
<small className={classes.errorMessage}>
{errors.email.message}
</small>
)}
</p>
<button
className={classes.AccountSettingsButton}
disabled={!isValid || isSubmitting}
>
{isLoading ? (
<SpinnerDotted
size={20}
thickness={150}
speed={100}
color='#fff'
/>
) : (
'Update your email'
)}
</button>
</form>
)}
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,121 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
//Components
import Error from '~/Components/UI/Error/Error';
import Info from '~/Components/UI/Info/Info';
import { useLoader } from '~/contexts/LoaderContext';
import { useFormStatus } from '~/hooks';
import { EmailSettingDTO } from '~/types/api/setting.types';
export default function EmailSettings(props: EmailSettingDTO) {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting },
} = useForm<EmailSettingDTO>({ mode: 'onChange' });
const { isLoading, error, setIsLoading, handleError, clearError } = useFormStatus();
const { start, stop } = useLoader();
////State
const [info, setInfo] = useState(false);
////Functions
const formSubmitHandler = async (data: EmailSettingDTO) => {
start();
clearError();
setIsLoading(true);
try {
const response = await fetch('/api/v1/account/email', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
reset();
handleError(result.message);
} else {
reset();
setIsLoading(false);
setInfo(true);
toast.success('Email edited !', toastOptions);
}
} catch (error) {
reset();
handleError('Updating your email failed.');
} finally {
stop();
setIsLoading(false);
}
};
return (
<>
{/* EMAIL */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Email</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{info ? ( //For local JWTs (cookie) without an OAuth provider, Next-Auth does not allow
//at the time this code is written to refresh client-side session information
//without triggering a logout.
//I chose to inform the user to reconnect rather than force logout.
<Info message='Please, logout to update your session.' />
) : (
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={classes.bwForm + ' ' + classes.currentSetting}
>
<p>
{error && <Error message={error} />}
<input
type='email'
placeholder={props.email}
{...register('email', {
required: true,
pattern: {
value:
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
message: 'Your email is not valid.',
},
})}
/>
{errors.email && (
<small className={classes.errorMessage}>{errors.email.message}</small>
)}
</p>
<button
className={classes.AccountSettingsButton}
disabled={isSubmitting || isLoading}
>
Update your email
</button>
</form>
)}
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,340 @@
import { IconExternalLink, IconTrash } from '@tabler/icons-react';
import { fromUnixTime } from 'date-fns';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useFormStatus } from '~/hooks';
import { IntegrationTokenType, Optional, TokenPermissionEnum, TokenPermissionsType } from '~/types';
import classes from '../UserSettings.module.css';
//Components
import CopyButton from '~/Components/UI/CopyButton/CopyButton';
import Error from '~/Components/UI/Error/Error';
import Info from '~/Components/UI/Info/Info';
import { useLoader } from '~/contexts/LoaderContext';
type IntegrationsDataForm = {
tokenName: string;
};
export default function Integrations() {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting, isValid },
} = useForm<IntegrationsDataForm>({ mode: 'onChange' });
const { start, stop } = useLoader();
const { error, handleError, clearError, setIsLoading, isLoading } = useFormStatus();
const renderPermissionBadges = (permissions: TokenPermissionsType) => {
return Object.entries(permissions)
.filter(([, hasPermission]) => hasPermission)
.map(([key]) => (
<div key={key} className={classes.permissionBadge}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</div>
));
};
////State
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [tokenList, setTokenList] = useState<Array<IntegrationTokenType>>();
const [lastGeneratedToken, setLastGeneratedToken] =
useState<Optional<{ name: string; value: string }>>();
const [deletingToken, setDeletingToken] = useState<Optional<IntegrationTokenType>>(undefined);
const [permissions, setPermissions] = useState<TokenPermissionsType>({
create: false,
read: false,
update: false,
delete: false,
});
const fetchTokenList = async () => {
start();
try {
const response = await fetch('/api/v1/integration/token-manager', {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
});
const data: Array<IntegrationTokenType> = await response.json();
setTokenList(data);
} catch (error) {
handleError('Fetching token list failed.');
} finally {
stop();
}
};
////LifeCycle
useEffect(() => {
fetchTokenList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Permissions handler
const hasNoPermissionSelected = () => {
return !Object.values(permissions).some((value) => value);
};
const togglePermission = (permissionType: TokenPermissionEnum) => {
const updatedPermissions = {
...permissions,
[permissionType]: !permissions[permissionType],
};
setPermissions(updatedPermissions);
};
const resetPermissions = () => {
setPermissions({
create: false,
read: false,
update: false,
delete: false,
});
};
//Form submit handler to ADD a new token
const formSubmitHandler = async (data: IntegrationsDataForm) => {
start();
clearError();
setIsLoading(true);
// Post API to send the new token integration
try {
const response = await fetch('/api/v1/integration/token-manager', {
method: 'POST',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
name: data.tokenName,
permissions: permissions,
}),
});
const result = await response.json();
setLastGeneratedToken({ name: data.tokenName, value: result.token });
if (!response.ok) {
toast.error(result.message, toastOptions);
} else {
fetchTokenList();
toast.success('🔑 Token generated !', toastOptions);
}
} catch (error) {
toast.error('Failed to generate a new token', toastOptions);
} finally {
setIsLoading(false);
resetPermissions();
reset();
stop();
}
};
//Delete token
const deleteTokenHandler = async (tokenName: string) => {
setIsDeleteLoading(true);
try {
const response = await fetch('/api/v1/integration/token-manager', {
method: 'DELETE',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
name: tokenName,
}),
});
const result = await response.json();
if (!response.ok) {
toast.error(result.message, toastOptions);
setIsDeleteLoading(false);
} else {
fetchTokenList();
setIsDeleteLoading(false);
toast.success('🗑️ Token deleted !', toastOptions);
}
} catch (error) {
setIsDeleteLoading(false);
toast.error('Failed to delete the token', toastOptions);
} finally {
setIsDeleteLoading(false);
setDeletingToken(undefined);
}
};
return (
<>
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2 style={{ alignSelf: 'baseline' }}>Generate token</h2>
<Link
style={{ alignSelf: 'baseline', marginLeft: '5px' }}
href='https://borgwarehouse.com/docs/developer-manual/api/'
rel='noreferrer'
target='_blank'
>
<IconExternalLink size={16} color='#6c737f' />
</Link>
</div>
<div className={classes.setting}>
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={[classes.bwForm, classes.tokenGen].join(' ')}
>
<div className={classes.tokenWrapper}>
<input
type='text'
autoComplete='off'
placeholder='Token name'
{...register('tokenName', {
required: true,
pattern: /^[a-zA-Z0-9_-]*$/,
maxLength: 25,
})}
/>
<div className={classes.permissionsWrapper}>
<div
className={`${classes.permissionBadge} ${permissions.create ? classes.highlight : ''}`}
onClick={() => togglePermission(TokenPermissionEnum.CREATE)}
>
Create
</div>
<div
className={`${classes.permissionBadge} ${permissions.read ? classes.highlight : ''}`}
onClick={() => togglePermission(TokenPermissionEnum.READ)}
>
Read
</div>
<div
className={`${classes.permissionBadge} ${permissions.update ? classes.highlight : ''}`}
onClick={() => togglePermission(TokenPermissionEnum.UPDATE)}
>
Update
</div>
<div
className={`${classes.permissionBadge} ${permissions.delete ? classes.highlight : ''}`}
onClick={() => togglePermission(TokenPermissionEnum.DELETE)}
>
Delete
</div>
</div>
</div>
<button
className={classes.AccountSettingsButton}
disabled={!isValid || isSubmitting || hasNoPermissionSelected()}
>
Generate
</button>
</form>
{errors.tokenName && errors.tokenName.type === 'maxLength' && (
<small className={classes.errorMessage}>25 characters max.</small>
)}
{errors.tokenName && errors.tokenName.type === 'pattern' && (
<small className={classes.errorMessage}>
Only alphanumeric characters, dashes, and underscores are allowed (no spaces).
</small>
)}
{error && <Error message={error} />}
</div>
</div>
{tokenList && tokenList.length > 0 && (
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>API Tokens</h2>
</div>
<div className={classes.tokenCardList}>
{tokenList
.slice()
.sort((a, b) => b.creation - a.creation)
.map((token, index) => (
<div key={index} className={classes.tokenCardWrapper}>
<div
className={`${classes.tokenCard} ${
lastGeneratedToken && lastGeneratedToken.name === token.name
? classes.tokenCardHighlight
: ''
} ${deletingToken && deletingToken.name === token.name ? classes.tokenCardBlurred : ''}`}
>
<div className={classes.tokenCardHeader}>{token.name}</div>
<div className={classes.tokenCardBody}>
<div className={classes.tokenInfo}>
<strong>Created at:</strong>
{fromUnixTime(token.creation).toLocaleString()}
</div>
<div className={classes.tokenInfo}>
<strong>Permission:</strong>
<div className={classes.permissionBadges}>
{renderPermissionBadges(token.permissions)}
</div>
</div>
{lastGeneratedToken && lastGeneratedToken.name === token.name && (
<>
<div className={classes.tokenInfo}>
<strong>Token:</strong>
<CopyButton
size={22}
displayIconConfirmation={true}
dataToCopy={lastGeneratedToken.value}
>
<span>{lastGeneratedToken.value}</span>
</CopyButton>
</div>
<Info
color='#3498db'
message='This token will not be shown again. Please save it.'
/>
</>
)}
{deletingToken && deletingToken.name === token.name && (
<div className={classes.deleteConfirmationButtons}>
<button
className={classes.confirmButton}
onClick={() => deleteTokenHandler(token.name)}
disabled={isDeleteLoading}
>
Confirm
</button>
{!isDeleteLoading && (
<button
className={classes.cancelButton}
onClick={() => setDeletingToken(undefined)}
>
Cancel
</button>
)}
</div>
)}
</div>
</div>
<div className={classes.deleteToken}>
<IconTrash
cursor={'pointer'}
color='#ea1313'
strokeWidth={2}
onClick={() => setDeletingToken(token)}
/>
</div>
</div>
))}
</div>
</div>
)}
</>
);
}

View file

@ -1,136 +0,0 @@
//Lib
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { SpinnerDotted } from 'spinners-react';
//Components
import Error from '../../../Components/UI/Error/Error';
export default function PasswordSettings(props) {
//Var
const toastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting, isValid },
} = useForm({ mode: 'onChange' });
////State
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
////Functions
//Form submit Handler for ADD a repo
const formSubmitHandler = async (data) => {
console.log(data);
//Remove old error
setError();
//Loading button on submit to avoid multiple send.
setIsLoading(true);
//POST API to send the new and old password
try {
const response = await fetch('/api/account/updatePassword', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
setIsLoading(false);
reset();
setError(result.message);
setTimeout(() => setError(), 4000);
} else {
reset();
setIsLoading(false);
toast.success('🔑 Password edited !', toastOptions);
}
} catch (error) {
reset();
setIsLoading(false);
setError("Can't update your password. Contact your administrator.");
setTimeout(() => setError(), 4000);
}
};
return (
<>
{/* PASSWORD */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Password</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={classes.bwForm}
>
{error && <Error message={error} />}
<p>
<input
type='password'
placeholder='Current password'
{...register('oldPassword', {
required: true,
})}
/>
{errors.oldPassword &&
errors.oldPassword.type === 'required' && (
<small className={classes.errorMessage}>
This field is required.
</small>
)}
</p>
<p>
<input
type='password'
placeholder='New password'
{...register('newPassword', {
required: true,
})}
/>
{errors.newPassword && (
<small className={classes.errorMessage}>
This field is required.
</small>
)}
</p>
<button
className={classes.AccountSettingsButton}
disabled={!isValid || isSubmitting}
>
{isLoading ? (
<SpinnerDotted
size={20}
thickness={150}
speed={100}
color='#fff'
/>
) : (
'Update your password'
)}
</button>
</form>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,100 @@
import { useForm } from 'react-hook-form';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useFormStatus } from '~/hooks';
import { PasswordSettingDTO } from '~/types';
import classes from '../UserSettings.module.css';
//Components
import { useLoader } from '~/contexts/LoaderContext';
export default function PasswordSettings() {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { isSubmitting },
} = useForm<PasswordSettingDTO>({ mode: 'onChange' });
const { start, stop } = useLoader();
const { isLoading, setIsLoading } = useFormStatus();
////Functions
const formSubmitHandler = async (data: PasswordSettingDTO) => {
start();
setIsLoading(true);
try {
const response = await fetch('/api/v1/account/password', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
toast.error(result.message, toastOptions);
} else {
toast.success('🔑 Password edited !', toastOptions);
}
} catch (error) {
toast.error('Failed to update password. Please try again.', toastOptions);
} finally {
stop();
reset();
setIsLoading(false);
}
};
return (
<>
{/* PASSWORD */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Password</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
<form onSubmit={handleSubmit(formSubmitHandler)} className={classes.bwForm}>
<p>
<input
type='password'
placeholder='Current password'
{...register('oldPassword', {
required: true,
})}
/>
</p>
<p>
<input
type='password'
placeholder='New password'
{...register('newPassword', {
required: true,
})}
/>
</p>
<button
className={classes.AccountSettingsButton}
disabled={isLoading || isSubmitting}
>
Update your password
</button>
</form>
</div>
</div>
</div>
</>
);
}

View file

@ -1,67 +0,0 @@
//Lib
import 'react-toastify/dist/ReactToastify.css';
import classes from './UserSettings.module.css';
import { useState } from 'react';
//Components
import EmailSettings from './EmailSettings/EmailSettings';
import PasswordSettings from './PasswordSettings/PasswordSettings';
import UsernameSettings from './UsernameSettings/UsernameSettings';
import EmailAlertSettings from './EmailAlertSettings/EmailAlertSettings';
import AppriseAlertSettings from './AppriseAlertSettings/AppriseAlertSettings';
export default function UserSettings(props) {
//States
const [tab, setTab] = useState('General');
return (
<div className={classes.containerSettings}>
<div>
<h1
style={{
color: '#494b7a',
textAlign: 'left',
marginLeft: '30px',
}}
>
Account{' '}
</h1>
</div>
<div className={classes.tabList}>
<button
className={
tab == 'General'
? classes.tabListButtonActive
: classes.tabListButton
}
onClick={() => setTab('General')}
>
General
</button>
<button
className={
tab == 'Notifications'
? classes.tabListButtonActive
: classes.tabListButton
}
onClick={() => setTab('Notifications')}
>
Notifications
</button>
</div>
{tab == 'General' && (
<>
<PasswordSettings username={props.data.user.name} />
<EmailSettings email={props.data.user.email} />
<UsernameSettings username={props.data.user.name} />{' '}
</>
)}
{tab == 'Notifications' && (
<>
<EmailAlertSettings />
<AppriseAlertSettings />
</>
)}
</div>
);
}

View file

@ -1,251 +1,490 @@
.containerSettings {
display: flex;
flex-direction: column;
width: 100%;
max-width: 1000px;
margin-top: 10px;
display: flex;
flex-direction: column;
width: 100%;
max-width: 1000px;
margin-top: 10px;
}
.containerSetting {
display: flex;
flex-flow: row wrap;
width: 100%;
margin: 40px 20px 0px 5px;
text-align: left;
padding: 28px 24px;
animation: entrance ease-in 0.3s 1 normal none;
border-bottom: 1px solid #e5e7eb;
display: flex;
flex-flow: row wrap;
width: 100%;
margin: 40px 20px 0px 5px;
text-align: left;
padding: 28px 24px;
animation: entrance ease-in 0.3s 1 normal none;
border-bottom: 1px solid #e5e7eb;
}
@keyframes entrance {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
50% {
opacity: 0.5;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.settingCategory {
max-width: 33.3333%;
width: 100%;
display: flex;
max-width: 33.3333%;
width: 100%;
display: flex;
}
.settingCategory h2 {
color: #494b7a;
margin: 0;
font-size: 1.3em;
color: #494b7a;
margin: 0;
font-size: 1.3em;
}
.setting {
max-width: 66.6666%;
width: 100%;
max-width: 66.6666%;
width: 100%;
}
/* Tokens generation */
.tokenGen {
display: flex;
align-items: baseline;
justify-content: flex-start;
gap: 10px;
width: 100%;
box-sizing: border-box;
}
.tokenGen input {
flex: 1;
margin-right: 10px;
}
.newTokenWrapper {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 5px;
color: #494b7a;
outline: 1px solid #6d4aff;
box-shadow: 0 0 10px 3px rgba(110, 74, 255, 0.605);
animation: entrance ease-in 0.3s 1 normal none;
padding: 10px;
font-family: (--pure-material-font, 'Roboto', 'Segoe UI', BlinkMacSystemFont, system-ui);
}
.tokenCardList {
min-width: 50%;
}
.tokenCardWrapper {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
justify-content: space-between;
margin-bottom: 20px;
}
.tokenCard {
width: 100%;
border: 1px solid #ccc;
border-radius: 5px;
padding: 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.tokenCardHeader {
font-size: 1.2em;
margin-bottom: 10px;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
color: #494b7a;
}
.tokenCardBody {
font-size: 0.9em;
}
.tokenCardBody .permissionBadges {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: baseline;
align-content: baseline;
}
.tokenInfo {
display: flex;
align-items: center;
gap: 5px;
margin: 10px 0;
color: #494b7a;
}
.tokenCardHighlight {
animation: highlightEffect 1s ease-out forwards;
}
@keyframes highlightEffect {
0% {
outline: 1px solid #6d4aff;
box-shadow: 0 0 0 rgba(110, 74, 255, 0.5); /* Pas d'ombre au début */
}
50% {
outline: 1px solid #6d4aff;
box-shadow: 0 0 15px rgba(110, 74, 255, 0.6); /* Ombre qui s'agrandit */
}
100% {
outline: 1px solid transparent; /* Bordure devient transparente */
box-shadow: 0;
}
}
.cancelButton {
border: 0;
padding: 10px 15px;
background-color: #c1c1c1;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.cancelButton:hover {
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.cancelButton:active {
border: 0;
padding: 10px 15px;
background-color: #9a9a9a;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
}
.deleteConfirmationButtons {
display: flex;
flex-direction: row;
}
.confirmButton {
border: 0;
padding: 10px 15px;
background-color: #ff0000;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
display: flex;
align-items: center;
gap: 10px;
}
.confirmButton:disabled {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.confirmButton:hover {
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
}
.confirmButton:active {
border: 0;
padding: 10px 15px;
background-color: #ff4b4b;
color: white;
margin: 5px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
font-size: 1em;
transform: scale(0.9);
}
.permissionBadge {
user-select: none;
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
padding: 2px 5px;
margin-right: 8px;
}
.tokenWrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.permissionsWrapper {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
gap: 15px;
}
.permissionsWrapper .permissionBadge {
user-select: none;
border-radius: 5px;
border: 1px solid #9798b2;
color: #9798b2;
font-size: 0.9em;
margin: 0;
cursor: pointer;
}
.permissionsWrapper .permissionBadge.highlight {
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
margin: 0;
cursor: pointer;
}
/* Forms */
.bwForm {
width: 80%;
border-radius: 5px;
text-align: left;
width: 80%;
border-radius: 5px;
text-align: left;
}
.bwFormWrapper {
text-align: left;
margin: auto;
width: 100%;
height: auto;
color: #494b7a;
font-family: var(
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui,
-apple-system
);
text-align: left;
margin: auto;
width: 100%;
height: auto;
color: #494b7a;
font-family: var(
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui,
-apple-system
);
}
.bwFormWrapper p {
margin-block-start: 0em;
margin-block-start: 0em;
}
.bwForm label {
display: block;
margin-bottom: 8px;
text-align: center;
/* margin-top: 20px; */
color: #494b7a;
display: block;
margin-bottom: 8px;
text-align: center;
/* margin-top: 20px; */
color: #494b7a;
}
.bwForm.tokenGen label {
margin-bottom: 0px;
}
.bwForm input,
.bwForm textarea,
.bwForm select {
border: 1px solid #6d4aff21;
font-size: 16px;
height: auto;
margin: 0;
margin-bottom: 0px;
outline: 0;
padding: 10px;
width: 100%;
background-color: #f5f5f5;
border-radius: 5px;
/* color: #1b1340; */
color: #494b7a;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03) inset;
font-family: (
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui
);
border: 1px solid #6d4aff21;
font-size: 16px;
height: auto;
margin: 0;
margin-bottom: 0px;
outline: 0;
padding: 10px;
width: 100%;
background-color: #f5f5f5;
border-radius: 5px;
/* color: #1b1340; */
color: #494b7a;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03) inset;
font-family: (--pure-material-font, 'Roboto', 'Segoe UI', BlinkMacSystemFont, system-ui);
}
.bwForm textarea {
resize: vertical;
overflow: auto;
white-space: pre;
resize: vertical;
overflow: auto;
white-space: pre;
}
.bwForm textarea:focus,
.bwForm input:focus,
.bwForm select:focus {
outline: 1px solid #6d4aff;
box-shadow: 0 0 10px 3px rgba(110, 74, 255, 0.605);
outline: 1px solid #6d4aff;
box-shadow: 0 0 10px 3px rgba(110, 74, 255, 0.605);
}
.bwForm .invalid {
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
}
.bwForm .invalid:focus {
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
box-shadow: 0 0 10px 3px rgba(255, 74, 74, 0.605);
background: #f3c7c7;
border: 1px solid #e45454;
outline: 1px solid #ff4a4a;
box-shadow: 0 0 10px 3px rgba(255, 74, 74, 0.605);
}
.bwForm button {
display: block;
display: block;
}
.bwForm button:hover {
display: block;
display: block;
}
.errorMessage {
color: red;
display: block;
margin-top: 3px;
color: red;
display: block;
margin-top: 3px;
}
.currentSetting input::placeholder {
opacity: 1;
opacity: 1;
}
.headerFormAppriseUrls {
font-weight: 500;
color: #494b7a;
margin: 40px 0px 10px 0px;
display: flex;
padding-right: 5px;
font-weight: 500;
color: #494b7a;
margin-bottom: 10px;
display: flex;
padding-right: 5px;
}
.formIsSavedMessage {
color: rgb(0, 164, 0);
animation: entrance 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
font-weight: 300;
color: rgb(0, 164, 0);
animation: entrance 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
font-weight: 300;
}
.tabList {
display: flex;
display: flex;
}
.tabListButton {
color: #494b7a;
padding: 12px 0px;
min-height: 48px;
overflow: hidden;
text-align: center;
flex-direction: column;
font-size: 1em;
font-weight: 500;
line-height: 1.71;
text-transform: none;
align-items: center;
cursor: pointer;
vertical-align: middle;
text-decoration: none;
border: 0;
background-color: transparent;
margin-left: 30px;
border-bottom: 2px solid transparent;
color: #494b7a;
padding: 12px 0px;
min-height: 48px;
overflow: hidden;
text-align: center;
flex-direction: column;
font-size: 1em;
font-weight: 500;
line-height: 1.71;
text-transform: none;
align-items: center;
cursor: pointer;
vertical-align: middle;
text-decoration: none;
border: 0;
background-color: transparent;
margin-left: 30px;
border-bottom: 2px solid transparent;
}
.tabListButton:hover {
color: #6d4aff;
border-bottom: 2px solid #6d4aff;
color: #6d4aff;
border-bottom: 2px solid #6d4aff;
}
.tabListButtonActive {
color: #6d4aff;
border: 0;
border-bottom: 2px solid #6d4aff;
padding: 12px 0px;
min-height: 48px;
overflow: hidden;
text-align: center;
flex-direction: column;
font-size: 1em;
font-weight: 500;
line-height: 1.71;
text-transform: none;
align-items: center;
cursor: pointer;
vertical-align: middle;
text-decoration: none;
background-color: transparent;
margin-left: 30px;
color: #6d4aff;
border: 0;
border-bottom: 2px solid #6d4aff;
padding: 12px 0px;
min-height: 48px;
overflow: hidden;
text-align: center;
flex-direction: column;
font-size: 1em;
font-weight: 500;
line-height: 1.71;
text-transform: none;
align-items: center;
cursor: pointer;
vertical-align: middle;
text-decoration: none;
background-color: transparent;
margin-left: 30px;
}
.AccountSettingsButton {
border: 0;
padding: 10px 15px;
background-color: #6d4aff;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #6d4aff;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
}
.AccountSettingsButton:hover {
border: 0;
padding: 10px 15px;
background-color: #4f31ce;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
border: 0;
padding: 10px 15px;
background-color: #4f31ce;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
}
.AccountSettingsButton:active {
border: 0;
padding: 10px 15px;
background-color: #4f31ce;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
transform: scale(0.95);
border: 0;
padding: 10px 15px;
background-color: #4f31ce;
color: white;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-size: 1em;
transform: scale(0.95);
}
.AccountSettingsButton:disabled {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}

View file

@ -0,0 +1,100 @@
import 'react-toastify/dist/ReactToastify.css';
import classes from './UserSettings.module.css';
import { useState, useEffect } from 'react';
import { Session } from 'next-auth';
import { Optional, WizardEnvType, SessionStatus } from '~/types';
// Components
import EmailSettings from './EmailSettings/EmailSettings';
import PasswordSettings from './PasswordSettings/PasswordSettings';
import UsernameSettings from './UsernameSettings/UsernameSettings';
import EmailAlertSettings from './EmailAlertSettings/EmailAlertSettings';
import AppriseAlertSettings from './AppriseAlertSettings/AppriseAlertSettings';
import Integrations from './Integrations/Integrations';
type UserSettingsProps = {
status: SessionStatus;
data: Session;
};
export default function UserSettings({ data }: UserSettingsProps) {
const [tab, setTab] = useState<'General' | 'Notifications' | 'Integrations'>('General');
const [wizardEnv, setWizardEnv] = useState<Optional<WizardEnvType>>(undefined);
// Fetch wizard environment on mount
useEffect(() => {
const fetchWizardEnv = async () => {
try {
const response = await fetch('/api/v1/account/wizard-env');
const data: WizardEnvType = await response.json();
setWizardEnv(data);
} catch (error) {
console.error('Failed to fetch wizard environment:', error);
}
};
fetchWizardEnv();
}, []);
// If Integrations tab is selected but disabled, fallback to General
useEffect(() => {
if (tab === 'Integrations' && wizardEnv?.DISABLE_INTEGRATIONS === 'true') {
setTab('General');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wizardEnv?.DISABLE_INTEGRATIONS]);
return (
<div className={classes.containerSettings}>
<h1 style={{ color: '#494b7a', textAlign: 'left', marginLeft: '30px' }}>Account</h1>
{wizardEnv != undefined && (
<>
<div className={classes.tabList}>
<button
className={tab === 'General' ? classes.tabListButtonActive : classes.tabListButton}
onClick={() => setTab('General')}
>
General
</button>
<button
className={
tab === 'Notifications' ? classes.tabListButtonActive : classes.tabListButton
}
onClick={() => setTab('Notifications')}
>
Notifications
</button>
{wizardEnv.DISABLE_INTEGRATIONS !== 'true' && (
<button
className={
tab === 'Integrations' ? classes.tabListButtonActive : classes.tabListButton
}
onClick={() => setTab('Integrations')}
>
Integrations
</button>
)}
</div>
{tab === 'General' && (
<>
<PasswordSettings />
<EmailSettings email={data.user?.email ?? undefined} />
<UsernameSettings username={data.user?.name ?? undefined} />
</>
)}
{tab === 'Notifications' && (
<>
<EmailAlertSettings />
<AppriseAlertSettings />
</>
)}
{tab === 'Integrations' && wizardEnv.DISABLE_INTEGRATIONS !== 'true' && <Integrations />}
</>
)}
</div>
);
}

View file

@ -1,147 +0,0 @@
//Lib
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import classes from '../UserSettings.module.css';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { SpinnerDotted } from 'spinners-react';
//Components
import Error from '../../../Components/UI/Error/Error';
import Info from '../../../Components/UI/Info/Info';
export default function UsernameSettings(props) {
//Var
const toastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting, isValid },
} = useForm({ mode: 'onChange' });
////State
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
const [info, setInfo] = useState(false);
////Functions
//Form submit Handler for ADD a repo
const formSubmitHandler = async (data) => {
//Remove old error
setError();
//Loading button on submit to avoid multiple send.
setIsLoading(true);
//POST API to update the username
try {
const response = await fetch('/api/account/updateUsername', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
setIsLoading(false);
reset();
setError(result.message);
setTimeout(() => setError(), 4000);
} else {
reset();
setIsLoading(false);
setInfo(true);
toast.success('Username edited !', toastOptions);
}
} catch (error) {
reset();
setIsLoading(false);
setError("Can't update your username. Contact your administrator.");
setTimeout(() => setError(), 4000);
}
};
return (
<>
{/* Username */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Username</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{info ? (
//For local JWTs (cookie) without an OAuth provider, Next-Auth does not allow
//at the time this code is written to refresh client-side session information
//without triggering a logout.
//I chose to inform the user to reconnect rather than force logout.
<Info message='Please, logout to update your session.' />
) : (
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={
classes.bwForm +
' ' +
classes.currentSetting
}
>
<p>
{error && <Error message={error} />}
<input
type='text'
placeholder={props.username}
{...register('username', {
required: 'A username is required.',
pattern: {
value: /^[a-z]{5,15}$/,
message:
'Only a-z characters are allowed.',
},
maxLength: {
value: 10,
message: '15 characters max.',
},
minLength: {
value: 5,
message: '5 characters min.',
},
})}
/>
{errors.username && (
<small className={classes.errorMessage}>
{errors.username.message}
</small>
)}
</p>
<button
className={classes.AccountSettingsButton}
disabled={!isValid || isSubmitting}
>
{isLoading ? (
<SpinnerDotted
size={20}
thickness={150}
speed={100}
color='#fff'
/>
) : (
'Update your username'
)}
</button>
</form>
)}
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,123 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useFormStatus } from '~/hooks';
import { UsernameSettingDTO } from '~/types';
import classes from '../UserSettings.module.css';
//Components
import Info from '~/Components/UI/Info/Info';
import { useLoader } from '~/contexts/LoaderContext';
export default function UsernameSettings(props: UsernameSettingDTO) {
const toastOptions: ToastOptions = {
position: 'top-right',
autoClose: 8000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting },
} = useForm<UsernameSettingDTO>({ mode: 'onChange' });
const { start, stop } = useLoader();
const { isLoading, setIsLoading } = useFormStatus();
////State
const [info, setInfo] = useState(false);
////Functions
const formSubmitHandler = async (data: UsernameSettingDTO) => {
start();
setIsLoading(true);
try {
const response = await fetch('/api/v1/account/username', {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok) {
toast.error(result.message, toastOptions);
} else {
setInfo(true);
toast.success('Username edited !', toastOptions);
}
} catch (error) {
toast.error('Failed to update username. Please try again.', toastOptions);
} finally {
reset();
stop();
setIsLoading(false);
}
};
return (
<>
{/* Username */}
<div className={classes.containerSetting}>
<div className={classes.settingCategory}>
<h2>Username</h2>
</div>
<div className={classes.setting}>
<div className={classes.bwFormWrapper}>
{info ? (
//For local JWTs (cookie) without an OAuth provider, Next-Auth does not allow
//at the time this code is written to refresh client-side session information
//without triggering a logout.
//I chose to inform the user to reconnect rather than force logout.
<Info message='Please, logout to update your session' />
) : (
<form
onSubmit={handleSubmit(formSubmitHandler)}
className={classes.bwForm + ' ' + classes.currentSetting}
>
<p>
<input
type='text'
placeholder={props.username}
{...register('username', {
required: 'A username is required.',
pattern: {
value: /^[a-z]{1,40}$/,
message: 'Only a-z characters are allowed',
},
maxLength: {
value: 40,
message: '40 characters max.',
},
minLength: {
value: 1,
message: '1 characters min.',
},
})}
/>
{errors.username && (
<small className={classes.errorMessage}>{errors.username.message}</small>
)}
</p>
<button
className={classes.AccountSettingsButton}
disabled={isLoading || isSubmitting}
>
Update your username
</button>
</form>
)}
</div>
</div>
</div>
</>
);
}

View file

@ -1,37 +1,47 @@
FROM node:20-bookworm-slim as base
ARG UID=1001
ARG GID=1001
FROM node:22-bookworm-slim as base
# build stage
FROM base AS deps
RUN corepack enable && corepack prepare pnpm@9 --activate
WORKDIR /app
COPY package.json package-lock.json ./
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN npm ci --only=production
RUN pnpm install --frozen-lockfile --prod
FROM base AS builder
RUN corepack enable && corepack prepare pnpm@9 --activate
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN sed -i "s/images:/output: 'standalone',images:/" next.config.js
RUN sed -i "s/images:/output: 'standalone',images:/" next.config.ts
RUN npm run build
RUN pnpm run build
# run stage
FROM base AS runner
ARG UID
ARG GID
ENV NODE_ENV production
ENV HOSTNAME=
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y \
supervisor curl jq jc borgbackup/bookworm-backports openssh-server rsyslog && \
supervisor curl jq jc borgbackup/bookworm-backports openssh-server && \
apt-get clean && rm -rf /var/lib/apt/lists/*
RUN groupadd -g ${GID} borgwarehouse && useradd -m -u ${UID} -g ${GID} borgwarehouse
@ -46,11 +56,10 @@ COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/.next/standalone ./
COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/public ./public
COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/.next/static ./.next/static
COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/docker/supervisord.conf ./
COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/docker/rsyslog.conf /etc/rsyslog.conf
COPY --from=builder --chown=borgwarehouse:borgwarehouse /app/docker/sshd_config ./
USER borgwarehouse
EXPOSE 3000 22
ENTRYPOINT ["./docker-bw-init.sh"]
ENTRYPOINT ["./docker-bw-init.sh"]

View file

@ -1,16 +1,19 @@
<div align="center">
[![TypeScript][typescript.js]][typescript-url]
[![Next][Next.js]][Next-url]
[![React][React.js]][React-url]
</div>
<div align="center">
[![Docker](https://img.shields.io/badge/Docker-borgwarehouse-blue?style=for-the-badge&logo=docker)](https://hub.docker.com/r/borgwarehouse/borgwarehouse)
[![Docker Pulls](https://img.shields.io/docker/pulls/borgwarehouse/borgwarehouse?label=borgwarehouse&style=for-the-badge&logo=docker)](https://hub.docker.com/r/borgwarehouse/borgwarehouse)
</div>
<h3 align="center">BorgWarehouse</h3>
<img src="public/borgwarehouse-logo-violet.svg" alt="BorgWarehouse" style="margin: 30px 0">
<p align="center">
A fast and modern WebUI for a BorgBackup's central repository server.
@ -20,17 +23,17 @@
<div align="center">
<a href="https://borgwarehouse.com">
<img src="medias/borgwarehouse-og.png" alt="presentation">
<img src="medias/borgwarehouse-og.jpg" alt="presentation">
</a>
</div>
## ⭐ Support the Project
<div align="center">
<a href="https://github.com/sponsors/Ravinou"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/Ravinou?style=for-the-badge&logo=github&label=Github%20Sponsors&link=https%3A%2F%2Fgithub.com%2Fsponsors%2FRavinou"></a>
<a href="https://liberapay.com/R4VEN/"><img alt="Liberapay patrons" src="https://img.shields.io/liberapay/patrons/R4VEN?style=for-the-badge&logo=liberapay&label=Liberapay%20Sponsors&link=https%3A%2F%2Fliberapay.com%2FR4VEN"></a>
</div>
If you find BorgWarehouse helpful or interesting, please consider **giving it a star on GitHub** and **[sponsoring](https://github.com/sponsors/Ravinou)**. Your support is greatly appreciated!
## ✨ What is BorgWarehouse ?
@ -41,13 +44,14 @@ Today, if you want to have a large server on which you centralize backups of Bor
With BorgWarehouse, you have an interface that allows you to do all this simply and quickly :
- **add** repositories
- **edit** existing repositories
- **delete** repositories
- be **alerted** if there are no recent backups
- **monitor** the volume of data
- **flexibly manage quotas** for each repository
- ...
- **add** repositories
- **edit** existing repositories
- **delete** repositories
- be **alerted** if there are no recent backups
- **monitor** the volume of data
- **flexibly manage quotas** for each repository
- manage everything you want through the **REST API**
- ...
The whole system part is automatically managed by BorgWarehouse and **you don't have to touch your terminal anymore** while enjoying a visual feedback on the status of your repositories.
@ -70,10 +74,23 @@ Check the online documentation [just here](https://borgwarehouse.com/docs/admin-
## ❤️ Special thanks to sponsors ❤️
<a href="https://github.com/shad-lp"><img src="https://avatars.githubusercontent.com/shad-lp" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/Magneticdud"><img src="https://avatars.githubusercontent.com/Magneticdud" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/dhenry123"><img src="https://avatars.githubusercontent.com/dhenry123" style="width:50px; border-radius:50%;"/></a>
### 🥇 Current sponsors 🥇
<a href="https://github.com/royalmoose"><img src="https://avatars.githubusercontent.com/royalmoose" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/dhenry123"><img src="https://avatars.githubusercontent.com/dhenry123" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/fphammerle"><img src="https://avatars.githubusercontent.com/fphammerle" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/MacH59-cos"><img src="https://avatars.githubusercontent.com/MacH59-cos" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/shrippen"><img src="https://avatars.githubusercontent.com/shrippen" style="width:50px; border-radius:50%;"/></a>
<a href="https://github.com/daschmidt1994"><img src="https://avatars.githubusercontent.com/daschmidt1994" style="width:50px; border-radius:50%;"/></a>
#### Past sponsors
<a href="https://github.com/Drallibor"><img src="https://avatars.githubusercontent.com/Drallibor" style="width:25px; border-radius:50%;"/></a>
<a href="https://github.com/shad-lp"><img src="https://avatars.githubusercontent.com/shad-lp" style="width:25px; border-radius:50%;"/></a>
<a href="https://github.com/Magneticdud"><img src="https://avatars.githubusercontent.com/Magneticdud" style="width:25px; border-radius:50%;"/></a>
[typescript.js]: https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white
[typescript-url]: https://www.typescriptlang.org/
[next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
[next-url]: https://nextjs.org/
[react.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB

Some files were not shown because too many files have changed in this diff Show more