Merge branch 'main' into fix/update-pt-br-copies
@@ -48,10 +48,12 @@ CLIENT_ID_HEROKU=
|
|||||||
CLIENT_ID_VERCEL=
|
CLIENT_ID_VERCEL=
|
||||||
CLIENT_ID_NETLIFY=
|
CLIENT_ID_NETLIFY=
|
||||||
CLIENT_ID_GITHUB=
|
CLIENT_ID_GITHUB=
|
||||||
|
CLIENT_ID_GITLAB=
|
||||||
CLIENT_SECRET_HEROKU=
|
CLIENT_SECRET_HEROKU=
|
||||||
CLIENT_SECRET_VERCEL=
|
CLIENT_SECRET_VERCEL=
|
||||||
CLIENT_SECRET_NETLIFY=
|
CLIENT_SECRET_NETLIFY=
|
||||||
CLIENT_SECRET_GITHUB=
|
CLIENT_SECRET_GITHUB=
|
||||||
|
CLIENT_SECRET_GITLAB=
|
||||||
CLIENT_SLUG_VERCEL=
|
CLIENT_SLUG_VERCEL=
|
||||||
|
|
||||||
# Sentry (optional) for monitoring errors
|
# Sentry (optional) for monitoring errors
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<kbd>[<img title="Turkish" alt="Turkish language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/tr.svg" width="22">](i18n/README.tr.md)</kbd>
|
<kbd>[<img title="Turkish" alt="Turkish language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/tr.svg" width="22">](i18n/README.tr.md)</kbd>
|
||||||
<kbd>[<img title="Bahasa Indonesia" alt="Bahasa Indonesia language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/id.svg" width="22">](i18n/README.id.md)</kbd>
|
<kbd>[<img title="Bahasa Indonesia" alt="Bahasa Indonesia language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/id.svg" width="22">](i18n/README.id.md)</kbd>
|
||||||
<kbd>[<img title="Portuguese - Brazil" alt="Portuguese - Brazil" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/br.svg" width="22">](i18n/README.pt-br.md)</kbd>
|
<kbd>[<img title="Portuguese - Brazil" alt="Portuguese - Brazil" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/br.svg" width="22">](i18n/README.pt-br.md)</kbd>
|
||||||
|
<kbd>[<img title="Japanese" alt="Japanese language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/jp.svg" width="22">](i18n/README.ja.md)</kbd>
|
||||||
|
|
||||||
**[Infisical](https://infisical.com)** is an open source, end-to-end encrypted secret manager which you can use to centralize your API keys and configs. From Infisical, you can then distribute these secrets across your whole development lifecycle - from development to production . It's designed to be simple and take minutes to get going.
|
**[Infisical](https://infisical.com)** is an open source, end-to-end encrypted secret manager which you can use to centralize your API keys and configs. From Infisical, you can then distribute these secrets across your whole development lifecycle - from development to production . It's designed to be simple and take minutes to get going.
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@
|
|||||||
- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure
|
- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure
|
||||||
- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform
|
- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform
|
||||||
- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret
|
- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret
|
||||||
- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project
|
- **[Audit Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project
|
||||||
- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets
|
- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets
|
||||||
- **Role-based Access Controls** per environment
|
- **Role-based Access Controls** per environment
|
||||||
- **2FA** (more options coming soon)
|
- **2FA** (more options coming soon)
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
FROM node:16-bullseye-slim
|
# Build stage
|
||||||
|
FROM node:16-alpine AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# RUN npm ci --only-production --ignore-scripts
|
|
||||||
# "prepare": "cd .. && npm install"
|
|
||||||
|
|
||||||
RUN npm ci --only-production
|
RUN npm ci --only-production
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM node:16-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --only-production
|
||||||
|
|
||||||
|
COPY --from=build /app .
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
||||||
CMD node healthcheck.js
|
CMD node healthcheck.js
|
||||||
|
|
||||||
|
EXPOSE 4000
|
||||||
|
|
||||||
CMD ["npm", "run", "start"]
|
CMD ["npm", "run", "start"]
|
||||||
@@ -22,10 +22,12 @@ declare global {
|
|||||||
CLIENT_ID_VERCEL: string;
|
CLIENT_ID_VERCEL: string;
|
||||||
CLIENT_ID_NETLIFY: string;
|
CLIENT_ID_NETLIFY: string;
|
||||||
CLIENT_ID_GITHUB: string;
|
CLIENT_ID_GITHUB: string;
|
||||||
|
CLIENT_ID_GITLAB: string;
|
||||||
CLIENT_SECRET_HEROKU: string;
|
CLIENT_SECRET_HEROKU: string;
|
||||||
CLIENT_SECRET_VERCEL: string;
|
CLIENT_SECRET_VERCEL: string;
|
||||||
CLIENT_SECRET_NETLIFY: string;
|
CLIENT_SECRET_NETLIFY: string;
|
||||||
CLIENT_SECRET_GITHUB: string;
|
CLIENT_SECRET_GITHUB: string;
|
||||||
|
CLIENT_SECRET_GITLAB: string;
|
||||||
CLIENT_SLUG_VERCEL: string;
|
CLIENT_SLUG_VERCEL: string;
|
||||||
POSTHOG_HOST: string;
|
POSTHOG_HOST: string;
|
||||||
POSTHOG_PROJECT_API_KEY: string;
|
POSTHOG_PROJECT_API_KEY: string;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run build && node build/index.js",
|
"start": "node build/index.js",
|
||||||
"dev": "nodemon",
|
"dev": "nodemon",
|
||||||
"swagger-autogen": "node ./swagger/index.ts",
|
"swagger-autogen": "node ./swagger/index.ts",
|
||||||
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
|
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@types/swagger-jsdoc": "^6.0.1",
|
"@types/swagger-jsdoc": "^6.0.1",
|
||||||
"@types/swagger-ui-express": "^4.1.3",
|
"@types/swagger-ui-express": "^4.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
"@typescript-eslint/parser": "^5.40.1",
|
"@typescript-eslint/parser": "^5.40.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ const NODE_ENV = process.env.NODE_ENV! || 'production';
|
|||||||
const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true;
|
const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true;
|
||||||
const LOKI_HOST = process.env.LOKI_HOST || undefined;
|
const LOKI_HOST = process.env.LOKI_HOST || undefined;
|
||||||
const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!;
|
const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!;
|
||||||
const TENANT_ID_AZURE = process.env.TENANT_ID_AZURE!;
|
|
||||||
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
|
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
|
||||||
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
|
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
|
||||||
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
|
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
|
||||||
const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!;
|
const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!;
|
||||||
|
const CLIENT_ID_GITLAB = process.env.CLIENT_ID_GITLAB!;
|
||||||
const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!;
|
const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!;
|
||||||
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
|
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
|
||||||
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
|
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
|
||||||
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
|
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
|
||||||
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!;
|
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!;
|
||||||
|
const CLIENT_SECRET_GITLAB = process.env.CLIENT_SECRET_GITLAB;
|
||||||
const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!;
|
const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!;
|
||||||
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
||||||
const POSTHOG_PROJECT_API_KEY =
|
const POSTHOG_PROJECT_API_KEY =
|
||||||
@@ -70,16 +71,17 @@ export {
|
|||||||
VERBOSE_ERROR_OUTPUT,
|
VERBOSE_ERROR_OUTPUT,
|
||||||
LOKI_HOST,
|
LOKI_HOST,
|
||||||
CLIENT_ID_AZURE,
|
CLIENT_ID_AZURE,
|
||||||
TENANT_ID_AZURE,
|
|
||||||
CLIENT_ID_HEROKU,
|
CLIENT_ID_HEROKU,
|
||||||
CLIENT_ID_VERCEL,
|
CLIENT_ID_VERCEL,
|
||||||
CLIENT_ID_NETLIFY,
|
CLIENT_ID_NETLIFY,
|
||||||
CLIENT_ID_GITHUB,
|
CLIENT_ID_GITHUB,
|
||||||
|
CLIENT_ID_GITLAB,
|
||||||
CLIENT_SECRET_AZURE,
|
CLIENT_SECRET_AZURE,
|
||||||
CLIENT_SECRET_HEROKU,
|
CLIENT_SECRET_HEROKU,
|
||||||
CLIENT_SECRET_VERCEL,
|
CLIENT_SECRET_VERCEL,
|
||||||
CLIENT_SECRET_NETLIFY,
|
CLIENT_SECRET_NETLIFY,
|
||||||
CLIENT_SECRET_GITHUB,
|
CLIENT_SECRET_GITHUB,
|
||||||
|
CLIENT_SECRET_GITLAB,
|
||||||
CLIENT_SLUG_VERCEL,
|
CLIENT_SLUG_VERCEL,
|
||||||
POSTHOG_HOST,
|
POSTHOG_HOST,
|
||||||
POSTHOG_PROJECT_API_KEY,
|
POSTHOG_PROJECT_API_KEY,
|
||||||
|
|||||||
@@ -358,8 +358,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
tags: string[]
|
tags: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const newlyCreatedSecrets = await Secret.insertMany(
|
const secretsToInsert: ISecret[] = listOfSecretsToCreate.map(({
|
||||||
listOfSecretsToCreate.map(({
|
|
||||||
type,
|
type,
|
||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
@@ -390,7 +389,8 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
tags
|
tags
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
|
||||||
|
const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map((insertedSecret) => insertedSecret.toObject());
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// trigger event - push secrets
|
// trigger event - push secrets
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const getMe = async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
user = await User
|
user = await User
|
||||||
.findById(req.user._id)
|
.findById(req.user._id)
|
||||||
.select('+publicKey +encryptedPrivateKey +iv +tag');
|
.select('+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser({ email: req.user.email });
|
Sentry.setUser({ email: req.user.email });
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ const v1PushSecrets = async ({
|
|||||||
|
|
||||||
if (toAdd.length > 0) {
|
if (toAdd.length > 0) {
|
||||||
// add secrets
|
// add secrets
|
||||||
const newSecrets = await Secret.insertMany(
|
const newSecrets: ISecret[] = (await Secret.insertMany(
|
||||||
toAdd.map((s, idx) => {
|
toAdd.map((s, idx) => {
|
||||||
const obj: any = {
|
const obj: any = {
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -294,7 +294,7 @@ const v1PushSecrets = async ({
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
})
|
})
|
||||||
);
|
)).map((insertedSecret) => insertedSecret.toObject());
|
||||||
|
|
||||||
// (EE) add secret versions for new secrets
|
// (EE) add secret versions for new secrets
|
||||||
EESecretService.addSecretVersions({
|
EESecretService.addSecretVersions({
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
@@ -77,6 +79,11 @@ const getApps = async ({
|
|||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_GITLAB:
|
||||||
|
apps = await getAppsGitlab({
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
break;
|
||||||
case INTEGRATION_RENDER:
|
case INTEGRATION_RENDER:
|
||||||
apps = await getAppsRender({
|
apps = await getAppsRender({
|
||||||
accessToken,
|
accessToken,
|
||||||
@@ -190,21 +197,40 @@ const getAppsVercel = async ({
|
|||||||
* @returns {String} apps.name - name of Netlify site
|
* @returns {String} apps.name - name of Netlify site
|
||||||
*/
|
*/
|
||||||
const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
||||||
let apps;
|
const apps: any = [];
|
||||||
try {
|
try {
|
||||||
const res = (
|
let page = 1;
|
||||||
await request.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, {
|
const perPage = 10;
|
||||||
|
let hasMorePages = true;
|
||||||
|
|
||||||
|
// paginate through all sites
|
||||||
|
while (hasMorePages) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
page: String(page),
|
||||||
|
per_page: String(perPage)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await request.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, {
|
||||||
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
'Accept-Encoding': 'application/json'
|
'Accept-Encoding': 'application/json'
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
).data;
|
|
||||||
|
|
||||||
apps = res.map((a: any) => ({
|
data.map((a: any) => {
|
||||||
|
apps.push({
|
||||||
name: a.name,
|
name: a.name,
|
||||||
appId: a.site_id,
|
appId: a.site_id
|
||||||
}));
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.length < perPage) {
|
||||||
|
hasMorePages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
page++;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
@@ -217,9 +243,9 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
/**
|
/**
|
||||||
* Return list of repositories for Github integration
|
* Return list of repositories for Github integration
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.accessToken - access token for Netlify API
|
* @param {String} obj.accessToken - access token for Github API
|
||||||
* @returns {Object[]} apps - names of Netlify sites
|
* @returns {Object[]} apps - names of Github sites
|
||||||
* @returns {String} apps.name - name of Netlify site
|
* @returns {String} apps.name - name of Github site
|
||||||
*/
|
*/
|
||||||
const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
||||||
let apps;
|
let apps;
|
||||||
@@ -401,4 +427,54 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
return apps;
|
return apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of repositories for GitLab integration
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {String} obj.accessToken - access token for GitLab API
|
||||||
|
* @returns {Object[]} apps - names of GitLab sites
|
||||||
|
* @returns {String} apps.name - name of GitLab site
|
||||||
|
*/
|
||||||
|
const getAppsGitlab = async ({ accessToken }: {accessToken: string}) => {
|
||||||
|
let apps;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { id } = (
|
||||||
|
await request.get(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/user`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
const res = (
|
||||||
|
await request.get(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
apps = res?.map((a: any) => {
|
||||||
|
return {
|
||||||
|
name: a?.name,
|
||||||
|
appId: `${a?.id}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
throw new Error("Failed to get GitLab repos");
|
||||||
|
}
|
||||||
|
|
||||||
|
return apps;
|
||||||
|
}
|
||||||
|
|
||||||
export { getApps };
|
export { getApps };
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
INTEGRATION_HEROKU_TOKEN_URL,
|
INTEGRATION_HEROKU_TOKEN_URL,
|
||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
import {
|
import {
|
||||||
SITE_URL,
|
SITE_URL,
|
||||||
@@ -18,11 +20,13 @@ import {
|
|||||||
CLIENT_ID_VERCEL,
|
CLIENT_ID_VERCEL,
|
||||||
CLIENT_ID_NETLIFY,
|
CLIENT_ID_NETLIFY,
|
||||||
CLIENT_ID_GITHUB,
|
CLIENT_ID_GITHUB,
|
||||||
|
CLIENT_ID_GITLAB,
|
||||||
CLIENT_SECRET_AZURE,
|
CLIENT_SECRET_AZURE,
|
||||||
CLIENT_SECRET_HEROKU,
|
CLIENT_SECRET_HEROKU,
|
||||||
CLIENT_SECRET_VERCEL,
|
CLIENT_SECRET_VERCEL,
|
||||||
CLIENT_SECRET_NETLIFY,
|
CLIENT_SECRET_NETLIFY,
|
||||||
CLIENT_SECRET_GITHUB
|
CLIENT_SECRET_GITHUB,
|
||||||
|
CLIENT_SECRET_GITLAB,
|
||||||
} from '../config';
|
} from '../config';
|
||||||
|
|
||||||
interface ExchangeCodeAzureResponse {
|
interface ExchangeCodeAzureResponse {
|
||||||
@@ -66,6 +70,15 @@ interface ExchangeCodeGithubResponse {
|
|||||||
token_type: string;
|
token_type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ExchangeCodeGitlabResponse {
|
||||||
|
access_token: string;
|
||||||
|
token_type: string;
|
||||||
|
expires_in: string;
|
||||||
|
refresh_token: string;
|
||||||
|
scope: string;
|
||||||
|
created_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for OAuth2
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for OAuth2
|
||||||
* code-token exchange for integration named [integration]
|
* code-token exchange for integration named [integration]
|
||||||
@@ -114,6 +127,10 @@ const exchangeCode = async ({
|
|||||||
code
|
code
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_GITLAB:
|
||||||
|
obj = await exchangeCodeGitlab({
|
||||||
|
code
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
@@ -341,4 +358,48 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Gitlab
|
||||||
|
* 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 Gitlab API
|
||||||
|
* @returns {String} obj2.refreshToken - refresh token for Gitlab API
|
||||||
|
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||||
|
*/
|
||||||
|
const exchangeCodeGitlab = async ({ code }: { code: string }) => {
|
||||||
|
let res: ExchangeCodeGitlabResponse;
|
||||||
|
|
||||||
|
try {
|
||||||
|
res = (
|
||||||
|
await request.post(
|
||||||
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
code: code,
|
||||||
|
client_id: CLIENT_ID_GITLAB,
|
||||||
|
client_secret: CLIENT_SECRET_GITLAB,
|
||||||
|
redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback`
|
||||||
|
} as any),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
}catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
throw new Error('Failed OAuth2 code-token exchange with Gitlab');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: res.access_token,
|
||||||
|
refreshToken: null,
|
||||||
|
accessExpiresAt: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export { exchangeCode };
|
export { exchangeCode };
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
INTEGRATION_HEROKU,
|
INTEGRATION_HEROKU,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
|
|
||||||
const revokeAccess = async ({
|
const revokeAccess = async ({
|
||||||
@@ -32,6 +33,8 @@ const revokeAccess = async ({
|
|||||||
break;
|
break;
|
||||||
case INTEGRATION_GITHUB:
|
case INTEGRATION_GITHUB:
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_GITLAB:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
|
deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
@@ -110,6 +112,13 @@ const syncSecrets = async ({
|
|||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_GITLAB:
|
||||||
|
await syncSecretsGitLab({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
break;
|
||||||
case INTEGRATION_RENDER:
|
case INTEGRATION_RENDER:
|
||||||
await syncSecretsRender({
|
await syncSecretsRender({
|
||||||
integration,
|
integration,
|
||||||
@@ -186,19 +195,24 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
*/
|
*/
|
||||||
const paginateAzureKeyVaultSecrets = async (url: string) => {
|
const paginateAzureKeyVaultSecrets = async (url: string) => {
|
||||||
let result: GetAzureKeyVaultSecret[] = [];
|
let result: GetAzureKeyVaultSecret[] = [];
|
||||||
|
try {
|
||||||
while (url) {
|
while (url) {
|
||||||
const res = await request.get(url, {
|
const res = await request.get(url, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`
|
||||||
'Accept-Encoding': 'application/json'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
result = result.concat(res.data.value);
|
result = result.concat(res.data.value);
|
||||||
|
|
||||||
url = res.data.nextLink;
|
url = res.data.nextLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,8 +226,7 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
|
|
||||||
const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, {
|
const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`
|
||||||
'Accept-Encoding': 'application/json'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -260,9 +273,23 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sync/push set secrets
|
const setSecretAzureKeyVault = async ({
|
||||||
if (setSecrets.length > 0) {
|
key,
|
||||||
setSecrets.forEach(async ({ key, value }) => {
|
value,
|
||||||
|
integration,
|
||||||
|
accessToken
|
||||||
|
}: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
integration: IIntegration;
|
||||||
|
accessToken: string;
|
||||||
|
}) => {
|
||||||
|
let isSecretSet = false;
|
||||||
|
let maxTries = 6;
|
||||||
|
|
||||||
|
while (!isSecretSet && maxTries > 0) {
|
||||||
|
// try to set secret
|
||||||
|
try {
|
||||||
await request.put(
|
await request.put(
|
||||||
`${integration.app}/secrets/${key}?api-version=7.3`,
|
`${integration.app}/secrets/${key}?api-version=7.3`,
|
||||||
{
|
{
|
||||||
@@ -270,23 +297,51 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`
|
||||||
'Accept-Encoding': 'application/json'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
isSecretSet = true;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
const error: any = err;
|
||||||
|
if (error?.response?.data?.error?.innererror?.code === 'ObjectIsDeletedButRecoverable') {
|
||||||
|
await request.post(
|
||||||
|
`${integration.app}/deletedsecrets/${key}/recover?api-version=7.3`, {},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||||
|
} else {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||||
|
maxTries--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync/push set secrets
|
||||||
|
for await (const setSecret of setSecrets) {
|
||||||
|
const { key, value } = setSecret;
|
||||||
|
setSecretAzureKeyVault({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
integration,
|
||||||
|
accessToken
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteSecrets.length > 0) {
|
for await (const deleteSecret of deleteSecrets) {
|
||||||
deleteSecrets.forEach(async (secret) => {
|
const { key } = deleteSecret;
|
||||||
await request.delete(`${integration.app}/secrets/${secret.key}?api-version=7.3`, {
|
await request.delete(`${integration.app}/secrets/${key}?api-version=7.3`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`
|
||||||
'Accept-Encoding': 'application/json'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
@@ -1422,7 +1477,98 @@ const syncSecretsTravisCI = async ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error("Failed to sync secrets to TravisCI");
|
throw new Error("Failed to sync secrets to GitLab");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync/push [secrets] to GitLab repo with name [integration.app]
|
||||||
|
* @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)
|
||||||
|
* @param {String} obj.accessToken - access token for GitLab integration
|
||||||
|
*/
|
||||||
|
const syncSecretsGitLab = async ({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken,
|
||||||
|
}: {
|
||||||
|
integration: IIntegration;
|
||||||
|
secrets: any;
|
||||||
|
accessToken: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
// get secrets from gitlab
|
||||||
|
const getSecretsRes = (
|
||||||
|
await request.get(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
for (const key of Object.keys(secrets)) {
|
||||||
|
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
|
||||||
|
if (!existingSecret) {
|
||||||
|
await request.post(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables`,
|
||||||
|
{
|
||||||
|
key: key,
|
||||||
|
value: secrets[key],
|
||||||
|
protected: false,
|
||||||
|
masked: false,
|
||||||
|
raw: false,
|
||||||
|
environment_scope:'*'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}else {
|
||||||
|
// udpate secret
|
||||||
|
await request.put(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}`,
|
||||||
|
{
|
||||||
|
...existingSecret,
|
||||||
|
value: secrets[existingSecret.key]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete secrets
|
||||||
|
for (const sec of getSecretsRes) {
|
||||||
|
if (!(sec.key in secrets)) {
|
||||||
|
await request.delete(
|
||||||
|
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
throw new Error("Failed to sync secrets to GitLab");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -32,6 +33,7 @@ export interface IIntegration {
|
|||||||
| 'vercel'
|
| 'vercel'
|
||||||
| 'netlify'
|
| 'netlify'
|
||||||
| 'github'
|
| 'github'
|
||||||
|
| 'gitlab'
|
||||||
| 'render'
|
| 'render'
|
||||||
| 'flyio'
|
| 'flyio'
|
||||||
| 'circleci'
|
| 'circleci'
|
||||||
@@ -96,6 +98,7 @@ const integrationSchema = new Schema<IIntegration>(
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -16,7 +17,7 @@ import {
|
|||||||
export interface IIntegrationAuth {
|
export interface IIntegrationAuth {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'render' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'aws-parameter-store' | 'aws-secret-manager';
|
integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'aws-parameter-store' | 'aws-secret-manager';
|
||||||
teamId: string;
|
teamId: string;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
refreshCiphertext?: string;
|
refreshCiphertext?: string;
|
||||||
@@ -48,6 +49,7 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SECURE } from '../config';
|
import {
|
||||||
|
SMTP_HOST,
|
||||||
|
SMTP_PORT,
|
||||||
|
SMTP_USERNAME,
|
||||||
|
SMTP_PASSWORD,
|
||||||
|
SMTP_SECURE
|
||||||
|
} from '../config';
|
||||||
import {
|
import {
|
||||||
SMTP_HOST_SENDGRID,
|
SMTP_HOST_SENDGRID,
|
||||||
SMTP_HOST_MAILGUN,
|
SMTP_HOST_MAILGUN,
|
||||||
SMTP_HOST_SOCKETLABS
|
SMTP_HOST_SOCKETLABS,
|
||||||
|
SMTP_HOST_ZOHOMAIL
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
@@ -37,6 +44,12 @@ if (SMTP_SECURE) {
|
|||||||
ciphers: 'TLSv1.2'
|
ciphers: 'TLSv1.2'
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SMTP_HOST_ZOHOMAIL:
|
||||||
|
mailOpts.requireTLS = true;
|
||||||
|
mailOpts.tls = {
|
||||||
|
ciphers: 'TLSv1.2'
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (SMTP_HOST.includes('amazonaws.com')) {
|
if (SMTP_HOST.includes('amazonaws.com')) {
|
||||||
mailOpts.tls = {
|
mailOpts.tls = {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -24,7 +25,9 @@ import {
|
|||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
@@ -47,7 +50,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
SMTP_HOST_SENDGRID,
|
SMTP_HOST_SENDGRID,
|
||||||
SMTP_HOST_MAILGUN,
|
SMTP_HOST_MAILGUN,
|
||||||
SMTP_HOST_SOCKETLABS
|
SMTP_HOST_SOCKETLABS,
|
||||||
|
SMTP_HOST_ZOHOMAIL
|
||||||
} from './smtp';
|
} from './smtp';
|
||||||
import { PLAN_STARTER, PLAN_PRO } from './stripe';
|
import { PLAN_STARTER, PLAN_PRO } from './stripe';
|
||||||
import {
|
import {
|
||||||
@@ -80,6 +84,7 @@ export {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -91,7 +96,9 @@ export {
|
|||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
@@ -110,6 +117,7 @@ export {
|
|||||||
SMTP_HOST_SENDGRID,
|
SMTP_HOST_SENDGRID,
|
||||||
SMTP_HOST_MAILGUN,
|
SMTP_HOST_MAILGUN,
|
||||||
SMTP_HOST_SOCKETLABS,
|
SMTP_HOST_SOCKETLABS,
|
||||||
|
SMTP_HOST_ZOHOMAIL,
|
||||||
PLAN_STARTER,
|
PLAN_STARTER,
|
||||||
PLAN_PRO,
|
PLAN_PRO,
|
||||||
MFA_METHOD_EMAIL,
|
MFA_METHOD_EMAIL,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
CLIENT_ID_AZURE,
|
CLIENT_ID_AZURE,
|
||||||
TENANT_ID_AZURE
|
CLIENT_ID_GITLAB
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import {
|
import {
|
||||||
CLIENT_ID_HEROKU,
|
CLIENT_ID_HEROKU,
|
||||||
CLIENT_ID_NETLIFY,
|
CLIENT_ID_NETLIFY,
|
||||||
CLIENT_ID_GITHUB,
|
CLIENT_ID_GITHUB,
|
||||||
CLIENT_SLUG_VERCEL,
|
CLIENT_SLUG_VERCEL
|
||||||
} from "../config";
|
} from "../config";
|
||||||
|
|
||||||
// integrations
|
// integrations
|
||||||
@@ -17,6 +17,7 @@ const INTEGRATION_HEROKU = "heroku";
|
|||||||
const INTEGRATION_VERCEL = "vercel";
|
const INTEGRATION_VERCEL = "vercel";
|
||||||
const INTEGRATION_NETLIFY = "netlify";
|
const INTEGRATION_NETLIFY = "netlify";
|
||||||
const INTEGRATION_GITHUB = "github";
|
const INTEGRATION_GITHUB = "github";
|
||||||
|
const INTEGRATION_GITLAB = "gitlab";
|
||||||
const INTEGRATION_RENDER = "render";
|
const INTEGRATION_RENDER = "render";
|
||||||
const INTEGRATION_FLYIO = "flyio";
|
const INTEGRATION_FLYIO = "flyio";
|
||||||
const INTEGRATION_CIRCLECI = "circleci";
|
const INTEGRATION_CIRCLECI = "circleci";
|
||||||
@@ -27,6 +28,7 @@ const INTEGRATION_SET = new Set([
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -37,16 +39,19 @@ const INTEGRATION_SET = new Set([
|
|||||||
const INTEGRATION_OAUTH2 = "oauth2";
|
const INTEGRATION_OAUTH2 = "oauth2";
|
||||||
|
|
||||||
// integration oauth endpoints
|
// integration oauth endpoints
|
||||||
const INTEGRATION_AZURE_TOKEN_URL = `https://login.microsoftonline.com/${TENANT_ID_AZURE}/oauth2/v2.0/token`;
|
const INTEGRATION_AZURE_TOKEN_URL = `https://login.microsoftonline.com/common/oauth2/v2.0/token`;
|
||||||
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
|
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
|
||||||
const INTEGRATION_VERCEL_TOKEN_URL =
|
const INTEGRATION_VERCEL_TOKEN_URL =
|
||||||
"https://api.vercel.com/v2/oauth/access_token";
|
"https://api.vercel.com/v2/oauth/access_token";
|
||||||
const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/token";
|
const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/token";
|
||||||
const INTEGRATION_GITHUB_TOKEN_URL =
|
const INTEGRATION_GITHUB_TOKEN_URL =
|
||||||
"https://github.com/login/oauth/access_token";
|
"https://github.com/login/oauth/access_token";
|
||||||
|
const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
|
||||||
|
|
||||||
|
|
||||||
// integration apps endpoints
|
// integration apps endpoints
|
||||||
const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
|
const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
|
||||||
|
const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api";
|
||||||
const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com";
|
const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com";
|
||||||
const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
|
const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
|
||||||
const INTEGRATION_RENDER_API_URL = "https://api.render.com";
|
const INTEGRATION_RENDER_API_URL = "https://api.render.com";
|
||||||
@@ -128,6 +133,15 @@ const INTEGRATION_OPTIONS = [
|
|||||||
clientId: '',
|
clientId: '',
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Azure Key Vault',
|
||||||
|
slug: 'azure-key-vault',
|
||||||
|
image: 'Microsoft Azure.png',
|
||||||
|
isAvailable: true,
|
||||||
|
type: 'oauth',
|
||||||
|
clientId: CLIENT_ID_AZURE,
|
||||||
|
docsLink: ''
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Circle CI',
|
name: 'Circle CI',
|
||||||
slug: 'circleci',
|
slug: 'circleci',
|
||||||
@@ -137,6 +151,15 @@ const INTEGRATION_OPTIONS = [
|
|||||||
clientId: '',
|
clientId: '',
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'GitLab',
|
||||||
|
slug: 'gitlab',
|
||||||
|
image: 'GitLab.png',
|
||||||
|
isAvailable: true,
|
||||||
|
type: 'oauth',
|
||||||
|
clientId: CLIENT_ID_GITLAB,
|
||||||
|
docsLink: ''
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Travis CI',
|
name: 'Travis CI',
|
||||||
slug: 'travisci',
|
slug: 'travisci',
|
||||||
@@ -146,16 +169,6 @@ const INTEGRATION_OPTIONS = [
|
|||||||
clientId: '',
|
clientId: '',
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'Azure Key Vault',
|
|
||||||
slug: 'azure-key-vault',
|
|
||||||
image: 'Microsoft Azure.png',
|
|
||||||
isAvailable: false,
|
|
||||||
type: 'oauth',
|
|
||||||
clientId: CLIENT_ID_AZURE,
|
|
||||||
tenantId: TENANT_ID_AZURE,
|
|
||||||
docsLink: ''
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Google Cloud Platform',
|
name: 'Google Cloud Platform',
|
||||||
slug: 'gcp',
|
slug: 'gcp',
|
||||||
@@ -175,6 +188,7 @@ export {
|
|||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
@@ -186,7 +200,9 @@ export {
|
|||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITLAB_API_URL,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
|
INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net';
|
const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net';
|
||||||
const SMTP_HOST_MAILGUN = 'smtp.mailgun.org';
|
const SMTP_HOST_MAILGUN = 'smtp.mailgun.org';
|
||||||
const SMTP_HOST_SOCKETLABS = 'smtp.socketlabs.com';
|
const SMTP_HOST_SOCKETLABS = 'smtp.socketlabs.com';
|
||||||
|
const SMTP_HOST_ZOHOMAIL = 'smtp.zoho.com';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SMTP_HOST_SENDGRID,
|
SMTP_HOST_SENDGRID,
|
||||||
SMTP_HOST_MAILGUN,
|
SMTP_HOST_MAILGUN,
|
||||||
SMTP_HOST_SOCKETLABS
|
SMTP_HOST_SOCKETLABS,
|
||||||
|
SMTP_HOST_ZOHOMAIL
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/muesli/mango-cobra v1.2.0
|
github.com/muesli/mango-cobra v1.2.0
|
||||||
github.com/muesli/roff v0.1.0
|
github.com/muesli/roff v0.1.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
golang.org/x/term v0.5.0
|
golang.org/x/term v0.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ require (
|
|||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.10.0 // indirect
|
go.mongodb.org/mongo-driver v1.10.0 // indirect
|
||||||
golang.org/x/net v0.6.0 // indirect
|
golang.org/x/net v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -103,17 +103,13 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk
|
|||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg=
|
go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg=
|
||||||
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
|
||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -125,13 +121,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
|
||||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Infisical/infisical-merge/packages/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterReservedEnvVars(t *testing.T) {
|
||||||
|
|
||||||
|
// some test env vars.
|
||||||
|
// HOME and PATH are reserved key words and should be filtered out
|
||||||
|
// XDG_SESSION_ID and LC_CTYPE are reserved key word prefixes and should be filtered out
|
||||||
|
// The filter function only checks the keys of the env map, so we dont need to set any values
|
||||||
|
env := map[string]models.SingleEnvironmentVariable{
|
||||||
|
"test": {},
|
||||||
|
"test2": {},
|
||||||
|
"HOME": {},
|
||||||
|
"PATH": {},
|
||||||
|
"XDG_SESSION_ID": {},
|
||||||
|
"LC_CTYPE": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if there are any reserved key words in secrets to inject
|
||||||
|
filterReservedEnvVars(env)
|
||||||
|
|
||||||
|
if len(env) != 2 {
|
||||||
|
t.Errorf("Expected 2 secrets to be returned, got %d", len(env))
|
||||||
|
}
|
||||||
|
if _, ok := env["test"]; !ok {
|
||||||
|
t.Errorf("Expected test to be returned")
|
||||||
|
}
|
||||||
|
if _, ok := env["test2"]; !ok {
|
||||||
|
t.Errorf("Expected test2 to be returned")
|
||||||
|
}
|
||||||
|
if _, ok := env["HOME"]; ok {
|
||||||
|
t.Errorf("Expected HOME to be filtered out")
|
||||||
|
}
|
||||||
|
if _, ok := env["PATH"]; ok {
|
||||||
|
t.Errorf("Expected PATH to be filtered out")
|
||||||
|
}
|
||||||
|
if _, ok := env["XDG_SESSION_ID"]; ok {
|
||||||
|
t.Errorf("Expected XDG_SESSION_ID to be filtered out")
|
||||||
|
}
|
||||||
|
if _, ok := env["LC_CTYPE"]; ok {
|
||||||
|
t.Errorf("Expected LC_CTYPE to be filtered out")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ var exportCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,11 @@ var exportCmd = &cobra.Command{
|
|||||||
util.HandleError(err)
|
util.HandleError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectId, err := cmd.Flags().GetString("projectId")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
format, err := cmd.Flags().GetString("format")
|
format, err := cmd.Flags().GetString("format")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err)
|
util.HandleError(err)
|
||||||
@@ -69,7 +74,7 @@ var exportCmd = &cobra.Command{
|
|||||||
util.HandleError(err, "Unable to parse flag")
|
util.HandleError(err, "Unable to parse flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "Unable to fetch secrets")
|
util.HandleError(err, "Unable to fetch secrets")
|
||||||
}
|
}
|
||||||
@@ -106,6 +111,7 @@ func init() {
|
|||||||
exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||||
exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||||
exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs")
|
exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs")
|
||||||
|
exportCmd.Flags().String("projectId", "", "manually set the projectId to fetch secrets from")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format according to the format flag
|
// Format according to the format flag
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/Infisical/infisical-merge/packages/api"
|
"github.com/Infisical/infisical-merge/packages/api"
|
||||||
"github.com/Infisical/infisical-merge/packages/models"
|
"github.com/Infisical/infisical-merge/packages/models"
|
||||||
@@ -91,12 +90,12 @@ func writeWorkspaceFile(selectedWorkspace models.Workspace) error {
|
|||||||
WorkspaceId: selectedWorkspace.ID,
|
WorkspaceId: selectedWorkspace.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
marshalledWorkspaceFile, err := json.Marshal(workspaceFileToSave)
|
marshalledWorkspaceFile, err := json.MarshalIndent(workspaceFileToSave, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = util.WriteToFile(util.INFISICAL_WORKSPACE_CONFIG_FILE_NAME, marshalledWorkspaceFile, os.ModePerm)
|
err = util.WriteToFile(util.INFISICAL_WORKSPACE_CONFIG_FILE_NAME, marshalledWorkspaceFile, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,13 +41,14 @@ var loginCmd = &cobra.Command{
|
|||||||
PreRun: toggleDebug,
|
PreRun: toggleDebug,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||||
if err != nil && (strings.Contains(err.Error(), "The specified item could not be found in the keyring") || strings.Contains(err.Error(), "unable to get key from Keyring")) { // if the key can't be found allow them to override
|
// if the key can't be found or there is an error getting current credentials from key ring, allow them to override
|
||||||
|
if err != nil && (strings.Contains(err.Error(), "The specified item could not be found in the keyring") || strings.Contains(err.Error(), "unable to get key from Keyring") || strings.Contains(err.Error(), "GetUserCredsFromKeyRing")) {
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
util.HandleError(err)
|
util.HandleError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired { // if you are logged in but not expired
|
if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired && len(currentLoggedInUserDetails.UserCredentials.PrivateKey) != 0 {
|
||||||
shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email)
|
shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err)
|
util.HandleError(err)
|
||||||
@@ -234,8 +235,16 @@ var loginCmd = &cobra.Command{
|
|||||||
// clear backed up secrets from prev account
|
// clear backed up secrets from prev account
|
||||||
util.DeleteBackupSecrets()
|
util.DeleteBackupSecrets()
|
||||||
|
|
||||||
color.Green("Nice! You are logged in as: %v", email)
|
whilte := color.New(color.FgGreen)
|
||||||
|
boldWhite := whilte.Add(color.Bold)
|
||||||
|
boldWhite.Printf(">>>> Welcome to Infisical!")
|
||||||
|
boldWhite.Printf(" You are now logged in as %v <<<< \n", email)
|
||||||
|
|
||||||
|
plainBold := color.New(color.Bold)
|
||||||
|
|
||||||
|
plainBold.Println("\nQuick links")
|
||||||
|
fmt.Println("- Learn to inject secrets into your application at https://infisical.com/docs/cli/usage")
|
||||||
|
fmt.Println("- Stuck? Join our slack for quick support https://infisical.com/slack")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ var resetCmd = &cobra.Command{
|
|||||||
|
|
||||||
keyringInstance.Remove(util.KEYRING_SERVICE_NAME)
|
keyringInstance.Remove(util.KEYRING_SERVICE_NAME)
|
||||||
|
|
||||||
|
// delete secrets backup
|
||||||
|
util.DeleteBackupSecrets()
|
||||||
|
|
||||||
util.PrintSuccessMessage("Reset successful")
|
util.PrintSuccessMessage("Reset successful")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ var runCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -110,13 +110,7 @@ var runCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check to see if there are any reserved key words in secrets to inject
|
// check to see if there are any reserved key words in secrets to inject
|
||||||
reservedEnvironmentVariables := []string{"HOME", "PATH", "PS1", "PS2"}
|
filterReservedEnvVars(secretsByKey)
|
||||||
for _, reservedEnvName := range reservedEnvironmentVariables {
|
|
||||||
if _, ok := secretsByKey[reservedEnvName]; ok {
|
|
||||||
delete(secretsByKey, reservedEnvName)
|
|
||||||
util.PrintWarning(fmt.Sprintf("Infisical secret named [%v] has been removed because it is a reserved secret name", reservedEnvName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now add infisical secrets
|
// now add infisical secrets
|
||||||
for k, v := range secretsByKey {
|
for k, v := range secretsByKey {
|
||||||
@@ -149,6 +143,37 @@ var runCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
reservedEnvVars = []string{
|
||||||
|
"HOME", "PATH", "PS1", "PS2",
|
||||||
|
"PWD", "EDITOR", "XAUTHORITY", "USER",
|
||||||
|
"TERM", "TERMINFO", "SHELL", "MAIL",
|
||||||
|
}
|
||||||
|
|
||||||
|
reservedEnvVarPrefixes = []string{
|
||||||
|
"XDG_",
|
||||||
|
"LC_",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func filterReservedEnvVars(env map[string]models.SingleEnvironmentVariable) {
|
||||||
|
for _, reservedEnvName := range reservedEnvVars {
|
||||||
|
if _, ok := env[reservedEnvName]; ok {
|
||||||
|
delete(env, reservedEnvName)
|
||||||
|
util.PrintWarning(fmt.Sprintf("Infisical secret named [%v] has been removed because it is a reserved secret name", reservedEnvName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, reservedEnvPrefix := range reservedEnvVarPrefixes {
|
||||||
|
for envName := range env {
|
||||||
|
if strings.HasPrefix(envName, reservedEnvPrefix) {
|
||||||
|
delete(env, envName)
|
||||||
|
util.PrintWarning(fmt.Sprintf("Infisical secret named [%v] has been removed because it contains a reserved prefix", envName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(runCmd)
|
rootCmd.AddCommand(runCmd)
|
||||||
runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/Infisical/infisical-merge/packages/util"
|
"github.com/Infisical/infisical-merge/packages/util"
|
||||||
"github.com/Infisical/infisical-merge/packages/visualize"
|
"github.com/Infisical/infisical-merge/packages/visualize"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ var secretsCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,7 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
|
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -132,6 +133,11 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
||||||
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(loggedInUserDetails.UserCredentials.PrivateKey)
|
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(loggedInUserDetails.UserCredentials.PrivateKey)
|
||||||
|
|
||||||
|
if len(currentUsersPrivateKey) == 0 || len(encryptedWorkspaceKeySenderPublicKey) == 0 {
|
||||||
|
log.Debugf("Missing credentials for generating plainTextEncryptionKey: [currentUsersPrivateKey=%s] [encryptedWorkspaceKeySenderPublicKey=%s]", currentUsersPrivateKey, encryptedWorkspaceKeySenderPublicKey)
|
||||||
|
util.PrintErrorMessageAndExit("Some required user credentials are missing to generate your [plainTextEncryptionKey]. Please run [infisical login] then try again")
|
||||||
|
}
|
||||||
|
|
||||||
// decrypt workspace key
|
// decrypt workspace key
|
||||||
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||||
|
|
||||||
@@ -277,7 +283,7 @@ var secretsDeleteCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -338,7 +344,7 @@ var secretsDeleteCmd = &cobra.Command{
|
|||||||
func getSecretsByNames(cmd *cobra.Command, args []string) {
|
func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -361,10 +367,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
requestedSecrets := []models.SingleEnvironmentVariable{}
|
requestedSecrets := []models.SingleEnvironmentVariable{}
|
||||||
|
|
||||||
secretsMap := make(map[string]models.SingleEnvironmentVariable)
|
secretsMap := getSecretsByKeys(secrets)
|
||||||
for _, secret := range secrets {
|
|
||||||
secretsMap[secret.Key] = secret
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, secretKeyFromArg := range args {
|
for _, secretKeyFromArg := range args {
|
||||||
if value, ok := secretsMap[strings.ToUpper(secretKeyFromArg)]; ok {
|
if value, ok := secretsMap[strings.ToUpper(secretKeyFromArg)]; ok {
|
||||||
@@ -384,7 +387,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
|||||||
func generateExampleEnv(cmd *cobra.Command, args []string) {
|
func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||||
environmentName, _ := cmd.Flags().GetString("env")
|
environmentName, _ := cmd.Flags().GetString("env")
|
||||||
if !cmd.Flags().Changed("env") {
|
if !cmd.Flags().Changed("env") {
|
||||||
environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch()
|
environmentFromWorkspace := util.GetEnvFromWorkspaceFile()
|
||||||
if environmentFromWorkspace != "" {
|
if environmentFromWorkspace != "" {
|
||||||
environmentName = environmentFromWorkspace
|
environmentName = environmentFromWorkspace
|
||||||
}
|
}
|
||||||
@@ -587,7 +590,7 @@ func addHash(input string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
|
func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
|
||||||
secretMapByName := make(map[string]models.SingleEnvironmentVariable)
|
secretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||||
|
|
||||||
for _, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
secretMapByName[secret.Key] = secret
|
secretMapByName[secret.Key] = secret
|
||||||
|
|||||||
@@ -55,4 +55,5 @@ type GetAllSecretsParameters struct {
|
|||||||
EnvironmentPassedViaFlag bool
|
EnvironmentPassedViaFlag bool
|
||||||
InfisicalToken string
|
InfisicalToken string
|
||||||
TagSlugs string
|
TagSlugs string
|
||||||
|
WorkspaceId string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckForUpdate() {
|
func CheckForUpdate() {
|
||||||
@@ -15,7 +20,26 @@ func CheckForUpdate() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if latestVersion != CLI_VERSION {
|
if latestVersion != CLI_VERSION {
|
||||||
PrintWarning(fmt.Sprintf("Please update your CLI. You are running version %s but the latest version is %s", CLI_VERSION, latestVersion))
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
blue := color.New(color.FgCyan).SprintFunc()
|
||||||
|
black := color.New(color.FgBlack).SprintFunc()
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("%s %s %s %s",
|
||||||
|
yellow("A new release of infisical is available:"),
|
||||||
|
blue(CLI_VERSION),
|
||||||
|
black("->"),
|
||||||
|
blue(latestVersion),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stderr, msg)
|
||||||
|
|
||||||
|
updateInstructions := GetUpdateInstructions()
|
||||||
|
|
||||||
|
if updateInstructions != "" {
|
||||||
|
msg = fmt.Sprintf("\n%s\n", GetUpdateInstructions())
|
||||||
|
fmt.Fprintln(os.Stderr, msg)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +50,7 @@ func getLatestTag(repoOwner string, repoName string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return "", errors.New(fmt.Sprintf("GitHub API returned status code %d", resp.StatusCode))
|
return "", errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
@@ -44,3 +68,53 @@ func getLatestTag(repoOwner string, repoName string) (string, error) {
|
|||||||
|
|
||||||
return tags[0].Name[1:], nil
|
return tags[0].Name[1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUpdateInstructions() string {
|
||||||
|
os := runtime.GOOS
|
||||||
|
switch os {
|
||||||
|
case "darwin":
|
||||||
|
return "To update, run: brew update && brew upgrade infisical"
|
||||||
|
case "windows":
|
||||||
|
return "To update, run: scoop update infisical"
|
||||||
|
case "linux":
|
||||||
|
pkgManager := getLinuxPackageManager()
|
||||||
|
switch pkgManager {
|
||||||
|
case "apt-get":
|
||||||
|
return "To update, run: sudo apt-get update && sudo apt-get install infisical"
|
||||||
|
case "yum":
|
||||||
|
return "To update, run: sudo yum update infisical"
|
||||||
|
case "apk":
|
||||||
|
return "To update, run: sudo apk update && sudo apk upgrade infisical"
|
||||||
|
case "yay":
|
||||||
|
return "To update, run: yay -Syu infisical"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLinuxPackageManager() string {
|
||||||
|
cmd := exec.Command("apt-get", "--version")
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return "apt-get"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command("yum", "--version")
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return "yum"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command("yay", "--version")
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return "yay"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command("apk", "--version")
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return "apk"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/Infisical/infisical-merge/packages/models"
|
"github.com/Infisical/infisical-merge/packages/models"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -41,7 +42,7 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create file in directory
|
// Create file in directory
|
||||||
err = WriteToFile(fullConfigFilePath, configFileMarshalled, os.ModePerm)
|
err = WriteToFile(fullConfigFilePath, configFileMarshalled, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,12 @@ func WorkspaceConfigFileExistsInCurrentPath() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) {
|
func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) {
|
||||||
configFileAsBytes, err := os.ReadFile(INFISICAL_WORKSPACE_CONFIG_FILE_NAME)
|
cfgFile, err := FindWorkspaceConfigFile()
|
||||||
|
if err != nil {
|
||||||
|
return models.WorkspaceConfigFile{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configFileAsBytes, err := os.ReadFile(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.WorkspaceConfigFile{}, err
|
return models.WorkspaceConfigFile{}, err
|
||||||
}
|
}
|
||||||
@@ -87,6 +93,37 @@ func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) {
|
|||||||
return workspaceConfigFile, nil
|
return workspaceConfigFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindWorkspaceConfigFile searches for a .infisical.json file in the current directory and all parent directories.
|
||||||
|
func FindWorkspaceConfigFile() (string, error) {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
path := filepath.Join(dir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME)
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
// file found
|
||||||
|
log.Debugf("FindWorkspaceConfigFile: workspace file found at [path=%s]", path)
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have reached the root directory
|
||||||
|
if dir == filepath.Dir(dir) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// move up one directory
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// file not found
|
||||||
|
return "", fmt.Errorf("file not found: %s", INFISICAL_WORKSPACE_CONFIG_FILE_NAME)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func GetFullConfigFilePath() (fullPathToFile string, fullPathToDirectory string, err error) {
|
func GetFullConfigFilePath() (fullPathToFile string, fullPathToDirectory string, err error) {
|
||||||
homeDir, err := GetHomeDir()
|
homeDir, err := GetHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,52 +151,6 @@ func GetWorkspaceConfigByPath(path string) (workspaceConfig models.WorkspaceConf
|
|||||||
return workspaceConfigFile, nil
|
return workspaceConfigFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will get the list of .infisical.json files that are located
|
|
||||||
// within the root of each sub folder from where the CLI is ran from
|
|
||||||
func GetAllWorkSpaceConfigsStartingFromCurrentPath() (workspaces []models.WorkspaceConfigFile, err error) {
|
|
||||||
currentDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetAllProjectConfigs: unable to get the current directory because [%s]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := os.ReadDir(currentDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetAllProjectConfigs: unable to read the contents of the current directory because [%s]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
listOfWorkSpaceConfigs := []models.WorkspaceConfigFile{}
|
|
||||||
for _, file := range files {
|
|
||||||
if !file.IsDir() && file.Name() == INFISICAL_WORKSPACE_CONFIG_FILE_NAME {
|
|
||||||
pathToWorkspaceConfigFile := currentDir + "/" + INFISICAL_WORKSPACE_CONFIG_FILE_NAME
|
|
||||||
|
|
||||||
workspaceConfig, err := GetWorkspaceConfigByPath(pathToWorkspaceConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetAllProjectConfigs: Unable to get config file because [%s]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
listOfWorkSpaceConfigs = append(listOfWorkSpaceConfigs, workspaceConfig)
|
|
||||||
|
|
||||||
} else if file.IsDir() {
|
|
||||||
pathToSubFolder := currentDir + "/" + file.Name()
|
|
||||||
pathToMaybeWorkspaceConfigFile := pathToSubFolder + "/" + INFISICAL_WORKSPACE_CONFIG_FILE_NAME
|
|
||||||
|
|
||||||
_, err := os.Stat(pathToMaybeWorkspaceConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
continue // workspace config file doesn't exist
|
|
||||||
}
|
|
||||||
|
|
||||||
workspaceConfig, err := GetWorkspaceConfigByPath(pathToMaybeWorkspaceConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetAllProjectConfigs: Unable to get config file because [%s]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
listOfWorkSpaceConfigs = append(listOfWorkSpaceConfigs, workspaceConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return listOfWorkSpaceConfigs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the infisical config file and if it doesn't exist, return empty config model, otherwise raise error
|
// Get the infisical config file and if it doesn't exist, return empty config model, otherwise raise error
|
||||||
func GetConfigFile() (models.ConfigFile, error) {
|
func GetConfigFile() (models.ConfigFile, error) {
|
||||||
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
||||||
@@ -206,7 +197,7 @@ func WriteConfigFile(configFile *models.ConfigFile) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create file in directory
|
// Create file in directory
|
||||||
err = os.WriteFile(fullConfigFilePath, configFileMarshalled, os.ModePerm)
|
err = os.WriteFile(fullConfigFilePath, configFileMarshalled, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("writeConfigFile: Unable to write to file [err=%s]", err)
|
return fmt.Errorf("writeConfigFile: Unable to write to file [err=%s]", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ func RequireServiceToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RequireLocalWorkspaceFile() {
|
func RequireLocalWorkspaceFile() {
|
||||||
workspaceFileExists := WorkspaceConfigFileExistsInCurrentPath()
|
workspaceFilePath, _ := FindWorkspaceConfigFile()
|
||||||
if !workspaceFileExists {
|
if workspaceFilePath == "" {
|
||||||
PrintErrorMessageAndExit("It looks you have not yet connected this project to Infisical", "To do so, run [infisical init] then run your command again")
|
PrintErrorMessageAndExit("It looks you have not yet connected this project to Infisical", "To do so, run [infisical init] then run your command again")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +115,20 @@ func GetHashFromStringList(list []string) string {
|
|||||||
return fmt.Sprintf("%x", sum)
|
return fmt.Sprintf("%x", sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execCmd is a struct that holds the command and arguments to be executed.
|
||||||
|
// By using this struct, we can easily mock the command and arguments.
|
||||||
|
type execCmd struct {
|
||||||
|
cmd string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var getCurrentBranchCmd = execCmd{
|
||||||
|
cmd: "git",
|
||||||
|
args: []string{"symbolic-ref", "--short", "HEAD"},
|
||||||
|
}
|
||||||
|
|
||||||
func getCurrentBranch() (string, error) {
|
func getCurrentBranch() (string, error) {
|
||||||
cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD")
|
cmd := exec.Command(getCurrentBranchCmd.cmd, getCurrentBranchCmd.args...)
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ func PrintErrorAndExit(exitCode int, err error, messages ...string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportMsg := fmt.Sprintf("\n\nIf this issue continues, get support at https://infisical.com/slack")
|
||||||
|
fmt.Fprintln(os.Stderr, supportMsg)
|
||||||
|
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,10 +76,31 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
|
|||||||
return nil, fmt.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
return nil, fmt.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
encryptedWorkspaceKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
||||||
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey)
|
if err != nil {
|
||||||
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKey")
|
||||||
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(receiversPrivateKey)
|
}
|
||||||
|
|
||||||
|
encryptedWorkspaceKeySenderPublicKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKeySenderPublicKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedWorkspaceKeyNonce, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKeyNonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(receiversPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(err, "Unable to get bytes represented by the base64 for currentUsersPrivateKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(currentUsersPrivateKey) == 0 || len(encryptedWorkspaceKeySenderPublicKey) == 0 {
|
||||||
|
log.Debugf("Missing credentials for generating plainTextEncryptionKey: [currentUsersPrivateKey=%s] [encryptedWorkspaceKeySenderPublicKey=%s]", currentUsersPrivateKey, encryptedWorkspaceKeySenderPublicKey)
|
||||||
|
PrintErrorMessageAndExit("Some required user credentials are missing to generate your [plainTextEncryptionKey]. Please run [infisical login] then try again")
|
||||||
|
}
|
||||||
|
|
||||||
plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||||
|
|
||||||
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||||
@@ -131,6 +152,10 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.WorkspaceId != "" {
|
||||||
|
workspaceFile.WorkspaceId = params.WorkspaceId
|
||||||
|
}
|
||||||
|
|
||||||
// Verify environment
|
// Verify environment
|
||||||
err = ValidateEnvironmentName(params.Environment, workspaceFile.WorkspaceId, loggedInUserDetails.UserCredentials)
|
err = ValidateEnvironmentName(params.Environment, workspaceFile.WorkspaceId, loggedInUserDetails.UserCredentials)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -415,7 +440,7 @@ func WriteBackupSecrets(workspace string, environment string, encryptionKey []by
|
|||||||
}
|
}
|
||||||
|
|
||||||
listOfSecretsMarshalled, _ := json.Marshal(encryptedSecrets)
|
listOfSecretsMarshalled, _ := json.Marshal(encryptedSecrets)
|
||||||
err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, os.ModePerm)
|
err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err)
|
return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err)
|
||||||
}
|
}
|
||||||
@@ -482,21 +507,29 @@ func DeleteBackupSecrets() error {
|
|||||||
return os.RemoveAll(fullPathToSecretsBackupFolder)
|
return os.RemoveAll(fullPathToSecretsBackupFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnvelopmentBasedOnGitBranch() string {
|
func GetEnvFromWorkspaceFile() string {
|
||||||
|
workspaceFile, err := GetWorkSpaceFromFile()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("getEnvFromWorkspaceFile: [err=%s]", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if env := GetEnvelopmentBasedOnGitBranch(workspaceFile); env != "" {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspaceFile.DefaultEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnvelopmentBasedOnGitBranch(workspaceFile models.WorkspaceConfigFile) string {
|
||||||
branch, err := getCurrentBranch()
|
branch, err := getCurrentBranch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err)
|
log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceFile, err := GetWorkSpaceFromFile()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
envBasedOnGitBranch, ok := workspaceFile.GitBranchToEnvironmentMapping[branch]
|
envBasedOnGitBranch, ok := workspaceFile.GitBranchToEnvironmentMapping[branch]
|
||||||
|
|
||||||
log.Debugf("GetEnvelopmentBasedOnGitBranch: [envBasedOnGitBranch=%s] [ok=%s]", envBasedOnGitBranch, ok)
|
log.Debugf("GetEnvelopmentBasedOnGitBranch: [envBasedOnGitBranch=%s] [ok=%t]", envBasedOnGitBranch, ok)
|
||||||
|
|
||||||
if err == nil && ok {
|
if err == nil && ok {
|
||||||
return envBasedOnGitBranch
|
return envBasedOnGitBranch
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Infisical/infisical-merge/packages/models"
|
"github.com/Infisical/infisical-merge/packages/models"
|
||||||
@@ -158,3 +161,98 @@ func Test_SubstituteSecrets_When_No_SubstituteNeeded(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_Read_Env_From_File(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
TestFile string
|
||||||
|
ExpectedEnv string
|
||||||
|
}
|
||||||
|
|
||||||
|
var cases = []testCase{
|
||||||
|
{
|
||||||
|
TestFile: "testdata/infisical-default-env.json",
|
||||||
|
ExpectedEnv: "myDefaultEnv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestFile: "testdata/infisical-branch-env.json",
|
||||||
|
ExpectedEnv: "myMainEnv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TestFile: "testdata/infisical-no-matching-branch-env.json",
|
||||||
|
ExpectedEnv: "myDefaultEnv",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a tmp directory for testing
|
||||||
|
testDir, err := os.MkdirTemp(os.TempDir(), "infisical-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to create temp directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// safe the current working directory
|
||||||
|
originalDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to get current working directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// backup the original git command
|
||||||
|
originalGitCmd := getCurrentBranchCmd
|
||||||
|
|
||||||
|
// make sure to clean up after the test
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Chdir(originalDir)
|
||||||
|
os.RemoveAll(testDir)
|
||||||
|
getCurrentBranchCmd = originalGitCmd
|
||||||
|
})
|
||||||
|
|
||||||
|
// mock the git command to return "main" as the current branch
|
||||||
|
getCurrentBranchCmd = execCmd{cmd: "echo", args: []string{"main"}}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
// make sure we start in the original directory
|
||||||
|
err = os.Chdir(originalDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove old test file if it exists
|
||||||
|
err = os.Remove(path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to remove old test file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploy the test file
|
||||||
|
copyTestFile(t, c.TestFile, path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
|
||||||
|
|
||||||
|
// change the working directory to the tmp directory
|
||||||
|
err = os.Chdir(testDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get env from file
|
||||||
|
env := GetEnvFromWorkspaceFile()
|
||||||
|
if env != c.ExpectedEnv {
|
||||||
|
t.Errorf("Test_Read_DefaultEnv_From_File: Expected env to be %s but got %s", c.ExpectedEnv, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyTestFile(t *testing.T, src, dst string) {
|
||||||
|
srcFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to open source file: %s", err)
|
||||||
|
}
|
||||||
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
dstFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to create destination file: %s", err)
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(dstFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to copy file: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"workspaceId": "12345678",
|
||||||
|
"defaultEnvironment": "myDefaultEnv",
|
||||||
|
"gitBranchToEnvironmentMapping": {
|
||||||
|
"main": "myMainEnv"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"workspaceId": "12345678",
|
||||||
|
"defaultEnvironment": "myDefaultEnv",
|
||||||
|
"gitBranchToEnvironmentMapping": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"workspaceId": "12345678",
|
||||||
|
"defaultEnvironment": "myDefaultEnv",
|
||||||
|
"gitBranchToEnvironmentMapping": {
|
||||||
|
"notmain": "myMainEnv"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '3'
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx:
|
nginx:
|
||||||
@@ -22,7 +22,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
image: infisical/backend
|
image: infisical/backend
|
||||||
command: npm run start
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
|||||||
@@ -11,18 +11,14 @@ infisical export [options]
|
|||||||
|
|
||||||
Export environment variables from the platform into a file format.
|
Export environment variables from the platform into a file format.
|
||||||
|
|
||||||
## Options
|
## Subcommands & flags
|
||||||
|
|
||||||
| Option | Description | Default value |
|
<Accordion title="infisical export" defaultOpen="true">
|
||||||
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
Use this command to export environment variables from the platform into a raw file formats
|
||||||
| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` |
|
|
||||||
| `--projectId` | Only required if injecting via the [service token method](../token). If you are not using service token, the project id will be automatically retrieved from the `.infisical.json` located at the root of your local project. | `None` |
|
|
||||||
| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` |
|
|
||||||
| `--format` | Format of the output file. Accepted values: `dotenv`, `dotenv-export`, `csv` and `json` | `dotenv` |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
$ infisical export
|
||||||
|
|
||||||
# Export variables to a .env file
|
# Export variables to a .env file
|
||||||
infisical export > .env
|
infisical export > .env
|
||||||
|
|
||||||
@@ -37,5 +33,62 @@ infisical export --format=json > secrets.json
|
|||||||
|
|
||||||
# Export variables to a YAML file
|
# Export variables to a YAML file
|
||||||
infisical export --format=yaml > secrets.yaml
|
infisical export --format=yaml > secrets.yaml
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### flags
|
||||||
|
<Accordion title="--env">
|
||||||
|
Used to set the environment that secrets are pulled from.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example
|
||||||
|
infisical export --env=prod
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: this flag only accepts environment slug names not the fully qualified name. To view the slug name of an environment, visit the project settings page.
|
||||||
|
|
||||||
|
default value: `dev`
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--projectId">
|
||||||
|
By default the project id is retrieved from the `.infisical.json` located at the root of your local project.
|
||||||
|
This flag allows you to override this behavior by explicitly defining the project to fetch your secrets from.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example
|
||||||
|
|
||||||
|
infisical export --projectId=XXXXXXXXXXXXXX
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--expand">
|
||||||
|
Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`)
|
||||||
|
|
||||||
|
Default value: `true`
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--format">
|
||||||
|
Format of the output file. Accepted values: `dotenv`, `dotenv-export`, `csv` and `json`
|
||||||
|
|
||||||
|
Default value: `dotenv`
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--secret-overriding">
|
||||||
|
Prioritizes personal secrets with the same name over shared secrets
|
||||||
|
|
||||||
|
Default value: `true`
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--tags">
|
||||||
|
When working with tags, you can use this flag to filter and retrieve only secrets that are associated with a specific tag(s).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example
|
||||||
|
infisical run --tags=tag1,tag2,tag3 -- npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: you must reference the tag by its slug name not its fully qualified name. Go to project settings to view all tag slugs.
|
||||||
|
|
||||||
|
By default, all secrets are fetched
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
|
|||||||
@@ -79,4 +79,17 @@ Inject secrets from Infisical into your application process.
|
|||||||
Default value: `true`
|
Default value: `true`
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="--tags">
|
||||||
|
When working with tags, you can use this flag to filter and retrieve only secrets that are associated with a specific tag(s).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example
|
||||||
|
infisical run --tags=tag1,tag2,tag3 -- npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: you must reference the tag by its slug name not its fully qualified name. Go to project settings to view all tag slugs.
|
||||||
|
|
||||||
|
By default, all secrets are fetched
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|||||||
@@ -45,19 +45,19 @@ The Infisical CLI provides a way to inject environment variables from the platfo
|
|||||||
<Tab title="Alpine">
|
<Tab title="Alpine">
|
||||||
Install prerequisite
|
Install prerequisite
|
||||||
```bash
|
```bash
|
||||||
sudo apk add --no-cache bash sudo
|
apk add --no-cache bash sudo
|
||||||
```
|
```
|
||||||
|
|
||||||
Add Infisical repository
|
Add Infisical repository
|
||||||
```bash
|
```bash
|
||||||
curl -1sLf \
|
curl -1sLf \
|
||||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
||||||
| sudo -E bash
|
| bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Then install CLI
|
Then install CLI
|
||||||
```bash
|
```bash
|
||||||
sudo apk update && sudo apk add infisical
|
apk update && sudo apk add infisical
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -7,6 +7,24 @@ To link your local project on your machine with an Infisical project, we suggest
|
|||||||
|
|
||||||
The `.infisical.json` file specifies various parameters, such as the Infisical project to retrieve secrets from, along with other configuration options. Furthermore, you can define additional properties in the file to further tailor your local development experience.
|
The `.infisical.json` file specifies various parameters, such as the Infisical project to retrieve secrets from, along with other configuration options. Furthermore, you can define additional properties in the file to further tailor your local development experience.
|
||||||
|
|
||||||
|
## Set default environment
|
||||||
|
If you need to change environments while using the CLI, you can do so by including the `--env` flag in your command.
|
||||||
|
However, this can be inconvenient if you typically work in just one environment.
|
||||||
|
To simplify the process, you can establish a default environment, which will be used for every command unless you specify otherwise.
|
||||||
|
|
||||||
|
```json .infisical.json
|
||||||
|
{
|
||||||
|
"workspaceId": "63ee5410a45f7a1ed39ba118",
|
||||||
|
"defaultEnvironment": "test",
|
||||||
|
"gitBranchToEnvironmentMapping": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### How it works
|
||||||
|
If both `defaultEnvironment` and `gitBranchToEnvironmentMapping` are configured, `gitBranchToEnvironmentMapping` will take precedence over `defaultEnvironment`.
|
||||||
|
However, if `gitBranchToEnvironmentMapping` is not set and `defaultEnvironment` is, then the `defaultEnvironment` will be used to execute your Infisical CLI commands.
|
||||||
|
If you wish to override the `defaultEnvironment`, you can do so by using the `--env` flag explicitly.
|
||||||
|
|
||||||
## Set Infisical environment based on GitHub branch
|
## Set Infisical environment based on GitHub branch
|
||||||
When fetching your secrets from Infisical, you can switch between environments by using the `--env` flag. However, in certain cases, you may prefer the environment to be automatically mapped based on the current GitHub branch you are working on.
|
When fetching your secrets from Infisical, you can switch between environments by using the `--env` flag. However, in certain cases, you may prefer the environment to be automatically mapped based on the current GitHub branch you are working on.
|
||||||
To achieve this, simply add the `gitBranchToEnvironmentMapping` property to your configuration file, as shown below.
|
To achieve this, simply add the `gitBranchToEnvironmentMapping` property to your configuration file, as shown below.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
title: "Activity Logs"
|
title: "Audit Logs"
|
||||||
description: "See which events are triggered within your Infisical project."
|
description: "See which events are triggered within your Infisical project."
|
||||||
---
|
---
|
||||||
|
|
||||||
Activity logs record all actions going through Infisical including who performed which CRUD operations on environment variables and from what IP address. They help answer questions like:
|
Audit logs record all actions going through Infisical including who performed which CRUD operations on environment variables and from what IP address. They help answer questions like:
|
||||||
|
|
||||||
- Who added or updated environment variables recently?
|
- Who added or updated environment variables recently?
|
||||||
- Did Bob read environment variables last week (if at all)?
|
- Did Bob read environment variables last week (if at all)?
|
||||||
- What IP address was used for that action?
|
- What IP address was used for that action?
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -12,7 +12,7 @@ This is a non-exhaustive list of features that Infisical offers:
|
|||||||
- Sync secrets to platforms via integrations to platforms like GitHub, Vercel, and Netlify.
|
- Sync secrets to platforms via integrations to platforms like GitHub, Vercel, and Netlify.
|
||||||
- Rollback secrets to any point in time.
|
- Rollback secrets to any point in time.
|
||||||
- Rollback each secrets to any version.
|
- Rollback each secrets to any version.
|
||||||
- Track actions through activity logs.
|
- Track actions through audit logs.
|
||||||
|
|
||||||
## CLI
|
## CLI
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 379 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
Before Width: | Height: | Size: 424 KiB After Width: | Height: | Size: 424 KiB |
@@ -1,19 +1,54 @@
|
|||||||
---
|
---
|
||||||
title: "Gitlab Pipeline"
|
title: "GitLab"
|
||||||
|
description: "How to automatically sync secrets from Infisical into GitLab."
|
||||||
---
|
---
|
||||||
|
|
||||||
To integrate Infisical secrets into your Gitlab CI/CD setup, three steps are required.
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Standard">
|
||||||
|
## Navigate to your project's integrations tab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Authorize Infisical for GitLab
|
||||||
|
|
||||||
|
Press on the GitLab tile and grant Infisical access to your GitLab account.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<Info>
|
||||||
|
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.
|
||||||
|
</Info>
|
||||||
|
## Start integration
|
||||||
|
|
||||||
|
Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab.
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Pipeline">
|
||||||
|
|
||||||
## Generate service token
|
## Generate service token
|
||||||
To expose Infisical secrets in Gitlab CI/CD, you must generate a service token for the specific project and environment in Infisical. For instructions on how to generate a service token, refer to [this page](../../getting-started/dashboard/token)
|
|
||||||
|
|
||||||
## Set Infisical service token in Gitlab
|
Generate an [Infisical Token](../../getting-started/dashboard/token) for the specific project and environment in Infisical.
|
||||||
To provide Infisical CLI with the service token generated in the previous step, go to **Settings > CI/CD > Variables** in Gitlab and create a new **INFISICAL_TOKEN** variable. Enter the generated service token as its value.
|
|
||||||
|
## Set the Infisical Token in Gitlab
|
||||||
|
|
||||||
|
Create a new variable called `INFISICAL_TOKEN` with the value set to the token from the previous step in Settings > CI/CD > Variables of your GitLab repository.
|
||||||
|
|
||||||
## Configure Infisical in your pipeline
|
## Configure Infisical in your pipeline
|
||||||
Edit your .gitlab-ci.yml to include the installation of the Infisical CLI. This will allow you to use the CLI for fetching and injecting secrets into any script or command within your Gitlab CI/CD process.
|
|
||||||
|
Edit your `.gitlab-ci.yml` to include the Infisical CLI installation. This will allow you to use the CLI for fetching and injecting secrets into any script or command within your Gitlab CI/CD process.
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ubuntu
|
image: ubuntu
|
||||||
|
|
||||||
@@ -29,6 +64,7 @@ build-job:
|
|||||||
- curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash
|
- curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash
|
||||||
- apt-get update && apt-get install -y infisical
|
- apt-get update && apt-get install -y infisical
|
||||||
- infisical run -- npm run build
|
- infisical run -- npm run build
|
||||||
|
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "Azure Key Vault"
|
||||||
|
description: "How to automatically sync secrets from Infisical into your Azure Key Vault."
|
||||||
|
---
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
- Set up Azure and have an existing key vault
|
||||||
|
|
||||||
|
## Navigate to your project's integrations tab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Authorize Infisical for Azure Key Vault
|
||||||
|
|
||||||
|
Press on the Azure Key Vault tile and grant Infisical access to Azure Key Vault.
|
||||||
|
|
||||||
|
## Start Integration
|
||||||
|
|
||||||
|
Obtain the Vault URI of your key vault in the Overview tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Select which Infisical environment secrets you want to sync to your key vault. Then, input your Vault URI from the previous step. Finally, press create integration to start syncing secrets to Azure Key Vault.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<Info>
|
||||||
|
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.
|
||||||
|
</Info>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
title: "Terraform"
|
||||||
|
description: "How to use Infisical to inject environment variables and secrets into terraform."
|
||||||
|
---
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
- [Install the CLI](/cli/overview)
|
||||||
|
|
||||||
|
## Initialize Infisical for your [Terraform](https://www.terraform.io/) project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# navigate to the root of your of your project
|
||||||
|
cd /path/to/project
|
||||||
|
|
||||||
|
# then initialize Infisical
|
||||||
|
infisical init
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run terraform as usual but with Infisical
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infisical run -- <your application start command>
|
||||||
|
|
||||||
|
# Example
|
||||||
|
infisical run -- terraform plan
|
||||||
|
```
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
To inject any arbitrary variable to terraform, you have
|
||||||
|
to prefix them with `TF_VAR`. Read more about that
|
||||||
|
[here](https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_var_name).
|
||||||
|
</Note>
|
||||||
@@ -12,6 +12,7 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
|||||||
| [Docker](/integrations/platforms/docker) | Platform | Available |
|
| [Docker](/integrations/platforms/docker) | Platform | Available |
|
||||||
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
|
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
|
||||||
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
|
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
|
||||||
|
| [Terraform](/integrations/frameworks/terraform) | Infrastructure as code | Available |
|
||||||
| [PM2](/integrations/platforms/pm2) | Platform | Available |
|
| [PM2](/integrations/platforms/pm2) | Platform | Available |
|
||||||
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
|
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
|
||||||
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
|
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
|
||||||
@@ -20,8 +21,9 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
|||||||
| [Fly.io](/integrations/cloud/flyio) | Cloud | Available |
|
| [Fly.io](/integrations/cloud/flyio) | Cloud | Available |
|
||||||
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
||||||
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
||||||
|
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
|
||||||
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
||||||
| [GitLab Pipeline](/integrations/cicd/gitlab) | CI/CD | Available |
|
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
|
||||||
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
|
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
|
||||||
| [Travis CI](/integrations/cicd/travisci) | CI/CD | Available |
|
| [Travis CI](/integrations/cicd/travisci) | CI/CD | Available |
|
||||||
| [React](/integrations/frameworks/react) | Framework | Available |
|
| [React](/integrations/frameworks/react) | Framework | Available |
|
||||||
@@ -38,9 +40,7 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
|||||||
| [Flask](/integrations/frameworks/flask) | Framework | Available |
|
| [Flask](/integrations/frameworks/flask) | Framework | Available |
|
||||||
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
|
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
|
||||||
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
|
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
|
||||||
| AWS | Cloud | Coming soon |
|
|
||||||
| GCP | Cloud | Coming soon |
|
| GCP | Cloud | Coming soon |
|
||||||
| Azure | Cloud | Coming soon |
|
|
||||||
| DigitalOcean | Cloud | Coming soon |
|
| DigitalOcean | Cloud | Coming soon |
|
||||||
| GitHub Actions | CI/CD | Coming soon |
|
| GitHub Actions | CI/CD | Coming soon |
|
||||||
| Jenkins | CI/CD | Coming soon |
|
| Jenkins | CI/CD | Coming soon |
|
||||||
|
|||||||
@@ -54,11 +54,6 @@
|
|||||||
"icon": "cloud",
|
"icon": "cloud",
|
||||||
"url": "api-reference"
|
"url": "api-reference"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Integrations",
|
|
||||||
"icon": "plug",
|
|
||||||
"url": "integrations"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Contributing",
|
"name": "Contributing",
|
||||||
"icon": "code",
|
"icon": "code",
|
||||||
@@ -94,7 +89,6 @@
|
|||||||
"pages": [
|
"pages": [
|
||||||
"getting-started/dashboard/organization",
|
"getting-started/dashboard/organization",
|
||||||
"getting-started/dashboard/project",
|
"getting-started/dashboard/project",
|
||||||
"getting-started/dashboard/integrations",
|
|
||||||
"getting-started/dashboard/pit-recovery",
|
"getting-started/dashboard/pit-recovery",
|
||||||
"getting-started/dashboard/secret-versioning",
|
"getting-started/dashboard/secret-versioning",
|
||||||
"getting-started/dashboard/audit-logs",
|
"getting-started/dashboard/audit-logs",
|
||||||
@@ -103,7 +97,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "CLI",
|
"group": "Command line",
|
||||||
"pages": [
|
"pages": [
|
||||||
"cli/overview",
|
"cli/overview",
|
||||||
"cli/usage",
|
"cli/usage",
|
||||||
@@ -123,6 +117,60 @@
|
|||||||
"cli/faq"
|
"cli/faq"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "Integrations",
|
||||||
|
"pages": [
|
||||||
|
"integrations/overview",
|
||||||
|
{
|
||||||
|
"group": "Docker",
|
||||||
|
"pages": [
|
||||||
|
"integrations/platforms/docker",
|
||||||
|
"integrations/platforms/docker-compose"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"integrations/platforms/kubernetes",
|
||||||
|
"integrations/frameworks/terraform",
|
||||||
|
|
||||||
|
{
|
||||||
|
"group": "AWS",
|
||||||
|
"pages": [
|
||||||
|
"integrations/cloud/aws-parameter-store",
|
||||||
|
"integrations/cloud/aws-secret-manager"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"integrations/cloud/heroku",
|
||||||
|
"integrations/cloud/vercel",
|
||||||
|
"integrations/cloud/netlify",
|
||||||
|
"integrations/cloud/render",
|
||||||
|
"integrations/cloud/flyio",
|
||||||
|
|
||||||
|
"integrations/cloud/azure-key-vault",
|
||||||
|
|
||||||
|
"integrations/cicd/githubactions",
|
||||||
|
"integrations/cicd/gitlab",
|
||||||
|
"integrations/cicd/circleci",
|
||||||
|
"integrations/cicd/travisci",
|
||||||
|
|
||||||
|
"integrations/frameworks/react",
|
||||||
|
"integrations/frameworks/vue",
|
||||||
|
"integrations/frameworks/express",
|
||||||
|
"integrations/frameworks/nextjs",
|
||||||
|
"integrations/frameworks/nestjs",
|
||||||
|
"integrations/frameworks/nuxt",
|
||||||
|
"integrations/frameworks/gatsby",
|
||||||
|
"integrations/frameworks/remix",
|
||||||
|
"integrations/frameworks/vite",
|
||||||
|
"integrations/frameworks/fiber",
|
||||||
|
"integrations/frameworks/django",
|
||||||
|
"integrations/frameworks/flask",
|
||||||
|
"integrations/frameworks/laravel",
|
||||||
|
"integrations/frameworks/rails",
|
||||||
|
"integrations/frameworks/dotnet",
|
||||||
|
"integrations/platforms/pm2"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Self-hosting",
|
"group": "Self-hosting",
|
||||||
"pages": [
|
"pages": [
|
||||||
@@ -203,60 +251,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"group": "Integrations",
|
|
||||||
"pages": ["integrations/overview"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Platforms",
|
|
||||||
"pages": [
|
|
||||||
"integrations/platforms/docker",
|
|
||||||
"integrations/platforms/docker-compose",
|
|
||||||
"integrations/platforms/kubernetes",
|
|
||||||
"integrations/platforms/pm2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Cloud",
|
|
||||||
"pages": [
|
|
||||||
"integrations/cloud/heroku",
|
|
||||||
"integrations/cloud/vercel",
|
|
||||||
"integrations/cloud/netlify",
|
|
||||||
"integrations/cloud/render",
|
|
||||||
"integrations/cloud/flyio",
|
|
||||||
"integrations/cloud/aws-parameter-store",
|
|
||||||
"integrations/cloud/aws-secret-manager"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "CI/CD",
|
|
||||||
"pages": [
|
|
||||||
"integrations/cicd/githubactions",
|
|
||||||
"integrations/cicd/gitlab",
|
|
||||||
"integrations/cicd/circleci",
|
|
||||||
"integrations/cicd/travisci"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Frameworks",
|
|
||||||
"pages": [
|
|
||||||
"integrations/frameworks/react",
|
|
||||||
"integrations/frameworks/vue",
|
|
||||||
"integrations/frameworks/express",
|
|
||||||
"integrations/frameworks/nextjs",
|
|
||||||
"integrations/frameworks/nestjs",
|
|
||||||
"integrations/frameworks/nuxt",
|
|
||||||
"integrations/frameworks/gatsby",
|
|
||||||
"integrations/frameworks/remix",
|
|
||||||
"integrations/frameworks/vite",
|
|
||||||
"integrations/frameworks/fiber",
|
|
||||||
"integrations/frameworks/django",
|
|
||||||
"integrations/frameworks/flask",
|
|
||||||
"integrations/frameworks/laravel",
|
|
||||||
"integrations/frameworks/rails",
|
|
||||||
"integrations/frameworks/dotnet"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"group": "Security",
|
"group": "Security",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
@@ -134,4 +134,33 @@ SMTP_FROM_NAME=Infisical
|
|||||||
Remember that you will need to restart Infisical for this to work properly.
|
Remember that you will need to restart Infisical for this to work properly.
|
||||||
</Info>
|
</Info>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Zoho Mail">
|
||||||
|
|
||||||
|
1. Create an account and configure [Zoho Mail](https://www.zoho.com/mail/) to send emails.
|
||||||
|
|
||||||
|
2. With your email credentials, you can now set up your SMTP environment variables:
|
||||||
|
|
||||||
|
```
|
||||||
|
SMTP_HOST=smtp.zoho.com
|
||||||
|
SMTP_USERNAME=username # your email
|
||||||
|
SMTP_PASSWORD=password # your password
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_SECURE=true
|
||||||
|
[email protected] # your personal Zoho email or domain-based email linked to Zoho Mail
|
||||||
|
SMTP_FROM_NAME=Infisical
|
||||||
|
```
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
You can use either your personal Zoho email address like `[email protected]` or
|
||||||
|
a domain-based email address like `[email protected]`. If using a
|
||||||
|
domain-based email address, then please make sure that you've configured and
|
||||||
|
verified it with Zoho Mail.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Remember that you will need to restart Infisical for this to work properly.
|
||||||
|
</Info>
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = {
|
|||||||
debug: false,
|
debug: false,
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: "en",
|
defaultLocale: "en",
|
||||||
locales: ["en", "ko", "fr", "pt-BR"],
|
locales: ["en", "ko", "fr", "pt-BR", "pt-PT", "es"],
|
||||||
},
|
},
|
||||||
fallbackLng: {
|
fallbackLng: {
|
||||||
default: ["en"],
|
default: ["en"],
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.19",
|
"@fortawesome/react-fontawesome": "^0.1.19",
|
||||||
"@headlessui/react": "^1.6.6",
|
"@headlessui/react": "^1.7.7",
|
||||||
"@hookform/resolvers": "^2.9.10",
|
"@hookform/resolvers": "^2.9.10",
|
||||||
"@octokit/rest": "^19.0.7",
|
"@octokit/rest": "^19.0.7",
|
||||||
"@radix-ui/react-accordion": "^1.1.0",
|
"@radix-ui/react-accordion": "^1.1.0",
|
||||||
@@ -30,14 +30,14 @@
|
|||||||
"@radix-ui/react-tabs": "^1.0.2",
|
"@radix-ui/react-tabs": "^1.0.2",
|
||||||
"@radix-ui/react-toast": "^1.1.2",
|
"@radix-ui/react-toast": "^1.1.2",
|
||||||
"@reduxjs/toolkit": "^1.8.3",
|
"@reduxjs/toolkit": "^1.8.3",
|
||||||
"@stripe/react-stripe-js": "^1.10.0",
|
"@stripe/react-stripe-js": "^1.16.3",
|
||||||
"@stripe/stripe-js": "^1.46.0",
|
"@stripe/stripe-js": "^1.46.0",
|
||||||
"@tanstack/react-query": "^4.23.0",
|
"@tanstack/react-query": "^4.23.0",
|
||||||
"@types/argon2-browser": "^1.18.1",
|
"@types/argon2-browser": "^1.18.1",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"argon2-browser": "^1.18.0",
|
"argon2-browser": "^1.18.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"axios-auth-refresh": "^3.3.3",
|
"axios-auth-refresh": "^3.3.6",
|
||||||
"base64-loader": "^1.0.0",
|
"base64-loader": "^1.0.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"cookies": "^0.8.0",
|
"cookies": "^0.8.0",
|
||||||
@@ -45,13 +45,13 @@
|
|||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"i18next": "^22.4.6",
|
"i18next": "^22.4.9",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"next": "^12.3.4",
|
"next": "^12.3.4",
|
||||||
"next-i18next": "^13.0.2",
|
"next-i18next": "^13.0.2",
|
||||||
"posthog-js": "^1.34.0",
|
"posthog-js": "^1.39.4",
|
||||||
"query-string": "^7.1.3",
|
"query-string": "^7.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
@@ -2544,9 +2544,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@headlessui/react": {
|
"node_modules/@headlessui/react": {
|
||||||
"version": "1.6.6",
|
"version": "1.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
||||||
"integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==",
|
"integrity": "sha512-BqDOd/tB9u2tA0T3Z0fn18ktw+KbVwMnkxxsGPIH2hzssrQhKB5n/6StZOyvLYP/FsYtvuXfi9I0YowKPv2c1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"client-only": "^0.0.1"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -3935,9 +3938,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/types": {
|
"node_modules/@sentry/types": {
|
||||||
"version": "7.15.0",
|
"version": "7.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.22.0.tgz",
|
||||||
"integrity": "sha512-MN9haDRh9ZOsTotoDTHu2BT3sT8Vs1F0alhizUpDyjN2YgBCqR6JV+AbAE1XNHwS2+5zbppch1PwJUVeE58URQ==",
|
"integrity": "sha512-LhCL+wb1Jch+OesB2CIt6xpfO1Ab6CRvoNYRRzVumWPLns1T3ZJkarYfhbLaOEIb38EIbPgREdxn2AJT560U4Q==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -6281,14 +6284,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@stripe/react-stripe-js": {
|
"node_modules/@stripe/react-stripe-js": {
|
||||||
"version": "1.10.0",
|
"version": "1.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.16.3.tgz",
|
||||||
"integrity": "sha512-vuIjJUZJ3nyiaGa5z5iyMCzZfGGsgzOOjWjqknbbhkNsewyyginfeky9EZLSz9+iSAsgC9K6MeNOTLKVGcMycQ==",
|
"integrity": "sha512-gS6UDGEuM92K50pFB3o//EqqPxmaqpC8IrsBda4P4LxeULoO0pBFSHXJ5Ab6uA7G2lyO2bluvSLereh0OH9GrQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@stripe/stripe-js": "^1.34.0",
|
"@stripe/stripe-js": "^1.44.1",
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
@@ -8157,9 +8160,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios-auth-refresh": {
|
"node_modules/axios-auth-refresh": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.6.tgz",
|
||||||
"integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==",
|
"integrity": "sha512-2CeBUce/SxIfFxow5/n8vApJ97yYF6qoV4gh1UrswT7aEOnlOdBLxxyhOI4IaxGs6BY0l8YujU2jlc4aCmK17Q==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"axios": ">= 0.18 < 0.19.0 || >= 0.19.1"
|
"axios": ">= 0.18 < 0.19.0 || >= 0.19.1"
|
||||||
}
|
}
|
||||||
@@ -9205,6 +9208,11 @@
|
|||||||
"@colors/colors": "1.5.0"
|
"@colors/colors": "1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/client-only": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
||||||
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
@@ -13224,9 +13232,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/i18next": {
|
"node_modules/i18next": {
|
||||||
"version": "22.4.6",
|
"version": "22.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.9.tgz",
|
||||||
"integrity": "sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw==",
|
"integrity": "sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -17079,11 +17087,11 @@
|
|||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
},
|
},
|
||||||
"node_modules/posthog-js": {
|
"node_modules/posthog-js": {
|
||||||
"version": "1.34.0",
|
"version": "1.39.4",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.39.4.tgz",
|
||||||
"integrity": "sha512-HkRwwzdz31N5ykQIO3SIkSS8nwhdqqnuDZ/qltitX4FhxrV9/tSRavEXz0YLvioOXeNVmQWtsN3krKajErwkwg==",
|
"integrity": "sha512-Elpf1gwyuObueXi89iH+9pP+WhpkiivP8Qwej4RzOLwSTa7Floaa4rgAw7rnCnX1PtRoJ3F0kqb6q9T+aZjRiA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/types": "^7.2.0",
|
"@sentry/types": "7.22.0",
|
||||||
"fflate": "^0.4.1",
|
"fflate": "^0.4.1",
|
||||||
"rrweb-snapshot": "^1.1.14"
|
"rrweb-snapshot": "^1.1.14"
|
||||||
}
|
}
|
||||||
@@ -23946,10 +23954,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@headlessui/react": {
|
"@headlessui/react": {
|
||||||
"version": "1.6.6",
|
"version": "1.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz",
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz",
|
||||||
"integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==",
|
"integrity": "sha512-BqDOd/tB9u2tA0T3Z0fn18ktw+KbVwMnkxxsGPIH2hzssrQhKB5n/6StZOyvLYP/FsYtvuXfi9I0YowKPv2c1w==",
|
||||||
"requires": {}
|
"requires": {
|
||||||
|
"client-only": "^0.0.1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"@hookform/resolvers": {
|
"@hookform/resolvers": {
|
||||||
"version": "2.9.10",
|
"version": "2.9.10",
|
||||||
@@ -24987,9 +24997,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@sentry/types": {
|
"@sentry/types": {
|
||||||
"version": "7.15.0",
|
"version": "7.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.22.0.tgz",
|
||||||
"integrity": "sha512-MN9haDRh9ZOsTotoDTHu2BT3sT8Vs1F0alhizUpDyjN2YgBCqR6JV+AbAE1XNHwS2+5zbppch1PwJUVeE58URQ=="
|
"integrity": "sha512-LhCL+wb1Jch+OesB2CIt6xpfO1Ab6CRvoNYRRzVumWPLns1T3ZJkarYfhbLaOEIb38EIbPgREdxn2AJT560U4Q=="
|
||||||
},
|
},
|
||||||
"@sinclair/typebox": {
|
"@sinclair/typebox": {
|
||||||
"version": "0.24.51",
|
"version": "0.24.51",
|
||||||
@@ -26659,9 +26669,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@stripe/react-stripe-js": {
|
"@stripe/react-stripe-js": {
|
||||||
"version": "1.10.0",
|
"version": "1.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.16.3.tgz",
|
||||||
"integrity": "sha512-vuIjJUZJ3nyiaGa5z5iyMCzZfGGsgzOOjWjqknbbhkNsewyyginfeky9EZLSz9+iSAsgC9K6MeNOTLKVGcMycQ==",
|
"integrity": "sha512-gS6UDGEuM92K50pFB3o//EqqPxmaqpC8IrsBda4P4LxeULoO0pBFSHXJ5Ab6uA7G2lyO2bluvSLereh0OH9GrQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
}
|
}
|
||||||
@@ -28115,9 +28125,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"axios-auth-refresh": {
|
"axios-auth-refresh": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.6.tgz",
|
||||||
"integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==",
|
"integrity": "sha512-2CeBUce/SxIfFxow5/n8vApJ97yYF6qoV4gh1UrswT7aEOnlOdBLxxyhOI4IaxGs6BY0l8YujU2jlc4aCmK17Q==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"axobject-query": {
|
"axobject-query": {
|
||||||
@@ -28888,6 +28898,11 @@
|
|||||||
"string-width": "^4.2.0"
|
"string-width": "^4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"client-only": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
||||||
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
@@ -31990,9 +32005,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"i18next": {
|
"i18next": {
|
||||||
"version": "22.4.6",
|
"version": "22.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.9.tgz",
|
||||||
"integrity": "sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw==",
|
"integrity": "sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.20.6"
|
"@babel/runtime": "^7.20.6"
|
||||||
}
|
}
|
||||||
@@ -34690,11 +34705,11 @@
|
|||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
},
|
},
|
||||||
"posthog-js": {
|
"posthog-js": {
|
||||||
"version": "1.34.0",
|
"version": "1.39.4",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.34.0.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.39.4.tgz",
|
||||||
"integrity": "sha512-HkRwwzdz31N5ykQIO3SIkSS8nwhdqqnuDZ/qltitX4FhxrV9/tSRavEXz0YLvioOXeNVmQWtsN3krKajErwkwg==",
|
"integrity": "sha512-Elpf1gwyuObueXi89iH+9pP+WhpkiivP8Qwej4RzOLwSTa7Floaa4rgAw7rnCnX1PtRoJ3F0kqb6q9T+aZjRiA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "^7.2.0",
|
"@sentry/types": "7.22.0",
|
||||||
"fflate": "^0.4.1",
|
"fflate": "^0.4.1",
|
||||||
"rrweb-snapshot": "^1.1.14"
|
"rrweb-snapshot": "^1.1.14"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.19",
|
"@fortawesome/react-fontawesome": "^0.1.19",
|
||||||
"@headlessui/react": "^1.6.6",
|
"@headlessui/react": "^1.7.7",
|
||||||
"@hookform/resolvers": "^2.9.10",
|
"@hookform/resolvers": "^2.9.10",
|
||||||
"@octokit/rest": "^19.0.7",
|
"@octokit/rest": "^19.0.7",
|
||||||
"@radix-ui/react-accordion": "^1.1.0",
|
"@radix-ui/react-accordion": "^1.1.0",
|
||||||
@@ -37,14 +37,14 @@
|
|||||||
"@radix-ui/react-tabs": "^1.0.2",
|
"@radix-ui/react-tabs": "^1.0.2",
|
||||||
"@radix-ui/react-toast": "^1.1.2",
|
"@radix-ui/react-toast": "^1.1.2",
|
||||||
"@reduxjs/toolkit": "^1.8.3",
|
"@reduxjs/toolkit": "^1.8.3",
|
||||||
"@stripe/react-stripe-js": "^1.10.0",
|
"@stripe/react-stripe-js": "^1.16.3",
|
||||||
"@stripe/stripe-js": "^1.46.0",
|
"@stripe/stripe-js": "^1.46.0",
|
||||||
"@types/argon2-browser": "^1.18.1",
|
"@types/argon2-browser": "^1.18.1",
|
||||||
"@tanstack/react-query": "^4.23.0",
|
"@tanstack/react-query": "^4.23.0",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"argon2-browser": "^1.18.0",
|
"argon2-browser": "^1.18.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"axios-auth-refresh": "^3.3.3",
|
"axios-auth-refresh": "^3.3.6",
|
||||||
"base64-loader": "^1.0.0",
|
"base64-loader": "^1.0.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"cookies": "^0.8.0",
|
"cookies": "^0.8.0",
|
||||||
@@ -52,13 +52,13 @@
|
|||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"i18next": "^22.4.6",
|
"i18next": "^22.4.9",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"next": "^12.3.4",
|
"next": "^12.3.4",
|
||||||
"next-i18next": "^13.0.2",
|
"next-i18next": "^13.0.2",
|
||||||
"posthog-js": "^1.34.0",
|
"posthog-js": "^1.39.4",
|
||||||
"query-string": "^7.1.3",
|
"query-string": "^7.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const integrationSlugNameMapping: Mapping = {
|
|||||||
'vercel': 'Vercel',
|
'vercel': 'Vercel',
|
||||||
'netlify': 'Netlify',
|
'netlify': 'Netlify',
|
||||||
'github': 'GitHub',
|
'github': 'GitHub',
|
||||||
|
'gitlab': 'GitLab',
|
||||||
'render': 'Render',
|
'render': 'Render',
|
||||||
'flyio': 'Fly.io',
|
'flyio': 'Fly.io',
|
||||||
'circleci': 'CircleCI',
|
'circleci': 'CircleCI',
|
||||||
|
|||||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -11,6 +11,12 @@
|
|||||||
"image": "Docker Compose",
|
"image": "Docker Compose",
|
||||||
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
|
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Kubernetes",
|
||||||
|
"slug": "kubernetes",
|
||||||
|
"image": "Kubernetes",
|
||||||
|
"docsLink": "https://infisical.com/docs/integrations/platforms/kubernetes"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "React",
|
"name": "React",
|
||||||
"slug": "react",
|
"slug": "react",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"title": "Activity Logs",
|
"title": "Audit Logs",
|
||||||
"subtitle": "Event history for this Infisical project.",
|
"subtitle": "Event history for this Infisical project.",
|
||||||
"event": {
|
"event": {
|
||||||
"readSecrets": "Secrets Viewed",
|
"readSecrets": "Secrets Viewed",
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
"project-id": "Project ID",
|
"project-id": "Project ID",
|
||||||
"save-changes": "Save Changes",
|
"save-changes": "Save Changes",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
"drop-zone": "Drag and drop a .env or .yml file here.",
|
"drop-zone": "Drag and drop a .env, .json, or .yml file here.",
|
||||||
"drop-zone-keys": "Drag and drop a .env or .yml file here to add more secrets.",
|
"drop-zone-keys": "Drag and drop a .env, .json, or .yml file here to add more secrets.",
|
||||||
"role": "Role",
|
"role": "Role",
|
||||||
"role_admin": "admin",
|
"role_admin": "admin",
|
||||||
"display-name": "Display Name",
|
"display-name": "Display Name",
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"title": "Historial de eventos",
|
||||||
|
"subtitle": "Historial de eventos para este proyecto de Infisical.",
|
||||||
|
"event": {
|
||||||
|
"readSecrets": "Secrets vistas",
|
||||||
|
"updateSecrets": "Secrets actualizadas",
|
||||||
|
"addSecrets": "Secrets añadidas",
|
||||||
|
"deleteSecrets": "Secrets eliminadas"
|
||||||
|
},
|
||||||
|
"ip-address": "Dirección IP"
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"title": "Uso & Facturación",
|
||||||
|
"description": "Visualiza y gestiona la suscripción de tu organización",
|
||||||
|
"subscription": "Suscripción",
|
||||||
|
"starter": {
|
||||||
|
"name": "Starter",
|
||||||
|
"price-explanation": "hasta 5 miembros",
|
||||||
|
"text": "¡Hasta 5 personas gratis!",
|
||||||
|
"subtext": "Después, $5 por miembro/mes."
|
||||||
|
},
|
||||||
|
"professional": {
|
||||||
|
"name": "Professional",
|
||||||
|
"price-explanation": "/miembro/mes",
|
||||||
|
"subtext": "Includes unlimited projects & members.",
|
||||||
|
"text": "Mantén la gestión de claves a medida que creces."
|
||||||
|
},
|
||||||
|
"enterprise": {
|
||||||
|
"name": "Enterprise",
|
||||||
|
"text": "Mantén la gestión de claves a medida que creces."
|
||||||
|
},
|
||||||
|
"current-usage": "Uso actual",
|
||||||
|
"free": "Gratis",
|
||||||
|
"downgrade": "Reducir",
|
||||||
|
"upgrade": "Mejorar",
|
||||||
|
"learn-more": "Saber más",
|
||||||
|
"custom-pricing": "A medida",
|
||||||
|
"schedule-demo": "Solicitar una demo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"head-title": "{{title}} | Infisical",
|
||||||
|
"error_project-already-exists": "Ya existe un proyecto con este nombre.",
|
||||||
|
"no-mobile": "Para usar Infisical, inicia sesión con un dispositivo de mayores dimesiones.",
|
||||||
|
"email": "Correo electrónico",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"first-name": "Nombre",
|
||||||
|
"last-name": "Apellidos",
|
||||||
|
"logout": "Cerrar sesión",
|
||||||
|
"validate-required": "Por favor, introduce tu {{name}}",
|
||||||
|
"maintenance-alert": "Estamos experimentando problemas técnicos. Estamos trabajando para resolverlos. Por favor, vuelve en unos minutos.",
|
||||||
|
"click-to-copy": "Click para copiar",
|
||||||
|
"project-id": "ID de proyecto",
|
||||||
|
"save-changes": "Guardar cambios",
|
||||||
|
"saved": "Guardado",
|
||||||
|
"drop-zone": "Arrastra y suelta un archivo .env, .json o .yml aquí.",
|
||||||
|
"drop-zone-keys": "Arrastra y suelta un archivo .env, .json, or .yml aquí para añadir más secrets.",
|
||||||
|
"role": "Rol",
|
||||||
|
"role_admin": "admin",
|
||||||
|
"display-name": "Nombre visible",
|
||||||
|
"environment": "Entorno",
|
||||||
|
"expired-in": "Expira en",
|
||||||
|
"language": "Idioma",
|
||||||
|
"search": "Buscar...",
|
||||||
|
"note": "Nota",
|
||||||
|
"view-more": "Ver más",
|
||||||
|
"end-of-history": "Fin del historial",
|
||||||
|
"select-event": "Selecciona un evento",
|
||||||
|
"event": "Evento",
|
||||||
|
"user": "Usuario",
|
||||||
|
"source": "Fuente",
|
||||||
|
"time": "Hora",
|
||||||
|
"timestamp": "Marca temporal"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"title": "Secrets",
|
||||||
|
"og-title": "Gestiona tus archivos .env en segundos",
|
||||||
|
"og-description": "Infisical es una solución cifrada de extremo a extremo que habilita a los equipos a compartir archivos .env de forma sencilla.",
|
||||||
|
"search-keys": "Buscar claves...",
|
||||||
|
"add-key": "Añadir clave",
|
||||||
|
"personal": "Personal",
|
||||||
|
"personal-description": "Las claves personales solo son visibles para ti",
|
||||||
|
"shared": "Compartido",
|
||||||
|
"shared-description": "Claves compartidas son visibles para todo tu equipo",
|
||||||
|
"make-shared": "Hacer compartida",
|
||||||
|
"make-personal": "Hacer personal",
|
||||||
|
"add-secret": "Añadir una nueva clave",
|
||||||
|
"check-docs": {
|
||||||
|
"button": "Comprueba la documentación",
|
||||||
|
"title": "¡Buen trabajo!",
|
||||||
|
"line1": "Enhorabuena por añadir claves.",
|
||||||
|
"line2": "Averigua como usarlo desde tu código."
|
||||||
|
},
|
||||||
|
"sidebar": {
|
||||||
|
"secret": "Secret",
|
||||||
|
"key": "Clave",
|
||||||
|
"value": "valor",
|
||||||
|
"override": "Sobreescribir valor con el valor personal",
|
||||||
|
"version-history": "Historial de versiones",
|
||||||
|
"comments": "Comentarios & Notas",
|
||||||
|
"personal-explanation": "Esta clave es personal. No será compartida con ninguno de tus compañeros.",
|
||||||
|
"generate-random-hex": "Generar Hex aleatorio",
|
||||||
|
"digits": "dígitos",
|
||||||
|
"delete-key-dialog": {
|
||||||
|
"title": "Eliminar clave",
|
||||||
|
"confirm-delete-message": "¿Estás seguro de que quieres eliminar esta clave? Esta operación no se puede deshacer."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"title": "Integraciones del proyecto",
|
||||||
|
"description": "Gestiona tus integraciones de Infisical con aplicaciones de terceros.",
|
||||||
|
"no-integrations1": "No tienes ninguna integración configurada. Cuando las tengas, aparecerán aquí.",
|
||||||
|
"no-integrations2": "Para empezar haz click en cualquiera de las opciones siguientes. Se necesitan tan solo 5 pasos.",
|
||||||
|
"available": "Integraciones con la nube",
|
||||||
|
"available-text1": "Haz click en la integración que quieras conectar. Esto hará que las variables de entorno se configuren automáticamente en los servicios de terceros seleccionados.",
|
||||||
|
"available-text2": "Nota: en la integración con Heroku, por motivos de seguridad, es imposible mantener el cifrado de extremo a extremo. En teoría, esto permite a Infisical descifrar tus variables de entorno. En la práctica, te podemos asegurar que esto nunca va a suceder. El núcleo del servicio Infisical siempre estará cifrado de extremo a extremo. Si tienes dudas, escríbenos a [email protected].",
|
||||||
|
"cloud-integrations": "Integraciones con la nube",
|
||||||
|
"framework-integrations": "Integraciones con frameworks",
|
||||||
|
"click-to-start": "Haz click en una integración para empezar a sincronizar las claves.",
|
||||||
|
"click-to-setup": "Haz click en un framework para ver las instrucciones de uso.",
|
||||||
|
"grant-access-to-secrets": "Autorizar Infisical el acceso a tus claves",
|
||||||
|
"why-infisical-needs-access": "La mayoría de las integraciones en la nube requieren a Infisical tener permiso para descifrar tus claves, para que puedan ser enviadas.",
|
||||||
|
"grant-access-button": "Autorizar acceso"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"title": "Iniciar sesión",
|
||||||
|
"og-title": "Iniciar sesión en Infisical",
|
||||||
|
"og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.",
|
||||||
|
"login": "Iniciar sesión",
|
||||||
|
"need-account": "¿Necesitas una cuenta de Infisical?",
|
||||||
|
"create-account": "Crea una cuenta",
|
||||||
|
"forgot-password": "¿Has olvidado tu contraseña?",
|
||||||
|
"error-login": "Datos incorrectos."
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"title": "Registrarse",
|
||||||
|
"og-title": "Reemplaza los archivos .env con una línea de código. Regístrate en Infisical en solo 3 minutos.",
|
||||||
|
"og-description": "Infisical es una plataforma sencilla cifrada de extremo a extremo que permite a equipos sincronizar y gestionar API-keys y variables de entorno. Funciona con Node.js, Next.js, Gatsby, Nest.js...",
|
||||||
|
"signup": "Registrarse",
|
||||||
|
"already-have-account": "¿Ya tienes una cuenta? Inicia sesión",
|
||||||
|
"forgot-password": "¿Has olvidado tu contraseña?",
|
||||||
|
"verify": "Verificar",
|
||||||
|
"step1-start": "Vamos a empezar",
|
||||||
|
"step1-privacy": "Al crear la cuenta, aceptas nuestros Términos y has leído y comprendido nuestra Política de privacidad.",
|
||||||
|
"step1-submit": "Empezar",
|
||||||
|
"step2-message": "Hemos enviado un código a",
|
||||||
|
"step2-code-error": "Oops. Tu código es incorrecto. Intentos restantes:",
|
||||||
|
"step2-resend-alert": "¿No ves el código?",
|
||||||
|
"step2-resend-submit": "Volver a enviar",
|
||||||
|
"step2-resend-progress": "Reenviando...",
|
||||||
|
"step2-spam-alert": "Asegúrate de comprobar la carpeta de spam.",
|
||||||
|
"step3-message": "¡Casi hemos terminado!",
|
||||||
|
"step4-message": "Guarda tu Kit de emergencia",
|
||||||
|
"step4-description1": "Si pierdes el acceso a tu cuenta, tu Kit de emergencia es la única forma de iniciar sesión.",
|
||||||
|
"step4-description2": "Te recomendamos que lo descargues y lo guardes en un sitio seguro.",
|
||||||
|
"step4-description3": "Contiene tu Clave secreta, a la que no tenemos acceso y tampoco podemos recuperar si la pierdes.",
|
||||||
|
"step4-download": "Descargar PDF",
|
||||||
|
"step5-send-invites": "Enviar invitaciones",
|
||||||
|
"step5-invite-team": "Invita a tu equipo",
|
||||||
|
"step5-subtitle": "Infisical está pensado para usarlo con tus compañeros. Invítalos a probarlo.",
|
||||||
|
"step5-skip": "Saltar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"support": {
|
||||||
|
"slack": "Unirse al Slack",
|
||||||
|
"docs": "Leer documentación",
|
||||||
|
"issue": "Abrir una Github Issue",
|
||||||
|
"email": "Envíanos un correo electrónico"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"signed-in-as": "INICIADO COMO",
|
||||||
|
"current-organization": "ORGANIZACIÓN ACTUAL",
|
||||||
|
"usage-billing": "Uso & Facturación",
|
||||||
|
"invite": "Invitar miembros",
|
||||||
|
"other-organizations": "OTRA ORGANIZACIÓN"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"project": "PROYECTO",
|
||||||
|
"secrets": "Claves",
|
||||||
|
"members": "Miembros",
|
||||||
|
"integrations": "Integraciones",
|
||||||
|
"project-settings": "Configuración del proyecto"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"api-keys": "Tokens de servicio",
|
||||||
|
"api-keys-description": "Cada token de servicio es específico para ti, para cada proyecto y para cada entorno en este proyecto.",
|
||||||
|
"add-new": "Añadir nuevo token",
|
||||||
|
"add-dialog": {
|
||||||
|
"title": "Añadir una API Key",
|
||||||
|
"description": "Especifica el nombre y el período de expiración. Cuando una API key es generada, solo podrás verla una vez hasta que desaparezca. Asegúrate de guardarla en algún lado.",
|
||||||
|
"name": "Nombre de la API Key",
|
||||||
|
"add": "Añadir API Key",
|
||||||
|
"copy-service-token": "Copia tu API key",
|
||||||
|
"copy-service-token-description": "Una vez que cierres esta ventana, no volverás a ver tu API key"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"incident-contacts": "Contactos de incidencias",
|
||||||
|
"incident-contacts-description": "Estos contactos serán notificados en caso de una incidencia grave.",
|
||||||
|
"no-incident-contacts": "No se han encontrado contactos para incidencias.",
|
||||||
|
"add-contact": "Añadir contacto",
|
||||||
|
"add-dialog": {
|
||||||
|
"title": "Añadir un contacto de incidencias",
|
||||||
|
"description": "Este contacto será notificado en el caso de una incidencia grave.",
|
||||||
|
"add-incident": "Añadir contacto de incidencias"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"add-member": "Añadir miembro",
|
||||||
|
"org-members": "Miembros de la organización",
|
||||||
|
"org-members-description": "Gestiona los miembros de tu organización. Estos usuarios podrán ser agrupados en proyectos más tarde.",
|
||||||
|
"search-members": "Buscar miembros...",
|
||||||
|
"add-dialog": {
|
||||||
|
"add-member-to-project": "Añadir un miembro al proyecto",
|
||||||
|
"already-all-invited": "Todos los uaurios en tu organización han sido invitados.",
|
||||||
|
"add-user-org-first": "Añadir primero más usuarios a la organización.",
|
||||||
|
"user-will-email": "El usuario recibirá un correo electrónico con la invitación.",
|
||||||
|
"looking-add": "<0>Si quieres añadir usuarios a tu organización, haz</0><1>click aquí</1>",
|
||||||
|
"add-user-to-org": "Añadir usuarios a la organización"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"password": "Contraseña",
|
||||||
|
"change": "Cambiar contraseña",
|
||||||
|
"current": "Contraseña actual",
|
||||||
|
"current-wrong": "La contraseña actual puede puede que sea incorrecta",
|
||||||
|
"new": "Nueva contraseña",
|
||||||
|
"validate-base": "La contraseña debe contener como mínimo:",
|
||||||
|
"validate-length": "14 caracteres",
|
||||||
|
"validate-case": "1 letra en minúsculas",
|
||||||
|
"validate-number": "1 número"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"service-tokens": "Tokens de servicio",
|
||||||
|
"service-tokens-description": "Cada token de servicio es específico para ti, para cada proyecto y para cada entorno en este proyectos.",
|
||||||
|
"add-new": "Añadir nuevo token",
|
||||||
|
"add-dialog": {
|
||||||
|
"title": "Añade un nuevo token de servicio para {{target}}",
|
||||||
|
"description": "Cuando se genera un token, solo podrás verlo una vez antes de que desaparezca. Asegúrate de guardarlo en algún lugar.",
|
||||||
|
"name": "Nombre del token de servicio",
|
||||||
|
"add": "Añadir token de servicio",
|
||||||
|
"copy-service-token": "Copiar tu token de servicio",
|
||||||
|
"copy-service-token-description": "Una vez cierres esta ventana, no volverás a ver tu token de nuevo"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Miembros del proyecto",
|
||||||
|
"description": "Esta página muestra los miembros del proyecto seleccionado, y permite modificar sus permisos."
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Configuración de la organización",
|
||||||
|
"description": "Gestiona los miembros de tu organización. Estos usuarios podrán ser gestionados en proyectos más adelante."
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"title": "Preferencias personales",
|
||||||
|
"description": "Visualiza y gestiona aquí tu información personal.",
|
||||||
|
"emergency": {
|
||||||
|
"name": "Kit de emergencia",
|
||||||
|
"text1": "Tu Kit de emergencia contiene la información que necesitarás para iniciar sesión en tu cuenta de Infisical.",
|
||||||
|
"text2": "Solo el último Kit de emergencia descargado es válido. Para conseguir uno nuevo, introduce tu contraseña.",
|
||||||
|
"download": "Descargar Kit de emergencia"
|
||||||
|
},
|
||||||
|
"change-language": "Cambiar idioma",
|
||||||
|
"api-keys": {
|
||||||
|
"title": "API Keys",
|
||||||
|
"description": "Gestiona tus API Keys personales para acceder a la API de Infisical.",
|
||||||
|
"add-new": "Añadir"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"title": "Configuración de proyecto",
|
||||||
|
"description": "Esta configuración solo afecta al proyecto seleccionado.",
|
||||||
|
"danger-zone": "Zona de peligro",
|
||||||
|
"delete-project": "Eliminar proyecto",
|
||||||
|
"project-to-delete": "Proyecto a eliminar",
|
||||||
|
"danger-zone-note": "Tan pronto como elimines el proyecto, no podrás volver atrás. Esto eliminará todas las claves. Si aún quieres hacerlo, introduce el nombre del proyecto a continuación.",
|
||||||
|
"delete-project-note": "Nota: Solo puedes eliminar un proyecto si tienes más de uno",
|
||||||
|
"project-id-description": "Para integrar Infisical en tu aplicación y obtener la inyección automática de variables de entorno, debes usar el siguiente ID de Proyecto.",
|
||||||
|
"project-id-description2": "Para más guías, incluyendo ejemplos de código en diferentes lenguajes y frameworks, visita ",
|
||||||
|
"auto-generated": "Este es el ID único y autogenerado de proyecto. No se puede modificar.",
|
||||||
|
"docs": "Documentación de Infisical",
|
||||||
|
"auto-capitalization": "Mayúsculas automáticas",
|
||||||
|
"auto-capitalization-description": "De acuerdo con los estándares, Infisical pondrá en mayúsculas tus claves. Si quieres desactivar esta funcionalidad, lo puedes hacer aquí."
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"title": "Registrarse",
|
||||||
|
"og-title": "Reemplaza los archivos .env con una línea de código. Regístrate en Infisical en solo 3 minutos.",
|
||||||
|
"og-description": "Infisical es una plataforma sencilla cifrada de extremo a extremo que permite a equipos sincronizar y gestionar API-keys y variables de entorno. Funciona con Node.js, Next.js, Gatsby, Nest.js...",
|
||||||
|
"signup": "Registrarse",
|
||||||
|
"already-have-account": "¿Ya tienes una cuenta? Inicia sesión",
|
||||||
|
"forgot-password": "¿Has olvidado tu contraseña?",
|
||||||
|
"verify": "Verificar",
|
||||||
|
"step1-start": "Vamos a empezar",
|
||||||
|
"step1-privacy": "Al crear la cuenta, aceptas nuestros Términos y has leído y comprendido nuestra Política de privacidad.",
|
||||||
|
"step1-submit": "Empezar",
|
||||||
|
"step2-message": "Hemos enviado un código a",
|
||||||
|
"step2-code-error": "Oops. Tu código es incorrecto. Intentos restantes:",
|
||||||
|
"step2-resend-alert": "¿No ves el código?",
|
||||||
|
"step2-resend-submit": "Volver a enviar",
|
||||||
|
"step2-resend-progress": "Reenviando...",
|
||||||
|
"step2-spam-alert": "Asegúrate de comprobar la carpeta de spam.",
|
||||||
|
"step3-message": "¡Casi hemos terminado!",
|
||||||
|
"step4-message": "Guarda tu Kit de emergencia",
|
||||||
|
"step4-description1": "Si pierdes el acceso a tu cuenta, tu Kit de emergencia es la única forma de iniciar sesión.",
|
||||||
|
"step4-description2": "Te recomendamos que lo descargues y lo guardes en un sitio seguro.",
|
||||||
|
"step4-description3": "Contiene tu Clave secreta, a la que no tenemos acceso y tampoco podemos recuperar si la pierdes.",
|
||||||
|
"step4-download": "Descargar PDF",
|
||||||
|
"step5-send-invites": "Enviar invitaciones",
|
||||||
|
"step5-invite-team": "Invita a tu equipo",
|
||||||
|
"step5-subtitle": "Infisical está pensado para usarlo con tus compañeros. Invítalos a probarlo.",
|
||||||
|
"step5-skip": "Saltar"
|
||||||
|
}
|
||||||
@@ -162,7 +162,7 @@ const Layout = ({ children }: LayoutProps) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: `/activity/${workspaceMapping[workspaceSelected as any]}`,
|
href: `/activity/${workspaceMapping[workspaceSelected as any]}`,
|
||||||
title: 'Activity Logs',
|
title: 'Audit Logs',
|
||||||
emoji: <FontAwesomeIcon icon={faFileLines} />
|
emoji: <FontAwesomeIcon icon={faFileLines} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,6 +71,19 @@ const DropZone = ({
|
|||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'json': {
|
||||||
|
const keyPairs = JSON.parse(String(file));
|
||||||
|
secrets = Object.keys(keyPairs).map((key, index) => ({
|
||||||
|
id: guidGenerator(),
|
||||||
|
pos: numCurrentRows + index,
|
||||||
|
key,
|
||||||
|
value: keyPairs[key as keyof typeof keyPairs],
|
||||||
|
comment: '',
|
||||||
|
type: 'shared',
|
||||||
|
tags: []
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'yml': {
|
case 'yml': {
|
||||||
const parsedFile = parseDocument(file.toString());
|
const parsedFile = parseDocument(file.toString());
|
||||||
const keyPairs = parsedFile.contents!.toJSON();
|
const keyPairs = parsedFile.contents!.toJSON();
|
||||||
@@ -97,6 +110,10 @@ const DropZone = ({
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
secrets = '';
|
secrets = '';
|
||||||
|
createNotification({
|
||||||
|
text: `The file you are dropping should have one of the following extensions: .env, .yml, .json.`,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return secrets;
|
return secrets;
|
||||||
@@ -126,9 +143,13 @@ const DropZone = ({
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
if (event.target === null || event.target.result === null) return;
|
if (event.target === null || event.target.result === null) return;
|
||||||
// parse function's argument looks like to be ArrayBuffer
|
// parse function's argument looks like to be ArrayBuffer
|
||||||
|
try {
|
||||||
const newData = getSecrets(event.target.result as ArrayBuffer, fileType);
|
const newData = getSecrets(event.target.result as ArrayBuffer, fileType);
|
||||||
setData(newData);
|
setData(newData);
|
||||||
setButtonReady(true);
|
setButtonReady(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error while dropping the file: ", error)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If something is wrong show an error
|
// If something is wrong show an error
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ interface Framework {
|
|||||||
const FrameworkIntegration = ({ framework }: { framework: Framework }) => (
|
const FrameworkIntegration = ({ framework }: { framework: Framework }) => (
|
||||||
<a
|
<a
|
||||||
href={framework.docsLink}
|
href={framework.docsLink}
|
||||||
rel="noopener"
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
className="relative flex flex-row justify-center bg-bunker-500 hover:bg-gradient-to-tr duration-200 h-32 rounded-md p-0.5 items-center cursor-pointer"
|
className="relative flex flex-row justify-center bg-bunker-500 hover:bg-gradient-to-tr duration-200 h-32 rounded-md p-0.5 items-center cursor-pointer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EmptyState = ({ title, className, children, icon = faCubesStacked }: Props) => (
|
export const EmptyState = ({ title, className, children, icon = faCubesStacked }: Props) => (
|
||||||
<div className={twMerge('flex w-full flex-col items-center px-2 pt-6 text-bunker-300', className)}>
|
<div className={twMerge('flex w-full bg-bunker-700 flex-col items-center px-2 pt-6 text-bunker-300', className)}>
|
||||||
<FontAwesomeIcon icon={icon} size="2x" className='mr-4' />
|
<FontAwesomeIcon icon={icon} size="2x" className='mr-4' />
|
||||||
<div className='flex flex-row items-center py-4'>
|
<div className='flex flex-row items-center py-4'>
|
||||||
<div className="text-bunker-300 text-sm">{title}</div>
|
<div className="text-bunker-300 text-sm">{title}</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const tagVariants = cva('inline-flex whitespace-nowrap text-sm rounded-sm mr-1.5
|
|||||||
red: 'bg-red/80 text-bunker-100'
|
red: 'bg-red/80 text-bunker-100'
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
sm: 'px-1 py-0.5'
|
sm: 'px-1.5 py-0.5'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ let privateKey;
|
|||||||
throw new Error('Insufficient details to decrypt private key');
|
throw new Error('Insufficient details to decrypt private key');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
|
||||||
throw new Error('Failed to decrypt private key');
|
throw new Error('Failed to decrypt private key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
isSelected={router.asPath === `/activity/${currentWorkspace?._id}`}
|
isSelected={router.asPath === `/activity/${currentWorkspace?._id}`}
|
||||||
icon={<FontAwesomeIcon icon={faFileLines} size="lg" />}
|
icon={<FontAwesomeIcon icon={faFileLines} size="lg" />}
|
||||||
>
|
>
|
||||||
Activity Logs
|
Audit Logs
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const supportOptions = (t: TFunction) => [
|
|||||||
[
|
[
|
||||||
<FontAwesomeIcon className="pl-1.5 pr-3 text-lg" icon={faSlack} />,
|
<FontAwesomeIcon className="pl-1.5 pr-3 text-lg" icon={faSlack} />,
|
||||||
t('nav:support.slack'),
|
t('nav:support.slack'),
|
||||||
'https://join.slack.com/t/infisical/shared_invite/zt-1dgg63ln8-G7PCNJdCymAT9YF3j1ewVA'
|
'https://infisical.com/slack'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
<FontAwesomeIcon className="pl-1.5 pr-3 text-lg" icon={faBook} />,
|
<FontAwesomeIcon className="pl-1.5 pr-3 text-lg" icon={faBook} />,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
@@ -125,7 +126,12 @@ export default function Activity() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-6 lg:mx-0 w-full h-screen">
|
<div className="mx-6 lg:mx-0 w-full h-screen">
|
||||||
<NavHeader pageName="Project Activity" isProjectRelated />
|
<Head>
|
||||||
|
<title>Audit Logs</title>
|
||||||
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
<meta property="og:image" content="/images/message.png" />
|
||||||
|
</Head>
|
||||||
|
<NavHeader pageName="Audit Logs" isProjectRelated />
|
||||||
{currentSidebarAction && (
|
{currentSidebarAction && (
|
||||||
<ActivitySideBar toggleSidebar={toggleSidebar} currentAction={currentSidebarAction} />
|
<ActivitySideBar toggleSidebar={toggleSidebar} currentAction={currentSidebarAction} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export default function Integrations() {
|
|||||||
let link = '';
|
let link = '';
|
||||||
switch (integrationOption.slug) {
|
switch (integrationOption.slug) {
|
||||||
case 'azure-key-vault':
|
case 'azure-key-vault':
|
||||||
link = `https://login.microsoftonline.com/${integrationOption.tenantId}/oauth2/v2.0/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
|
link = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
|
||||||
break;
|
break;
|
||||||
case 'aws-parameter-store':
|
case 'aws-parameter-store':
|
||||||
link = `${window.location.origin}/integrations/aws-parameter-store/authorize`;
|
link = `${window.location.origin}/integrations/aws-parameter-store/authorize`;
|
||||||
@@ -192,6 +192,9 @@ export default function Integrations() {
|
|||||||
case 'github':
|
case 'github':
|
||||||
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
||||||
break;
|
break;
|
||||||
|
case 'gitlab':
|
||||||
|
link = `https://gitlab.com/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
|
||||||
|
break;
|
||||||
case 'render':
|
case 'render':
|
||||||
link = `${window.location.origin}/integrations/render/authorize`
|
link = `${window.location.origin}/integrations/render/authorize`
|
||||||
break;
|
break;
|
||||||
@@ -241,6 +244,9 @@ export default function Integrations() {
|
|||||||
case 'github':
|
case 'github':
|
||||||
link = `${window.location.origin}/integrations/github/create?integrationAuthId=${integrationAuth._id}`;
|
link = `${window.location.origin}/integrations/github/create?integrationAuthId=${integrationAuth._id}`;
|
||||||
break;
|
break;
|
||||||
|
case 'gitlab':
|
||||||
|
link = `${window.location.origin}/integrations/gitlab/create?integrationAuthId=${integrationAuth._id}`;
|
||||||
|
break;
|
||||||
case 'render':
|
case 'render':
|
||||||
link = `${window.location.origin}/integrations/render/create?integrationAuthId=${integrationAuth._id}`;
|
link = `${window.location.origin}/integrations/render/create?integrationAuthId=${integrationAuth._id}`;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import queryString from 'query-string';
|
||||||
|
|
||||||
|
import { getTranslatedServerSideProps } from '../../../components/utilities/withTranslateProps';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardTitle,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
SelectItem
|
||||||
|
} from '../../../components/v2';
|
||||||
|
import { useGetIntegrationAuthApps,useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth';
|
||||||
|
import { useGetWorkspaceById } from '../../../hooks/api/workspace';
|
||||||
|
import createIntegration from "../../api/integrations/createIntegration";
|
||||||
|
|
||||||
|
export default function GitLabCreateIntegrationPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { integrationAuthId } = queryString.parse(router.asPath.split('?')[1]);
|
||||||
|
|
||||||
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? '');
|
||||||
|
const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? '');
|
||||||
|
const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? '');
|
||||||
|
|
||||||
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
|
||||||
|
const [owner, setOwner] = useState<string | null>(null);
|
||||||
|
const [targetApp, setTargetApp] = useState('');
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (workspace) {
|
||||||
|
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [workspace]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// TODO: handle case where apps can be empty
|
||||||
|
if (integrationAuthApps) {
|
||||||
|
setTargetApp(integrationAuthApps[0].name);
|
||||||
|
setOwner(integrationAuthApps[0]?.owner ?? null);
|
||||||
|
}
|
||||||
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
|
const handleButtonClick = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
if (!integrationAuth?._id) return;
|
||||||
|
|
||||||
|
await createIntegration({
|
||||||
|
integrationAuthId: integrationAuth?._id,
|
||||||
|
isActive: true,
|
||||||
|
app: targetApp,
|
||||||
|
appId: (integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp))?.appId ?? null,
|
||||||
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
|
targetEnvironment: null,
|
||||||
|
owner,
|
||||||
|
path: null,
|
||||||
|
region: null
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
router.push(
|
||||||
|
`/integrations/${localStorage.getItem('projectData.id')}`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && targetApp) ? (
|
||||||
|
<div className="h-full w-full flex justify-center items-center">
|
||||||
|
<Card className="max-w-md p-8 rounded-md">
|
||||||
|
<CardTitle className='text-center'>GitLab Integration</CardTitle>
|
||||||
|
<FormControl
|
||||||
|
label="Project Environment"
|
||||||
|
className='mt-4'
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={selectedSourceEnvironment}
|
||||||
|
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
||||||
|
className='w-full border border-mineshaft-500'
|
||||||
|
>
|
||||||
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
|
<SelectItem value={sourceEnvironment.slug} key={`azure-key-vault-environment-${sourceEnvironment.slug}`}>
|
||||||
|
{sourceEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl
|
||||||
|
label="GitLab Repo"
|
||||||
|
className='mt-4'
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={targetApp}
|
||||||
|
onValueChange={(val) => setTargetApp(val)}
|
||||||
|
className='w-full border border-mineshaft-500'
|
||||||
|
>
|
||||||
|
{integrationAuthApps.map((integrationAuthApp) => (
|
||||||
|
<SelectItem value={integrationAuthApp.name} key={`gitlab-environment-${integrationAuthApp.name}`}>
|
||||||
|
{integrationAuthApp.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
color="mineshaft"
|
||||||
|
className='mt-4'
|
||||||
|
isLoading={isLoading}
|
||||||
|
>
|
||||||
|
Create Integration
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
) : <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
GitLabCreateIntegrationPage.requireAuth = true;
|
||||||
|
|
||||||
|
export const getServerSideProps = getTranslatedServerSideProps(['integrations']);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import queryString from 'query-string';
|
||||||
|
|
||||||
|
import { getTranslatedServerSideProps } from '../../../../components/utilities/withTranslateProps';
|
||||||
|
import AuthorizeIntegration from "../../../api/integrations/authorizeIntegration";
|
||||||
|
|
||||||
|
export default function GitLabOAuth2CallbackPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { code, state } = queryString.parse(router.asPath.split('?')[1]);
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// validate state
|
||||||
|
if (state !== localStorage.getItem('latestCSRFToken')) return;
|
||||||
|
localStorage.removeItem('latestCSRFToken');
|
||||||
|
|
||||||
|
const integrationAuth = await AuthorizeIntegration({
|
||||||
|
workspaceId: localStorage.getItem('projectData.id') as string,
|
||||||
|
code: code as string,
|
||||||
|
integration: 'gitlab'
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(
|
||||||
|
`/integrations/gitlab/create?integrationAuthId=${integrationAuth._id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
GitLabOAuth2CallbackPage.requireAuth = true;
|
||||||
|
|
||||||
|
export const getServerSideProps = getTranslatedServerSideProps(['integrations']);
|
||||||
@@ -77,13 +77,13 @@ export default function SettingsBilling() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bunker-800 max-h-screen flex flex-col justify-between text-white">
|
<div className="bg-bunker-800 pb-4 flex flex-col justify-between text-white">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{t('common:head-title', { title: t('billing:title') })}</title>
|
<title>{t('common:head-title', { title: t('billing:title') })}</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<div className="w-full max-h-screen pb-2 overflow-y-auto">
|
<div className="w-full pb-2">
|
||||||
<NavHeader pageName={t('billing:title')} />
|
<NavHeader pageName={t('billing:title')} />
|
||||||
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
<div className="flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl">
|
||||||
<div className="flex flex-col justify-start items-start text-3xl">
|
<div className="flex flex-col justify-start items-start text-3xl">
|
||||||
@@ -93,7 +93,6 @@ export default function SettingsBilling() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col ml-6 text-mineshaft-50 w-max">
|
<div className="flex flex-col ml-6 text-mineshaft-50 w-max">
|
||||||
<p className="text-xl font-semibold">{t('billing:subscription')}</p>
|
<p className="text-xl font-semibold">{t('billing:subscription')}</p>
|
||||||
<div className="mt-4 text-bunker-200 h-14 flex justify-center items-center rounded-md bg-bunker-600 mr-4"> If you are looking to get an annual plan, please reach out to <a className="ml-1.5 underline text-primary underline-offset-2" href="mailto:[email protected]">team@infisical.com</a></div>
|
|
||||||
<div className="grid grid-cols-2 grid-rows-2 gap-y-6 gap-x-3 mt-4 overflow-x-auto">
|
<div className="grid grid-cols-2 grid-rows-2 gap-y-6 gap-x-3 mt-4 overflow-x-auto">
|
||||||
{plans.map((plan) => (
|
{plans.map((plan) => (
|
||||||
<Plan key={plan.name} plan={plan} />
|
<Plan key={plan.name} plan={plan} />
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default function PersonalSettings() {
|
|||||||
<ListBox
|
<ListBox
|
||||||
isSelected={lang}
|
isSelected={lang}
|
||||||
onChange={setLanguage}
|
onChange={setLanguage}
|
||||||
data={['en', 'ko', 'fr']}
|
data={['en', 'ko', 'fr', 'es']}
|
||||||
text={`${t('common:language')}: `}
|
text={`${t('common:language')}: `}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||