diff --git a/.env.example b/.env.example
index 989a285e3..932846bfe 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,5 @@
# Keys
-# Required keys for platform encryption/decryption ops
-PRIVATE_KEY=replace_with_nacl_sk
-PUBLIC_KEY=replace_with_nacl_pk
+# Required key for platform encryption/decryption ops
ENCRYPTION_KEY=replace_with_lengthy_secure_hex
# JWT
@@ -9,13 +7,13 @@ ENCRYPTION_KEY=replace_with_lengthy_secure_hex
JWT_SIGNUP_SECRET=replace_with_lengthy_secure_hex
JWT_REFRESH_SECRET=replace_with_lengthy_secure_hex
JWT_AUTH_SECRET=replace_with_lengthy_secure_hex
+JWT_SERVICE_SECRET=replace_with_lengthy_secure_hex
# JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string
# describing a time span (e.g. 60, "2 days", "10h", "7d")
JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME=
-JWT_SERVICE_SECRET=
JWT_SIGNUP_LIFETIME=
# Optional lifetimes for OTP expressed in seconds
@@ -33,26 +31,31 @@ MONGO_PASSWORD=example
# Website URL
# Required
-
SITE_URL=http://localhost:8080
# Mail/SMTP
# Required to send emails
-# By default, SMTP_HOST is set to smtp.gmail.com
+# By default, SMTP_HOST is set to smtp.gmail.com, SMTP_PORT is set to 587, SMTP_TLS is set to false, and SMTP_FROM_NAME is set to Infisical
SMTP_HOST=smtp.gmail.com
+# If STARTTLS is supported, the connection will be upgraded to TLS when SMTP_SECURE is set to false
+SMTP_SECURE=false
SMTP_PORT=587
-SMTP_NAME=Team
-SMTP_USERNAME=team@infisical.com
+SMTP_USERNAME=
SMTP_PASSWORD=
+SMTP_FROM_ADDRESS=
+SMTP_FROM_NAME=Infisical
# Integration
# Optional only if integration is used
CLIENT_ID_HEROKU=
CLIENT_ID_VERCEL=
CLIENT_ID_NETLIFY=
+CLIENT_ID_GITHUB=
CLIENT_SECRET_HEROKU=
CLIENT_SECRET_VERCEL=
CLIENT_SECRET_NETLIFY=
+CLIENT_SECRET_GITHUB=
+CLIENT_SLUG_VERCEL=
# Sentry (optional) for monitoring errors
SENTRY_DSN=
diff --git a/.github/workflows/be-test-report.yml b/.github/workflows/be-test-report.yml
new file mode 100644
index 000000000..bd57b377e
--- /dev/null
+++ b/.github/workflows/be-test-report.yml
@@ -0,0 +1,41 @@
+name: "Backend Test Report"
+
+on:
+ workflow_run:
+ workflows: ["Check Backend Pull Request"]
+ types:
+ - completed
+
+jobs:
+ be-report:
+ name: Backend test report
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: ๐ Download test results
+ id: download-artifact
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ name: be-test-results
+ path: backend
+ workflow: check-be-pull-request.yml
+ workflow_conclusion: success
+ - name: ๐ Publish test results
+ uses: dorny/test-reporter@v1
+ with:
+ name: Test Results
+ path: reports/jest-*.xml
+ reporter: jest-junit
+ working-directory: backend
+ - name: ๐ Publish coverage
+ uses: ArtiomTr/jest-coverage-report-action@v2
+ id: coverage
+ with:
+ output: comment, report-markdown
+ coverage-file: coverage/report.json
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ working-directory: backend
+ - uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ message: ${{ steps.coverage.outputs.report }}
diff --git a/.github/workflows/check-be-pull-request.yml b/.github/workflows/check-be-pull-request.yml
index f17d8c5c8..8022a25bc 100644
--- a/.github/workflows/check-be-pull-request.yml
+++ b/.github/workflows/check-be-pull-request.yml
@@ -1,41 +1,42 @@
-name: Check Backend Pull Request
+name: "Check Backend Pull Request"
on:
pull_request:
- types: [ opened, synchronize ]
+ types: [opened, synchronize]
paths:
- - 'backend/**'
- - '!backend/README.md'
- - '!backend/.*'
- - 'backend/.eslintrc.js'
-
+ - "backend/**"
+ - "!backend/README.md"
+ - "!backend/.*"
+ - "backend/.eslintrc.js"
jobs:
-
check-be-pr:
name: Check
runs-on: ubuntu-latest
steps:
- -
- name: โ๏ธ Checkout source
+ - name: โ๏ธ Checkout source
uses: actions/checkout@v3
- -
- name: ๐ง Setup Node 16
+ - name: ๐ง Setup Node 16
uses: actions/setup-node@v3
with:
- node-version: '16'
- cache: 'npm'
+ node-version: "16"
+ cache: "npm"
cache-dependency-path: backend/package-lock.json
- -
- name: ๐ฆ Install dependencies
+ - name: ๐ฆ Install dependencies
run: npm ci --only-production --ignore-scripts
working-directory: backend
- # -
- # name: ๐งช Run tests
- # run: npm run test:ci
- # working-directory: backend
- -
- name: ๐๏ธ Run build
+ - name: ๐งช Run tests
+ run: npm run test:ci
+ working-directory: backend
+ - name: ๐ Upload test results
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: be-test-results
+ path: |
+ ./backend/reports
+ ./backend/coverage
+ - name: ๐๏ธ Run build
run: npm run build
working-directory: backend
diff --git a/.github/workflows/close_inactive_issues.yml b/.github/workflows/close_inactive_issues.yml
deleted file mode 100644
index 315c9e929..000000000
--- a/.github/workflows/close_inactive_issues.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Close inactive issues
-on:
- schedule:
- - cron: "30 1 * * *"
-
-jobs:
- close-issues:
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
- steps:
- - uses: actions/stale@v4
- with:
- days-before-issue-stale: 30
- days-before-issue-close: 14
- stale-issue-label: "stale"
- stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
- close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
- days-before-pr-stale: -1
- days-before-pr-close: -1
- repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index f32a51384..6c4414313 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,9 @@ node_modules
.env
# testing
-/coverage
+coverage
+reports
+junit.xml
# next.js
/.next/
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index e83973397..9ac38524b 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -81,6 +81,7 @@ nfpms:
- rpm
- deb
- apk
+ - archlinux
bindir: /usr/bin
scoop:
bucket:
diff --git a/README.md b/README.md
index 999cbac59..1f2df3863 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,9 @@ We're currently setting the foundation and building [integrations](https://infis
|
- ๐ Vercel (https://github.com/Infisical/infisical/issues/60)
+
+ โ๏ธ Vercel
+
|
@@ -144,7 +146,9 @@ We're currently setting the foundation and building [integrations](https://infis
๐ AWS
|
- ๐ GitHub Actions (https://github.com/Infisical/infisical/issues/54)
+
+ โ๏ธ GitHub Actions
+
|
๐ Railway
@@ -155,10 +159,10 @@ We're currently setting the foundation and building [integrations](https://infis
๐ GCP
|
- ๐ GitLab CI/CD
+ ๐ GitLab CI/CD (https://github.com/Infisical/infisical/issues/134)
|
- ๐ CircleCI
+ ๐ CircleCI (https://github.com/Infisical/infisical/issues/91)
|
@@ -177,7 +181,9 @@ We're currently setting the foundation and building [integrations](https://infis
๐ TravisCI
|
- ๐ Netlify (https://github.com/Infisical/infisical/issues/55)
+
+ โ๏ธ Netlify
+
|
๐ Railway
@@ -191,7 +197,7 @@ We're currently setting the foundation and building [integrations](https://infis
๐ Supabase
|
- ๐ Serverless
+ ๐ Render (https://github.com/Infisical/infisical/issues/132)
|
@@ -315,4 +321,4 @@ Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of
-
+
diff --git a/backend/__tests__/healthcheck.test.ts b/backend/__tests__/healthcheck.test.ts
new file mode 100644
index 000000000..234d2d8eb
--- /dev/null
+++ b/backend/__tests__/healthcheck.test.ts
@@ -0,0 +1,19 @@
+import { server } from '../src/app';
+import { describe, expect, it, beforeAll, afterAll } from '@jest/globals';
+import supertest from 'supertest';
+import { setUpHealthEndpoint } from '../src/services/health';
+
+const requestWithSupertest = supertest(server);
+describe('Healthcheck endpoint', () => {
+ beforeAll(async () => {
+ setUpHealthEndpoint(server);
+ });
+ afterAll(async () => {
+ server.close();
+ });
+
+ it('GET /healthcheck should return OK', async () => {
+ const res = await requestWithSupertest.get('/healthcheck');
+ expect(res.status).toEqual(200);
+ });
+});
diff --git a/backend/environment.d.ts b/backend/environment.d.ts
index 853f52e5b..33034fdd5 100644
--- a/backend/environment.d.ts
+++ b/backend/environment.d.ts
@@ -22,8 +22,6 @@ declare global {
CLIENT_SECRET_NETLIFY: string;
POSTHOG_HOST: string;
POSTHOG_PROJECT_API_KEY: string;
- PRIVATE_KEY: string;
- PUBLIC_KEY: string;
SENTRY_DSN: string;
SITE_URL: string;
SMTP_HOST: string;
diff --git a/backend/package-lock.json b/backend/package-lock.json
index b5da475f0..9020620dd 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -10,9 +10,11 @@
"license": "ISC",
"dependencies": {
"@godaddy/terminus": "^4.11.2",
+ "@octokit/rest": "^19.0.5",
"@sentry/node": "^7.14.0",
"@sentry/tracing": "^7.19.0",
"@types/crypto-js": "^4.1.1",
+ "@types/libsodium-wrappers": "^0.7.10",
"axios": "^1.1.3",
"bigint-conversion": "^2.2.2",
"cookie-parser": "^1.4.6",
@@ -24,11 +26,12 @@
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
- "jsonwebtoken": "^8.5.1",
+ "jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",
+ "libsodium-wrappers": "^0.7.10",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0",
- "posthog-node": "^2.1.0",
+ "posthog-node": "^2.2.0",
"query-string": "^7.1.3",
"rimraf": "^3.0.2",
"stripe": "^10.7.0",
@@ -37,22 +40,29 @@
"typescript": "^4.9.3"
},
"devDependencies": {
+ "@jest/globals": "^29.3.1",
"@posthog/plugin-scaffold": "^1.3.4",
"@types/cookie-parser": "^1.4.3",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.14",
+ "@types/jest": "^29.2.4",
"@types/jsonwebtoken": "^8.5.9",
"@types/node": "^18.11.3",
"@types/nodemailer": "^6.4.6",
+ "@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
+ "cross-env": "^7.0.3",
"eslint": "^8.26.0",
"install": "^0.13.0",
"jest": "^29.3.1",
+ "jest-junit": "^15.0.0",
"nodemon": "^2.0.19",
"npm": "^8.19.3",
+ "supertest": "^6.3.3",
+ "ts-jest": "^29.0.3",
"ts-node": "^10.9.1"
}
},
@@ -1073,6 +1083,7 @@
"version": "3.188.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.188.0.tgz",
"integrity": "sha512-qlH+5NZBLiyKziL335BEPedYxX6j+p7KFRWXvDQox9S+s+gLCayednpK+fteOhBenCcR9fUZOVuAPScy1I8qCg==",
+ "deprecated": "The package @aws-sdk/util-base64-browser has been renamed to @aws-sdk/util-base64. Please install the renamed package.",
"optional": true,
"dependencies": {
"tslib": "^2.3.1"
@@ -1088,6 +1099,7 @@
"version": "3.201.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.201.0.tgz",
"integrity": "sha512-ydZqNpB3l5kiicInpPDExPb5xHI7uyVIa1vMupnuIrJ412iNb0F2+K8LlFynzw6fSJShVKnqFcWOYRA96z1iIw==",
+ "deprecated": "The package @aws-sdk/util-base64-node has been renamed to @aws-sdk/util-base64. Please install the renamed package.",
"optional": true,
"dependencies": {
"@aws-sdk/util-buffer-from": "3.201.0",
@@ -2590,6 +2602,153 @@
"node": ">= 8"
}
},
+ "node_modules/@octokit/auth-token": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
+ "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
+ "dependencies": {
+ "@octokit/types": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/core": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
+ "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
+ "dependencies": {
+ "@octokit/auth-token": "^3.0.0",
+ "@octokit/graphql": "^5.0.0",
+ "@octokit/request": "^6.0.0",
+ "@octokit/request-error": "^3.0.0",
+ "@octokit/types": "^8.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/endpoint": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
+ "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
+ "dependencies": {
+ "@octokit/types": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
+ "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
+ "dependencies": {
+ "@octokit/request": "^6.0.0",
+ "@octokit/types": "^8.0.0",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/openapi-types": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
+ "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
+ },
+ "node_modules/@octokit/plugin-paginate-rest": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
+ "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
+ "dependencies": {
+ "@octokit/types": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=4"
+ }
+ },
+ "node_modules/@octokit/plugin-request-log": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
+ "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
+ "peerDependencies": {
+ "@octokit/core": ">=3"
+ }
+ },
+ "node_modules/@octokit/plugin-rest-endpoint-methods": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
+ "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
+ "dependencies": {
+ "@octokit/types": "^8.0.0",
+ "deprecation": "^2.3.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "@octokit/core": ">=3"
+ }
+ },
+ "node_modules/@octokit/request": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
+ "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
+ "dependencies": {
+ "@octokit/endpoint": "^7.0.0",
+ "@octokit/request-error": "^3.0.0",
+ "@octokit/types": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "node-fetch": "^2.6.7",
+ "universal-user-agent": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/request-error": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
+ "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
+ "dependencies": {
+ "@octokit/types": "^8.0.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/rest": {
+ "version": "19.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
+ "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
+ "dependencies": {
+ "@octokit/core": "^4.1.0",
+ "@octokit/plugin-paginate-rest": "^5.0.0",
+ "@octokit/plugin-request-log": "^1.0.4",
+ "@octokit/plugin-rest-endpoint-methods": "^6.7.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/@octokit/types": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
+ "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
+ "dependencies": {
+ "@octokit/openapi-types": "^14.0.0"
+ }
+ },
"node_modules/@posthog/plugin-scaffold": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@posthog/plugin-scaffold/-/plugin-scaffold-1.3.4.tgz",
@@ -2813,6 +2972,12 @@
"@types/express": "*"
}
},
+ "node_modules/@types/cookiejar": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
+ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
+ "dev": true
+ },
"node_modules/@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
@@ -2880,6 +3045,16 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/jest": {
+ "version": "29.2.4",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz",
+ "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==",
+ "dev": true,
+ "dependencies": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -2895,6 +3070,11 @@
"@types/node": "*"
}
},
+ "node_modules/@types/libsodium-wrappers": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
+ "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA=="
+ },
"node_modules/@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@@ -2949,6 +3129,25 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
+ "node_modules/@types/superagent": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz",
+ "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/cookiejar": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/supertest": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz",
+ "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/superagent": "*"
+ }
+ },
"node_modules/@types/swagger-jsdoc": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.1.tgz",
@@ -3346,6 +3545,12 @@
"node": ">=8"
}
},
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true
+ },
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -3485,6 +3690,11 @@
}
]
},
+ "node_modules/before-after-hook": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
+ },
"node_modules/bigint-conversion": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/bigint-conversion/-/bigint-conversion-2.2.2.tgz",
@@ -3593,6 +3803,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/bs-logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+ "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "dev": true,
+ "dependencies": {
+ "fast-json-stable-stringify": "2.x"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -3866,6 +4088,12 @@
"node": ">= 0.8"
}
},
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3929,6 +4157,12 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
+ "node_modules/cookiejar": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+ "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
+ "dev": true
+ },
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -3965,6 +4199,24 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -4053,6 +4305,11 @@
"node": ">= 0.8"
}
},
+ "node_modules/deprecation": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
+ },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -4071,6 +4328,16 @@
"node": ">=8"
}
},
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "dev": true,
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -4614,6 +4881,12 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true
+ },
"node_modules/fast-xml-parser": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz",
@@ -4777,6 +5050,21 @@
"node": ">= 6"
}
},
+ "node_modules/formidable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
+ "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
+ "dev": true,
+ "dependencies": {
+ "dezalgo": "^1.0.4",
+ "hexoid": "^1.0.0",
+ "once": "^1.4.0",
+ "qs": "^6.11.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -5031,6 +5319,15 @@
"node": ">=12.0.0"
}
},
+ "node_modules/hexoid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
+ "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -5295,6 +5592,14 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -5630,6 +5935,21 @@
"fsevents": "^2.3.2"
}
},
+ "node_modules/jest-junit": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-15.0.0.tgz",
+ "integrity": "sha512-Z5sVX0Ag3HZdMUnD5DFlG+1gciIFSy7yIVPhOdGUi8YJaI9iLvvBb530gtQL2CHmv0JJeiwRZenr0VrSR7frvg==",
+ "dev": true,
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "strip-ansi": "^6.0.1",
+ "uuid": "^8.3.2",
+ "xml": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
"node_modules/jest-leak-detector": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz",
@@ -6011,32 +6331,18 @@
}
},
"node_modules/jsonwebtoken": {
- "version": "8.5.1",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
- "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"dependencies": {
"jws": "^3.2.2",
- "lodash.includes": "^4.3.0",
- "lodash.isboolean": "^3.0.3",
- "lodash.isinteger": "^4.0.4",
- "lodash.isnumber": "^3.0.3",
- "lodash.isplainobject": "^4.0.6",
- "lodash.isstring": "^4.0.1",
- "lodash.once": "^4.0.0",
+ "lodash": "^4.17.21",
"ms": "^2.1.1",
- "semver": "^5.6.0"
+ "semver": "^7.3.8"
},
"engines": {
- "node": ">=4",
- "npm": ">=1.4.28"
- }
- },
- "node_modules/jsonwebtoken/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "bin": {
- "semver": "bin/semver"
+ "node": ">=12",
+ "npm": ">=6"
}
},
"node_modules/jsprim": {
@@ -6119,6 +6425,19 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/libsodium": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz",
+ "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ=="
+ },
+ "node_modules/libsodium-wrappers": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
+ "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==",
+ "dependencies": {
+ "libsodium": "^0.7.0"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -6145,35 +6464,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
- "node_modules/lodash.includes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
- "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
- },
- "node_modules/lodash.isboolean": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
- "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
- },
- "node_modules/lodash.isinteger": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
- "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
- },
- "node_modules/lodash.isnumber": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
- "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
- },
- "node_modules/lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
- },
- "node_modules/lodash.isstring": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
- "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
},
"node_modules/lodash.merge": {
"version": "4.6.2",
@@ -6181,11 +6476,6 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
- "node_modules/lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
- },
"node_modules/lru_map": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
@@ -6195,7 +6485,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -6391,6 +6680,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/mmdb-lib": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-2.0.2.tgz",
@@ -6503,6 +6804,44 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
+ "node_modules/node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-fetch/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/node-fetch/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/node-fetch/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -6600,9 +6939,6 @@
},
"bin": {
"nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "*"
}
},
"node_modules/normalize-path": {
@@ -6691,129 +7027,7 @@
"treeverse",
"validate-npm-package-name",
"which",
- "write-file-atomic",
- "@colors/colors",
- "@gar/promisify",
- "@npmcli/disparity-colors",
- "@npmcli/git",
- "@npmcli/installed-package-contents",
- "@npmcli/metavuln-calculator",
- "@npmcli/move-file",
- "@npmcli/name-from-folder",
- "@npmcli/node-gyp",
- "@npmcli/promise-spawn",
- "@npmcli/query",
- "@tootallnate/once",
- "agent-base",
- "agentkeepalive",
- "aggregate-error",
- "ansi-regex",
- "ansi-styles",
- "aproba",
- "are-we-there-yet",
- "asap",
- "balanced-match",
- "bin-links",
- "binary-extensions",
- "brace-expansion",
- "builtins",
- "cidr-regex",
- "clean-stack",
- "clone",
- "cmd-shim",
- "color-convert",
- "color-name",
- "color-support",
- "common-ancestor-path",
- "concat-map",
- "console-control-strings",
- "cssesc",
- "debug",
- "debuglog",
- "defaults",
- "delegates",
- "depd",
- "dezalgo",
- "diff",
- "emoji-regex",
- "encoding",
- "env-paths",
- "err-code",
- "fs.realpath",
- "function-bind",
- "gauge",
- "has",
- "has-flag",
- "has-unicode",
- "http-cache-semantics",
- "http-proxy-agent",
- "https-proxy-agent",
- "humanize-ms",
- "iconv-lite",
- "ignore-walk",
- "imurmurhash",
- "indent-string",
- "infer-owner",
- "inflight",
- "inherits",
- "ip",
- "ip-regex",
- "is-core-module",
- "is-fullwidth-code-point",
- "is-lambda",
- "isexe",
- "json-stringify-nice",
- "jsonparse",
- "just-diff",
- "just-diff-apply",
- "lru-cache",
- "minipass-collect",
- "minipass-fetch",
- "minipass-flush",
- "minipass-json-stream",
- "minipass-sized",
- "minizlib",
- "mute-stream",
- "negotiator",
- "normalize-package-data",
- "npm-bundled",
- "npm-normalize-package-bin",
- "npm-packlist",
- "once",
- "path-is-absolute",
- "postcss-selector-parser",
- "promise-all-reject-late",
- "promise-call-limit",
- "promise-inflight",
- "promise-retry",
- "promzard",
- "read-cmd-shim",
- "readable-stream",
- "retry",
- "safe-buffer",
- "safer-buffer",
- "set-blocking",
- "signal-exit",
- "smart-buffer",
- "socks",
- "socks-proxy-agent",
- "spdx-correct",
- "spdx-exceptions",
- "spdx-expression-parse",
- "spdx-license-ids",
- "string_decoder",
- "string-width",
- "strip-ansi",
- "supports-color",
- "unique-filename",
- "unique-slug",
- "util-deprecate",
- "validate-npm-package-license",
- "walk-up-path",
- "wcwidth",
- "wide-align",
- "wrappy",
- "yallist"
+ "write-file-atomic"
],
"dev": true,
"dependencies": {
@@ -9596,9 +9810,9 @@
}
},
"node_modules/posthog-node": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.1.0.tgz",
- "integrity": "sha512-xr56mZRQo7rnL2YdwbipcxTZeyi5dcI6IM4++wIN7JLYwinrJYcQv01nan4gU4kMy33Qz5qT6boWMQRwpKZJVQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.0.tgz",
+ "integrity": "sha512-p5ltKQO6YvBNy09OmpJvcknvxBAs8MvHv1AGbXGWDdnGCwKZxa9Ln2cN5XHnzU7rYuNT7YMAVQzZ6cE7Mu1yPA==",
"dependencies": {
"axios": "^0.27.0"
},
@@ -9991,7 +10205,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -10359,6 +10572,52 @@
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==",
"optional": true
},
+ "node_modules/superagent": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.6.tgz",
+ "integrity": "sha512-HqSe6DSIh3hEn6cJvCkaM1BLi466f1LHi4yubR0tpewlMpk4RUFFy35bKz8SsPBwYfIIJy5eclp+3tCYAuX0bw==",
+ "dev": true,
+ "dependencies": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.3",
+ "debug": "^4.3.4",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.0",
+ "formidable": "^2.1.1",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.11.0",
+ "semver": "^7.3.8"
+ },
+ "engines": {
+ "node": ">=6.4.0 <13 || >=14"
+ }
+ },
+ "node_modules/superagent/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/supertest": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz",
+ "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==",
+ "dev": true,
+ "dependencies": {
+ "methods": "^1.1.2",
+ "superagent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">=6.4.0"
+ }
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -10470,6 +10729,49 @@
"node": ">=12"
}
},
+ "node_modules/ts-jest": {
+ "version": "29.0.3",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz",
+ "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==",
+ "dev": true,
+ "dependencies": {
+ "bs-logger": "0.x",
+ "fast-json-stable-stringify": "2.x",
+ "jest-util": "^29.0.0",
+ "json5": "^2.2.1",
+ "lodash.memoize": "4.x",
+ "make-error": "1.x",
+ "semver": "7.x",
+ "yargs-parser": "^21.0.1"
+ },
+ "bin": {
+ "ts-jest": "cli.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": ">=7.0.0-beta.0 <8",
+ "@jest/types": "^29.0.0",
+ "babel-jest": "^29.0.0",
+ "jest": "^29.0.0",
+ "typescript": ">=4.3"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "@jest/types": {
+ "optional": true
+ },
+ "babel-jest": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ }
+ }
+ },
"node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@@ -10618,6 +10920,11 @@
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
+ "node_modules/universal-user-agent": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
+ "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
+ },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -10678,7 +10985,7 @@
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "optional": true,
+ "devOptional": true,
"bin": {
"uuid": "dist/bin/uuid"
}
@@ -10842,6 +11149,12 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
+ "node_modules/xml": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
+ "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
+ "dev": true
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -10854,8 +11167,7 @@
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yargs": {
"version": "17.6.2",
@@ -13127,6 +13439,118 @@
"fastq": "^1.6.0"
}
},
+ "@octokit/auth-token": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
+ "integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
+ "requires": {
+ "@octokit/types": "^8.0.0"
+ }
+ },
+ "@octokit/core": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
+ "integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
+ "requires": {
+ "@octokit/auth-token": "^3.0.0",
+ "@octokit/graphql": "^5.0.0",
+ "@octokit/request": "^6.0.0",
+ "@octokit/request-error": "^3.0.0",
+ "@octokit/types": "^8.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/endpoint": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
+ "integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
+ "requires": {
+ "@octokit/types": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/graphql": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
+ "integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
+ "requires": {
+ "@octokit/request": "^6.0.0",
+ "@octokit/types": "^8.0.0",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/openapi-types": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
+ "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
+ },
+ "@octokit/plugin-paginate-rest": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
+ "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
+ "requires": {
+ "@octokit/types": "^8.0.0"
+ }
+ },
+ "@octokit/plugin-request-log": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
+ "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
+ "requires": {}
+ },
+ "@octokit/plugin-rest-endpoint-methods": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
+ "integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
+ "requires": {
+ "@octokit/types": "^8.0.0",
+ "deprecation": "^2.3.1"
+ }
+ },
+ "@octokit/request": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
+ "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
+ "requires": {
+ "@octokit/endpoint": "^7.0.0",
+ "@octokit/request-error": "^3.0.0",
+ "@octokit/types": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "node-fetch": "^2.6.7",
+ "universal-user-agent": "^6.0.0"
+ }
+ },
+ "@octokit/request-error": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
+ "integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
+ "requires": {
+ "@octokit/types": "^8.0.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
+ }
+ },
+ "@octokit/rest": {
+ "version": "19.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
+ "integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
+ "requires": {
+ "@octokit/core": "^4.1.0",
+ "@octokit/plugin-paginate-rest": "^5.0.0",
+ "@octokit/plugin-request-log": "^1.0.4",
+ "@octokit/plugin-rest-endpoint-methods": "^6.7.0"
+ }
+ },
+ "@octokit/types": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
+ "integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
+ "requires": {
+ "@octokit/openapi-types": "^14.0.0"
+ }
+ },
"@posthog/plugin-scaffold": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@posthog/plugin-scaffold/-/plugin-scaffold-1.3.4.tgz",
@@ -13330,6 +13754,12 @@
"@types/express": "*"
}
},
+ "@types/cookiejar": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz",
+ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
+ "dev": true
+ },
"@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
@@ -13397,6 +13827,16 @@
"@types/istanbul-lib-report": "*"
}
},
+ "@types/jest": {
+ "version": "29.2.4",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz",
+ "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==",
+ "dev": true,
+ "requires": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -13412,6 +13852,11 @@
"@types/node": "*"
}
},
+ "@types/libsodium-wrappers": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
+ "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA=="
+ },
"@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@@ -13466,6 +13911,25 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
+ "@types/superagent": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz",
+ "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==",
+ "dev": true,
+ "requires": {
+ "@types/cookiejar": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/supertest": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz",
+ "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==",
+ "dev": true,
+ "requires": {
+ "@types/superagent": "*"
+ }
+ },
"@types/swagger-jsdoc": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.1.tgz",
@@ -13728,6 +14192,12 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true
+ },
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -13829,6 +14299,11 @@
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
+ "before-after-hook": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
+ },
"bigint-conversion": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/bigint-conversion/-/bigint-conversion-2.2.2.tgz",
@@ -13913,6 +14388,15 @@
"update-browserslist-db": "^1.0.9"
}
},
+ "bs-logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+ "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "dev": true,
+ "requires": {
+ "fast-json-stable-stringify": "2.x"
+ }
+ },
"bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -14104,6 +14588,12 @@
"delayed-stream": "~1.0.0"
}
},
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -14154,6 +14644,12 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
+ "cookiejar": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
+ "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
+ "dev": true
+ },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -14187,6 +14683,15 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
+ "cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.1"
+ }
+ },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -14249,6 +14754,11 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
+ "deprecation": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
+ },
"destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -14260,6 +14770,16 @@
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
"dev": true
},
+ "dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "dev": true,
+ "requires": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -14680,6 +15200,12 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true
+ },
"fast-xml-parser": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz",
@@ -14800,6 +15326,18 @@
"mime-types": "^2.1.12"
}
},
+ "formidable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
+ "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
+ "dev": true,
+ "requires": {
+ "dezalgo": "^1.0.4",
+ "hexoid": "^1.0.0",
+ "once": "^1.4.0",
+ "qs": "^6.11.0"
+ }
+ },
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -14972,6 +15510,12 @@
"resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.1.tgz",
"integrity": "sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ=="
},
+ "hexoid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
+ "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
+ "dev": true
+ },
"html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -15159,6 +15703,11 @@
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true
},
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ },
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -15403,6 +15952,18 @@
"walker": "^1.0.8"
}
},
+ "jest-junit": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-15.0.0.tgz",
+ "integrity": "sha512-Z5sVX0Ag3HZdMUnD5DFlG+1gciIFSy7yIVPhOdGUi8YJaI9iLvvBb530gtQL2CHmv0JJeiwRZenr0VrSR7frvg==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4",
+ "strip-ansi": "^6.0.1",
+ "uuid": "^8.3.2",
+ "xml": "^1.0.1"
+ }
+ },
"jest-leak-detector": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz",
@@ -15713,27 +16274,14 @@
"dev": true
},
"jsonwebtoken": {
- "version": "8.5.1",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
- "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
"requires": {
"jws": "^3.2.2",
- "lodash.includes": "^4.3.0",
- "lodash.isboolean": "^3.0.3",
- "lodash.isinteger": "^4.0.4",
- "lodash.isnumber": "^3.0.3",
- "lodash.isplainobject": "^4.0.6",
- "lodash.isstring": "^4.0.1",
- "lodash.once": "^4.0.0",
+ "lodash": "^4.17.21",
"ms": "^2.1.1",
- "semver": "^5.6.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
- }
+ "semver": "^7.3.8"
}
},
"jsprim": {
@@ -15804,6 +16352,19 @@
"type-check": "~0.4.0"
}
},
+ "libsodium": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz",
+ "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ=="
+ },
+ "libsodium-wrappers": {
+ "version": "0.7.10",
+ "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz",
+ "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==",
+ "requires": {
+ "libsodium": "^0.7.0"
+ }
+ },
"lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -15824,35 +16385,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
- "lodash.includes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
- "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
- },
- "lodash.isboolean": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
- "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
- },
- "lodash.isinteger": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
- "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
- },
- "lodash.isnumber": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
- "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
- },
- "lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
- },
- "lodash.isstring": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
- "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
},
"lodash.merge": {
"version": "4.6.2",
@@ -15860,11 +16397,6 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
- "lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
- },
"lru_map": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
@@ -15874,7 +16406,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"requires": {
"yallist": "^4.0.0"
}
@@ -16017,6 +16548,12 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
},
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
"mmdb-lib": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-2.0.2.tgz",
@@ -16106,6 +16643,35 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
+ "node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ },
+ "dependencies": {
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
+ }
+ },
"node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -18186,9 +18752,9 @@
}
},
"posthog-node": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.1.0.tgz",
- "integrity": "sha512-xr56mZRQo7rnL2YdwbipcxTZeyi5dcI6IM4++wIN7JLYwinrJYcQv01nan4gU4kMy33Qz5qT6boWMQRwpKZJVQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.0.tgz",
+ "integrity": "sha512-p5ltKQO6YvBNy09OmpJvcknvxBAs8MvHv1AGbXGWDdnGCwKZxa9Ln2cN5XHnzU7rYuNT7YMAVQzZ6cE7Mu1yPA==",
"requires": {
"axios": "^0.27.0"
},
@@ -18451,7 +19017,6 @@
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
- "dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
@@ -18740,6 +19305,42 @@
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==",
"optional": true
},
+ "superagent": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.6.tgz",
+ "integrity": "sha512-HqSe6DSIh3hEn6cJvCkaM1BLi466f1LHi4yubR0tpewlMpk4RUFFy35bKz8SsPBwYfIIJy5eclp+3tCYAuX0bw==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.3",
+ "debug": "^4.3.4",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.0",
+ "formidable": "^2.1.1",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.11.0",
+ "semver": "^7.3.8"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true
+ }
+ }
+ },
+ "supertest": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz",
+ "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==",
+ "dev": true,
+ "requires": {
+ "methods": "^1.1.2",
+ "superagent": "^8.0.5"
+ }
+ },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -18821,6 +19422,22 @@
"punycode": "^2.1.1"
}
},
+ "ts-jest": {
+ "version": "29.0.3",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz",
+ "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==",
+ "dev": true,
+ "requires": {
+ "bs-logger": "0.x",
+ "fast-json-stable-stringify": "2.x",
+ "jest-util": "^29.0.0",
+ "json5": "^2.2.1",
+ "lodash.memoize": "4.x",
+ "make-error": "1.x",
+ "semver": "7.x",
+ "yargs-parser": "^21.0.1"
+ }
+ },
"ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@@ -18913,6 +19530,11 @@
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
+ "universal-user-agent": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
+ "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -18951,7 +19573,7 @@
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "optional": true
+ "devOptional": true
},
"v8-compile-cache-lib": {
"version": "3.0.1",
@@ -19078,6 +19700,12 @@
"signal-exit": "^3.0.7"
}
},
+ "xml": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
+ "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
+ "dev": true
+ },
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -19087,8 +19715,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "17.6.2",
diff --git a/backend/package.json b/backend/package.json
index bad2908d9..329e027b5 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,9 +1,11 @@
{
"dependencies": {
"@godaddy/terminus": "^4.11.2",
+ "@octokit/rest": "^19.0.5",
"@sentry/node": "^7.14.0",
"@sentry/tracing": "^7.19.0",
"@types/crypto-js": "^4.1.1",
+ "@types/libsodium-wrappers": "^0.7.10",
"axios": "^1.1.3",
"bigint-conversion": "^2.2.2",
"cookie-parser": "^1.4.6",
@@ -15,11 +17,12 @@
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
- "jsonwebtoken": "^8.5.1",
+ "jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",
+ "libsodium-wrappers": "^0.7.10",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0",
- "posthog-node": "^2.1.0",
+ "posthog-node": "^2.2.0",
"query-string": "^7.1.3",
"rimraf": "^3.0.2",
"stripe": "^10.7.0",
@@ -37,7 +40,11 @@
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
"lint": "eslint . --ext .ts",
"lint-and-fix": "eslint . --ext .ts --fix",
- "lint-staged": "lint-staged"
+ "lint-staged": "lint-staged",
+ "pretest": "docker compose -f test-resources/docker-compose.test.yml up -d",
+ "test": "cross-env NODE_ENV=test jest --testTimeout=10000 --detectOpenHandles",
+ "test:ci": "npm test -- --watchAll=false --ci --reporters=default --reporters=jest-junit --reporters=github-actions --coverage --testLocationInResults --json --outputFile=coverage/report.json",
+ "posttest": "docker compose -f test-resources/docker-compose.test.yml down"
},
"repository": {
"type": "git",
@@ -51,22 +58,49 @@
"homepage": "https://github.com/Infisical/infisical-api#readme",
"description": "",
"devDependencies": {
+ "@jest/globals": "^29.3.1",
"@posthog/plugin-scaffold": "^1.3.4",
"@types/cookie-parser": "^1.4.3",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.14",
+ "@types/jest": "^29.2.4",
"@types/jsonwebtoken": "^8.5.9",
"@types/node": "^18.11.3",
"@types/nodemailer": "^6.4.6",
+ "@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
+ "cross-env": "^7.0.3",
"eslint": "^8.26.0",
"install": "^0.13.0",
"jest": "^29.3.1",
+ "jest-junit": "^15.0.0",
"nodemon": "^2.0.19",
"npm": "^8.19.3",
+ "supertest": "^6.3.3",
+ "ts-jest": "^29.0.3",
"ts-node": "^10.9.1"
+ },
+ "jest": {
+ "preset": "ts-jest",
+ "testEnvironment": "node",
+ "collectCoverageFrom": [
+ "src/*.{js,ts}",
+ "!**/node_modules/**"
+ ],
+ "setupFiles": [
+ "/test-resources/env-vars.js"
+ ]
+ },
+ "jest-junit": {
+ "outputDirectory": "reports",
+ "outputName": "jest-junit.xml",
+ "ancestorSeparator": " โบ ",
+ "uniqueOutputName": "false",
+ "suiteNameTemplate": "{filepath}",
+ "classNameTemplate": "{classname}",
+ "titleTemplate": "{title}"
}
}
diff --git a/backend/src/app.ts b/backend/src/app.ts
new file mode 100644
index 000000000..fa11b5f1c
--- /dev/null
+++ b/backend/src/app.ts
@@ -0,0 +1,74 @@
+/* eslint-disable no-console */
+
+import express from 'express';
+import helmet from 'helmet';
+import cors from 'cors';
+import cookieParser from 'cookie-parser';
+import dotenv from 'dotenv';
+
+dotenv.config();
+import { PORT, NODE_ENV, SITE_URL } from './config';
+import { apiLimiter } from './helpers/rateLimiter';
+
+import {
+ signup as signupRouter,
+ auth as authRouter,
+ bot as botRouter,
+ organization as organizationRouter,
+ workspace as workspaceRouter,
+ membershipOrg as membershipOrgRouter,
+ membership as membershipRouter,
+ key as keyRouter,
+ inviteOrg as inviteOrgRouter,
+ user as userRouter,
+ userAction as userActionRouter,
+ secret as secretRouter,
+ serviceToken as serviceTokenRouter,
+ password as passwordRouter,
+ stripe as stripeRouter,
+ integration as integrationRouter,
+ integrationAuth as integrationAuthRouter
+} from './routes';
+
+export const app = express();
+
+app.enable('trust proxy');
+app.use(express.json());
+app.use(cookieParser());
+app.use(
+ cors({
+ credentials: true,
+ origin: SITE_URL
+ })
+);
+
+if (NODE_ENV === 'production') {
+ // enable app-wide rate-limiting + helmet security
+ // in production
+ app.disable('x-powered-by');
+ app.use(apiLimiter);
+ app.use(helmet());
+}
+
+// routers
+app.use('/api/v1/signup', signupRouter);
+app.use('/api/v1/auth', authRouter);
+app.use('/api/v1/bot', botRouter);
+app.use('/api/v1/user', userRouter);
+app.use('/api/v1/user-action', userActionRouter);
+app.use('/api/v1/organization', organizationRouter);
+app.use('/api/v1/workspace', workspaceRouter);
+app.use('/api/v1/membership-org', membershipOrgRouter);
+app.use('/api/v1/membership', membershipRouter);
+app.use('/api/v1/key', keyRouter);
+app.use('/api/v1/invite-org', inviteOrgRouter);
+app.use('/api/v1/secret', secretRouter);
+app.use('/api/v1/service-token', serviceTokenRouter);
+app.use('/api/v1/password', passwordRouter);
+app.use('/api/v1/stripe', stripeRouter);
+app.use('/api/v1/integration', integrationRouter);
+app.use('/api/v1/integration-auth', integrationAuthRouter);
+
+export const server = app.listen(PORT, () => {
+ console.log(`Listening on PORT ${[PORT]}`);
+});
diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts
index 09d384771..43a9026ac 100644
--- a/backend/src/config/index.ts
+++ b/backend/src/config/index.ts
@@ -14,21 +14,24 @@ const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
+const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
+const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!;
+const CLIENT_SLUG_VERCEL= process.env.CLIENT_SLUG_VERCEL!;
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
const POSTHOG_PROJECT_API_KEY =
process.env.POSTHOG_PROJECT_API_KEY! ||
'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
-const PRIVATE_KEY = process.env.PRIVATE_KEY!;
-const PUBLIC_KEY = process.env.PUBLIC_KEY!;
const SENTRY_DSN = process.env.SENTRY_DSN!;
const SITE_URL = process.env.SITE_URL!;
const SMTP_HOST = process.env.SMTP_HOST! || 'smtp.gmail.com';
+const SMTP_SECURE = process.env.SMTP_SECURE! || false;
const SMTP_PORT = process.env.SMTP_PORT! || 587;
-const SMTP_NAME = process.env.SMTP_NAME!;
const SMTP_USERNAME = process.env.SMTP_USERNAME!;
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!;
+const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!;
+const SMTP_FROM_NAME = process.env.SMTP_FROM_NAME! || 'Infisical';
const STRIPE_PRODUCT_CARD_AUTH = process.env.STRIPE_PRODUCT_CARD_AUTH!;
const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!;
@@ -53,20 +56,23 @@ export {
CLIENT_ID_HEROKU,
CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY,
+ CLIENT_ID_GITHUB,
CLIENT_SECRET_HEROKU,
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY,
+ CLIENT_SECRET_GITHUB,
+ CLIENT_SLUG_VERCEL,
POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY,
- PRIVATE_KEY,
- PUBLIC_KEY,
SENTRY_DSN,
SITE_URL,
SMTP_HOST,
SMTP_PORT,
- SMTP_NAME,
+ SMTP_SECURE,
SMTP_USERNAME,
SMTP_PASSWORD,
+ SMTP_FROM_ADDRESS,
+ SMTP_FROM_NAME,
STRIPE_PRODUCT_CARD_AUTH,
STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_STARTER,
diff --git a/backend/src/controllers/keyController.ts b/backend/src/controllers/keyController.ts
index 778d44b60..70446a76c 100644
--- a/backend/src/controllers/keyController.ts
+++ b/backend/src/controllers/keyController.ts
@@ -2,7 +2,6 @@ import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Key } from '../models';
import { findMembership } from '../helpers/membership';
-import { PUBLIC_KEY } from '../config';
import { GRANTED } from '../variables';
/**
@@ -84,16 +83,4 @@ export const getLatestKey = async (req: Request, res: Response) => {
}
return res.status(200).send(resObj);
-};
-
-/**
- * Return public key of Infisical
- * @param req
- * @param res
- * @returns
- */
-export const getPublicKeyInfisical = async (req: Request, res: Response) => {
- return res.status(200).send({
- publicKey: PUBLIC_KEY
- });
-};
+};
\ No newline at end of file
diff --git a/backend/src/helpers/bot.ts b/backend/src/helpers/bot.ts
index 3285ccd6f..abaf73af4 100644
--- a/backend/src/helpers/bot.ts
+++ b/backend/src/helpers/bot.ts
@@ -69,7 +69,7 @@ const getSecretsHelper = async ({
workspaceId: string;
environment: string;
}) => {
- let content = {} as any;
+ const content = {} as any;
try {
const key = await getKey({ workspaceId });
const secrets = await Secret.find({
diff --git a/backend/src/helpers/integration.ts b/backend/src/helpers/integration.ts
index 9aaff9741..ccfd72a53 100644
--- a/backend/src/helpers/integration.ts
+++ b/backend/src/helpers/integration.ts
@@ -53,12 +53,12 @@ const handleOAuthExchangeHelper = async ({
if (!bot) throw new Error('Bot must be enabled for OAuth2 code-token exchange');
// exchange code for access and refresh tokens
- let res = await exchangeCode({
+ const res = await exchangeCode({
integration,
code
});
- let update: Update = {
+ const update: Update = {
workspace: workspaceId,
integration
}
@@ -138,7 +138,7 @@ const syncIntegrationsHelper = async ({
// to that integration
for await (const integration of integrations) {
// get workspace, environment (shared) secrets
- const secrets = await BotService.getSecrets({
+ const secrets = await BotService.getSecrets({ // issue here?
workspaceId: integration.workspace.toString(),
environment: integration.environment
});
diff --git a/backend/src/helpers/nodemailer.ts b/backend/src/helpers/nodemailer.ts
index 1c92af93f..958342aae 100644
--- a/backend/src/helpers/nodemailer.ts
+++ b/backend/src/helpers/nodemailer.ts
@@ -2,40 +2,10 @@ import fs from 'fs';
import path from 'path';
import handlebars from 'handlebars';
import nodemailer from 'nodemailer';
-import {
- SMTP_HOST,
- SMTP_PORT,
- SMTP_NAME,
- SMTP_USERNAME,
- SMTP_PASSWORD
-} from '../config';
-import SMTPConnection from 'nodemailer/lib/smtp-connection';
+import { SMTP_FROM_NAME, SMTP_FROM_ADDRESS } from '../config';
import * as Sentry from '@sentry/node';
-const mailOpts: SMTPConnection.Options = {
- host: SMTP_HOST,
- port: SMTP_PORT as number
-};
-if (SMTP_USERNAME && SMTP_PASSWORD) {
- mailOpts.auth = {
- user: SMTP_USERNAME,
- pass: SMTP_PASSWORD
- };
-}
-// create nodemailer transporter
-const transporter = nodemailer.createTransport(mailOpts);
-transporter
- .verify()
- .then(() => {
- Sentry.setUser(null);
- Sentry.captureMessage('SMTP - Successfully connected');
- })
- .catch((err) => {
- Sentry.setUser(null);
- Sentry.captureException(
- `SMTP - Failed to connect to ${SMTP_HOST}:${SMTP_PORT} \n\t${err}`
- );
- });
+let smtpTransporter: nodemailer.Transporter;
/**
* @param {Object} obj
@@ -63,8 +33,8 @@ const sendMail = async ({
const temp = handlebars.compile(html);
const htmlToSend = temp(substitutions);
- await transporter.sendMail({
- from: `"${SMTP_NAME}" <${SMTP_USERNAME}>`,
+ await smtpTransporter.sendMail({
+ from: `"${SMTP_FROM_NAME}" <${SMTP_FROM_ADDRESS}>`,
to: recipients.join(', '),
subject: subjectLine,
html: htmlToSend
@@ -75,4 +45,8 @@ const sendMail = async ({
}
};
-export { sendMail };
+const setTransporter = (transporter: nodemailer.Transporter) => {
+ smtpTransporter = transporter;
+};
+
+export { sendMail, setTransporter };
diff --git a/backend/src/index.ts b/backend/src/index.ts
index 6abc65420..d182c2655 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -1,129 +1,25 @@
-/* eslint-disable no-console */
-import http from 'http';
-import express from 'express';
-import helmet from 'helmet';
-import cors from 'cors';
-import cookieParser from 'cookie-parser';
-import mongoose from 'mongoose';
import dotenv from 'dotenv';
-
dotenv.config();
+
import * as Sentry from '@sentry/node';
-import { PORT, SENTRY_DSN, NODE_ENV, MONGO_URL, SITE_URL } from './config';
-import { apiLimiter } from './helpers/rateLimiter';
-import { createTerminus } from '@godaddy/terminus';
+import { SENTRY_DSN, NODE_ENV, MONGO_URL } from './config';
+import { server } from './app';
+import { initDatabase } from './services/database';
+import { setUpHealthEndpoint } from './services/health';
+import { initSmtp } from './services/smtp';
+import { setTransporter } from './helpers/nodemailer';
-const app = express();
+initDatabase(MONGO_URL);
-Sentry.init({
- dsn: SENTRY_DSN,
- tracesSampleRate: 1.0,
- debug: NODE_ENV === 'production' ? false : true,
- environment: NODE_ENV
-});
+setUpHealthEndpoint(server);
-import {
- signup as signupRouter,
- auth as authRouter,
- bot as botRouter,
- organization as organizationRouter,
- workspace as workspaceRouter,
- membershipOrg as membershipOrgRouter,
- membership as membershipRouter,
- key as keyRouter,
- inviteOrg as inviteOrgRouter,
- user as userRouter,
- userAction as userActionRouter,
- secret as secretRouter,
- serviceToken as serviceTokenRouter,
- password as passwordRouter,
- stripe as stripeRouter,
- integration as integrationRouter,
- integrationAuth as integrationAuthRouter
-} from './routes';
+setTransporter(initSmtp());
-const connectWithRetry = () => {
- mongoose
- .connect(MONGO_URL)
- .then(() => console.log('Successfully connected to DB'))
- .catch((e) => {
- console.log('Failed to connect to DB ', e);
- setTimeout(() => {
- console.log(e);
- }, 5000);
- });
- return mongoose.connection;
-};
-
-const dbConnection = connectWithRetry();
-
-app.enable('trust proxy');
-app.use(cookieParser());
-app.use(
- cors({
- credentials: true,
- origin: SITE_URL
- })
-);
-
-if (NODE_ENV === 'production') {
- // enable app-wide rate-limiting + helmet security
- // in production
- app.disable('x-powered-by');
- app.use(apiLimiter);
- app.use(helmet());
+if (NODE_ENV !== 'test') {
+ Sentry.init({
+ dsn: SENTRY_DSN,
+ tracesSampleRate: 1.0,
+ debug: NODE_ENV === 'production' ? false : true,
+ environment: NODE_ENV
+ });
}
-
-app.use(express.json());
-
-// routers
-app.use('/api/v1/signup', signupRouter);
-app.use('/api/v1/auth', authRouter);
-app.use('/api/v1/bot', botRouter);
-app.use('/api/v1/user', userRouter);
-app.use('/api/v1/user-action', userActionRouter);
-app.use('/api/v1/organization', organizationRouter);
-app.use('/api/v1/workspace', workspaceRouter);
-app.use('/api/v1/membership-org', membershipOrgRouter);
-app.use('/api/v1/membership', membershipRouter);
-app.use('/api/v1/key', keyRouter);
-app.use('/api/v1/invite-org', inviteOrgRouter);
-app.use('/api/v1/secret', secretRouter);
-app.use('/api/v1/service-token', serviceTokenRouter);
-app.use('/api/v1/password', passwordRouter);
-app.use('/api/v1/stripe', stripeRouter);
-app.use('/api/v1/integration', integrationRouter);
-app.use('/api/v1/integration-auth', integrationAuthRouter);
-
-const server = http.createServer(app);
-
-const onSignal = () => {
- console.log('Server is starting clean-up');
- return Promise.all([
- () => {
- dbConnection.close(() => {
- console.info('Database connection closed');
- });
- }
- ]);
-};
-
-const healthCheck = () => {
- // `state.isShuttingDown` (boolean) shows whether the server is shutting down or not
- return Promise
- .resolve
- // optionally include a resolve value to be included as
- // info in the health check response
- ();
-};
-
-createTerminus(server, {
- healthChecks: {
- '/healthcheck': healthCheck,
- onSignal
- }
-});
-
-server.listen(PORT, () => {
- console.log('Listening on PORT ' + PORT);
-});
diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts
index 70680ef7d..e3b78c481 100644
--- a/backend/src/integrations/apps.ts
+++ b/backend/src/integrations/apps.ts
@@ -1,17 +1,22 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
+import { Octokit } from '@octokit/rest';
+import { IIntegrationAuth } from '../models';
import {
- IIntegrationAuth
-} from '../models';
-import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_HEROKU_API_URL,
- INTEGRATION_VERCEL_API_URL,
- INTEGRATION_NETLIFY_API_URL
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_HEROKU_API_URL,
+ INTEGRATION_VERCEL_API_URL,
+ INTEGRATION_NETLIFY_API_URL,
+ INTEGRATION_GITHUB_API_URL
} from '../variables';
+interface GitHubApp {
+ name: string;
+}
+
/**
* Return list of names of apps for integration named [integration]
* @param {Object} obj
@@ -21,47 +26,51 @@ import {
* @returns {String} apps.name - name of integration app
*/
const getApps = async ({
- integrationAuth,
- accessToken
+ integrationAuth,
+ accessToken
}: {
- integrationAuth: IIntegrationAuth;
- accessToken: string;
+ integrationAuth: IIntegrationAuth;
+ accessToken: string;
}) => {
-
- interface App {
- name: string;
- siteId?: string;
- }
+ interface App {
+ name: string;
+ siteId?: string;
+ }
- let apps: App[]; // TODO: add type and define payloads for apps
- try {
- switch (integrationAuth.integration) {
- case INTEGRATION_HEROKU:
- apps = await getAppsHeroku({
- accessToken
- });
- break;
- case INTEGRATION_VERCEL:
- apps = await getAppsVercel({
- accessToken
- });
- break;
- case INTEGRATION_NETLIFY:
- apps = await getAppsNetlify({
- integrationAuth,
- accessToken
- });
- break;
- }
-
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get integration apps');
+ let apps: App[]; // TODO: add type and define payloads for apps
+ try {
+ switch (integrationAuth.integration) {
+ case INTEGRATION_HEROKU:
+ apps = await getAppsHeroku({
+ accessToken
+ });
+ break;
+ case INTEGRATION_VERCEL:
+ apps = await getAppsVercel({
+ accessToken
+ });
+ break;
+ case INTEGRATION_NETLIFY:
+ apps = await getAppsNetlify({
+ integrationAuth,
+ accessToken
+ });
+ break;
+ case INTEGRATION_GITHUB:
+ apps = await getAppsGithub({
+ integrationAuth,
+ accessToken
+ });
+ break;
}
-
- return apps;
-}
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get integration apps');
+ }
+
+ return apps;
+};
/**
* Return list of names of apps for Heroku integration
@@ -70,31 +79,29 @@ const getApps = async ({
* @returns {Object[]} apps - names of Heroku apps
* @returns {String} apps.name - name of Heroku app
*/
-const getAppsHeroku = async ({
- accessToken
-}: {
- accessToken: string;
-}) => {
- let apps;
- try {
- const res = (await axios.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
- headers: {
- Accept: 'application/vnd.heroku+json; version=3',
- Authorization: `Bearer ${accessToken}`
- }
- })).data;
-
- apps = res.map((a: any) => ({
- name: a.name
- }));
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get Heroku integration apps');
- }
-
- return apps;
-}
+const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
+ let apps;
+ try {
+ const res = (
+ await axios.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
+ headers: {
+ Accept: 'application/vnd.heroku+json; version=3',
+ Authorization: `Bearer ${accessToken}`
+ }
+ })
+ ).data;
+
+ apps = res.map((a: any) => ({
+ name: a.name
+ }));
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get Heroku integration apps');
+ }
+
+ return apps;
+};
/**
* Return list of names of apps for Vercel integration
@@ -103,30 +110,28 @@ const getAppsHeroku = async ({
* @returns {Object[]} apps - names of Vercel apps
* @returns {String} apps.name - name of Vercel app
*/
-const getAppsVercel = async ({
- accessToken
-}: {
- accessToken: string;
-}) => {
- let apps;
- try {
- const res = (await axios.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- })).data;
-
- apps = res.projects.map((a: any) => ({
- name: a.name
- }));
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get Vercel integration apps');
- }
-
- return apps;
-}
+const getAppsVercel = async ({ accessToken }: { accessToken: string }) => {
+ let apps;
+ try {
+ const res = (
+ await axios.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ })
+ ).data;
+
+ apps = res.projects.map((a: any) => ({
+ name: a.name
+ }));
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get Vercel integration apps');
+ }
+
+ return apps;
+};
/**
* Return list of names of sites for Netlify integration
@@ -136,34 +141,73 @@ const getAppsVercel = async ({
* @returns {String} apps.name - name of Netlify site
*/
const getAppsNetlify = async ({
- integrationAuth,
- accessToken
+ integrationAuth,
+ accessToken
}: {
- integrationAuth: IIntegrationAuth;
- accessToken: string;
+ integrationAuth: IIntegrationAuth;
+ accessToken: string;
}) => {
- let apps;
- try {
- const res = (await axios.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- })).data;
-
- apps = res.map((a: any) => ({
- name: a.name,
- siteId: a.site_id
- }));
-
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get Netlify integration apps');
- }
-
- return apps;
-}
+ let apps;
+ try {
+ const res = (
+ await axios.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ })
+ ).data;
-export {
- getApps
-}
\ No newline at end of file
+ apps = res.map((a: any) => ({
+ name: a.name,
+ siteId: a.site_id
+ }));
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get Netlify integration apps');
+ }
+
+ return apps;
+};
+
+/**
+ * Return list of names of repositories for Github integration
+ * @param {Object} obj
+ * @param {String} obj.accessToken - access token for Netlify API
+ * @returns {Object[]} apps - names of Netlify sites
+ * @returns {String} apps.name - name of Netlify site
+ */
+const getAppsGithub = async ({
+ integrationAuth,
+ accessToken
+}: {
+ integrationAuth: IIntegrationAuth;
+ accessToken: string;
+}) => {
+ let apps;
+ try {
+ const octokit = new Octokit({
+ auth: accessToken
+ });
+
+ const repos = (await octokit.request(
+ 'GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}',
+ {}
+ )).data;
+
+ apps = repos
+ .filter((a:any) => a.permissions.admin === true)
+ .map((a: any) => ({
+ name: a.name
+ })
+ );
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get Github repos');
+ }
+
+ return apps;
+};
+
+export { getApps };
diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts
index 26e6521a1..dafddc785 100644
--- a/backend/src/integrations/exchange.ts
+++ b/backend/src/integrations/exchange.ts
@@ -1,46 +1,58 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_HEROKU_TOKEN_URL,
- INTEGRATION_VERCEL_TOKEN_URL,
- INTEGRATION_NETLIFY_TOKEN_URL,
- ACTION_PUSH_TO_HEROKU
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_HEROKU_TOKEN_URL,
+ INTEGRATION_VERCEL_TOKEN_URL,
+ INTEGRATION_NETLIFY_TOKEN_URL,
+ INTEGRATION_GITHUB_TOKEN_URL,
+ INTEGRATION_GITHUB_API_URL,
+ ACTION_PUSH_TO_HEROKU
} from '../variables';
-import {
- SITE_URL,
- CLIENT_SECRET_HEROKU,
- CLIENT_ID_VERCEL,
- CLIENT_ID_NETLIFY,
- CLIENT_SECRET_VERCEL,
- CLIENT_SECRET_NETLIFY
+import {
+ SITE_URL,
+ CLIENT_ID_VERCEL,
+ CLIENT_ID_NETLIFY,
+ CLIENT_ID_GITHUB,
+ CLIENT_SECRET_HEROKU,
+ CLIENT_SECRET_VERCEL,
+ CLIENT_SECRET_NETLIFY,
+ CLIENT_SECRET_GITHUB
} from '../config';
+import { user } from '../routes';
interface ExchangeCodeHerokuResponse {
- token_type: string;
- access_token: string;
- expires_in: number;
- refresh_token: string;
- user_id: string;
- session_nonce?: string;
+ token_type: string;
+ access_token: string;
+ expires_in: number;
+ refresh_token: string;
+ user_id: string;
+ session_nonce?: string;
}
interface ExchangeCodeVercelResponse {
- token_type: string;
- access_token: string;
- installation_id: string;
- user_id: string;
- team_id?: string;
+ token_type: string;
+ access_token: string;
+ installation_id: string;
+ user_id: string;
+ team_id?: string;
}
interface ExchangeCodeNetlifyResponse {
- access_token: string;
- token_type: string;
- refresh_token: string;
- scope: string;
- created_at: number;
+ access_token: string;
+ token_type: string;
+ refresh_token: string;
+ scope: string;
+ created_at: number;
+}
+
+interface ExchangeCodeGithubResponse {
+ access_token: string;
+ scope: string;
+ token_type: string;
}
/**
@@ -56,40 +68,45 @@ interface ExchangeCodeNetlifyResponse {
* @returns {String} obj.action - integration action for bot sequence
*/
const exchangeCode = async ({
- integration,
- code
-}: {
- integration: string;
- code: string;
+ integration,
+ code
+}: {
+ integration: string;
+ code: string;
}) => {
- let obj = {} as any;
-
- try {
- switch (integration) {
- case INTEGRATION_HEROKU:
- obj = await exchangeCodeHeroku({
- code
- });
- break;
- case INTEGRATION_VERCEL:
- obj = await exchangeCodeVercel({
- code
- });
- break;
- case INTEGRATION_NETLIFY:
- obj = await exchangeCodeNetlify({
- code
- });
- break;
- }
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed OAuth2 code-token exchange');
+ let obj = {} as any;
+
+ try {
+ switch (integration) {
+ case INTEGRATION_HEROKU:
+ obj = await exchangeCodeHeroku({
+ code
+ });
+ break;
+ case INTEGRATION_VERCEL:
+ obj = await exchangeCodeVercel({
+ code
+ });
+ break;
+ case INTEGRATION_NETLIFY:
+ obj = await exchangeCodeNetlify({
+ code
+ });
+ break;
+ case INTEGRATION_GITHUB:
+ obj = await exchangeCodeGithub({
+ code
+ });
+ break;
}
-
- return obj;
-}
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed OAuth2 code-token exchange');
+ }
+
+ return obj;
+};
/**
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Heroku
@@ -107,7 +124,7 @@ const exchangeCodeHeroku = async ({
code: string;
}) => {
let res: ExchangeCodeHerokuResponse;
- let accessExpiresAt = new Date();
+ const accessExpiresAt = new Date();
try {
res = (await axios.post(
INTEGRATION_HEROKU_TOKEN_URL,
@@ -144,35 +161,33 @@ const exchangeCodeHeroku = async ({
* @returns {String} obj2.refreshToken - refresh token for Heroku API
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
-const exchangeCodeVercel = async ({
- code
-}: {
- code: string;
-}) => {
- let res: ExchangeCodeVercelResponse;
- try {
- res = (await axios.post(
- INTEGRATION_VERCEL_TOKEN_URL,
- new URLSearchParams({
- code: code,
- client_id: CLIENT_ID_VERCEL,
- client_secret: CLIENT_SECRET_VERCEL,
- redirect_uri: `${SITE_URL}/vercel`
- } as any)
- )).data;
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed OAuth2 code-token exchange with Vercel');
- }
-
- return ({
- accessToken: res.access_token,
- refreshToken: null,
- accessExpiresAt: null,
- teamId: res.team_id
- });
-}
+const exchangeCodeVercel = async ({ code }: { code: string }) => {
+ let res: ExchangeCodeVercelResponse;
+ try {
+ res = (
+ await axios.post(
+ INTEGRATION_VERCEL_TOKEN_URL,
+ new URLSearchParams({
+ code: code,
+ client_id: CLIENT_ID_VERCEL,
+ client_secret: CLIENT_SECRET_VERCEL,
+ redirect_uri: `${SITE_URL}/vercel`
+ } as any)
+ )
+ ).data;
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed OAuth2 code-token exchange with Vercel');
+ }
+
+ return {
+ accessToken: res.access_token,
+ refreshToken: null,
+ accessExpiresAt: null,
+ teamId: res.team_id
+ };
+};
/**
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
@@ -184,58 +199,89 @@ const exchangeCodeVercel = async ({
* @returns {String} obj2.refreshToken - refresh token for Heroku API
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
-const exchangeCodeNetlify = async ({
- code
-}: {
- code: string;
-}) => {
- let res: ExchangeCodeNetlifyResponse;
- let accountId;
- try {
- res = (await axios.post(
- INTEGRATION_NETLIFY_TOKEN_URL,
- new URLSearchParams({
- grant_type: 'authorization_code',
- code: code,
- client_id: CLIENT_ID_NETLIFY,
- client_secret: CLIENT_SECRET_NETLIFY,
- redirect_uri: `${SITE_URL}/netlify`
- } as any)
- )).data;
+const exchangeCodeNetlify = async ({ code }: { code: string }) => {
+ let res: ExchangeCodeNetlifyResponse;
+ let accountId;
+ try {
+ res = (
+ await axios.post(
+ INTEGRATION_NETLIFY_TOKEN_URL,
+ new URLSearchParams({
+ grant_type: 'authorization_code',
+ code: code,
+ client_id: CLIENT_ID_NETLIFY,
+ client_secret: CLIENT_SECRET_NETLIFY,
+ redirect_uri: `${SITE_URL}/netlify`
+ } as any)
+ )
+ ).data;
- const res2 = await axios.get(
- 'https://api.netlify.com/api/v1/sites',
- {
- headers: {
- Authorization: `Bearer ${res.access_token}`
- }
- }
- );
-
- const res3 = (await axios.get(
- 'https://api.netlify.com/api/v1/accounts',
- {
- headers: {
- Authorization: `Bearer ${res.access_token}`
- }
- }
- )).data;
-
- accountId = res3[0].id;
-
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed OAuth2 code-token exchange with Netlify');
- }
-
- return ({
- accessToken: res.access_token,
- refreshToken: res.refresh_token,
- accountId
+ const res2 = await axios.get('https://api.netlify.com/api/v1/sites', {
+ headers: {
+ Authorization: `Bearer ${res.access_token}`
+ }
});
-}
-export {
- exchangeCode
-}
\ No newline at end of file
+ const res3 = (
+ await axios.get('https://api.netlify.com/api/v1/accounts', {
+ headers: {
+ Authorization: `Bearer ${res.access_token}`
+ }
+ })
+ ).data;
+
+ accountId = res3[0].id;
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed OAuth2 code-token exchange with Netlify');
+ }
+
+ return {
+ accessToken: res.access_token,
+ refreshToken: res.refresh_token,
+ accountId
+ };
+};
+
+/**
+ * Return [accessToken], [accessExpiresAt], and [refreshToken] for Github
+ * code-token exchange
+ * @param {Object} obj1
+ * @param {Object} obj1.code - code for code-token exchange
+ * @returns {Object} obj2
+ * @returns {String} obj2.accessToken - access token for Github API
+ * @returns {String} obj2.refreshToken - refresh token for Github API
+ * @returns {Date} obj2.accessExpiresAt - date of expiration for access token
+ */
+const exchangeCodeGithub = async ({ code }: { code: string }) => {
+ let res: ExchangeCodeGithubResponse;
+ try {
+ res = (
+ await axios.get(INTEGRATION_GITHUB_TOKEN_URL, {
+ params: {
+ client_id: CLIENT_ID_GITHUB,
+ client_secret: CLIENT_SECRET_GITHUB,
+ code: code,
+ redirect_uri: `${SITE_URL}/github`
+ },
+ headers: {
+ Accept: 'application/json'
+ }
+ })
+ ).data;
+
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed OAuth2 code-token exchange with Github');
+ }
+
+ return {
+ accessToken: res.access_token,
+ refreshToken: null,
+ accessExpiresAt: null
+ };
+};
+
+export { exchangeCode };
diff --git a/backend/src/integrations/refresh.ts b/backend/src/integrations/refresh.ts
index 16870944d..8ddb6a651 100644
--- a/backend/src/integrations/refresh.ts
+++ b/backend/src/integrations/refresh.ts
@@ -13,44 +13,44 @@ import {
* named [integration]
* @param {Object} obj
* @param {String} obj.integration - name of integration
- * @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku
+ * @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku
*/
const exchangeRefresh = async ({
- integration,
- refreshToken
+ integration,
+ refreshToken
}: {
- integration: string;
- refreshToken: string;
+ integration: string;
+ refreshToken: string;
}) => {
- let accessToken;
- try {
- switch (integration) {
- case INTEGRATION_HEROKU:
- accessToken = await exchangeRefreshHeroku({
- refreshToken
- });
- break;
- }
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get new OAuth2 access token');
+ let accessToken;
+ try {
+ switch (integration) {
+ case INTEGRATION_HEROKU:
+ accessToken = await exchangeRefreshHeroku({
+ refreshToken
+ });
+ break;
}
-
- return accessToken;
-}
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get new OAuth2 access token');
+ }
+
+ return accessToken;
+};
/**
* Return new access token by exchanging refresh token [refreshToken] for the
* Heroku integration
* @param {Object} obj
* @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku
- * @returns
+ * @returns
*/
const exchangeRefreshHeroku = async ({
- refreshToken
+ refreshToken
}: {
- refreshToken: string;
+ refreshToken: string;
}) => {
let accessToken;
try {
@@ -63,16 +63,14 @@ const exchangeRefreshHeroku = async ({
} as any)
);
- accessToken = res.data.access_token;
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to get new OAuth2 access token for Heroku');
- }
-
- return accessToken;
-}
+ accessToken = res.data.access_token;
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to get new OAuth2 access token for Heroku');
+ }
-export {
- exchangeRefresh
-}
\ No newline at end of file
+ return accessToken;
+};
+
+export { exchangeRefresh };
diff --git a/backend/src/integrations/revoke.ts b/backend/src/integrations/revoke.ts
index 833e6c88a..483486343 100644
--- a/backend/src/integrations/revoke.ts
+++ b/backend/src/integrations/revoke.ts
@@ -1,50 +1,47 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
+import { IIntegrationAuth, IntegrationAuth, Integration } from '../models';
import {
- IIntegrationAuth,
- IntegrationAuth,
- Integration
-} from '../models';
-import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
} from '../variables';
const revokeAccess = async ({
- integrationAuth,
- accessToken
+ integrationAuth,
+ accessToken
}: {
- integrationAuth: IIntegrationAuth,
- accessToken: String
+ integrationAuth: IIntegrationAuth;
+ accessToken: string;
}) => {
- try {
- // add any integration-specific revocation logic
- switch (integrationAuth.integration) {
- case INTEGRATION_HEROKU:
- break;
- case INTEGRATION_VERCEL:
- break;
- case INTEGRATION_NETLIFY:
- break;
- }
-
- const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
- _id: integrationAuth._id
- });
-
- if (deletedIntegrationAuth) {
- await Integration.deleteMany({
- integrationAuth: deletedIntegrationAuth._id
- });
- }
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to delete integration authorization');
+ try {
+ // add any integration-specific revocation logic
+ switch (integrationAuth.integration) {
+ case INTEGRATION_HEROKU:
+ break;
+ case INTEGRATION_VERCEL:
+ break;
+ case INTEGRATION_NETLIFY:
+ break;
+ case INTEGRATION_GITHUB:
+ break;
}
-}
-export {
- revokeAccess
-}
\ No newline at end of file
+ const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
+ _id: integrationAuth._id
+ });
+
+ if (deletedIntegrationAuth) {
+ await Integration.deleteMany({
+ integrationAuth: deletedIntegrationAuth._id
+ });
+ }
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to delete integration authorization');
+ }
+};
+
+export { revokeAccess };
diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts
index 3cef519cc..30628fb9a 100644
--- a/backend/src/integrations/sync.ts
+++ b/backend/src/integrations/sync.ts
@@ -1,16 +1,21 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
+import { Octokit } from '@octokit/rest';
+// import * as sodium from 'libsodium-wrappers';
+import sodium from 'libsodium-wrappers';
+// const sodium = require('libsodium-wrappers');
+import { IIntegration, IIntegrationAuth } from '../models';
import {
- IIntegration, IIntegrationAuth
-} from '../models';
-import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_HEROKU_API_URL,
- INTEGRATION_VERCEL_API_URL,
- INTEGRATION_NETLIFY_API_URL
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_HEROKU_API_URL,
+ INTEGRATION_VERCEL_API_URL,
+ INTEGRATION_NETLIFY_API_URL,
+ INTEGRATION_GITHUB_API_URL
} from '../variables';
+import { access, appendFile } from 'fs';
// TODO: need a helper function in the future to handle integration
// envar priorities (i.e. prioritize secrets within integration or those on Infisical)
@@ -26,47 +31,54 @@ import {
* @param {String} obj.accessToken - access token for integration
*/
const syncSecrets = async ({
- integration,
- integrationAuth,
- secrets,
- accessToken,
+ integration,
+ integrationAuth,
+ secrets,
+ accessToken
}: {
- integration: IIntegration;
- integrationAuth: IIntegrationAuth;
- secrets: any;
- accessToken: string;
+ integration: IIntegration;
+ integrationAuth: IIntegrationAuth;
+ secrets: any;
+ accessToken: string;
}) => {
- try {
- switch (integration.integration) {
- case INTEGRATION_HEROKU:
- await syncSecretsHeroku({
- integration,
- secrets,
- accessToken
- });
- break;
- case INTEGRATION_VERCEL:
- await syncSecretsVercel({
- integration,
- secrets,
- accessToken
- });
- break;
- case INTEGRATION_NETLIFY:
- await syncSecretsNetlify({
- integration,
- integrationAuth,
- secrets,
- accessToken
- });
- break;
- }
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to sync secrets to integration');
+ try {
+ switch (integration.integration) {
+ case INTEGRATION_HEROKU:
+ await syncSecretsHeroku({
+ integration,
+ secrets,
+ accessToken
+ });
+ break;
+ case INTEGRATION_VERCEL:
+ await syncSecretsVercel({
+ integration,
+ secrets,
+ accessToken
+ });
+ break;
+ case INTEGRATION_NETLIFY:
+ await syncSecretsNetlify({
+ integration,
+ integrationAuth,
+ secrets,
+ accessToken
+ });
+ break;
+ case INTEGRATION_GITHUB:
+ await syncSecretsGitHub({
+ integration,
+ secrets,
+ accessToken
+ });
+ break;
}
-}
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to sync secrets to integration');
+ }
+};
/**
* Sync/push [secrets] to Heroku [app]
@@ -75,47 +87,49 @@ const syncSecrets = async ({
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
*/
const syncSecretsHeroku = async ({
- integration,
- secrets,
- accessToken
+ integration,
+ secrets,
+ accessToken
}: {
- integration: IIntegration,
- secrets: any;
- accessToken: string;
+ integration: IIntegration;
+ secrets: any;
+ accessToken: string;
}) => {
- try {
- const herokuSecrets = (await axios.get(
- `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
- {
- headers: {
- Accept: 'application/vnd.heroku+json; version=3',
- Authorization: `Bearer ${accessToken}`
- }
- }
- )).data;
-
- Object.keys(herokuSecrets).forEach(key => {
- if (!(key in secrets)) {
- secrets[key] = null;
- }
- });
+ try {
+ const herokuSecrets = (
+ await axios.get(
+ `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
+ {
+ headers: {
+ Accept: 'application/vnd.heroku+json; version=3',
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ )
+ ).data;
- await axios.patch(
- `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
- secrets,
- {
- headers: {
- Accept: 'application/vnd.heroku+json; version=3',
- Authorization: `Bearer ${accessToken}`
- }
- }
- );
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to sync secrets to Heroku');
- }
-}
+ Object.keys(herokuSecrets).forEach((key) => {
+ if (!(key in secrets)) {
+ secrets[key] = null;
+ }
+ });
+
+ await axios.patch(
+ `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
+ secrets,
+ {
+ headers: {
+ Accept: 'application/vnd.heroku+json; version=3',
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to sync secrets to Heroku');
+ }
+};
/**
* Sync/push [secrets] to Heroku [app]
@@ -174,9 +188,9 @@ const syncSecretsVercel = async ({
[secret.key]: secret
}), {});
- let updateSecrets: VercelSecret[] = [];
- let deleteSecrets: VercelSecret[] = [];
- let newSecrets: VercelSecret[] = [];
+ const updateSecrets: VercelSecret[] = [];
+ const deleteSecrets: VercelSecret[] = [];
+ const newSecrets: VercelSecret[] = [];
// Identify secrets to create
Object.keys(secrets).map((key) => {
@@ -287,8 +301,24 @@ const syncSecretsNetlify = async ({
accessToken: string;
}) => {
try {
+
+ interface NetlifyValue {
+ id?: string;
+ context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production',
+ value: string;
+ }
+
+ interface NetlifySecret {
+ key: string;
+ values: NetlifyValue[];
+ }
+
+ interface NetlifySecretsRes {
+ [index: string]: NetlifySecret;
+ }
+
const getParams = new URLSearchParams({
- context_name: integration.context,
+ context_name: 'all', // integration.context or all
site_id: integration.siteId
});
@@ -304,71 +334,94 @@ const syncSecretsNetlify = async ({
.data
.reduce((obj: any, secret: any) => ({
...obj,
- [secret.key]: secret.values[0].value
+ [secret.key]: secret
}), {});
- interface UpdateNetlifySecret {
- key: string;
- context: string;
- value: string;
- }
-
- interface DeleteNetlifySecret {
- key: string;
- }
-
- interface NewNetlifySecretValue {
- value: string;
- context: string;
- }
-
- interface NewNetlifySecret {
- key: string;
- values: NewNetlifySecretValue[];
- }
-
- let updateSecrets: UpdateNetlifySecret[] = [];
- let deleteSecrets: DeleteNetlifySecret[] = [];
- let newSecrets: NewNetlifySecret[] = [];
+ const newSecrets: NetlifySecret[] = []; // createEnvVars
+ const deleteSecrets: string[] = []; // deleteEnvVar
+ const deleteSecretValues: NetlifySecret[] = []; // deleteEnvVarValue
+ const updateSecrets: NetlifySecret[] = []; // setEnvVarValue
- // Identify secrets to create
+ // identify secrets to create and update
Object.keys(secrets).map((key) => {
if (!(key in res)) {
- // case: secret has been created
+ // case: Infisical secret does not exist in Netlify -> create secret
newSecrets.push({
- key: key,
+ key,
values: [{
- value: secrets[key], // include id?
+ value: secrets[key],
context: integration.context
}]
});
- }
- });
-
- // Identify secrets to update and delete
- Object.keys(res).map((key) => {
- if (key in secrets) {
- if (res[key] !== secrets[key]) {
- // case: secret value has changed
+ } else {
+ // case: Infisical secret exists in Netlify
+ const contexts = res[key].values
+ .reduce((obj: any, value: NetlifyValue) => ({
+ ...obj,
+ [value.context]: value
+ }), {});
+
+ if (integration.context in contexts) {
+ // case: Netlify secret value exists in integration context
+ if (secrets[key] !== contexts[integration.context].value) {
+ // case: Infisical and Netlify secret values are different
+ // -> update Netlify secret context and value
+ updateSecrets.push({
+ key,
+ values: [{
+ context: integration.context,
+ value: secrets[key]
+ }]
+ });
+ }
+ } else {
+ // case: Netlify secret value does not exist in integration context
+ // -> add the new Netlify secret context and value
updateSecrets.push({
- key: key,
- context: integration.context,
- value: secrets[key]
+ key,
+ values: [{
+ context: integration.context,
+ value: secrets[key]
+ }]
});
}
- } else {
- // case: secret has been deleted
- deleteSecrets.push({
- key
+ }
+ })
+
+ // identify secrets to delete
+ // TODO: revise (patch case where 1 context was deleted but others still there
+ Object.keys(res).map((key) => {
+ // loop through each key's context
+ if (!(key in secrets)) {
+ // case: Netlify secret does not exist in Infisical
+
+ const numberOfValues = res[key].values.length;
+
+ res[key].values.forEach((value: NetlifyValue) => {
+ if (value.context === integration.context) {
+ if (numberOfValues <= 1) {
+ // case: Netlify secret value has less than 1 context -> delete secret
+ deleteSecrets.push(key);
+ } else {
+ // case: Netlify secret value has more than 1 context -> delete secret value context
+ deleteSecretValues.push({
+ key,
+ values: [{
+ id: value.id,
+ context: integration.context,
+ value: value.value
+ }]
+ });
+ }
+ }
});
}
});
-
+
const syncParams = new URLSearchParams({
site_id: integration.siteId
});
- // Sync/push new secrets
if (newSecrets.length > 0) {
await axios.post(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
@@ -382,15 +435,13 @@ const syncSecretsNetlify = async ({
);
}
- // Sync/push updated secrets
if (updateSecrets.length > 0) {
-
- updateSecrets.forEach(async (secret: UpdateNetlifySecret) => {
+ updateSecrets.forEach(async (secret: NetlifySecret) => {
await axios.patch(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
{
- context: secret.context,
- value: secret.value
+ context: secret.values[0].context,
+ value: secret.values[0].value
},
{
params: syncParams,
@@ -402,11 +453,24 @@ const syncSecretsNetlify = async ({
});
}
- // Delete secrets
if (deleteSecrets.length > 0) {
- deleteSecrets.forEach(async (secret: DeleteNetlifySecret) => {
+ deleteSecrets.forEach(async (key: string) => {
await axios.delete(
- `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`,
+ {
+ params: syncParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
+ }
+
+ if (deleteSecretValues.length > 0) {
+ deleteSecretValues.forEach(async (secret: NetlifySecret) => {
+ await axios.delete(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`,
{
params: syncParams,
headers: {
@@ -416,7 +480,6 @@ const syncSecretsNetlify = async ({
);
});
}
-
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
@@ -424,6 +487,119 @@ const syncSecretsNetlify = async ({
}
}
-export {
- syncSecrets
-}
\ No newline at end of file
+/**
+ * Sync/push [secrets] to GitHub [repo]
+ * @param {Object} obj
+ * @param {IIntegration} obj.integration - integration details
+ * @param {IIntegrationAuth} obj.integrationAuth - integration auth details
+ * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
+ */
+const syncSecretsGitHub = async ({
+ integration,
+ secrets,
+ accessToken
+}: {
+ integration: IIntegration;
+ secrets: any;
+ accessToken: string;
+}) => {
+ try {
+
+ interface GitHubRepoKey {
+ key_id: string;
+ key: string;
+ }
+
+ interface GitHubSecret {
+ name: string;
+ created_at: string;
+ updated_at: string;
+ }
+
+ interface GitHubSecretRes {
+ [index: string]: GitHubSecret;
+ }
+
+ const deleteSecrets: GitHubSecret[] = [];
+
+ const octokit = new Octokit({
+ auth: accessToken
+ });
+
+ const user = (await octokit.request('GET /user', {})).data;
+
+ const repoPublicKey: GitHubRepoKey = (await octokit.request(
+ 'GET /repos/{owner}/{repo}/actions/secrets/public-key',
+ {
+ owner: user.login,
+ repo: integration.app
+ }
+ )).data;
+
+ // // Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
+ const encryptedSecrets: GitHubSecretRes = (await octokit.request(
+ 'GET /repos/{owner}/{repo}/actions/secrets',
+ {
+ owner: user.login,
+ repo: integration.app
+ }
+ ))
+ .data
+ .secrets
+ .reduce((obj: any, secret: any) => ({
+ ...obj,
+ [secret.name]: secret
+ }), {});
+
+ Object.keys(encryptedSecrets).map(async (key) => {
+ if (!(key in secrets)) {
+ await octokit.request(
+ 'DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}',
+ {
+ owner: user.login,
+ repo: integration.app,
+ secret_name: key
+ }
+ );
+ }
+ });
+
+ Object.keys(secrets).map((key) => {
+ // let encryptedSecret;
+ sodium.ready.then(async () => {
+ // convert secret & base64 key to Uint8Array.
+ const binkey = sodium.from_base64(
+ repoPublicKey.key,
+ sodium.base64_variants.ORIGINAL
+ );
+ const binsec = sodium.from_string(secrets[key]);
+
+ // encrypt secret using libsodium
+ const encBytes = sodium.crypto_box_seal(binsec, binkey);
+
+ // convert encrypted Uint8Array to base64
+ const encryptedSecret = sodium.to_base64(
+ encBytes,
+ sodium.base64_variants.ORIGINAL
+ );
+
+ await octokit.request(
+ 'PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}',
+ {
+ owner: user.login,
+ repo: integration.app,
+ secret_name: key,
+ encrypted_value: encryptedSecret,
+ key_id: repoPublicKey.key_id
+ }
+ );
+ });
+ });
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to sync secrets to GitHub');
+ }
+};
+
+export { syncSecrets };
\ No newline at end of file
diff --git a/backend/src/models/integration.ts b/backend/src/models/integration.ts
index edbe0234e..6da699216 100644
--- a/backend/src/models/integration.ts
+++ b/backend/src/models/integration.ts
@@ -1,77 +1,83 @@
import { Schema, model, Types } from 'mongoose';
import {
- ENV_DEV,
- ENV_TESTING,
- ENV_STAGING,
- ENV_PROD,
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
+ ENV_DEV,
+ ENV_TESTING,
+ ENV_STAGING,
+ ENV_PROD,
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
} from '../variables';
export interface IIntegration {
- _id: Types.ObjectId;
- workspace: Types.ObjectId;
- environment: 'dev' | 'test' | 'staging' | 'prod';
- isActive: boolean;
- app: string;
- target: string;
- context: string;
- siteId: string;
- integration: 'heroku' | 'vercel' | 'netlify';
- integrationAuth: Types.ObjectId;
+ _id: Types.ObjectId;
+ workspace: Types.ObjectId;
+ environment: 'dev' | 'test' | 'staging' | 'prod';
+ isActive: boolean;
+ app: string;
+ target: string;
+ context: string;
+ siteId: string;
+ integration: 'heroku' | 'vercel' | 'netlify' | 'github';
+ integrationAuth: Types.ObjectId;
}
const integrationSchema = new Schema(
- {
- workspace: {
- type: Schema.Types.ObjectId,
- ref: 'Workspace',
- required: true
- },
- environment: {
- type: String,
- enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD],
- required: true
- },
- isActive: {
- type: Boolean,
- required: true
- },
- app: { // name of app in provider
- type: String,
- default: null
- },
- target: { // vercel-specific target (environment)
- type: String,
- default: null
- },
- context: { // netlify-specific context (deploy)
- type: String,
- default: null
- },
- siteId: { // netlify-specific site (app) id
- type: String,
- default: null
- },
- integration: {
- type: String,
- enum: [
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
- ],
- required: true
- },
- integrationAuth: {
- type: Schema.Types.ObjectId,
- ref: 'IntegrationAuth',
- required: true
- }
- },
- {
- timestamps: true
- }
+ {
+ workspace: {
+ type: Schema.Types.ObjectId,
+ ref: 'Workspace',
+ required: true
+ },
+ environment: {
+ type: String,
+ enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD],
+ required: true
+ },
+ isActive: {
+ type: Boolean,
+ required: true
+ },
+ app: {
+ // name of app in provider
+ type: String,
+ default: null
+ },
+ target: {
+ // vercel-specific target (environment)
+ type: String,
+ default: null
+ },
+ context: {
+ // netlify-specific context (deploy)
+ type: String,
+ default: null
+ },
+ siteId: {
+ // netlify-specific site (app) id
+ type: String,
+ default: null
+ },
+ integration: {
+ type: String,
+ enum: [
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
+ ],
+ required: true
+ },
+ integrationAuth: {
+ type: Schema.Types.ObjectId,
+ ref: 'IntegrationAuth',
+ required: true
+ }
+ },
+ {
+ timestamps: true
+ }
);
const Integration = model('Integration', integrationSchema);
diff --git a/backend/src/models/integrationAuth.ts b/backend/src/models/integrationAuth.ts
index 0da3eb0d8..231416588 100644
--- a/backend/src/models/integrationAuth.ts
+++ b/backend/src/models/integrationAuth.ts
@@ -1,83 +1,87 @@
import { Schema, model, Types } from 'mongoose';
-import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
+import {
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
} from '../variables';
export interface IIntegrationAuth {
- _id: Types.ObjectId;
- workspace: Types.ObjectId;
- integration: 'heroku' | 'vercel' | 'netlify';
- teamId: string;
- accountId: string;
- refreshCiphertext?: string;
- refreshIV?: string;
- refreshTag?: string;
- accessCiphertext?: string;
- accessIV?: string;
- accessTag?: string;
- accessExpiresAt?: Date;
+ _id: Types.ObjectId;
+ workspace: Types.ObjectId;
+ integration: 'heroku' | 'vercel' | 'netlify' | 'github';
+ teamId: string;
+ accountId: string;
+ refreshCiphertext?: string;
+ refreshIV?: string;
+ refreshTag?: string;
+ accessCiphertext?: string;
+ accessIV?: string;
+ accessTag?: string;
+ accessExpiresAt?: Date;
}
const integrationAuthSchema = new Schema(
- {
- workspace: {
- type: Schema.Types.ObjectId,
- required: true
- },
- integration: {
- type: String,
- enum: [
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
- ],
- required: true
- },
- teamId: { // vercel-specific integration param
- type: String
- },
- accountId: { // netlify-specific integration param
- type: String
- },
- refreshCiphertext: {
- type: String,
- select: false
- },
- refreshIV: {
- type: String,
- select: false
- },
- refreshTag: {
- type: String,
- select: false
- },
- accessCiphertext: {
- type: String,
- select: false
- },
- accessIV: {
- type: String,
- select: false
- },
- accessTag: {
- type: String,
- select: false
- },
- accessExpiresAt: {
- type: Date,
- select: false
- }
- },
- {
- timestamps: true
- }
+ {
+ workspace: {
+ type: Schema.Types.ObjectId,
+ required: true
+ },
+ integration: {
+ type: String,
+ enum: [
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
+ ],
+ required: true
+ },
+ teamId: {
+ // vercel-specific integration param
+ type: String
+ },
+ accountId: {
+ // netlify-specific integration param
+ type: String
+ },
+ refreshCiphertext: {
+ type: String,
+ select: false
+ },
+ refreshIV: {
+ type: String,
+ select: false
+ },
+ refreshTag: {
+ type: String,
+ select: false
+ },
+ accessCiphertext: {
+ type: String,
+ select: false
+ },
+ accessIV: {
+ type: String,
+ select: false
+ },
+ accessTag: {
+ type: String,
+ select: false
+ },
+ accessExpiresAt: {
+ type: Date,
+ select: false
+ }
+ },
+ {
+ timestamps: true
+ }
);
const IntegrationAuth = model(
- 'IntegrationAuth',
- integrationAuthSchema
+ 'IntegrationAuth',
+ integrationAuthSchema
);
export default IntegrationAuth;
diff --git a/backend/src/routes/key.ts b/backend/src/routes/key.ts
index 9541ca123..a67a729b1 100644
--- a/backend/src/routes/key.ts
+++ b/backend/src/routes/key.ts
@@ -34,6 +34,4 @@ router.get(
keyController.getLatestKey
);
-router.get('/publicKey/infisical', keyController.getPublicKeyInfisical);
-
export default router;
diff --git a/backend/src/services/database.ts b/backend/src/services/database.ts
new file mode 100644
index 000000000..5fc4955f1
--- /dev/null
+++ b/backend/src/services/database.ts
@@ -0,0 +1,10 @@
+/* eslint-disable no-console */
+import mongoose from 'mongoose';
+
+export const initDatabase = (MONGO_URL: string) => {
+ mongoose
+ .connect(MONGO_URL)
+ .then(() => console.log('Successfully connected to DB'))
+ .catch((e) => console.log('Failed to connect to DB ', e));
+ return mongoose.connection;
+};
diff --git a/backend/src/services/health.ts b/backend/src/services/health.ts
new file mode 100644
index 000000000..b4ab2a97b
--- /dev/null
+++ b/backend/src/services/health.ts
@@ -0,0 +1,32 @@
+/* eslint-disable no-console */
+import mongoose from 'mongoose';
+import { createTerminus } from '@godaddy/terminus';
+
+export const setUpHealthEndpoint = (server: T) => {
+ const onSignal = () => {
+ console.log('Server is starting clean-up');
+ return Promise.all([
+ new Promise((resolve) => {
+ if (mongoose.connection && mongoose.connection.readyState == 1) {
+ mongoose.connection.close()
+ .then(() => resolve('Database connection closed'));
+ } else {
+ resolve('Database connection already closed');
+ }
+ })
+ ]);
+ };
+
+ const healthCheck = () => {
+ // `state.isShuttingDown` (boolean) shows whether the server is shutting down or not
+ // optionally include a resolve value to be included as info in the health check response
+ return Promise.resolve();
+ };
+
+ createTerminus(server, {
+ healthChecks: {
+ '/healthcheck': healthCheck,
+ onSignal
+ }
+ });
+};
diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts
new file mode 100644
index 000000000..14d543439
--- /dev/null
+++ b/backend/src/services/smtp.ts
@@ -0,0 +1,34 @@
+import nodemailer from 'nodemailer';
+import { SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SECURE } from '../config';
+import SMTPConnection from 'nodemailer/lib/smtp-connection';
+import * as Sentry from '@sentry/node';
+
+const mailOpts: SMTPConnection.Options = {
+ host: SMTP_HOST,
+ secure: SMTP_SECURE as boolean,
+ port: SMTP_PORT as number
+};
+if (SMTP_USERNAME && SMTP_PASSWORD) {
+ mailOpts.auth = {
+ user: SMTP_USERNAME,
+ pass: SMTP_PASSWORD
+ };
+}
+
+export const initSmtp = () => {
+ const transporter = nodemailer.createTransport(mailOpts);
+ transporter
+ .verify()
+ .then(() => {
+ Sentry.setUser(null);
+ Sentry.captureMessage('SMTP - Successfully connected');
+ })
+ .catch((err) => {
+ Sentry.setUser(null);
+ Sentry.captureException(
+ `SMTP - Failed to connect to ${SMTP_HOST}:${SMTP_PORT} \n\t${err}`
+ );
+ });
+
+ return transporter;
+};
diff --git a/backend/src/utils/crypto.ts b/backend/src/utils/crypto.ts
index 585dae51d..28f96b0cf 100644
--- a/backend/src/utils/crypto.ts
+++ b/backend/src/utils/crypto.ts
@@ -1,6 +1,7 @@
import nacl from 'tweetnacl';
import util from 'tweetnacl-util';
import AesGCM from './aes-gcm';
+import * as Sentry from '@sentry/node';
/**
* Return new base64, NaCl, public-private key pair.
@@ -47,6 +48,8 @@ const encryptAsymmetric = ({
util.decodeBase64(privateKey)
);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform asymmetric encryption');
}
@@ -86,6 +89,8 @@ const decryptAsymmetric = ({
util.decodeBase64(privateKey)
);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform asymmetric decryption');
}
@@ -112,6 +117,8 @@ const encryptSymmetric = ({
iv = obj.iv;
tag = obj.tag;
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform symmetric encryption');
}
@@ -147,6 +154,8 @@ const decryptSymmetric = ({
try {
plaintext = AesGCM.decrypt(ciphertext, iv, tag, key);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform symmetric decryption');
}
diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts
index b21324423..c69ed8176 100644
--- a/backend/src/variables/index.ts
+++ b/backend/src/variables/index.ts
@@ -1,79 +1,74 @@
import {
- ENV_DEV,
- ENV_TESTING,
- ENV_STAGING,
- ENV_PROD,
- ENV_SET
+ ENV_DEV,
+ ENV_TESTING,
+ ENV_STAGING,
+ ENV_PROD,
+ ENV_SET
} from './environment';
import {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_SET,
- INTEGRATION_OAUTH2,
- INTEGRATION_HEROKU_TOKEN_URL,
- INTEGRATION_VERCEL_TOKEN_URL,
- INTEGRATION_NETLIFY_TOKEN_URL,
- INTEGRATION_HEROKU_API_URL,
- INTEGRATION_VERCEL_API_URL,
- INTEGRATION_NETLIFY_API_URL,
- INTEGRATION_OPTIONS
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_SET,
+ INTEGRATION_OAUTH2,
+ INTEGRATION_HEROKU_TOKEN_URL,
+ INTEGRATION_VERCEL_TOKEN_URL,
+ INTEGRATION_NETLIFY_TOKEN_URL,
+ INTEGRATION_GITHUB_TOKEN_URL,
+ INTEGRATION_HEROKU_API_URL,
+ INTEGRATION_VERCEL_API_URL,
+ INTEGRATION_NETLIFY_API_URL,
+ INTEGRATION_GITHUB_API_URL,
+ INTEGRATION_OPTIONS
} from './integration';
import {
- OWNER,
- ADMIN,
- MEMBER,
- INVITED,
- ACCEPTED,
- COMPLETED,
- GRANTED
+ OWNER,
+ ADMIN,
+ MEMBER,
+ INVITED,
+ ACCEPTED,
+ COMPLETED,
+ GRANTED
} from './organization';
-import {
- SECRET_SHARED,
- SECRET_PERSONAL
-} from './secret';
-import {
- PLAN_STARTER,
- PLAN_PRO
-} from './stripe';
-import {
- EVENT_PUSH_SECRETS,
- EVENT_PULL_SECRETS
-} from './event';
-import {
- ACTION_PUSH_TO_HEROKU
-} from './action';
+import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
+import { PLAN_STARTER, PLAN_PRO } from './stripe';
+import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
+import { ACTION_PUSH_TO_HEROKU } from './action';
export {
- OWNER,
- ADMIN,
- MEMBER,
- INVITED,
- ACCEPTED,
- COMPLETED,
- GRANTED,
- PLAN_STARTER,
- PLAN_PRO,
- SECRET_SHARED,
- SECRET_PERSONAL,
- ENV_DEV,
- ENV_TESTING,
- ENV_STAGING,
- ENV_PROD,
- ENV_SET,
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_SET,
- INTEGRATION_OAUTH2,
- INTEGRATION_HEROKU_TOKEN_URL,
- INTEGRATION_VERCEL_TOKEN_URL,
- INTEGRATION_NETLIFY_TOKEN_URL,
- INTEGRATION_HEROKU_API_URL,
- INTEGRATION_VERCEL_API_URL,
- INTEGRATION_NETLIFY_API_URL,
- EVENT_PUSH_SECRETS,
- EVENT_PULL_SECRETS,
- ACTION_PUSH_TO_HEROKU,
- INTEGRATION_OPTIONS
-};
\ No newline at end of file
+ OWNER,
+ ADMIN,
+ MEMBER,
+ INVITED,
+ ACCEPTED,
+ COMPLETED,
+ GRANTED,
+ PLAN_STARTER,
+ PLAN_PRO,
+ SECRET_SHARED,
+ SECRET_PERSONAL,
+ ENV_DEV,
+ ENV_TESTING,
+ ENV_STAGING,
+ ENV_PROD,
+ ENV_SET,
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_SET,
+ INTEGRATION_OAUTH2,
+ INTEGRATION_HEROKU_TOKEN_URL,
+ INTEGRATION_VERCEL_TOKEN_URL,
+ INTEGRATION_NETLIFY_TOKEN_URL,
+ INTEGRATION_GITHUB_TOKEN_URL,
+ INTEGRATION_HEROKU_API_URL,
+ INTEGRATION_VERCEL_API_URL,
+ INTEGRATION_NETLIFY_API_URL,
+ INTEGRATION_GITHUB_API_URL,
+ EVENT_PUSH_SECRETS,
+ EVENT_PULL_SECRETS,
+ ACTION_PUSH_TO_HEROKU,
+ INTEGRATION_OPTIONS
+};
diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts
index c02a2389f..00e817c57 100644
--- a/backend/src/variables/integration.ts
+++ b/backend/src/variables/integration.ts
@@ -1,16 +1,20 @@
import {
CLIENT_ID_HEROKU,
- CLIENT_ID_NETLIFY
+ CLIENT_ID_NETLIFY,
+ CLIENT_ID_GITHUB,
+ CLIENT_SLUG_VERCEL
} from '../config';
// integrations
const INTEGRATION_HEROKU = 'heroku';
const INTEGRATION_VERCEL = 'vercel';
const INTEGRATION_NETLIFY = 'netlify';
+const INTEGRATION_GITHUB = 'github';
const INTEGRATION_SET = new Set([
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB
]);
// integration types
@@ -18,13 +22,17 @@ const INTEGRATION_OAUTH2 = 'oauth2';
// integration oauth endpoints
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
-const INTEGRATION_VERCEL_TOKEN_URL = 'https://api.vercel.com/v2/oauth/access_token';
+const INTEGRATION_VERCEL_TOKEN_URL =
+ 'https://api.vercel.com/v2/oauth/access_token';
const INTEGRATION_NETLIFY_TOKEN_URL = 'https://api.netlify.com/oauth/token';
+const INTEGRATION_GITHUB_TOKEN_URL =
+ 'https://github.com/login/oauth/access_token';
// integration apps endpoints
const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com';
const INTEGRATION_VERCEL_API_URL = 'https://api.vercel.com';
const INTEGRATION_NETLIFY_API_URL = 'https://api.netlify.com';
+const INTEGRATION_GITHUB_API_URL = 'https://api.github.com';
const INTEGRATION_OPTIONS = [
{
@@ -43,6 +51,7 @@ const INTEGRATION_OPTIONS = [
isAvailable: true,
type: 'vercel',
clientId: '',
+ clientSlug: CLIENT_SLUG_VERCEL,
docsLink: ''
},
{
@@ -54,6 +63,16 @@ const INTEGRATION_OPTIONS = [
clientId: CLIENT_ID_NETLIFY,
docsLink: ''
},
+ {
+ name: 'GitHub',
+ slug: 'github',
+ image: 'GitHub',
+ isAvailable: true,
+ type: 'oauth2',
+ clientId: CLIENT_ID_GITHUB,
+ docsLink: ''
+
+ },
{
name: 'Google Cloud Platform',
slug: 'gcp',
@@ -102,16 +121,19 @@ const INTEGRATION_OPTIONS = [
]
export {
- INTEGRATION_HEROKU,
- INTEGRATION_VERCEL,
- INTEGRATION_NETLIFY,
- INTEGRATION_SET,
- INTEGRATION_OAUTH2,
- INTEGRATION_HEROKU_TOKEN_URL,
- INTEGRATION_VERCEL_TOKEN_URL,
- INTEGRATION_NETLIFY_TOKEN_URL,
- INTEGRATION_HEROKU_API_URL,
- INTEGRATION_VERCEL_API_URL,
- INTEGRATION_NETLIFY_API_URL,
- INTEGRATION_OPTIONS
-}
\ No newline at end of file
+ INTEGRATION_HEROKU,
+ INTEGRATION_VERCEL,
+ INTEGRATION_NETLIFY,
+ INTEGRATION_GITHUB,
+ INTEGRATION_SET,
+ INTEGRATION_OAUTH2,
+ INTEGRATION_HEROKU_TOKEN_URL,
+ INTEGRATION_VERCEL_TOKEN_URL,
+ INTEGRATION_NETLIFY_TOKEN_URL,
+ INTEGRATION_GITHUB_TOKEN_URL,
+ INTEGRATION_HEROKU_API_URL,
+ INTEGRATION_VERCEL_API_URL,
+ INTEGRATION_NETLIFY_API_URL,
+ INTEGRATION_GITHUB_API_URL,
+ INTEGRATION_OPTIONS
+};
diff --git a/backend/test-resources/docker-compose.test.yml b/backend/test-resources/docker-compose.test.yml
new file mode 100644
index 000000000..e9a8c519a
--- /dev/null
+++ b/backend/test-resources/docker-compose.test.yml
@@ -0,0 +1,12 @@
+version: '3'
+
+services:
+ mongo-test:
+ image: mongo
+ container_name: infisical-test-mongo
+ restart: always
+ ports:
+ - 27018:27017
+ environment:
+ - MONGO_INITDB_ROOT_USERNAME=test
+ - MONGO_INITDB_ROOT_PASSWORD=test1234
diff --git a/backend/test-resources/env-vars.js b/backend/test-resources/env-vars.js
new file mode 100644
index 000000000..a7542728f
--- /dev/null
+++ b/backend/test-resources/env-vars.js
@@ -0,0 +1,5 @@
+/* eslint-disable no-undef */
+process.env.MONGO_URL =
+ 'mongodb://test:test1234@localhost:27018/?authSource=admin';
+process.env.MONGO_USERNAME = 'test';
+process.env.MONGO_PASSWORD = 'test1234';
diff --git a/backend/tsconfig.json b/backend/tsconfig.json
index d6d293372..0bfe3c372 100644
--- a/backend/tsconfig.json
+++ b/backend/tsconfig.json
@@ -8,16 +8,13 @@
"allowJs": true,
"outDir": "build",
"esModuleInterop": true,
+ "moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
- "typeRoots" : ["./src/types", "./node_modules/@types"]
+ "typeRoots": ["./src/types", "./node_modules/@types"]
},
- "include": [
- "src/**/*"
- ],
- "exclude": [
- "node_modules"
- ]
+ "include": ["src/**/*"],
+ "exclude": ["node_modules"]
}
diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go
index ec5210b1f..7518fe98d 100644
--- a/cli/packages/cmd/run.go
+++ b/cli/packages/cmd/run.go
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"os/signal"
+ "runtime"
"strings"
"syscall"
@@ -19,12 +20,38 @@ import (
// runCmd represents the run command
var runCmd = &cobra.Command{
+ Example: `
+ infisical run --env=dev -- npm run dev
+ infisical run --command "first-command && second-command; more-commands..."
+ `,
Use: "run [any infisical run command flags] -- [your application start command]",
Short: "Used to inject environments variables into your application process",
DisableFlagsInUseLine: true,
- Example: "infisical run --env=prod -- npm run dev",
- Args: cobra.MinimumNArgs(1),
PreRun: toggleDebug,
+ Args: func(cmd *cobra.Command, args []string) error {
+ // Check if the --command flag has been set
+ commandFlagSet := cmd.Flags().Changed("command")
+
+ // If the --command flag has been set, check if a value was provided
+ if commandFlagSet {
+ command := cmd.Flag("command").Value.String()
+ if command == "" {
+ return fmt.Errorf("you need to provide a command after the flag --command")
+ }
+
+ // If the --command flag has been set, args should not be provided
+ if len(args) > 0 {
+ return fmt.Errorf("you cannot set any arguments after --command flag. --command only takes a string command")
+ }
+ } else {
+ // If the --command flag has not been set, at least one arg should be provided
+ if len(args) == 0 {
+ return fmt.Errorf("at least one argument is required after the run command, received %d", len(args))
+ }
+ }
+
+ return nil
+ },
Run: func(cmd *cobra.Command, args []string) {
envName, err := cmd.Flags().GetString("env")
if err != nil {
@@ -54,10 +81,23 @@ var runCmd = &cobra.Command{
}
if shouldExpandSecrets {
- secretsWithSubstitutions := util.SubstituteSecrets(secrets)
- execCmd(args[0], args[1:], secretsWithSubstitutions)
+ secrets = util.SubstituteSecrets(secrets)
+ }
+
+ if cmd.Flags().Changed("command") {
+ command := cmd.Flag("command").Value.String()
+ err = executeMultipleCommandWithEnvs(command, secrets)
+ if err != nil {
+ log.Errorf("Something went wrong when executing your command [error=%s]", err)
+ return
+ }
} else {
- execCmd(args[0], args[1:], secrets)
+ err = executeSingleCommandWithEnvs(args, secrets)
+ if err != nil {
+ log.Errorf("Something went wrong when executing your command [error=%s]", err)
+ return
+ }
+ return
}
},
@@ -68,22 +108,51 @@ func init() {
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from")
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
+ runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")")
}
-// Credit: inspired by AWS Valut
-func execCmd(command string, args []string, envs []models.SingleEnvironmentVariable) error {
- numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(envs))
-
+// Will execute a single command and pass in the given secrets into the process
+func executeSingleCommandWithEnvs(args []string, secrets []models.SingleEnvironmentVariable) error {
+ command := args[0]
+ argsForCommand := args[1:]
+ numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
- log.Debugf("executing command: %s %s \n", command, strings.Join(args, " "))
- log.Debugln("Secrets injected:", envs)
+ log.Debugf("executing command: %s %s \n", command, strings.Join(argsForCommand, " "))
+ log.Debugln("Secrets injected:", secrets)
- cmd := exec.Command(command, args...)
+ cmd := exec.Command(command, argsForCommand...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- cmd.Env = getAllEnvs(envs)
+ cmd.Env = getAllEnvs(secrets)
+ return execCmd(cmd)
+}
+
+func executeMultipleCommandWithEnvs(fullCommand string, secrets []models.SingleEnvironmentVariable) error {
+ shell := [2]string{"sh", "-c"}
+ if runtime.GOOS == "windows" {
+ shell = [2]string{"cmd", "/C"}
+ } else {
+ shell[0] = os.Getenv("SHELL")
+ }
+
+ cmd := exec.Command(shell[0], shell[1], fullCommand)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Env = getAllEnvs(secrets)
+
+ numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
+ log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
+ log.Debugf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand)
+ log.Debugln("Secrets injected:", secrets)
+
+ return execCmd(cmd)
+}
+
+// Credit: inspired by AWS Valut
+func execCmd(cmd *exec.Cmd) error {
sigChannel := make(chan os.Signal, 1)
signal.Notify(sigChannel)
@@ -100,7 +169,7 @@ func execCmd(command string, args []string, envs []models.SingleEnvironmentVaria
if err := cmd.Wait(); err != nil {
_ = cmd.Process.Signal(os.Kill)
- return fmt.Errorf("Failed to wait for command termination: %v", err)
+ return fmt.Errorf("failed to wait for command termination: %v", err)
}
waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus)
diff --git a/docker-compose.yml b/docker-compose.yml
index 206e7afa3..bd9022cef 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,7 +9,7 @@ services:
- 80:80
- 443:443
volumes:
- - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
+ - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- frontend
- backend
diff --git a/docs/cli/commands/export.mdx b/docs/cli/commands/export.mdx
index fd58868ff..b80fb7470 100644
--- a/docs/cli/commands/export.mdx
+++ b/docs/cli/commands/export.mdx
@@ -30,4 +30,7 @@ infisical export --format=csv > secrets.csv
# Export variables to a JSON file
infisical export --format=json > secrets.json
+
+# Export variables to a YAML file
+infisical export --format=yaml > secrets.yaml
```
diff --git a/docs/cli/commands/run.mdx b/docs/cli/commands/run.mdx
index 7fb207612..2c65ef53e 100644
--- a/docs/cli/commands/run.mdx
+++ b/docs/cli/commands/run.mdx
@@ -2,9 +2,25 @@
title: "infisical run"
---
-```bash
-infisical run [options] -- [your application start command]
-```
+
+
+ ```bash
+ infisical run [options] -- [your application start command]
+
+ # Example
+ infisical run [options] -- npm run dev
+ ```
+
+
+
+ ```bash
+ infisical run [options] --command [string command]
+
+ # Example
+ infisical run [options] --command "npm run bootstrap && npm run dev start; other-bash-command"
+ ```
+
+
## Description
@@ -15,5 +31,6 @@ Inject environment variables from the platform into an application process.
| Option | Description | Default value |
| -------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` |
-| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | `None` |
+| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | None |
| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` |
+| `--command` | Pass secrets into chained commands (e.g., `"first-command && second-command; more-commands..."`) | None |
diff --git a/docs/contributing/developing.mdx b/docs/contributing/developing.mdx
index 3be1a1b6b..812025dbc 100644
--- a/docs/contributing/developing.mdx
+++ b/docs/contributing/developing.mdx
@@ -16,59 +16,54 @@ cd infisical
## Set up environment variables
-Before running the docker-compose we have to generate the .env file with the environment variables, you can create your own file or start with the
-`.env.example` as an example guide.
+Start by creating a .env file at the root of the Infisical directory
-Mandatory variables in the `.env` file:
+
+ Reference the [environment variable list](https://infisical.com/docs/self-hosting/configuration/envars) and provided [`.env.example`](https://raw.githubusercontent.com/Infisical/infisical/main/.env.example) template to fill out your .env file.
+
-1. Keys and JWT variables
+### Keys
-
+`ENCRYPTION_KEY`, `JWT_SIGNUP_SECRET`, `JWT_REFRESH_SECRET`, `JWT_AUTH_SECRET`, `JWT_SERVICE_SECRET` values can be generated with this [32-byte random hex generator](https://www.browserling.com/tools/random-hex).
-The `.env.example` has these variables empty, you can self generate the `JWT and ENCRYPTION_KEY` with this [32-byte random hex strings generator](https://www.browserling.com/tools/random-hex).
+### Database
-For the `PRIVATE_KEY and PUBLIC_KEY` you can use the ones shown in the screenshot:
+Use to the following `MONGO_URL`, `MONGO_USERNAME`, `MONGO_PASSWORD`, `SITE_URL` values:
```
-PRIVATE_KEY='oGVv5rThrpZ7WLgQW27chY1cXngr4wLQIZnGfSKgHPk='
-PUBLIC_KEY='ldr6JaC7AY+tun3omGLdE4SWpkJbtVBOI54KfUP53Xc='
+MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
+MONGO_USERNAME=root
+MONGO_PASSWORD=example
+
+SITE_URL=http://localhost:8080
```
-2. Mongo variables and site URL
+
+ If you decide to use your own `MONGO_USERNAME` and `MONGO_PASSWORD`, you'll have to modify `MONGO_URL` to take the form: `mongodb://[MONGO_USERNAME]:[MONGO_PASSWORD]@mongo:27017/?authSource=admin`.
+
-
+### Mailing
-These variables are used to connect the MongoDB and set the URL for the localhost.
+Option 1: Bring your own SMTP server and credentials by filling in `SMTP_HOST`, `SMTP_FROM_ADDRESS`, `SMTP_FROM_NAME`, `SMTP_USERNAME`, and `SMTP_PASSWORD`.
+
+ `SMTP_HOST` is set to `smtp.gmail.com` by default. For `SMTP_USERNAME` and `SMTP_PASSWORD`, you'll need an email with 2-step-verification and an [app password](https://support.google.com/mail/answer/185833?hl=en) for it.
+
-For development, you can use `root` for the `MONGO_USERNAME` and `example` for the `MONGO_PASSWORD` as shown in the screenshot.
-Take into account that if you use your own `MONGO_USERNAME` and `MONGO_PASSWORD`, you also have to change the `MONGO_URL` with the form of `MONGO_USERNAME:MONGO_PASSWORD` after the `//` part of the URL.
-
-3. Mail SMTP service variables
-
-
-
-If you want to receive actual emails (e.g. you want to test how the email message will look like), take note of the following.
-
-For the `SMTP_USERNAME` variable, you will need an email with 2-steps-verification.
-
-For the `SMTP_PASSWORD` variable, you will need to [generate an app password](https://support.google.com/mail/answer/185833?hl=en) with the email you used in the `SMTP_USERNAME` variable.
-
-Otherwise, a local SMTP server (MailHog) is available for testing purposes. Set the following values to use this:
+Option 2: Use the provided (Mailhog) SMTP server and browse emails sent by the backend on `http://localhost:8025`. To use this option, set the following `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM_NAME`, `SMTP_USERNAME`, `SMTP_PASSWORD` values:
```
SMTP_HOST=smtp-server
SMTP_PORT=1025
-SMTP_NAME=
+SMTP_FROM_ADDRESS=team@infisical.com
+SMTP_FROM_NAME=[whatever you like]
SMTP_USERNAME=team@infisical.com
SMTP_PASSWORD=
```
-Make sure to leave the `SMTP_PASSWORD` blank so the backend will be able to connect to MailHog
-
-You can browse `http://localhost:8025/` to browse email messages sent by the backend.
-
-With these environment variables, you will be ready to run the docker-compose.
+
+ Make sure to leave the `SMTP_PASSWORD` blank so the backend can connect to MailHog.
+
## Docker for development
@@ -84,12 +79,4 @@ Then browse http://localhost:8080
docker-compose -f docker-compose.dev.yml down
# start services
docker-compose -f docker-compose.dev.yml up
-```
-
-The docker-compose development environment consists of:
-
-- nginx
-- frontend
-- backend
-- mongo
-- mongo-express
+```
\ No newline at end of file
diff --git a/docs/images/integrations-github-auth.png b/docs/images/integrations-github-auth.png
new file mode 100644
index 000000000..92d7158ac
Binary files /dev/null and b/docs/images/integrations-github-auth.png differ
diff --git a/docs/images/integrations-github.png b/docs/images/integrations-github.png
new file mode 100644
index 000000000..d34ea2690
Binary files /dev/null and b/docs/images/integrations-github.png differ
diff --git a/docs/images/integrations-heroku-auth.png b/docs/images/integrations-heroku-auth.png
new file mode 100644
index 000000000..da9b4cf52
Binary files /dev/null and b/docs/images/integrations-heroku-auth.png differ
diff --git a/docs/images/integrations-heroku.png b/docs/images/integrations-heroku.png
new file mode 100644
index 000000000..cc225f286
Binary files /dev/null and b/docs/images/integrations-heroku.png differ
diff --git a/docs/images/integrations-netlify-auth.png b/docs/images/integrations-netlify-auth.png
new file mode 100644
index 000000000..fe25d7acf
Binary files /dev/null and b/docs/images/integrations-netlify-auth.png differ
diff --git a/docs/images/integrations-netlify.png b/docs/images/integrations-netlify.png
new file mode 100644
index 000000000..60261043e
Binary files /dev/null and b/docs/images/integrations-netlify.png differ
diff --git a/docs/images/integrations-vercel-auth.png b/docs/images/integrations-vercel-auth.png
new file mode 100644
index 000000000..d8f3d2d18
Binary files /dev/null and b/docs/images/integrations-vercel-auth.png differ
diff --git a/docs/images/integrations-vercel.png b/docs/images/integrations-vercel.png
new file mode 100644
index 000000000..f3a814c7a
Binary files /dev/null and b/docs/images/integrations-vercel.png differ
diff --git a/docs/images/integrations.png b/docs/images/integrations.png
new file mode 100644
index 000000000..7359aa198
Binary files /dev/null and b/docs/images/integrations.png differ
diff --git a/docs/integrations/cicd/githubactions.mdx b/docs/integrations/cicd/githubactions.mdx
new file mode 100644
index 000000000..23dbc419e
--- /dev/null
+++ b/docs/integrations/cicd/githubactions.mdx
@@ -0,0 +1,34 @@
+---
+title: "GitHub Actions"
+---
+
+
+ Infisical can sync secrets to GitHub repo secrets only. If your repo uses environment secrets, then stay tuned with this [issue](https://github.com/Infisical/infisical/issues/54).
+
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+- Ensure you have admin privileges to the repo you want to sync secrets to.
+
+## Navigate to your project's integrations tab
+
+
+
+## Authorize Infisical for GitHub
+
+Press on the GitHub tile and grant Infisical access to your GitHub account (repo privileges only).
+
+
+
+
+ If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
+ Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
+
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which GitHub repo and press start integration to start syncing secrets to the repo.
+
+
+
diff --git a/docs/integrations/cloud/heroku.mdx b/docs/integrations/cloud/heroku.mdx
index 5f0debd3e..e16cd0d54 100644
--- a/docs/integrations/cloud/heroku.mdx
+++ b/docs/integrations/cloud/heroku.mdx
@@ -1,26 +1,29 @@
---
title: "Heroku"
-description: "With this integration, you can automatically sync your secrets to Heroku as soon as you update secrets in Infisical."
---
-## Instructions
+Prerequisites:
-### Step 1: Open the integrations console
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
-Open the Infisical Dashboard. Choose the project in which you want to set up the intergation. Go to the integrations tab in the left sidebar.
+## Navigate to your project's integrations tab
-### Step 2: Authenticate with Heroku
+
-Click on "Heroku" tile. Log in if required and provide the necessary permissions to Infisical. You will afterwards be redirected back to the integrations page.
+## Authorize Infisical for Heroku
-Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. With any questions, reach out support@infisical.com.
+Press on the Heroku tile and grant Infisical access to your Heroku account.
-### Step 3: Start integration
+
-Choose a Heroku App that you want to sync the secrets to, and the Infisical project environment that you want to sync the secrets from. Start the integration.
-
-The integration should now show status 'In Sync'. Every time you edit secrets, they will be automatically pushed to Heroku.
-
-
- If you need to update your integration, you will have to delete the current one and create a new one.
+
+ If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
+ Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Heroku app and press start integration to start syncing secrets to Heroku.
+
+
+
diff --git a/docs/integrations/cloud/netlify.mdx b/docs/integrations/cloud/netlify.mdx
new file mode 100644
index 000000000..e78f01368
--- /dev/null
+++ b/docs/integrations/cloud/netlify.mdx
@@ -0,0 +1,32 @@
+---
+title: "Netlify"
+---
+
+
+ Infisical integrates with Netlify's new environment variable experience. If your site uses Netlify's old environment variable experience, you'll have to upgrade it to the new one to use this integration.
+
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+
+## Navigate to your project's integrations tab
+
+
+
+## Authorize Infisical for Netlify
+
+Press on the Netlify tile and grant Infisical access to your Netlify account.
+
+
+
+
+ If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
+ Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
+
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Netlify app and context. Lastly, press start integration to start syncing secrets to Netlify.
+
+
\ No newline at end of file
diff --git a/docs/integrations/cloud/vercel.mdx b/docs/integrations/cloud/vercel.mdx
index eb09203b5..59b416c44 100644
--- a/docs/integrations/cloud/vercel.mdx
+++ b/docs/integrations/cloud/vercel.mdx
@@ -2,4 +2,22 @@
title: "Vercel"
---
-Coming soon.
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+
+## Navigate to your project's integrations tab
+
+
+
+## Authorize Infisical for Vercel
+
+Press on the Vercel tile and grant Infisical access to your Vercel account.
+
+
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Vercel app and environment. Lastly, press start integration to start syncing secrets to Vercel.
+
+
\ No newline at end of file
diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx
index 676fe7941..0ccb9da26 100644
--- a/docs/integrations/overview.mdx
+++ b/docs/integrations/overview.mdx
@@ -12,6 +12,9 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
+| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
+| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
+| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
| [React](/integrations/frameworks/react) | Framework | Available |
| [Vue](/integrations/frameworks/vue) | Framework | Available |
| [Express](/integrations/frameworks/express) | Framework | Available |
@@ -26,7 +29,6 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
| [Flask](/integrations/frameworks/flask) | Framework | Available |
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
-| [Vercel](/integrations/cloud/vercel) | Cloud | Coming soon |
| [Render](/integrations/cloud/render) | Cloud | Coming soon |
| [Fly.io](/integrations/cloud/flyio) | Cloud | Coming soon |
| AWS | Cloud | Coming soon |
diff --git a/docs/mint.json b/docs/mint.json
index 19a499fe8..92a952767 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -133,13 +133,17 @@
"pages": [
"integrations/cloud/heroku",
"integrations/cloud/vercel",
+ "integrations/cloud/netlify",
"integrations/cloud/render",
"integrations/cloud/flyio"
]
},
{
"group": "CI/CD",
- "pages": ["integrations/cicd/circleci"]
+ "pages": [
+ "integrations/cicd/githubactions",
+ "integrations/cicd/circleci"
+ ]
},
{
"group": "Frameworks",
diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx
index 9c6697df5..8bbb507d8 100644
--- a/docs/self-hosting/configuration/envars.mdx
+++ b/docs/self-hosting/configuration/envars.mdx
@@ -9,12 +9,11 @@ Configuring Infisical requires setting some environment variables. There is a fi
| Variable | Description | Default Value |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------- |
-| `PRIVATE_KEY` | โ๏ธ NaCl-generated server secret key | `None` |
-| `PUBLIC_KEY` | โ๏ธ NaCl-generated server public key | `None` |
| `ENCRYPTION_KEY` | โ๏ธ Strong hex encryption key | `None` |
| `JWT_SIGNUP_SECRET` | โ๏ธ JWT token secret | `None` |
| `JWT_REFRESH_SECRET` | โ๏ธ JWT token secret | `None` |
| `JWT_AUTH_SECRET` | โ๏ธ JWT token secret | `None` |
+| `JWT_SERVICE_SECRET` | โ๏ธ JWT token secret | `None` |
| `JWT_SIGNUP_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `15m` |
| `JWT_REFRESH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `90d` |
| `JWT_AUTH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `10d` |
@@ -24,13 +23,20 @@ Configuring Infisical requires setting some environment variables. There is a fi
| `MONGO_PASSWORD` | MongoDB password if using container | `None` |
| `SITE_URL` | โ๏ธ Site URL - should be an absolute URL including the protocol (e.g. `https://app.infisical.com`) | `None` |
| `SMTP_HOST` | Hostname to connect to for establishing SMTP connections | `smtp.gmail.com` |
-| `SMTP_NAME` | Name label to be used in From field (e.g. `Team`) | `None` |
+| `SMTP_SECURE` | Use TLS when connecting to host. If false, TLS will be used if STARTTLS is supported | `false` |
+| `SMTP_PORT` | Port to connect to for establishing SMTP connections | `587` |
+| `SMTP_FROM_ADDRESS` | โ๏ธ Email address to be used for sending emails (e.g. `team@infisical.com`) | `None` |
+| `SMTP_FROM_NAME` | Name label to be used in From field (e.g. `Team`) | `Infisical` |
| `SMTP_USERNAME` | โ๏ธ Credential to connect to host (e.g. `team@infisical.com`) | `None` |
| `SMTP_PASSWORD` | โ๏ธ Credential to connect to host | `None` |
| `TELEMETRY_ENABLED` | `true` or `false`. [More](../overview). | `true` |
-| `CLIENT_ID_VERCEL` | OAuth client id for Vercel integration | `None` |
-| `CLIENT_ID_NETLIFY` | OAuth client id for Netlify integration | `None` |
-| `CLIENT_SECRET_HEROKU` | OAuth client secret for Heroku integration | `None` |
-| `CLIENT_SECRET_VERCEL` | OAuth client secret for Vercel integration | `None` |
-| `CLIENT_SECRET_NETLIFY` | OAuth client secret for Netlify integration | `None` |
+| `CLIENT_ID_HEROKU` | OAuth2 client ID for Heroku integration | `None` |
+| `CLIENT_ID_VERCEL` | OAuth2 client ID for Vercel integration | `None` |
+| `CLIENT_ID_NETLIFY` | OAuth2 client ID for Netlify integration | `None` |
+| `CLIENT_ID_GITHUB` | OAuth2 client ID for GitHub integration | `None` |
+| `CLIENT_SECRET_HEROKU` | OAuth2 client secret for Heroku integration | `None` |
+| `CLIENT_SECRET_VERCEL` | OAuth2 client secret for Vercel integration | `None` |
+| `CLIENT_SECRET_NETLIFY` | OAuth2 client secret for Netlify integration | `None` |
+| `CLIENT_SECRET_GITHUB` | OAuth2 client secret for GitHub integration | `None` |
+| `CLIENT_SLUG_VERCEL` | OAuth2 slug for Netlify integration | `None` |
| `SENTRY_DSN` | DSN for error-monitoring with Sentry | `None` |
diff --git a/docs/self-hosting/deployments/kubernetes.mdx b/docs/self-hosting/deployments/kubernetes.mdx
index 8ed2c0e05..19499d8ae 100644
--- a/docs/self-hosting/deployments/kubernetes.mdx
+++ b/docs/self-hosting/deployments/kubernetes.mdx
@@ -42,7 +42,7 @@ that by adding the `--namespace ` to your `helm install
```bash
## Installs to default namespace
-helm install infisical-helm-charts/infisical --values
+helm install infisical-helm-charts/infisical --generate-name --values
```
@@ -50,5 +50,4 @@ If you have not filled out all of the required environment variables, you will s
do so.
-4. Your Infisical installation is complete and should be running on the host name you specified in Ingress in `values.yaml`.
-Note: Please allow an additional time (2 minutes) for the frontend pods to be fully ready.
\ No newline at end of file
+#### 4. Your Infisical installation is complete and should be running on the host name you specified in Ingress in `values.yaml`.
\ No newline at end of file
diff --git a/frontend/components/basic/Listbox.tsx b/frontend/components/basic/Listbox.tsx
index 2ff0179dd..e726b7f56 100644
--- a/frontend/components/basic/Listbox.tsx
+++ b/frontend/components/basic/Listbox.tsx
@@ -11,7 +11,7 @@ import { Listbox, Transition } from "@headlessui/react";
interface ListBoxProps {
selected: string;
onChange: (arg: string) => void;
- data: string[];
+ data: string[] | null;
text?: string;
buttonAction?: () => void;
isFull?: boolean;
diff --git a/frontend/components/basic/dialog/ActivateBotDialog.js b/frontend/components/basic/dialog/ActivateBotDialog.js
index 600b61c20..79d8cd693 100644
--- a/frontend/components/basic/dialog/ActivateBotDialog.js
+++ b/frontend/components/basic/dialog/ActivateBotDialog.js
@@ -1,7 +1,8 @@
import { Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
-import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
+
import setBotActiveStatus from "../../../pages/api/bot/setBotActiveStatus";
+import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
import {
decryptAssymmetric,
encryptAssymmetric
diff --git a/frontend/components/basic/dialog/IntegrationAccessTokenDialog.js b/frontend/components/basic/dialog/IntegrationAccessTokenDialog.js
index dca8d672b..375dc8804 100644
--- a/frontend/components/basic/dialog/IntegrationAccessTokenDialog.js
+++ b/frontend/components/basic/dialog/IntegrationAccessTokenDialog.js
@@ -1,7 +1,8 @@
import { Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
-import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
+
import setBotActiveStatus from "../../../pages/api/bot/setBotActiveStatus";
+import getLatestFileKey from "../../../pages/api/workspace/getLatestFileKey";
import {
decryptAssymmetric,
encryptAssymmetric
diff --git a/frontend/components/integrations/Integration.tsx b/frontend/components/integrations/Integration.tsx
index de0747409..3bba41534 100644
--- a/frontend/components/integrations/Integration.tsx
+++ b/frontend/components/integrations/Integration.tsx
@@ -14,9 +14,11 @@ import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
import updateIntegration from "../../pages/api/integrations/updateIntegration"
import {
+ contextNetlifyMapping,
envMapping,
reverseContextNetlifyMapping,
- reverseEnvMapping} from "../../public/data/frequentConstants";
+ reverseEnvMapping,
+} from "../../public/data/frequentConstants";
interface Integration {
_id: string;
@@ -25,6 +27,7 @@ interface Integration {
integration: string;
integrationAuth: string;
isActive: boolean;
+ context: string;
}
interface IntegrationApp {
@@ -69,7 +72,7 @@ const Integration = ({
setIntegrationTarget("Development");
break;
case "netlify":
- setIntegrationContext("All");
+ setIntegrationContext(integration?.context ? contextNetlifyMapping[integration.context] : "Local development");
break;
default:
break;
@@ -93,7 +96,7 @@ const Integration = ({
"Production",
"Preview",
"Development"
- ] : []}
+ ] : null}
selected={"Production"}
onChange={setIntegrationTarget}
/>
@@ -107,12 +110,11 @@ const Integration = ({
@@ -138,7 +140,7 @@ const Integration = ({
"Staging",
"Testing",
"Production",
- ] : []}
+ ] : null}
selected={integrationEnvironment}
onChange={(environment) => {
setIntegrationEnvironment(environment);
@@ -166,7 +168,7 @@ const Integration = ({
APP
app.name) : []}
+ data={!integration.isActive ? apps.map((app) => app.name) : null}
selected={integrationApp}
onChange={(app) => {
setIntegrationApp(app);
@@ -190,7 +192,8 @@ const Integration = ({
onButtonPressed={async () => {
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
- const siteId = siteApp ? siteApp.siteId : null;
+ const siteId = siteApp?.siteId ? siteApp.siteId : null;
+
const result = await updateIntegration({
integrationId: integration._id,
environment: envMapping[integrationEnvironment],
@@ -200,6 +203,7 @@ const Integration = ({
context: integrationContext ? reverseContextNetlifyMapping[integrationContext] : null,
siteId
});
+
router.reload();
}}
color="mineshaft"
diff --git a/frontend/components/integrations/IntegrationSection.tsx b/frontend/components/integrations/IntegrationSection.tsx
index 9b4366278..52d5565ff 100644
--- a/frontend/components/integrations/IntegrationSection.tsx
+++ b/frontend/components/integrations/IntegrationSection.tsx
@@ -15,6 +15,7 @@ interface IntegrationType {
integration: string;
integrationAuth: string;
isActive: boolean;
+ context: string;
}
const ProjectIntegrationSection = ({
diff --git a/frontend/components/utilities/attemptLogin.js b/frontend/components/utilities/attemptLogin.js
index b0d70146e..6bf575bd1 100644
--- a/frontend/components/utilities/attemptLogin.js
+++ b/frontend/components/utilities/attemptLogin.js
@@ -42,9 +42,9 @@ const attemptLogin = async (
async () => {
const clientPublicKey = client.getPublicKey();
- const { serverPublicKey, salt } = await login1(email, clientPublicKey);
-
try {
+ const { serverPublicKey, salt } = await login1(email, clientPublicKey);
+
client.setSalt(salt);
client.setServerPublicKey(serverPublicKey);
const clientProof = client.getProof(); // called M1
diff --git a/frontend/pages/github.js b/frontend/pages/github.js
new file mode 100644
index 000000000..af2b2b087
--- /dev/null
+++ b/frontend/pages/github.js
@@ -0,0 +1,37 @@
+import React, { useEffect } from "react";
+import Head from "next/head";
+import { useRouter } from "next/router";
+const queryString = require("query-string");
+import AuthorizeIntegration from "./api/integrations/authorizeIntegration";
+
+export default function Github() {
+ const router = useRouter();
+ const parsedUrl = queryString.parse(router.asPath.split("?")[1]);
+ const code = parsedUrl.code;
+ const state = parsedUrl.state;
+
+ /**
+ * Here we forward to the default workspace if a user opens this url
+ */
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ useEffect(async () => {
+ try {
+ if (state === localStorage.getItem('latestCSRFToken')) {
+ localStorage.removeItem('latestCSRFToken');
+ await AuthorizeIntegration({
+ workspaceId: localStorage.getItem('projectData.id'),
+ code,
+ integration: "github",
+ });
+ router.push("/integrations/" + localStorage.getItem("projectData.id"));
+ }
+ } catch (error) {
+ console.error('Github integration error: ', error);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return ;
+}
+
+Github.requireAuth = true;
diff --git a/frontend/pages/integrations/[id].js b/frontend/pages/integrations/[id].js
index be0aff921..32a8e515e 100644
--- a/frontend/pages/integrations/[id].js
+++ b/frontend/pages/integrations/[id].js
@@ -41,7 +41,7 @@ export default function Integrations() {
setCloudIntegrationOptions(
await getIntegrationOptions()
);
-
+
// get project integration authorizations
setIntegrationAuths(
await getWorkspaceAuthorizations({
@@ -123,6 +123,8 @@ export default function Integrations() {
* @returns
*/
const handleIntegrationOption = async ({ integrationOption }) => {
+
+ console.log('handleIntegrationOption', integrationOption);
try {
// generate CSRF token for OAuth2 code-token exchange integrations
@@ -134,11 +136,14 @@ export default function Integrations() {
window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
break;
case 'Vercel':
- window.location = `https://vercel.com/integrations/infisical-dev/new?state=${state}`;
+ window.location = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`;
break;
case 'Netlify':
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/netlify`;
break;
+ case 'GitHub':
+ window.location = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/github&state=${state}`;
+ break;
// case 'Fly.io':
// console.log('fly.io');
// setIntegrationAccessTokenDialogOpen(true);
diff --git a/frontend/public/data/frequentConstants.ts b/frontend/public/data/frequentConstants.ts
index a447225d4..d2beee6d2 100644
--- a/frontend/public/data/frequentConstants.ts
+++ b/frontend/public/data/frequentConstants.ts
@@ -16,8 +16,14 @@ const reverseEnvMapping: Mapping = {
test: "Testing",
};
+const contextNetlifyMapping: Mapping = {
+ "dev": "Local development",
+ "branch-deploy": "Branch deploys",
+ "deploy-review": "Deploy Previews",
+ "production": "Production"
+}
+
const reverseContextNetlifyMapping: Mapping = {
- "All": "all",
"Local development": "dev",
"Branch deploys": "branch-deploy",
"Deploy Previews": "deploy-preview",
@@ -25,6 +31,7 @@ const reverseContextNetlifyMapping: Mapping = {
}
export {
+ contextNetlifyMapping,
envMapping,
reverseContextNetlifyMapping,
- reverseEnvMapping};
+ reverseEnvMapping}
diff --git a/frontend/public/images/integrations/GitHub.png b/frontend/public/images/integrations/GitHub.png
new file mode 100644
index 000000000..9490ffc6d
Binary files /dev/null and b/frontend/public/images/integrations/GitHub.png differ
diff --git a/helm-charts/infisical/Chart.yaml b/helm-charts/infisical/Chart.yaml
index a9297b385..3b56dcfc2 100644
--- a/helm-charts/infisical/Chart.yaml
+++ b/helm-charts/infisical/Chart.yaml
@@ -7,7 +7,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.1.3
+version: 0.1.6
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
diff --git a/helm-charts/infisical/templates/backend-deployment.yaml b/helm-charts/infisical/templates/backend-deployment.yaml
index 6a2faa688..437995e58 100644
--- a/helm-charts/infisical/templates/backend-deployment.yaml
+++ b/helm-charts/infisical/templates/backend-deployment.yaml
@@ -20,6 +20,11 @@ spec:
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
ports:
- containerPort: 4000
+ {{- if .Values.backend.kubeSecretRef }}
+ envFrom:
+ - secretRef:
+ name: {{ .Values.backend.kubeSecretRef }}
+ {{- end }}
env:
{{- range $key, $value := .Values.backendEnvironmentVariables }}
{{- if $value | quote | eq "MUST_REPLACE" }}
diff --git a/helm-charts/infisical/templates/frontend-deployment.yaml b/helm-charts/infisical/templates/frontend-deployment.yaml
index 3cd29f326..14be95506 100644
--- a/helm-charts/infisical/templates/frontend-deployment.yaml
+++ b/helm-charts/infisical/templates/frontend-deployment.yaml
@@ -18,6 +18,12 @@ spec:
- name: frontend
image: infisical/frontend
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
+ {{- if .Values.frontend.kubeSecretRef }}
+ envFrom:
+ - secretRef:
+ name: {{ .Values.frontend.kubeSecretRef }}
+ {{- end }}
+ {{- if .Values.frontendEnvironmentVariables }}
env:
{{- range $key, $value := .Values.frontendEnvironmentVariables }}
{{- if $value | quote | eq "MUST_REPLACE" }}
@@ -26,8 +32,9 @@ spec:
- name: {{ $key }}
value: {{ quote $value }}
{{- end }}
+ {{- end }}
ports:
- - containerPort: 4000
+ - containerPort: 3000
---
apiVersion: v1
kind: Service
diff --git a/helm-charts/infisical/values.yaml b/helm-charts/infisical/values.yaml
index a93a4769d..743c1e25b 100644
--- a/helm-charts/infisical/values.yaml
+++ b/helm-charts/infisical/values.yaml
@@ -3,14 +3,14 @@
# PLEASE REPLACE VALUES/EDIT AS REQUIRED
#####
-namespace: infisical
-
frontend:
replicaCount: 1
image:
repository:
pullPolicy: IfNotPresent
tag: "latest"
+ # kubeSecretRef: some-kube-secret-name
+
backend:
replicaCount: 1
@@ -18,10 +18,12 @@ backend:
repository:
pullPolicy: IfNotPresent
tag: "latest"
+ # kubeSecretRef: some-kube-secret-name
ingress:
enabled: true
- annotations: {}
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
hostName: example.com
frontend:
path: /
@@ -54,8 +56,6 @@ ingress:
###
backendEnvironmentVariables:
# Required keys for platform encryption/decryption ops. Replace with nacl sk keys
- PRIVATE_KEY: MUST_REPLACE
- PUBLIC_KEY: MUST_REPLACE
ENCRYPTION_KEY: MUST_REPLACE
# JWT
@@ -71,9 +71,8 @@ backendEnvironmentVariables:
SMTP_USERNAME: MUST_REPLACE
SMTP_PASSWORD: MUST_REPLACE
- # You may replace with Mongo Cloud URI
+ # Recommended to replace with Mongo Cloud URI as the DB instance in the cluster does not have persistence yet
MONGO_URL: mongodb://root:root@mongodb-service:27017/
# frontendEnvironmentVariables:
-# INFISICAL_TELEMETRY_ENABLED: true
\ No newline at end of file