mirror of
https://github.com/meienberger/runtipi.git
synced 2024-10-03 23:28:33 +03:00
Release/3.3.1 (#1438)
* refactor(app-store): improve apps payload to include only necessary fields * chore: downgrade to pnpm v9 * chore(deps): bump the minor-patch group across 1 directory with 7 updates Bumps the minor-patch group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [next](https://github.com/vercel/next.js) | `14.2.2` | `14.2.3` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `18.2.0` | `18.3.1` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.2.79` | `18.3.1` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `18.2.0` | `18.3.1` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.25` | `18.3.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.12.7` | `20.12.8` | | [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `14.2.2` | `14.2.3` | Updates `next` from 14.2.2 to 14.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/v14.2.2...v14.2.3) Updates `react` from 18.2.0 to 18.3.1 - [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/v18.3.1/packages/react) Updates `@types/react` from 18.2.79 to 18.3.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 18.2.0 to 18.3.1 - [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/v18.3.1/packages/react-dom) Updates `@types/react-dom` from 18.2.25 to 18.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `@types/node` from 20.12.7 to 20.12.8 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react` from 18.2.79 to 18.3.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@types/react-dom` from 18.2.25 to 18.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `eslint-config-next` from 14.2.2 to 14.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/commits/v14.2.3/packages/eslint-config-next) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: react dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: react-dom dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: eslint-config-next dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> * feat: add tipi minimum version requirement when installing and updating * feat(dashboard): add warning if internal ip is public fix(dashboard): fix eslint error * New Crowdin updates (#1387) * New translations en.json (French) * New translations en.json (Turkish) * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Afrikaans) * New translations en.json (Arabic) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Dutch) * New translations en.json (Norwegian) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Serbian (Cyrillic)) * New translations en.json (Swedish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (English) * New translations en.json (Vietnamese) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Russian) * New translations en.json (Vietnamese) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Portuguese, Brazilian) * New translations en.json (French) * New translations en.json (Turkish) * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Afrikaans) * New translations en.json (Arabic) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Dutch) * New translations en.json (Norwegian) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Serbian (Cyrillic)) * New translations en.json (Swedish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (English) * New translations en.json (Vietnamese) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Russian) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Portuguese, Brazilian) * New translations en.json (French) * New translations en.json (Turkish) * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Afrikaans) * New translations en.json (Arabic) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Dutch) * New translations en.json (Norwegian) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Serbian (Cyrillic)) * New translations en.json (Swedish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (English) * New translations en.json (Vietnamese) * New translations en.json (Portuguese, Brazilian) * chore(deps-dev): bump @testing-library/react from 14.3.1 to 15.0.6 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 14.3.1 to 15.0.6. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v14.3.1...v15.0.6) --- updated-dependencies: - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps-dev): bump @testing-library/dom from 9.3.4 to 10.1.0 Bumps [@testing-library/dom](https://github.com/testing-library/dom-testing-library) from 9.3.4 to 10.1.0. - [Release notes](https://github.com/testing-library/dom-testing-library/releases) - [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/dom-testing-library/compare/v9.3.4...v10.1.0) --- updated-dependencies: - dependency-name: "@testing-library/dom" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * New Crowdin updates (#1396) * New translations en.json (French) * New translations en.json (Turkish) * New translations en.json (Romanian) * New translations en.json (Spanish) * New translations en.json (Afrikaans) * New translations en.json (Arabic) * New translations en.json (Catalan) * New translations en.json (Czech) * New translations en.json (Danish) * New translations en.json (German) * New translations en.json (Greek) * New translations en.json (Finnish) * New translations en.json (Hebrew) * New translations en.json (Hungarian) * New translations en.json (Italian) * New translations en.json (Japanese) * New translations en.json (Korean) * New translations en.json (Dutch) * New translations en.json (Norwegian) * New translations en.json (Polish) * New translations en.json (Portuguese) * New translations en.json (Russian) * New translations en.json (Serbian (Cyrillic)) * New translations en.json (Swedish) * New translations en.json (Ukrainian) * New translations en.json (Chinese Simplified) * New translations en.json (Chinese Traditional) * New translations en.json (English) * New translations en.json (Vietnamese) * New translations en.json (Portuguese, Brazilian) * New translations en.json (Russian) * chore(deps): bump the minor-patch group across 1 directory with 24 updates (#1403) Bumps the minor-patch group with 24 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@sentry/integrations](https://github.com/getsentry/sentry-javascript) | `7.113.0` | `7.114.0` | | [@sentry/nextjs](https://github.com/getsentry/sentry-javascript) | `7.113.0` | `7.114.0` | | [next-intl](https://github.com/amannn/next-intl) | `3.12.1` | `3.13.0` | | [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.51.3` | `7.51.4` | | [sass](https://github.com/sass/dart-sass) | `1.76.0` | `1.77.0` | | [semver](https://github.com/npm/node-semver) | `7.6.0` | `7.6.2` | | [validator](https://github.com/validatorjs/validator.js) | `13.11.0` | `13.12.0` | | [zod](https://github.com/colinhacks/zod) | `3.23.6` | `3.23.8` | | [@playwright/test](https://github.com/microsoft/playwright) | `1.43.1` | `1.44.0` | | [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) | `6.4.3` | `6.4.5` | | [@testing-library/react](https://github.com/testing-library/react-testing-library) | `15.0.6` | `15.0.7` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.12.8` | `20.12.11` | | [@types/pg](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg) | `8.11.5` | `8.11.6` | | [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `1.5.3` | `1.6.0` | | [dotenv-cli](https://github.com/entropitor/dotenv-cli) | `7.4.1` | `7.4.2` | | [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) | `28.4.0` | `28.5.0` | | [knip](https://github.com/webpro/knip/tree/HEAD/packages/knip) | `5.12.0` | `5.13.0` | | [msw](https://github.com/mswjs/msw) | `2.2.14` | `2.3.0` | | [tsx](https://github.com/privatenumber/tsx) | `4.8.2` | `4.9.3` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `1.5.3` | `1.6.0` | | [@sentry/types](https://github.com/getsentry/sentry-javascript) | `7.113.0` | `7.114.0` | | [@sentry/node](https://github.com/getsentry/sentry-javascript) | `7.113.0` | `7.114.0` | | [hono](https://github.com/honojs/hono) | `4.2.9` | `4.3.4` | | [systeminformation](https://github.com/sebhildebrandt/systeminformation) | `5.22.7` | `5.22.8` | Updates `@sentry/integrations` from 7.113.0 to 7.114.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.114.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.113.0...7.114.0) Updates `@sentry/nextjs` from 7.113.0 to 7.114.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.114.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.113.0...7.114.0) Updates `next-intl` from 3.12.1 to 3.13.0 - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v3.12.1...v3.13.0) Updates `react-hook-form` from 7.51.3 to 7.51.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.51.3...v7.51.4) Updates `sass` from 1.76.0 to 1.77.0 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.76.0...1.77.0) Updates `semver` from 7.6.0 to 7.6.2 - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.6.0...v7.6.2) Updates `validator` from 13.11.0 to 13.12.0 - [Release notes](https://github.com/validatorjs/validator.js/releases) - [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/validatorjs/validator.js/compare/13.11.0...13.12.0) Updates `zod` from 3.23.6 to 3.23.8 - [Release notes](https://github.com/colinhacks/zod/releases) - [Changelog](https://github.com/colinhacks/zod/blob/master/CHANGELOG.md) - [Commits](https://github.com/colinhacks/zod/compare/v3.23.6...v3.23.8) Updates `@playwright/test` from 1.43.1 to 1.44.0 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.43.1...v1.44.0) Updates `@testing-library/jest-dom` from 6.4.3 to 6.4.5 - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v6.4.3...v6.4.5) Updates `@testing-library/react` from 15.0.6 to 15.0.7 - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v15.0.6...v15.0.7) Updates `@types/node` from 20.12.8 to 20.12.11 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/pg` from 8.11.5 to 8.11.6 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pg) Updates `@vitest/coverage-v8` from 1.5.3 to 1.6.0 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v1.6.0/packages/coverage-v8) Updates `dotenv-cli` from 7.4.1 to 7.4.2 - [Release notes](https://github.com/entropitor/dotenv-cli/releases) - [Commits](https://github.com/entropitor/dotenv-cli/compare/v7.4.1...v7.4.2) Updates `eslint-plugin-jest` from 28.4.0 to 28.5.0 - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v28.4.0...v28.5.0) Updates `knip` from 5.12.0 to 5.13.0 - [Release notes](https://github.com/webpro/knip/releases) - [Changelog](https://github.com/webpro/knip/blob/main/packages/knip/.release-it.json) - [Commits](https://github.com/webpro/knip/commits/5.13.0/packages/knip) Updates `msw` from 2.2.14 to 2.3.0 - [Release notes](https://github.com/mswjs/msw/releases) - [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md) - [Commits](https://github.com/mswjs/msw/compare/v2.2.14...v2.3.0) Updates `tsx` from 4.8.2 to 4.9.3 - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.8.2...v4.9.3) Updates `vitest` from 1.5.3 to 1.6.0 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v1.6.0/packages/vitest) Updates `@sentry/types` from 7.113.0 to 7.114.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.114.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.113.0...7.114.0) Updates `@sentry/node` from 7.113.0 to 7.114.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/7.114.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.113.0...7.114.0) Updates `hono` from 4.2.9 to 4.3.4 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.2.9...v4.3.4) Updates `systeminformation` from 5.22.7 to 5.22.8 - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.22.7...v5.22.8) --- updated-dependencies: - dependency-name: "@sentry/integrations" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/nextjs" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: next-intl dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: react-hook-form dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: sass dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: validator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: zod dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@testing-library/jest-dom" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@types/pg" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@vitest/coverage-v8" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: dotenv-cli dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: eslint-plugin-jest dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: knip dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: msw dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: tsx dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: vitest dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/types" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: hono dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: systeminformation dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add password reset request expiry check and automatic session invalidation * chore(deps): bump the minor-patch group across 1 directory with 7 updates (#1405) * Update auth.service.ts Move request expiry time to a constant so it can be changed easier * chore: bump sentry dependencies * chore(deps): bump the minor-patch group across 1 directory with 7 updates (#1405) * chore: bump sentry dependencies * chore(auth.service): small async improvements * fix: missing sentry/next in worker * New Crowdin updates (#1400) * New translations en.json (Spanish) * New translations en.json (Spanish) * New translations en.json (French) * chore(deps): bump pnpm/action-setup from 3.0.0 to 4.0.0 Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 3.0.0 to 4.0.0. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/v3.0.0...v4.0.0) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix(app-actions): missing open buttons with legacy app setup * test: enable app folder in tests * test: migrate to vitest * feat/section-app-install-form (#1414) * done * fix: wrong conditions --------- Co-authored-by: Nicolas Meienberger <github@thisprops.com> * refactor(docker-compose.json): fallback to classic docker compose in case of failing to parse file * chore(app.executors): add more logging * chore(watcher): pull repo only in non-dev mode * chore: cleanup eslint config and add no-floating-promise rule * chore: cleanup eslint configs in packages * chore(deps): bump the minor-patch group across 1 directory with 13 updates Bumps the minor-patch group with 3 updates in the / directory: [@hookform/resolvers](https://github.com/react-hook-form/resolvers), [sharp](https://github.com/lovell/sharp) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node). Updates `@hookform/resolvers` from 3.4.0 to 3.4.2 - [Release notes](https://github.com/react-hook-form/resolvers/releases) - [Commits](https://github.com/react-hook-form/resolvers/compare/v3.4.0...v3.4.2) Updates `@sentry/nextjs` from 8.2.1 to 8.3.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.2.1...8.3.0) Updates `@tabler/icons-react` from 3.4.0 to 3.5.0 - [Release notes](https://github.com/tabler/tabler-icons/releases) - [Commits](https://github.com/tabler/tabler-icons/commits/v3.5.0/packages/icons-react) Updates `bullmq` from 5.7.9 to 5.7.11 - [Release notes](https://github.com/taskforcesh/bullmq/releases) - [Commits](https://github.com/taskforcesh/bullmq/compare/v5.7.9...v5.7.11) Updates `react-hook-form` from 7.51.4 to 7.51.5 - [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.4...v7.51.5) Updates `sharp` from 0.33.3 to 0.33.4 - [Release notes](https://github.com/lovell/sharp/releases) - [Changelog](https://github.com/lovell/sharp/blob/main/docs/changelog.md) - [Commits](https://github.com/lovell/sharp/compare/v0.33.3...v0.33.4) Updates `@types/node` from 20.12.11 to 20.12.12 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@vitejs/plugin-react` from 4.2.1 to 4.3.0 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/v4.3.0/packages/plugin-react) Updates `ts-jest` from 29.1.2 to 29.1.3 - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.2...v29.1.3) Updates `tsx` from 4.10.4 to 4.11.0 - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.10.4...v4.11.0) Updates `@sentry/types` from 8.2.1 to 8.3.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.2.1...8.3.0) Updates `@sentry/node` from 8.2.1 to 8.3.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.2.1...8.3.0) Updates `hono` from 4.3.7 to 4.3.9 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.3.7...v4.3.9) --- updated-dependencies: - dependency-name: "@hookform/resolvers" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@sentry/nextjs" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@tabler/icons-react" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: bullmq dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: react-hook-form dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: sharp dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@vitejs/plugin-react" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: ts-jest dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: tsx dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/types" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: hono dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Update regex in install script (#1426) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] * chore(deps): bump the minor-patch group across 1 directory with 10 updates (#1434) Bumps the minor-patch group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@sentry/nextjs](https://github.com/getsentry/sentry-javascript) | `8.3.0` | `8.4.0` | | [bullmq](https://github.com/taskforcesh/bullmq) | `5.7.11` | `5.7.12` | | [next-intl](https://github.com/amannn/next-intl) | `3.14.0` | `3.14.1` | | [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.24.5` | `7.24.6` | | [@playwright/test](https://github.com/microsoft/playwright) | `1.44.0` | `1.44.1` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.3.2` | `18.3.3` | | [@sentry/types](https://github.com/getsentry/sentry-javascript) | `8.3.0` | `8.4.0` | | [@sentry/node](https://github.com/getsentry/sentry-javascript) | `8.3.0` | `8.4.0` | | [hono](https://github.com/honojs/hono) | `4.3.9` | `4.3.11` | | [@sentry/esbuild-plugin](https://github.com/getsentry/sentry-javascript-bundler-plugins) | `2.16.1` | `2.17.0` | Updates `@sentry/nextjs` from 8.3.0 to 8.4.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.3.0...8.4.0) Updates `bullmq` from 5.7.11 to 5.7.12 - [Release notes](https://github.com/taskforcesh/bullmq/releases) - [Commits](https://github.com/taskforcesh/bullmq/compare/v5.7.11...v5.7.12) Updates `next-intl` from 3.14.0 to 3.14.1 - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v3.14.0...v3.14.1) Updates `@babel/core` from 7.24.5 to 7.24.6 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-core) Updates `@playwright/test` from 1.44.0 to 1.44.1 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.44.0...v1.44.1) Updates `@types/react` from 18.3.2 to 18.3.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@sentry/types` from 8.3.0 to 8.4.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.3.0...8.4.0) Updates `@sentry/node` from 8.3.0 to 8.4.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/8.3.0...8.4.0) Updates `hono` from 4.3.9 to 4.3.11 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.3.9...v4.3.11) Updates `@sentry/esbuild-plugin` from 2.16.1 to 2.17.0 - [Release notes](https://github.com/getsentry/sentry-javascript-bundler-plugins/releases) - [Changelog](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript-bundler-plugins/compare/2.16.1...2.17.0) --- updated-dependencies: - dependency-name: "@sentry/nextjs" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: bullmq dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: next-intl dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@sentry/types" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-patch - dependency-name: hono dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-patch - dependency-name: "@sentry/esbuild-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1432) * ci: local e2e * ci(e2e): add workflow dispatch * ci(e2e): replace usages of old e2e workflow (#1436) * chore: various cleanups and deprecations fixes (#1437) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stavros <steveiliop56@gmail.com> Co-authored-by: hex-developer <77530549+hex-developer@users.noreply.github.com> Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
This commit is contained in:
parent
0c718ee1fe
commit
06f36881c3
@ -466,6 +466,15 @@
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hex-developer",
|
||||
"name": "hex-developer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/77530549?v=4",
|
||||
"profile": "https://github.com/hex-developer",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@ -16,3 +16,4 @@
|
||||
!src/**
|
||||
!tests/**
|
||||
!start.*.sh
|
||||
!scripts/**
|
||||
|
19
.eslintrc.js
19
.eslintrc.js
@ -1,18 +1,12 @@
|
||||
module.exports = {
|
||||
plugins: ['@typescript-eslint', 'import', 'react', 'jest', 'jsx-a11y', 'testing-library', 'jest-dom', 'jsonc', 'drizzle'],
|
||||
plugins: ['@typescript-eslint', 'import', 'react', 'jsx-a11y'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'next/core-web-vitals',
|
||||
'next',
|
||||
'airbnb',
|
||||
'airbnb-typescript',
|
||||
'eslint:recommended',
|
||||
'plugin:import/typescript',
|
||||
'plugin:react/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'plugin:jsonc/recommended-with-json',
|
||||
'plugin:jsonc/prettier',
|
||||
'plugin:drizzle/recommended',
|
||||
'prettier',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
@ -23,6 +17,7 @@ module.exports = {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 1,
|
||||
'no-restricted-exports': 0,
|
||||
'no-redeclare': 0, // already handled by @typescript-eslint/no-redeclare
|
||||
'react/display-name': 0,
|
||||
@ -70,10 +65,13 @@ module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.test.ts', '*.test.tsx'],
|
||||
plugins: ['jest', 'jest-dom', 'testing-library'],
|
||||
extends: ['plugin:jest-dom/recommended', 'plugin:testing-library/react'],
|
||||
},
|
||||
{
|
||||
files: ['**/*.json', '*.json5', '*.jsonc'],
|
||||
plugins: ['jsonc'],
|
||||
extends: ['plugin:jsonc/recommended-with-json', 'plugin:jsonc/prettier'],
|
||||
parser: 'jsonc-eslint-parser',
|
||||
rules: {
|
||||
// Disable all @typescript-eslint rules as they don't apply here
|
||||
@ -84,7 +82,7 @@ module.exports = {
|
||||
'@typescript-eslint/return-await': 'off',
|
||||
// jsonc rules
|
||||
'jsonc/sort-keys': 2,
|
||||
'jsonc/key-name-casing': 0,
|
||||
'jsonc/key-name-casing': 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -92,7 +90,4 @@ module.exports = {
|
||||
JSX: true,
|
||||
NodeJS: true,
|
||||
},
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
};
|
||||
|
184
.github/workflows/e2e.yml
vendored
184
.github/workflows/e2e.yml
vendored
@ -1,5 +1,4 @@
|
||||
name: E2E Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
@ -7,110 +6,90 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
description: 'Version to test (e.g. v1.6.0-beta.1)'
|
||||
outputs:
|
||||
page_url:
|
||||
description: 'URL of the deployed report'
|
||||
value: ${{ jobs.report-deployment.outputs.page_url }}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
description: 'Version to test (e.g. v1.6.0-beta.1)'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
timeout-minutes: 15
|
||||
build-images:
|
||||
if: ${{ !inputs.version }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
droplet_id: ${{ steps.create-droplet.outputs.droplet_id }}
|
||||
droplet_ip: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
|
||||
postgres_password: ${{ steps.get-postgres-password.outputs.postgres_password }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install SSH key
|
||||
uses: shimataro/ssh-key-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
known_hosts: unnecessary
|
||||
name: id_rsa
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get sha of last commit
|
||||
id: get-sha
|
||||
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install doctl
|
||||
uses: digitalocean/action-doctl@v2
|
||||
- name: Build and push images
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
||||
context: .
|
||||
build-args: |
|
||||
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
TIPI_VERSION=e2e
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ghcr.io/runtipi/runtipi:e2e
|
||||
cache-from: type=registry,ref=ghcr.io/runtipi/runtipi:buildcache
|
||||
cache-to: type=registry,ref=ghcr.io/runtipi/runtipi:buildcache,mode=max
|
||||
|
||||
- name: Create new Droplet
|
||||
id: create-droplet
|
||||
- name: Create cli folder
|
||||
run: mkdir -p bin
|
||||
|
||||
- name: Download CLI form release on runtipi/cli repo
|
||||
run: |
|
||||
droplet_id=$(doctl compute droplet create runtipi-${{ steps.get-sha.outputs.sha }} \
|
||||
--image ubuntu-20-04-x64 \
|
||||
--size s-2vcpu-2gb \
|
||||
--format ID \
|
||||
--no-header \
|
||||
--ssh-keys ${{ secrets.SSH_KEY_FINGERPRINT }})
|
||||
echo "droplet_id=$droplet_id" >> $GITHUB_OUTPUT
|
||||
REPO="runtipi/cli"
|
||||
VERSION="nightly"
|
||||
|
||||
- name: Wait for Droplet to become active
|
||||
run: |
|
||||
while ! doctl compute droplet get ${{ steps.create-droplet.outputs.droplet_id }} --format Status --no-header | grep -q "active"; do sleep 5; done
|
||||
ASSETS_URL=$(curl -s \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/$REPO/releases/tags/$VERSION" \
|
||||
| jq -r '.assets[] | select(.name | test("runtipi-cli.+")) | .browser_download_url')
|
||||
|
||||
- name: Get Droplet IP address
|
||||
id: get-droplet-ip
|
||||
run: |
|
||||
droplet_ip=$(doctl compute droplet get ${{ steps.create-droplet.outputs.droplet_id }} --format PublicIPv4 --no-header)
|
||||
echo "droplet_ip=$droplet_ip" >> $GITHUB_OUTPUT
|
||||
echo "Assets URL: $ASSETS_URL"
|
||||
|
||||
- name: Wait for SSH to be ready on Droplet
|
||||
run: |
|
||||
while ! ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa root@${{ steps.get-droplet-ip.outputs.droplet_ip }} "echo 'SSH is ready'"; do sleep 5; done
|
||||
for url in $ASSETS_URL; do
|
||||
echo "Downloading from $url"
|
||||
curl -L -o "bin/${url##*/}" -H "Accept: application/octet-stream" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$url"
|
||||
done
|
||||
|
||||
- name: Create docker group on Droplet
|
||||
uses: fifsky/ssh-action@master
|
||||
- name: Print files
|
||||
run: tree bin
|
||||
|
||||
- uses: pyTooling/Actions/releaser@r0
|
||||
with:
|
||||
command: |
|
||||
groupadd docker
|
||||
usermod -aG docker root
|
||||
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
|
||||
user: root
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
|
||||
- name: Wait 90 seconds for Docker to be ready on Droplet
|
||||
run: sleep 90
|
||||
|
||||
- name: Deploy app to Droplet
|
||||
uses: fifsky/ssh-action@master
|
||||
with:
|
||||
command: |
|
||||
echo 'Downloading install script from GitHub'
|
||||
curl -s https://raw.githubusercontent.com/runtipi/runtipi/${{ inputs.version }}/scripts/install.sh > install.sh
|
||||
chmod +x install.sh
|
||||
echo 'Running install script'
|
||||
./install.sh --version ${{ inputs.version }} --asset runtipi-cli-linux-x86_64.tar.gz
|
||||
echo 'App deployed'
|
||||
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
|
||||
user: root # TODO: use non-root user
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
|
||||
- name: Get POSTGRES_PASSWORD from .env file
|
||||
id: get-postgres-password
|
||||
run: |
|
||||
postgres_password=$(ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa root@${{ steps.get-droplet-ip.outputs.droplet_ip }} "cat ./runtipi/.env | grep POSTGRES_PASSWORD | cut -d '=' -f2")
|
||||
echo "postgres_password=$postgres_password" >> $GITHUB_OUTPUT
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: e2e
|
||||
rm: true
|
||||
files: bin/runtipi-cli-*
|
||||
|
||||
e2e:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy]
|
||||
needs: [build-images]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run install script
|
||||
if: ${{ inputs.version }}
|
||||
run: |
|
||||
curl -s https://raw.githubusercontent.com/runtipi/runtipi/${{ inputs.version }}/scripts/install.sh > install.sh
|
||||
chmod +x install.sh
|
||||
./install.sh --version ${{ inputs.version }} --asset runtipi-cli-linux-x86_64.tar.gz
|
||||
|
||||
- name: Run install script
|
||||
if: ${{ !inputs.version }}
|
||||
run: |
|
||||
./scripts/install.sh --version e2e
|
||||
cd ..
|
||||
|
||||
- uses: pnpm/action-setup@v4.0.0
|
||||
name: Install pnpm
|
||||
@ -128,34 +107,26 @@ jobs:
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Create .env.e2e file with Droplet IP
|
||||
run: |
|
||||
echo "SERVER_IP=$(hostname -I | awk '{print $1}')" > .env.e2e
|
||||
echo "POSTGRES_PASSWORD=$(grep POSTGRES_PASSWORD runtipi/.env | cut -d '=' -f2)" >> .env.e2e
|
||||
echo "BASE_PATH=./runtipi" >> .env.e2e
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Create .env.e2e file with Droplet IP
|
||||
run: |
|
||||
echo "SERVER_IP=${{ needs.deploy.outputs.droplet_ip }}" > .env.e2e
|
||||
echo "POSTGRES_PASSWORD=${{ needs.deploy.outputs.postgres_password }}" >> .env.e2e
|
||||
echo "REMOTE=true" >> .env.e2e
|
||||
echo "${{ secrets.SSH_KEY }}" > .temp-ssh-key
|
||||
# Base 64 encode SSH key to avoid issues with newlines
|
||||
echo "SSH_PRIVATE_KEY=$(base64 -w 0 .temp-ssh-key)" >> .env.e2e
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: Create state/settings.json
|
||||
run: |
|
||||
mkdir -p state
|
||||
echo '{}' > state/settings.json
|
||||
|
||||
- name: Run Playwright tests
|
||||
id: run-e2e
|
||||
run: npm run test:e2e
|
||||
@ -197,16 +168,3 @@ jobs:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
teardown:
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
needs: [e2e, deploy]
|
||||
steps:
|
||||
- name: Install doctl
|
||||
uses: digitalocean/action-doctl@v2
|
||||
with:
|
||||
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
|
||||
|
||||
- name: Delete Droplet
|
||||
run: doctl compute droplet delete ${{ needs.deploy.outputs.droplet_id }} --force
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -70,3 +70,6 @@ temp
|
||||
|
||||
# Sentry Config File
|
||||
.sentryclirc
|
||||
|
||||
public/js/*
|
||||
!public/js/.gitkeep
|
||||
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
packages
|
||||
coverage
|
||||
.github
|
@ -34,12 +34,13 @@ RUN pnpm fetch
|
||||
COPY ./pnpm-workspace.yaml ./
|
||||
COPY ./package*.json ./
|
||||
COPY ./packages/shared ./packages/shared
|
||||
COPY ./scripts ./scripts
|
||||
COPY ./public ./public
|
||||
|
||||
RUN pnpm install -r --prefer-offline
|
||||
COPY ./src ./src
|
||||
COPY ./tsconfig.json ./tsconfig.json
|
||||
COPY ./next.config.mjs ./next.config.mjs
|
||||
COPY ./public ./public
|
||||
COPY ./tests ./tests
|
||||
|
||||
# Sentry
|
||||
|
@ -32,13 +32,14 @@ RUN pnpm fetch --ignore-scripts
|
||||
COPY ./package*.json ./
|
||||
COPY ./packages/worker/package.json ./packages/worker/package.json
|
||||
COPY ./packages/shared/package.json ./packages/shared/package.json
|
||||
COPY ./scripts ./scripts
|
||||
COPY ./public ./public
|
||||
|
||||
RUN pnpm install -r --prefer-offline
|
||||
|
||||
COPY ./packages ./packages
|
||||
COPY ./tsconfig.json ./tsconfig.json
|
||||
COPY ./next.config.mjs ./next.config.mjs
|
||||
COPY ./public ./public
|
||||
|
||||
# Sentry
|
||||
COPY ./sentry.client.config.ts ./sentry.client.config.ts
|
||||
|
@ -1,9 +1,7 @@
|
||||
# Tipi — A personal homeserver for everyone
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-49-orange.svg?style=flat-square)](#contributors-)
|
||||
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-50-orange.svg?style=flat-square)](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
[![License](https://img.shields.io/github/license/runtipi/runtipi)](https://github.com/runtipi/runtipi/blob/master/LICENSE)
|
||||
@ -140,6 +138,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thibaultclaude.be"><img src="https://avatars.githubusercontent.com/u/23203061?v=4?s=100" width="100px;" alt="Thibault Claude"/><br /><sub><b>Thibault Claude</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=thclaude" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DansNewLegs"><img src="https://avatars.githubusercontent.com/u/152246049?v=4?s=100" width="100px;" alt="Joshua Banks"/><br /><sub><b>Joshua Banks</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=DansNewLegs" title="Code">💻</a> <a href="https://github.com/runtipi/runtipi/commits?author=DansNewLegs" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hex-developer"><img src="https://avatars.githubusercontent.com/u/77530549?v=4?s=100" width="100px;" alt="hex-developer"/><br /><sub><b>hex-developer</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=hex-developer" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -14,8 +14,8 @@ test.beforeEach(async ({ page, context, isMobile }) => {
|
||||
await page.getByRole('link', { name: 'App store' }).click();
|
||||
}
|
||||
|
||||
await page.getByPlaceholder('Search').fill('hello');
|
||||
await page.getByRole('link', { name: 'Hello World' }).click();
|
||||
await page.getByPlaceholder('Search').fill('nginx');
|
||||
await page.getByRole('link', { name: 'Nginx' }).click();
|
||||
});
|
||||
|
||||
test('user can install and uninstall app', async ({ page, context }) => {
|
||||
@ -23,7 +23,7 @@ test('user can install and uninstall app', async ({ page, context }) => {
|
||||
// Install app
|
||||
await page.getByRole('button', { name: 'Install' }).click();
|
||||
|
||||
await expect(page.getByText('Install Hello World')).toBeVisible();
|
||||
await expect(page.getByText('Install Nginx')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Install' }).click();
|
||||
|
||||
@ -33,16 +33,16 @@ test('user can install and uninstall app', async ({ page, context }) => {
|
||||
await page.getByTestId('app-details').getByRole('button', { name: 'Open' }).press('ArrowDown');
|
||||
const [newPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
await page.getByRole('menuitem', { name: `${process.env.SERVER_IP}:8000` }).click(),
|
||||
await page.getByRole('menuitem', { name: `${process.env.SERVER_IP}:8754` }).click(),
|
||||
]);
|
||||
|
||||
await newPage.waitForLoadState();
|
||||
await expect(newPage.getByText('Hello World')).toBeVisible();
|
||||
await expect(newPage.getByText('Welcome to nginx!')).toBeVisible();
|
||||
await newPage.close();
|
||||
|
||||
// Stop app
|
||||
await page.getByRole('button', { name: 'Stop' }).click();
|
||||
await expect(page.getByText('Stop Hello World')).toBeVisible();
|
||||
await expect(page.getByText('Stop Nginx')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Stop' }).click();
|
||||
|
||||
@ -50,7 +50,7 @@ test('user can install and uninstall app', async ({ page, context }) => {
|
||||
|
||||
// Uninstall app
|
||||
await page.getByRole('button', { name: 'Remove' }).click();
|
||||
await expect(page.getByText('Uninstall Hello World ?')).toBeVisible();
|
||||
await expect(page.getByText('Uninstall Nginx ?')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Uninstall' }).click();
|
||||
|
||||
|
@ -10,12 +10,15 @@ export const createTestUser = async () => {
|
||||
await db.insert(userTable).values({ password, username: testUser.email, operator: true });
|
||||
};
|
||||
|
||||
export const loginUser = async (page: Page, context: BrowserContext) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const loginUser = async (page: Page, _: BrowserContext) => {
|
||||
await page.addLocatorHandler(page.getByText('Insecure configuration'), async () => {
|
||||
await page.getByRole('button', { name: 'Close' }).click();
|
||||
});
|
||||
|
||||
// Create user in database
|
||||
await createTestUser();
|
||||
|
||||
await context.addCookies([{ name: 'hide-insecure-instance', value: 'true', path: '/', domain: process.env.SERVER_IP }]);
|
||||
|
||||
// Login flow
|
||||
await page.goto('/login');
|
||||
|
||||
|
@ -2,3 +2,5 @@ export const testUser = {
|
||||
email: 'tester@test.com',
|
||||
password: 'password',
|
||||
};
|
||||
|
||||
export const BASE_PATH = process.env.BASE_PATH || '.';
|
||||
|
@ -2,52 +2,34 @@ import { promises } from 'fs';
|
||||
import { z } from 'zod';
|
||||
import { settingsSchema } from '@runtipi/shared';
|
||||
import { pathExists } from '@runtipi/shared/node';
|
||||
import { execRemoteCommand } from './write-remote-file';
|
||||
import path from 'path';
|
||||
import { BASE_PATH } from './constants';
|
||||
|
||||
export const setSettings = async (settings: z.infer<typeof settingsSchema>) => {
|
||||
if (process.env.REMOTE === 'true') {
|
||||
await execRemoteCommand(`mkdir -p ./runtipi/state`);
|
||||
await execRemoteCommand(`echo '${JSON.stringify(settings)}' > ./runtipi/state/settings.json`);
|
||||
} else {
|
||||
// Create state folder if it doesn't exist
|
||||
await promises.mkdir('./state', { recursive: true });
|
||||
|
||||
await promises.writeFile('./state/settings.json', JSON.stringify(settings));
|
||||
}
|
||||
await promises.mkdir(path.join(BASE_PATH, 'state'), { recursive: true });
|
||||
await promises.writeFile(path.join(BASE_PATH, 'state', 'settings.json'), JSON.stringify(settings));
|
||||
};
|
||||
|
||||
export const setPassowrdChangeRequest = async () => {
|
||||
if (process.env.REMOTE === 'true') {
|
||||
// Write date in ms to file
|
||||
await execRemoteCommand('touch ./runtipi/state/password-change-request && echo $(date +%s) >> ./runtipi/state/password-change-request');
|
||||
} else {
|
||||
await promises.writeFile('./state/password-change-request', `${new Date().getTime() / 1000}`);
|
||||
}
|
||||
await promises.writeFile(path.join(BASE_PATH, 'state', 'password-change-request'), `${new Date().getTime() / 1000}`);
|
||||
};
|
||||
|
||||
export const unsetPasswordChangeRequest = async () => {
|
||||
if (process.env.REMOTE === 'true') {
|
||||
await execRemoteCommand('rm ./runtipi/state/password-change-request');
|
||||
} else if (await pathExists('./state/password-change-request')) {
|
||||
await promises.unlink('./state/password-change-request');
|
||||
const requestPath = path.join(BASE_PATH, 'state', 'password-change-request');
|
||||
if (await pathExists(requestPath)) {
|
||||
await promises.unlink(requestPath);
|
||||
}
|
||||
};
|
||||
|
||||
export const setWelcomeSeen = async (seen: boolean) => {
|
||||
if (seen && process.env.REMOTE === 'true') {
|
||||
return execRemoteCommand('touch ./runtipi/state/seen-welcome');
|
||||
const seenPath = path.join(BASE_PATH, 'state', 'seen-welcome');
|
||||
|
||||
if (seen && !(await pathExists(seenPath))) {
|
||||
return promises.writeFile(seenPath, '');
|
||||
}
|
||||
|
||||
if (!seen && process.env.REMOTE === 'true') {
|
||||
return execRemoteCommand('rm ./runtipi/state/seen-welcome');
|
||||
}
|
||||
|
||||
if (seen && !(await pathExists('./state/seen-welcome'))) {
|
||||
return promises.writeFile('./state/seen-welcome', '');
|
||||
}
|
||||
|
||||
if (!seen && (await pathExists('./state/seen-welcome'))) {
|
||||
return promises.unlink('./state/seen-welcome');
|
||||
if (!seen && (await pathExists(seenPath))) {
|
||||
return promises.unlink(seenPath);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
|
@ -1,32 +0,0 @@
|
||||
import { Client } from 'ssh2';
|
||||
|
||||
export const execRemoteCommand = async (command: string): Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
const conn = new Client();
|
||||
|
||||
conn.connect({
|
||||
host: process.env.SERVER_IP,
|
||||
port: 22,
|
||||
username: 'root',
|
||||
privateKey: atob(process.env.SSH_PRIVATE_KEY as string),
|
||||
});
|
||||
|
||||
conn.on('ready', () => {
|
||||
conn.exec(command, (err, stream) => {
|
||||
if (err) throw err;
|
||||
|
||||
stream
|
||||
.on('close', () => {
|
||||
conn.end();
|
||||
resolve();
|
||||
})
|
||||
.on('data', (data: Buffer) => {
|
||||
console.log('STDOUT:', data.toString());
|
||||
})
|
||||
.stderr.on('data', (data: Buffer) => {
|
||||
console.log('STDERR:', data.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
@ -1,53 +0,0 @@
|
||||
import nextJest from 'next/jest';
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||
dir: './',
|
||||
});
|
||||
|
||||
const customClientConfig = {
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
setupFilesAfterEnv: ['<rootDir>/tests/client/jest.setup.tsx'],
|
||||
testMatch: [
|
||||
'<rootDir>/src/client/**/*.{spec,test}.{ts,tsx}',
|
||||
'<rootDir>/src/app/**/*.{spec,test}.{ts,tsx}',
|
||||
'!<rootDir>/src/server/**/*.{spec,test}.{ts,tsx}',
|
||||
],
|
||||
};
|
||||
|
||||
const customServerConfig = {
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['<rootDir>/src/server/**/*.test.ts'],
|
||||
setupFilesAfterEnv: ['<rootDir>/tests/server/jest.setup.ts'],
|
||||
globals: {
|
||||
fetch,
|
||||
},
|
||||
};
|
||||
|
||||
export default async () => {
|
||||
const clientConfig = await createJestConfig(customClientConfig)();
|
||||
const serverConfig = await createJestConfig(customServerConfig)();
|
||||
|
||||
return {
|
||||
randomize: true,
|
||||
verbose: true,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/server/**/*.{ts,tsx}',
|
||||
'src/client/**/*.{ts,tsx}',
|
||||
'!src/**/mocks/**/*.{ts,tsx}',
|
||||
'!**/*.{spec,test}.{ts,tsx}',
|
||||
'!**/index.{ts,tsx}',
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
displayName: 'client',
|
||||
...clientConfig,
|
||||
},
|
||||
{
|
||||
displayName: 'server',
|
||||
...serverConfig,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
49
package.json
49
package.json
@ -9,30 +9,23 @@
|
||||
"test:ui": "dotenv -e .env.test -- vitest --ui",
|
||||
"test:e2e": "NODE_ENV=test dotenv -e .env -e .env.e2e -- playwright test",
|
||||
"test:e2e:ui": "NODE_ENV=test dotenv -e .env -e .env.e2e -- playwright test --ui",
|
||||
"test:client": "jest --colors --selectProjects client --",
|
||||
"test:server": "jest --colors --selectProjects server --",
|
||||
"test:vite": "dotenv -e .env.test -- vitest run --coverage",
|
||||
"dev": "next dev --turbo",
|
||||
"db:migrate": "NODE_ENV=development dotenv -e .env.local -- tsx ./src/server/run-migrations-dev.ts",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "next lint --fix",
|
||||
"build": "next build --experimental-build-mode compile",
|
||||
"start": "NODE_ENV=production node server.js",
|
||||
"start:dev-container": "./.devcontainer/filewatcher.sh && npm run start:dev",
|
||||
"start:rc": "docker compose -f docker-compose.rc.yml --env-file .env up --build",
|
||||
"start:dev": "docker compose -f docker-compose.dev.yml up --build",
|
||||
"start:prod": "docker compose --env-file ./.env -f docker-compose.prod.yml up --build",
|
||||
"start:pg": "docker run --name test-db -p 5433:5432 -d --rm -e POSTGRES_PASSWORD=postgres postgres:14",
|
||||
"version": "echo $npm_package_version",
|
||||
"release:rc": "./scripts/deploy/release-rc.sh",
|
||||
"test:build": "docker buildx build --platform linux/amd64,linux/arm64 -t meienberger/runtipi:test .",
|
||||
"test:build:arm64": "docker buildx build --platform linux/arm64 -t meienberger/runtipi:test .",
|
||||
"test:build:arm7": "docker buildx build --platform linux/arm/v7 -t meienberger/runtipi:test .",
|
||||
"test:build:amd64": "docker buildx build --platform linux/amd64 -t meienberger/runtipi:test .",
|
||||
"tsc": "tsc"
|
||||
"tsc": "tsc",
|
||||
"gen:certs": "mkcert -key-file ./traefik/tls/key.pem -cert-file ./traefik/tls/cert.pem tipi.local \"*.tipi.local\"",
|
||||
"prettier-check": "prettier --check .",
|
||||
"postinstall": "./scripts/postinstall.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@hookform/resolvers": "^3.4.2",
|
||||
"@otplib/core": "^12.0.1",
|
||||
"@otplib/plugin-crypto": "^12.0.1",
|
||||
"@otplib/plugin-thirty-two": "^12.0.1",
|
||||
@ -45,13 +38,12 @@
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@runtipi/postgres-migrations": "^5.3.0",
|
||||
"@runtipi/shared": "workspace:^",
|
||||
"@sentry/nextjs": "^8.0.0",
|
||||
"@sentry/nextjs": "^8.4.0",
|
||||
"@tabler/core": "1.0.0-beta20",
|
||||
"@tabler/icons-react": "^3.2.0",
|
||||
"argon2": "^0.40.1",
|
||||
"bullmq": "^5.7.4",
|
||||
"bullmq": "^5.7.12",
|
||||
"clsx": "^2.1.0",
|
||||
"connect-redis": "^7.1.1",
|
||||
"drizzle-orm": "^0.30.9",
|
||||
"fs-extra": "^11.2.0",
|
||||
"geist": "^1.3.0",
|
||||
@ -62,7 +54,7 @@
|
||||
"minisearch": "^6.3.0",
|
||||
"next": "14.2.3",
|
||||
"next-client-cookies": "^1.1.1",
|
||||
"next-intl": "^3.13.0",
|
||||
"next-intl": "^3.14.1",
|
||||
"next-safe-action": "^6.2.0",
|
||||
"pg": "^8.11.5",
|
||||
"qrcode.react": "^3.1.0",
|
||||
@ -80,7 +72,7 @@
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sass": "^1.77.1",
|
||||
"semver": "^7.6.2",
|
||||
"sharp": "0.33.3",
|
||||
"sharp": "0.33.4",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"uuid": "^9.0.1",
|
||||
"validator": "^13.12.0",
|
||||
@ -89,9 +81,9 @@
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.5",
|
||||
"@babel/core": "^7.24.6",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@testing-library/dom": "^10.1.0",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
@ -100,30 +92,24 @@
|
||||
"@total-typescript/ts-reset": "^0.5.1",
|
||||
"@types/eslint": "^8.56.10",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "20.12.11",
|
||||
"@types/node": "20.12.12",
|
||||
"@types/pg": "^8.11.6",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/validator": "^13.11.10",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitest/coverage-v8": "^1.5.0",
|
||||
"@vitest/ui": "^1.6.0",
|
||||
"dotenv-cli": "^7.4.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-drizzle": "^0.2.3",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^28.5.0",
|
||||
"eslint-plugin-jest-dom": "^5.4.0",
|
||||
@ -132,19 +118,14 @@
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^6.2.2",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"jsonc-eslint-parser": "^2.4.0",
|
||||
"knip": "^5.9.4",
|
||||
"memfs": "^4.8.2",
|
||||
"msw": "^2.3.0",
|
||||
"next-router-mock": "^0.9.13",
|
||||
"prettier": "^3.2.5",
|
||||
"ssh2": "^1.15.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.10.2",
|
||||
"typescript": "5.4.5",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.0",
|
||||
|
@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'airbnb', 'airbnb-typescript', 'eslint:recommended', 'plugin:import/typescript', 'prettier'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/typescript', 'prettier'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
@ -10,6 +10,7 @@ module.exports = {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 1,
|
||||
'import/prefer-default-export': 0,
|
||||
'class-methods-use-this': 0,
|
||||
'import/extensions': [
|
||||
@ -26,7 +27,15 @@ module.exports = {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: ['build.js', '**/*.test.{ts,tsx}', '**/mocks/**', '**/__mocks__/**', '**/*.setup.{ts,js}', '**/*.config.{ts,js}', '**/tests/**'],
|
||||
devDependencies: [
|
||||
'build.js',
|
||||
'**/*.test.{ts,tsx}',
|
||||
'**/mocks/**',
|
||||
'**/__mocks__/**',
|
||||
'**/*.setup.{ts,js}',
|
||||
'**/*.config.{ts,js}',
|
||||
'**/tests/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
'arrow-body-style': 0,
|
||||
|
@ -25,7 +25,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .ts src",
|
||||
"tsc": "tsc --noEmit"
|
||||
"tsc": "tsc --noEmit",
|
||||
"prettier-check": "prettier --check ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@ -37,7 +38,12 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/types": "^8.0.0",
|
||||
"@types/lodash.clonedeep": "^4.5.9"
|
||||
"@sentry/types": "^8.4.0",
|
||||
"@types/lodash.clonedeep": "^4.5.9",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"prettier": "^3.2.5"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'airbnb', 'airbnb-typescript', 'eslint:recommended', 'plugin:import/typescript', 'prettier'],
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/typescript', 'prettier'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
@ -10,6 +10,7 @@ module.exports = {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 1,
|
||||
'import/prefer-default-export': 0,
|
||||
'class-methods-use-this': 0,
|
||||
'import/extensions': [
|
||||
@ -26,7 +27,15 @@ module.exports = {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: ['build.js', '**/*.test.{ts,tsx}', '**/mocks/**', '**/__mocks__/**', '**/*.setup.{ts,js}', '**/*.config.{ts,js}', '**/tests/**'],
|
||||
devDependencies: [
|
||||
'build.js',
|
||||
'**/*.test.{ts,tsx}',
|
||||
'**/mocks/**',
|
||||
'**/__mocks__/**',
|
||||
'**/*.setup.{ts,js}',
|
||||
'**/*.config.{ts,js}',
|
||||
'**/tests/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
'arrow-body-style': 0,
|
||||
|
@ -10,21 +10,26 @@
|
||||
"tsc": "tsc",
|
||||
"dev": "dotenv -e ../../.env nodemon",
|
||||
"knip": "knip",
|
||||
"lint": "eslint . --ext .ts"
|
||||
"lint": "eslint . --ext .ts",
|
||||
"prettier-check": "prettier --check ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@sentry/esbuild-plugin": "^2.16.1",
|
||||
"@total-typescript/shoehorn": "^0.1.2",
|
||||
"@sentry/esbuild-plugin": "^2.17.0",
|
||||
"@types/web-push": "^3.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"dotenv-cli": "^7.4.2",
|
||||
"esbuild": "^0.19.4",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"knip": "^5.15.1",
|
||||
"memfs": "^4.8.2",
|
||||
"nodemon": "^3.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"tsx": "^4.10.2",
|
||||
"typescript": "^5.4.5",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
@ -35,10 +40,10 @@
|
||||
"@runtipi/postgres-migrations": "^5.3.0",
|
||||
"@runtipi/shared": "workspace:^",
|
||||
"@sentry/integrations": "^7.114.0",
|
||||
"@sentry/node": "^8.0.0",
|
||||
"bullmq": "^5.7.4",
|
||||
"@sentry/node": "^8.4.0",
|
||||
"bullmq": "^5.7.12",
|
||||
"dotenv": "^16.4.5",
|
||||
"hono": "^4.3.6",
|
||||
"hono": "^4.3.11",
|
||||
"ioredis": "^5.4.1",
|
||||
"pg": "^8.11.5",
|
||||
"socket.io": "^4.7.5",
|
||||
|
@ -138,7 +138,9 @@ const main = async () => {
|
||||
// Start all apps
|
||||
const appExecutor = new AppExecutors();
|
||||
logger.info('Starting all apps...');
|
||||
appExecutor.startAllApps();
|
||||
|
||||
// Fire and forget
|
||||
void appExecutor.startAllApps();
|
||||
|
||||
const app = new Hono().basePath('/worker-api');
|
||||
serve({ fetch: app.fetch, port: 5000 }, (info) => {
|
||||
@ -157,4 +159,4 @@ const main = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
||||
void main();
|
||||
|
@ -11,7 +11,9 @@ describe('app helpers', () => {
|
||||
describe('Test: generateEnvFile()', () => {
|
||||
it('should generate an env file', async () => {
|
||||
// arrange
|
||||
const appConfig = createAppConfig({ form_fields: [{ env_variable: 'TEST_FIELD', type: 'text', label: 'test', required: true }] });
|
||||
const appConfig = createAppConfig({
|
||||
form_fields: [{ env_variable: 'TEST_FIELD', type: 'text', label: 'test', required: true }],
|
||||
});
|
||||
const fakevalue = faker.string.alphanumeric(10);
|
||||
|
||||
// act
|
||||
@ -23,7 +25,9 @@ describe('app helpers', () => {
|
||||
expect(envmap.get('APP_PORT')).toBe(String(appConfig.port));
|
||||
expect(envmap.get('APP_ID')).toBe(appConfig.id);
|
||||
expect(envmap.get('ROOT_FOLDER_HOST')).toBe(process.env.ROOT_FOLDER_HOST);
|
||||
expect(envmap.get('APP_DATA_DIR')).toBe(`${process.env.RUNTIPI_APP_DATA_PATH}/app-data/${appConfig.id}`);
|
||||
expect(envmap.get('APP_DATA_DIR')).toBe(
|
||||
`${process.env.RUNTIPI_APP_DATA_PATH}/app-data/${appConfig.id}`,
|
||||
);
|
||||
expect(envmap.get('APP_DOMAIN')).toBe(`localhost:${appConfig.port}`);
|
||||
expect(envmap.get('APP_HOST')).toBe(`localhost`);
|
||||
expect(envmap.get('APP_PROTOCOL')).toBe(`http`);
|
||||
@ -35,12 +39,25 @@ describe('app helpers', () => {
|
||||
await fs.promises.writeFile(`${DATA_DIR}/apps/${appConfig.id}/config.json`, '{}');
|
||||
|
||||
// act & assert
|
||||
expect(generateEnvFile(appConfig.id, {})).rejects.toThrowError(`App ${appConfig.id} has invalid config.json file`);
|
||||
await expect(generateEnvFile(appConfig.id, {})).rejects.toThrowError(
|
||||
`App ${appConfig.id} has invalid config.json file`,
|
||||
);
|
||||
});
|
||||
|
||||
it('Should automatically generate value for random field', async () => {
|
||||
// arrange
|
||||
const appConfig = createAppConfig({ form_fields: [{ env_variable: 'RANDOM_FIELD', type: 'random', label: 'test', min: 32, max: 32, required: true }] });
|
||||
const appConfig = createAppConfig({
|
||||
form_fields: [
|
||||
{
|
||||
env_variable: 'RANDOM_FIELD',
|
||||
type: 'random',
|
||||
label: 'test',
|
||||
min: 32,
|
||||
max: 32,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// act
|
||||
await generateEnvFile(appConfig.id, {});
|
||||
@ -53,10 +70,24 @@ describe('app helpers', () => {
|
||||
|
||||
it('Should not re-generate random field if it already exists', async () => {
|
||||
// arrange
|
||||
const appConfig = createAppConfig({ form_fields: [{ env_variable: 'RANDOM_FIELD', type: 'random', label: 'test', min: 32, max: 32, required: true }] });
|
||||
const appConfig = createAppConfig({
|
||||
form_fields: [
|
||||
{
|
||||
env_variable: 'RANDOM_FIELD',
|
||||
type: 'random',
|
||||
label: 'test',
|
||||
min: 32,
|
||||
max: 32,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
const randomField = faker.string.alphanumeric(32);
|
||||
await fs.promises.mkdir(`${APP_DATA_DIR}/${appConfig.id}`, { recursive: true });
|
||||
await fs.promises.writeFile(`${APP_DATA_DIR}/${appConfig.id}/app.env`, `RANDOM_FIELD=${randomField}`);
|
||||
await fs.promises.writeFile(
|
||||
`${APP_DATA_DIR}/${appConfig.id}/app.env`,
|
||||
`RANDOM_FIELD=${randomField}`,
|
||||
);
|
||||
|
||||
// act
|
||||
await generateEnvFile(appConfig.id, {});
|
||||
@ -68,7 +99,9 @@ describe('app helpers', () => {
|
||||
|
||||
it('Should throw an error if required field is not provided', async () => {
|
||||
// arrange
|
||||
const appConfig = createAppConfig({ form_fields: [{ env_variable: 'TEST_FIELD', type: 'text', label: 'test', required: true }] });
|
||||
const appConfig = createAppConfig({
|
||||
form_fields: [{ env_variable: 'TEST_FIELD', type: 'text', label: 'test', required: true }],
|
||||
});
|
||||
|
||||
// act & assert
|
||||
await expect(generateEnvFile(appConfig.id, {})).rejects.toThrowError();
|
||||
@ -167,7 +200,10 @@ describe('app helpers', () => {
|
||||
|
||||
// act
|
||||
await fs.promises.mkdir(`${APP_DATA_DIR}/${appConfig.id}`, { recursive: true });
|
||||
await fs.promises.writeFile(`${APP_DATA_DIR}/${appConfig.id}/app.env`, `VAPID_PRIVATE_KEY=${vapidPrivateKey}\nVAPID_PUBLIC_KEY=${vapidPublicKey}`);
|
||||
await fs.promises.writeFile(
|
||||
`${APP_DATA_DIR}/${appConfig.id}/app.env`,
|
||||
`VAPID_PRIVATE_KEY=${vapidPrivateKey}\nVAPID_PUBLIC_KEY=${vapidPublicKey}`,
|
||||
);
|
||||
await generateEnvFile(appConfig.id, {});
|
||||
const envmap = await getAppEnvMap(appConfig.id);
|
||||
|
||||
@ -223,7 +259,9 @@ describe('app helpers', () => {
|
||||
|
||||
// assert
|
||||
const appDataDir = `${APP_DATA_DIR}/${appConfig.id}`;
|
||||
expect(await fs.promises.readFile(`${appDataDir}/data/subdir/subsubdir/test.txt`, 'utf8')).toBe('test');
|
||||
expect(
|
||||
await fs.promises.readFile(`${appDataDir}/data/subdir/subsubdir/test.txt`, 'utf8'),
|
||||
).toBe('test');
|
||||
expect(await fs.promises.readFile(`${appDataDir}/data/test.txt`, 'utf8')).toBe('test');
|
||||
});
|
||||
|
||||
|
@ -21,7 +21,7 @@ export class AppExecutors {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private handleAppError = (
|
||||
private handleAppError = async (
|
||||
err: unknown,
|
||||
appId: string,
|
||||
event: Extract<SocketEvent, { type: 'app' }>['event'],
|
||||
@ -31,12 +31,12 @@ export class AppExecutors {
|
||||
});
|
||||
|
||||
if (err instanceof Error) {
|
||||
SocketManager.emit({ type: 'app', event, data: { appId, error: err.message } });
|
||||
await SocketManager.emit({ type: 'app', event, data: { appId, error: err.message } });
|
||||
this.logger.error(`An error occurred: ${err.message}`);
|
||||
return { success: false, message: err.message };
|
||||
}
|
||||
|
||||
SocketManager.emit({ type: 'app', event, data: { appId, error: String(err) } });
|
||||
await SocketManager.emit({ type: 'app', event, data: { appId, error: String(err) } });
|
||||
return { success: false, message: `An error occurred: ${String(err)}` };
|
||||
};
|
||||
|
||||
@ -105,7 +105,7 @@ export class AppExecutors {
|
||||
await this.ensureAppDir(appId, form);
|
||||
await generateEnvFile(appId, form);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'generate_env_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'generate_env_success', data: { appId } });
|
||||
return { success: true, message: `App ${appId} env file regenerated successfully` };
|
||||
} catch (err) {
|
||||
return this.handleAppError(err, appId, 'generate_env_error');
|
||||
@ -119,7 +119,7 @@ export class AppExecutors {
|
||||
*/
|
||||
public installApp = async (appId: string, form: AppEventForm) => {
|
||||
try {
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
if (process.getuid && process.getgid) {
|
||||
this.logger.info(
|
||||
@ -177,7 +177,7 @@ export class AppExecutors {
|
||||
|
||||
this.logger.info(`Docker-compose up for app ${appId} finished`);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'install_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'install_success', data: { appId } });
|
||||
|
||||
return { success: true, message: `App ${appId} installed successfully` };
|
||||
} catch (err) {
|
||||
@ -200,7 +200,7 @@ export class AppExecutors {
|
||||
return { success: true, message: `App ${appId} is not an app. Skipping...` };
|
||||
}
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
this.logger.info(`Stopping app ${appId}`);
|
||||
|
||||
await this.ensureAppDir(appId, form);
|
||||
@ -213,7 +213,7 @@ export class AppExecutors {
|
||||
|
||||
this.logger.info(`App ${appId} stopped`);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'stop_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'stop_success', data: { appId } });
|
||||
|
||||
const client = await getDbClient();
|
||||
await client?.query('UPDATE app SET status = $1 WHERE id = $2', ['stopped', appId]);
|
||||
@ -234,7 +234,7 @@ export class AppExecutors {
|
||||
return { success: true, message: `App ${appId} is not an app. Skipping...` };
|
||||
}
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
this.logger.info(`Restarting app ${appId}`);
|
||||
|
||||
@ -262,7 +262,7 @@ export class AppExecutors {
|
||||
|
||||
this.logger.info(`App ${appId} restarted`);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'restart_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'restart_success', data: { appId } });
|
||||
|
||||
return { success: true, message: `App ${appId} restarted successfully` };
|
||||
} catch (err) {
|
||||
@ -272,7 +272,7 @@ export class AppExecutors {
|
||||
|
||||
public startApp = async (appId: string, form: AppEventForm, skipEnvGeneration = false) => {
|
||||
try {
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
this.logger.info(`Starting app ${appId}`);
|
||||
|
||||
@ -287,7 +287,7 @@ export class AppExecutors {
|
||||
|
||||
this.logger.info(`App ${appId} started`);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'start_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'start_success', data: { appId } });
|
||||
|
||||
const client = await getDbClient();
|
||||
await client?.query('UPDATE app SET status = $1 WHERE id = $2', ['running', appId]);
|
||||
@ -299,7 +299,7 @@ export class AppExecutors {
|
||||
|
||||
public uninstallApp = async (appId: string, form: AppEventForm) => {
|
||||
try {
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
const { appDirPath, appDataDirPath } = this.getAppPaths(appId);
|
||||
this.logger.info(`Uninstalling app ${appId}`);
|
||||
@ -331,7 +331,7 @@ export class AppExecutors {
|
||||
|
||||
this.logger.info(`App ${appId} uninstalled`);
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'uninstall_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'uninstall_success', data: { appId } });
|
||||
|
||||
const client = await getDbClient();
|
||||
await client?.query(`DELETE FROM app WHERE id = $1`, [appId]);
|
||||
@ -343,7 +343,7 @@ export class AppExecutors {
|
||||
|
||||
public resetApp = async (appId: string, form: AppEventForm) => {
|
||||
try {
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
const { appDataDirPath } = this.getAppPaths(appId);
|
||||
this.logger.info(`Resetting app ${appId}`);
|
||||
@ -385,7 +385,7 @@ export class AppExecutors {
|
||||
this.logger.info(`Running docker-compose up for app ${appId}`);
|
||||
await compose(appId, 'up -d');
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'reset_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'reset_success', data: { appId } });
|
||||
|
||||
const client = await getDbClient();
|
||||
await client?.query(`UPDATE app SET status = $1 WHERE id = $2`, ['running', appId]);
|
||||
@ -397,7 +397,7 @@ export class AppExecutors {
|
||||
|
||||
public updateApp = async (appId: string, form: AppEventForm) => {
|
||||
try {
|
||||
SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'status_change', data: { appId } });
|
||||
|
||||
const { appDirPath, repoPath } = this.getAppPaths(appId);
|
||||
this.logger.info(`Updating app ${appId}`);
|
||||
@ -423,7 +423,7 @@ export class AppExecutors {
|
||||
|
||||
await compose(appId, 'pull');
|
||||
|
||||
SocketManager.emit({ type: 'app', event: 'update_success', data: { appId } });
|
||||
await SocketManager.emit({ type: 'app', event: 'update_success', data: { appId } });
|
||||
|
||||
return { success: true, message: `App ${appId} updated successfully` };
|
||||
} catch (err) {
|
||||
|
@ -76,7 +76,7 @@ const runCommand = async (jobData: unknown) => {
|
||||
/**
|
||||
* Start the worker for the events queue
|
||||
*/
|
||||
export const startWorker = async () => {
|
||||
export const startWorker = () => {
|
||||
const repeatWorker = new Worker(
|
||||
'repeat',
|
||||
async (job) => {
|
||||
|
2076
pnpm-lock.yaml
2076
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
0
public/js/.gitkeep
Normal file
0
public/js/.gitkeep
Normal file
@ -45,8 +45,8 @@ while [ -n "${1-}" ]; do
|
||||
shift
|
||||
done
|
||||
|
||||
OS="$(cat /etc/[A-Za-z]*[_-][rv]e[lr]* | grep "^ID=" | cut -d= -f2 | uniq | tr '[:upper:]' '[:lower:]' | tr -d '"')"
|
||||
SUB_OS="$(cat /etc/[A-Za-z]*[_-][rv]e[lr]* | grep "^ID_LIKE=" | cut -d= -f2 | uniq | tr '[:upper:]' '[:lower:]' | tr -d '"' || echo 'unknown')"
|
||||
OS="$(cat $(ls -p /etc | grep -v / | grep "[A-Za-z]*[_-][rv]e[lr]" | awk '{print "/etc/" $1}') | grep "^ID=" | cut -d= -f2 | uniq | tr '[:upper:]' '[:lower:]' | tr -d '"')"
|
||||
SUB_OS="$(cat $(ls -p /etc | grep -v / | grep "[A-Za-z]*[_-][rv]e[lr]" | awk '{print "/etc/" $1}') | grep "^ID_LIKE=" | cut -d= -f2 | uniq | tr '[:upper:]' '[:lower:]' | tr -d '"' || echo 'unknown')"
|
||||
|
||||
function install_generic() {
|
||||
local dependency="${1}"
|
||||
|
3
scripts/postinstall.sh
Executable file
3
scripts/postinstall.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cp ./node_modules/@tabler/core/dist/js/tabler.min.js ./public/js/tabler.min.js
|
@ -61,7 +61,7 @@ export const InstallFormField = (props: IProps) => {
|
||||
control={control}
|
||||
name={field.env_variable}
|
||||
defaultValue={field.default}
|
||||
render={({ field: { onChange, value, ref, ...rest } }) => (
|
||||
render={({ field: { onChange, value, ...rest } }) => (
|
||||
<Select value={value as string} defaultValue={initialValue} onValueChange={onChange} {...rest}>
|
||||
<SelectTrigger className="mb-3" error={error} label={label}>
|
||||
<SelectValue placeholder={t('APP_INSTALL_FORM_CHOOSE_OPTION')} />
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { AppInfo } from '@runtipi/shared';
|
||||
|
||||
export type SortableColumns = keyof Pick<AppInfo, 'id'>;
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
export type AppTableData = Omit<AppInfo, 'description' | 'form_fields' | 'source' | 'status' | 'url_suffix' | 'version'>[];
|
||||
|
@ -45,7 +45,7 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable, authenticated = tr
|
||||
|
||||
return (
|
||||
<header className="text-white navbar navbar-expand-md navbar-dark navbar-overlap d-print-none" data-bs-theme="dark">
|
||||
<Script src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js" async />
|
||||
<Script src="/js/tabler.min.js" async />
|
||||
<div className="container-xl">
|
||||
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu">
|
||||
<span className="navbar-toggler-icon" />
|
||||
|
@ -12,7 +12,6 @@ export const deleteLinkAction = action(z.number(), async (linkId: number) => {
|
||||
|
||||
const linksService = new CustomLinksServiceClass();
|
||||
|
||||
// eslint-disable-next-line drizzle/enforce-delete-with-where -- False positive
|
||||
await linksService.delete(linkId, user.id);
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { CookiesProvider } from 'next-client-cookies';
|
||||
import React from 'react';
|
||||
import { AbstractIntlMessages, NextIntlClientProvider } from 'next-intl';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { ThemeProvider } from './ThemeProvider';
|
||||
@ -9,7 +8,6 @@ import { SocketProvider } from './SocketProvider/SocketProvider';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
cookies: ComponentProps<typeof CookiesProvider>['value'];
|
||||
initialTheme?: string;
|
||||
locale?: string;
|
||||
messages: AbstractIntlMessages;
|
||||
@ -17,18 +15,14 @@ type Props = {
|
||||
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: false } } });
|
||||
|
||||
export const ClientProviders = ({ children, initialTheme, cookies, locale, messages }: Props) => {
|
||||
export const ClientProviders = ({ children, initialTheme, locale, messages }: Props) => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<NextIntlClientProvider locale={locale} messages={messages} timeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}>
|
||||
<SocketProvider>
|
||||
<CookiesProvider value={cookies}>
|
||||
<ThemeProvider initialTheme={initialTheme}>{children}</ThemeProvider>
|
||||
</CookiesProvider>
|
||||
<ThemeProvider initialTheme={initialTheme}>{children}</ThemeProvider>
|
||||
</SocketProvider>
|
||||
</NextIntlClientProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const ClientCookiesProvider: typeof CookiesProvider = (props) => <CookiesProvider {...props} />;
|
||||
|
@ -42,7 +42,7 @@ export const ThemeProvider = (props: Props) => {
|
||||
useEffect(() => {
|
||||
const autoTheme = getAutoTheme();
|
||||
if (autoTheme === 'christmas' && allowAutoThemes && typeof window !== 'undefined') {
|
||||
loadChristmasTheme();
|
||||
void loadChristmasTheme();
|
||||
}
|
||||
}, [allowAutoThemes]);
|
||||
|
||||
|
@ -11,6 +11,7 @@ import clsx from 'clsx';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { getCurrentLocale } from '../utils/getCurrentLocale';
|
||||
import { ClientProviders } from './components/ClientProviders';
|
||||
import { CookiesProvider } from 'next-client-cookies/server';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Tipi',
|
||||
@ -30,13 +31,15 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
|
||||
return (
|
||||
<html lang={locale} className={clsx(GeistSans.className, 'border-top-wide border-primary')}>
|
||||
<ClientProviders messages={mergedMessages} locale={locale} initialTheme={theme?.value} cookies={cookies().getAll()}>
|
||||
<body data-bs-theme={theme?.value}>
|
||||
<input type="hidden" value={JSON.stringify(clientSettings)} id="client-settings" />
|
||||
{children}
|
||||
<Toaster />
|
||||
</body>
|
||||
</ClientProviders>
|
||||
<CookiesProvider>
|
||||
<ClientProviders messages={mergedMessages} locale={locale} initialTheme={theme?.value}>
|
||||
<body data-bs-theme={theme?.value}>
|
||||
<input type="hidden" value={JSON.stringify(clientSettings)} id="client-settings" />
|
||||
{children}
|
||||
<Toaster />
|
||||
</body>
|
||||
</ClientProviders>
|
||||
</CookiesProvider>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, children, ...props }, ref) => <DialogPrimitive.Overlay className={clsx('', className)} {...props} ref={ref} />);
|
||||
>(({ className, ...props }, ref) => <DialogPrimitive.Overlay className={clsx('', className)} {...props} ref={ref} />);
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
|
@ -12,8 +12,6 @@ type TriggerProps = {
|
||||
|
||||
const Select: React.FC<React.ComponentPropsWithoutRef<typeof SelectPrimitive.Root> & { label?: string; error?: string; className?: string }> = ({
|
||||
children,
|
||||
error,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return <SelectPrimitive.Root {...props}>{children}</SelectPrimitive.Root>;
|
||||
|
@ -7,10 +7,10 @@ async function initMocks() {
|
||||
server.listen();
|
||||
} else {
|
||||
const { worker } = await import('./browser');
|
||||
worker.start();
|
||||
await worker.start();
|
||||
}
|
||||
}
|
||||
|
||||
initMocks();
|
||||
void initMocks();
|
||||
|
||||
export { initMocks };
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const THEMES = {
|
||||
const THEMES = {
|
||||
christmas: {
|
||||
name: 'christmas',
|
||||
month: 11,
|
||||
@ -7,7 +7,7 @@ export const THEMES = {
|
||||
},
|
||||
};
|
||||
|
||||
export type Theme = keyof typeof THEMES | 'default';
|
||||
type Theme = keyof typeof THEMES | 'default';
|
||||
|
||||
export const getAutoTheme = (): Theme => {
|
||||
const date = new Date();
|
||||
|
@ -21,5 +21,3 @@ export const readFile = (path: string): string => {
|
||||
export const readdirSync = (path: string): string[] => fs.readdirSync(path);
|
||||
|
||||
export const fileExists = (path: string): boolean => fs.existsSync(path);
|
||||
|
||||
export const unlinkFile = (path: string) => fs.promises.unlink(path);
|
||||
|
@ -85,7 +85,7 @@ export class EventDispatcher {
|
||||
Logger.info(`Scheduling event ${JSON.stringify(event)} with cron expression ${cronExpression}`);
|
||||
const jobid = this.generateJobId(event);
|
||||
|
||||
this.queue.add(jobid, eventSchema.parse(event), { repeat: { pattern: cronExpression } });
|
||||
void this.queue.add(jobid, eventSchema.parse(event), { repeat: { pattern: cronExpression } });
|
||||
}
|
||||
|
||||
public async close() {
|
||||
|
@ -40,13 +40,13 @@ describe('Test: getConfig', () => {
|
||||
});
|
||||
|
||||
describe('Test: setConfig', () => {
|
||||
it('It should be able set config', () => {
|
||||
it('It should be able set config', async () => {
|
||||
// arrange
|
||||
const randomWord = faker.internet.url();
|
||||
|
||||
// act
|
||||
const tipiConfig = new TipiConfigClass(0);
|
||||
tipiConfig.setConfig('appsRepoUrl', randomWord);
|
||||
await tipiConfig.setConfig('appsRepoUrl', randomWord);
|
||||
const config = tipiConfig.getConfig();
|
||||
|
||||
// assert
|
||||
@ -136,12 +136,12 @@ describe('Test: setSettings', () => {
|
||||
expect(settingsJson.appsRepoUrl).toBe(fakeSettings.appsRepoUrl);
|
||||
});
|
||||
|
||||
it('should not write settings to json file if there are invalid values', () => {
|
||||
it('should not write settings to json file if there are invalid values', async () => {
|
||||
// arrange
|
||||
const fakeSettings = { appsRepoUrl: 10 };
|
||||
|
||||
// act
|
||||
new TipiConfigClass(0).setSettings(fakeSettings as object);
|
||||
await new TipiConfigClass(0).setSettings(fakeSettings as object);
|
||||
const settingsJson = (readJsonFile('/data/state/settings.json') || {}) as { [key: string]: string };
|
||||
|
||||
// assert
|
||||
@ -154,7 +154,7 @@ describe('Test: setSettings', () => {
|
||||
let error;
|
||||
const fakeSettings = { appsRepoUrl: faker.internet.url() };
|
||||
const tipiConf = new TipiConfigClass(0);
|
||||
tipiConf.setConfig('demoMode', true);
|
||||
await tipiConf.setConfig('demoMode', true);
|
||||
|
||||
// act
|
||||
try {
|
||||
|
@ -43,7 +43,7 @@ describe('Test: checkAppRequirements()', () => {
|
||||
|
||||
it('Should throw if architecture is not supported', async () => {
|
||||
// arrange
|
||||
TipiConfig.setConfig('architecture', 'arm64');
|
||||
await TipiConfig.setConfig('architecture', 'arm64');
|
||||
const appConfig = createAppConfig({ supported_architectures: ['arm'] });
|
||||
|
||||
// assert
|
||||
|
@ -101,13 +101,13 @@ export class AppServiceClass {
|
||||
try {
|
||||
await this.queries.updateApp(app.id, { status: 'starting' });
|
||||
|
||||
eventDispatcher
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({ type: 'app', command: 'start', appid: app.id, form: castAppConfig(app.config) })
|
||||
.then(({ success }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(app.id, { status: 'running' });
|
||||
this.queries.updateApp(app.id, { status: 'running' }).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.updateApp(app.id, { status: 'stopped' });
|
||||
this.queries.updateApp(app.id, { status: 'stopped' }).catch(Logger.error);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
@ -135,7 +135,7 @@ export class AppServiceClass {
|
||||
|
||||
await this.queries.updateApp(appName, { status: 'starting' });
|
||||
const eventDispatcher = new EventDispatcher('startApp');
|
||||
eventDispatcher
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({
|
||||
type: 'app',
|
||||
command: 'start',
|
||||
@ -144,13 +144,13 @@ export class AppServiceClass {
|
||||
})
|
||||
.then(({ success, stdout }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(appName, { status: 'running' });
|
||||
this.queries.updateApp(appName, { status: 'running' }).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.updateApp(appName, { status: 'stopped' });
|
||||
Logger.error(`Failed to start app ${appName}: ${stdout}`);
|
||||
this.queries.updateApp(appName, { status: 'stopped' }).catch(Logger.error);
|
||||
}
|
||||
|
||||
eventDispatcher.close();
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
|
||||
const updatedApp = await this.queries.getApp(appName);
|
||||
@ -228,15 +228,15 @@ export class AppServiceClass {
|
||||
|
||||
// Run script
|
||||
const eventDispatcher = new EventDispatcher('installApp');
|
||||
eventDispatcher.dispatchEventAsync({ type: 'app', command: 'install', appid: id, form }).then(({ success, stdout }) => {
|
||||
void eventDispatcher.dispatchEventAsync({ type: 'app', command: 'install', appid: id, form }).then(({ success, stdout }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(id, { status: 'running' });
|
||||
this.queries.updateApp(id, { status: 'running' }).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.deleteApp(id);
|
||||
this.queries.deleteApp(id).catch(Logger.error);
|
||||
Logger.error(`Failed to install app ${id}: ${stdout}`);
|
||||
}
|
||||
|
||||
eventDispatcher.close();
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -358,16 +358,18 @@ export class AppServiceClass {
|
||||
await this.queries.updateApp(id, { status: 'stopping' });
|
||||
|
||||
const eventDispatcher = new EventDispatcher('stopApp');
|
||||
eventDispatcher.dispatchEventAsync({ type: 'app', command: 'stop', appid: id, form: castAppConfig(app.config) }).then(({ success, stdout }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(id, { status: 'stopped' });
|
||||
} else {
|
||||
Logger.error(`Failed to stop app ${id}: ${stdout}`);
|
||||
this.queries.updateApp(id, { status: 'running' });
|
||||
}
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({ type: 'app', command: 'stop', appid: id, form: castAppConfig(app.config) })
|
||||
.then(({ success, stdout }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(id, { status: 'stopped' }).catch(Logger.error);
|
||||
} else {
|
||||
Logger.error(`Failed to stop app ${id}: ${stdout}`);
|
||||
this.queries.updateApp(id, { status: 'running' }).catch(Logger.error);
|
||||
}
|
||||
|
||||
eventDispatcher.close();
|
||||
});
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
|
||||
const updatedApp = await this.queries.getApp(id);
|
||||
return updatedApp;
|
||||
@ -392,16 +394,16 @@ export class AppServiceClass {
|
||||
await this.queries.updateApp(id, { status: 'uninstalling' });
|
||||
|
||||
const eventDispatcher = new EventDispatcher('uninstallApp');
|
||||
eventDispatcher
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({ type: 'app', command: 'uninstall', appid: id, form: castAppConfig(app.config) })
|
||||
.then(({ stdout, success }) => {
|
||||
if (success) {
|
||||
this.queries.deleteApp(id);
|
||||
this.queries.deleteApp(id).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.updateApp(id, { status: 'stopped' });
|
||||
this.queries.updateApp(id, { status: 'stopped' }).catch(Logger.error);
|
||||
Logger.error(`Failed to uninstall app ${id}: ${stdout}`);
|
||||
}
|
||||
eventDispatcher.close();
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
|
||||
return { id, status: 'missing', config: {} };
|
||||
@ -416,18 +418,20 @@ export class AppServiceClass {
|
||||
public resetApp = async (id: string) => {
|
||||
const app = await this.getApp(id);
|
||||
|
||||
this.queries.updateApp(id, { status: 'resetting' });
|
||||
await this.queries.updateApp(id, { status: 'resetting' });
|
||||
|
||||
const eventDispatcher = new EventDispatcher('resetApp');
|
||||
eventDispatcher.dispatchEventAsync({ type: 'app', command: 'reset', appid: id, form: castAppConfig(app.config) }).then(({ stdout, success }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(id, { status: 'running' });
|
||||
} else {
|
||||
this.queries.updateApp(id, { status: 'stopped' });
|
||||
Logger.error(`Failed to reset app ${id}: ${stdout}`);
|
||||
}
|
||||
eventDispatcher.close();
|
||||
});
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({ type: 'app', command: 'reset', appid: id, form: castAppConfig(app.config) })
|
||||
.then(({ stdout, success }) => {
|
||||
if (success) {
|
||||
this.queries.updateApp(id, { status: 'running' }).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.updateApp(id, { status: 'stopped' }).catch(Logger.error);
|
||||
Logger.error(`Failed to reset app ${id}: ${stdout}`);
|
||||
}
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -447,16 +451,16 @@ export class AppServiceClass {
|
||||
await this.queries.updateApp(id, { status: 'restarting' });
|
||||
|
||||
const eventDispatcher = new EventDispatcher('restartApp');
|
||||
eventDispatcher
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({ type: 'app', command: 'restart', appid: id, form: castAppConfig(app.config) })
|
||||
.then(({ success, stdout }) => {
|
||||
if (!success) {
|
||||
Logger.error(`Failed to restart app ${id}: ${stdout}`);
|
||||
}
|
||||
|
||||
this.queries.updateApp(id, { status: 'running' });
|
||||
this.queries.updateApp(id, { status: 'running' }).catch(Logger.error);
|
||||
|
||||
eventDispatcher.close();
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
|
||||
const updatedApp = await this.queries.getApp(id);
|
||||
@ -508,7 +512,7 @@ export class AppServiceClass {
|
||||
await this.queries.updateApp(id, { status: 'updating' });
|
||||
|
||||
const eventDispatcher = new EventDispatcher('updateApp');
|
||||
eventDispatcher
|
||||
void eventDispatcher
|
||||
.dispatchEventAsync({
|
||||
type: 'app',
|
||||
command: 'update',
|
||||
@ -519,18 +523,18 @@ export class AppServiceClass {
|
||||
if (success) {
|
||||
const appInfo = getAppInfo(app.id, app.status);
|
||||
|
||||
this.queries.updateApp(id, { version: appInfo?.tipi_version });
|
||||
this.queries.updateApp(id, { version: appInfo?.tipi_version }).catch(Logger.error);
|
||||
if (appStatusBeforeUpdate === 'running') {
|
||||
this.startApp(id);
|
||||
this.startApp(id).catch(Logger.error);
|
||||
} else {
|
||||
this.queries.updateApp(id, { status: appStatusBeforeUpdate });
|
||||
this.queries.updateApp(id, { status: appStatusBeforeUpdate }).catch(Logger.error);
|
||||
}
|
||||
} else {
|
||||
this.queries.updateApp(id, { status: 'stopped' });
|
||||
this.queries.updateApp(id, { status: 'stopped' }).catch(Logger.error);
|
||||
Logger.error(`Failed to update app ${id}: ${stdout}`);
|
||||
}
|
||||
|
||||
eventDispatcher.close();
|
||||
void eventDispatcher.close();
|
||||
});
|
||||
|
||||
const updatedApp = await this.getApp(id);
|
||||
|
@ -48,9 +48,7 @@ const createDatabase = async (testsuite: string): Promise<TestDatabase> => {
|
||||
* @param {TestDatabase} database - database to clear
|
||||
*/
|
||||
const clearDatabase = async (database: TestDatabase) => {
|
||||
// eslint-disable-next-line drizzle/enforce-delete-with-where -- we want to clear the whole table
|
||||
await database.db.delete(schema.userTable);
|
||||
// eslint-disable-next-line drizzle/enforce-delete-with-where -- we want to clear the whole table
|
||||
await database.db.delete(schema.appTable);
|
||||
};
|
||||
|
||||
|
@ -4,9 +4,9 @@ import { TipiConfig } from '../../core/TipiConfig';
|
||||
import { encrypt, decrypt } from '../encryption';
|
||||
|
||||
describe('Test: encrypt', () => {
|
||||
it('should encrypt the provided data', () => {
|
||||
it('should encrypt the provided data', async () => {
|
||||
// arrange
|
||||
TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
await TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
const data = faker.lorem.word();
|
||||
const salt = faker.lorem.word();
|
||||
|
||||
@ -17,9 +17,9 @@ describe('Test: encrypt', () => {
|
||||
expect(encryptedData).not.toEqual(data);
|
||||
});
|
||||
|
||||
it('should decrypt the provided data', () => {
|
||||
it('should decrypt the provided data', async () => {
|
||||
// arrange
|
||||
TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
await TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
const data = faker.lorem.word();
|
||||
const salt = faker.lorem.word();
|
||||
|
||||
@ -31,24 +31,24 @@ describe('Test: encrypt', () => {
|
||||
expect(decryptedData).toEqual(data);
|
||||
});
|
||||
|
||||
it('should throw an error if jwtSecret has changed', () => {
|
||||
it('should throw an error if jwtSecret has changed', async () => {
|
||||
// arrange
|
||||
TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
await TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
const data = faker.lorem.word();
|
||||
const salt = faker.lorem.word();
|
||||
|
||||
// act
|
||||
const encryptedData = encrypt(data, salt);
|
||||
TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
await TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
const decrypting = () => decrypt(encryptedData, salt);
|
||||
|
||||
// assert
|
||||
expect(decrypting).toThrow();
|
||||
});
|
||||
|
||||
it('should throw an error if salt has changed', () => {
|
||||
it('should throw an error if salt has changed', async () => {
|
||||
// arrange
|
||||
TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
await TipiConfig.setConfig('jwtSecret', faker.lorem.word());
|
||||
const data = faker.lorem.word();
|
||||
const salt = faker.lorem.word();
|
||||
|
||||
|
@ -12,14 +12,7 @@ export const isInstanceInsecure = () => {
|
||||
const myHeaders = headers();
|
||||
const ip = myHeaders.get('x-forwarded-host')?.split(':')[0] || '';
|
||||
|
||||
if (ipaddrjs.isValid(ip)) {
|
||||
const range = ipaddrjs.parse(ip!).range();
|
||||
if (range !== 'private' && range !== 'carrierGradeNat' && range !== 'loopback') {
|
||||
return true;
|
||||
}
|
||||
} else if (ip !== 'localhost' && myHeaders.get('x-forwarded-proto') === 'http') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const isPublicIp = ipaddrjs.isValid(ip) && !['private', 'carrierGradeNat', 'loopback'].includes(ipaddrjs.parse(ip).range());
|
||||
const isHttpProtocol = ip !== 'localhost' && myHeaders.get('x-forwarded-proto') === 'http';
|
||||
return isPublicIp || isHttpProtocol;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user