mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge remote-tracking branch 'origin' into update-node-sdk
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
|
||||
</a>
|
||||
<a href="https://cloudsmith.io/~infisical/repos/">
|
||||
<img src="https://img.shields.io/badge/Downloads-128.2k-orange" alt="Cloudsmith downloads" />
|
||||
<img src="https://img.shields.io/badge/Downloads-150.8k-orange" alt="Cloudsmith downloads" />
|
||||
</a>
|
||||
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">
|
||||
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
|
||||
@@ -46,6 +46,7 @@
|
||||
<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>
|
||||
<kbd>[<img title="Italian" alt="Italian language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/it.svg" width="22">](i18n/README.it.md)</kbd>
|
||||
<kbd>[<img title="Indian" alt="Hindi language" src="https://cdn.staticaly.com/gh/hjnilsson/country-flags/master/svg/in.svg" width="22">](i18n/README.hi.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.
|
||||
|
||||
|
||||
89
backend/src/controllers/v1/secretsFolderController.ts
Normal file
89
backend/src/controllers/v1/secretsFolderController.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Secret } from '../../models';
|
||||
import Folder from '../../models/folder';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import { ROOT_FOLDER_PATH, getFolderPath, getParentPath, normalizePath, validateFolderName } from '../../utils/folder';
|
||||
import { ADMIN, MEMBER } from '../../variables';
|
||||
import { validateMembership } from '../../helpers/membership';
|
||||
|
||||
// TODO
|
||||
// verify workspace id/environment
|
||||
export const createFolder = async (req: Request, res: Response) => {
|
||||
const { workspaceId, environment, folderName, parentFolderId } = req.body
|
||||
if (!validateFolderName(folderName)) {
|
||||
throw BadRequestError({ message: "Folder name cannot contain spaces. Only underscore and dashes" })
|
||||
}
|
||||
|
||||
if (parentFolderId) {
|
||||
const parentFolder = await Folder.find({ environment: environment, workspace: workspaceId, id: parentFolderId });
|
||||
if (!parentFolder) {
|
||||
throw BadRequestError({ message: "The parent folder doesn't exist" })
|
||||
}
|
||||
}
|
||||
|
||||
let completePath = await getFolderPath(parentFolderId)
|
||||
if (completePath == ROOT_FOLDER_PATH) {
|
||||
completePath = ""
|
||||
}
|
||||
|
||||
const currentFolderPath = completePath + "/" + folderName // construct new path with current folder to be created
|
||||
const normalizedCurrentPath = normalizePath(currentFolderPath)
|
||||
const normalizedParentPath = getParentPath(normalizedCurrentPath)
|
||||
|
||||
const existingFolder = await Folder.findOne({
|
||||
name: folderName,
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parent: parentFolderId,
|
||||
path: normalizedCurrentPath
|
||||
});
|
||||
|
||||
if (existingFolder) {
|
||||
return res.json(existingFolder)
|
||||
}
|
||||
|
||||
const newFolder = new Folder({
|
||||
name: folderName,
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parent: parentFolderId,
|
||||
path: normalizedCurrentPath,
|
||||
parentPath: normalizedParentPath
|
||||
});
|
||||
|
||||
await newFolder.save();
|
||||
|
||||
return res.json(newFolder)
|
||||
}
|
||||
|
||||
export const deleteFolder = async (req: Request, res: Response) => {
|
||||
const { folderId } = req.params
|
||||
const queue: any[] = [folderId];
|
||||
|
||||
const folder = await Folder.findById(folderId);
|
||||
if (!folder) {
|
||||
throw BadRequestError({ message: "The folder doesn't exist" })
|
||||
}
|
||||
|
||||
// check that user is a member of the workspace
|
||||
await validateMembership({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: folder.workspace as any,
|
||||
acceptedRoles: [ADMIN, MEMBER]
|
||||
});
|
||||
|
||||
while (queue.length > 0) {
|
||||
const currentFolderId = queue.shift();
|
||||
|
||||
const childFolders = await Folder.find({ parent: currentFolderId });
|
||||
for (const childFolder of childFolders) {
|
||||
queue.push(childFolder._id);
|
||||
}
|
||||
|
||||
await Secret.deleteMany({ folder: currentFolderId });
|
||||
|
||||
await Folder.deleteOne({ _id: currentFolderId });
|
||||
}
|
||||
|
||||
res.send()
|
||||
}
|
||||
36
backend/src/models/folder.ts
Normal file
36
backend/src/models/folder.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Schema, Types, model } from 'mongoose';
|
||||
|
||||
const folderSchema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace',
|
||||
required: true,
|
||||
},
|
||||
environment: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
parent: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Folder',
|
||||
required: false, // optional for root folders
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
parentPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
}, {
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
const Folder = model('Folder', folderSchema);
|
||||
|
||||
export default Folder;
|
||||
87
backend/src/utils/folder.ts
Normal file
87
backend/src/utils/folder.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import Folder from "../models/folder";
|
||||
|
||||
export const ROOT_FOLDER_PATH = "/"
|
||||
|
||||
export const getFolderPath = async (folderId: string) => {
|
||||
let currentFolder = await Folder.findById(folderId);
|
||||
const pathSegments = [];
|
||||
|
||||
while (currentFolder) {
|
||||
pathSegments.unshift(currentFolder.name);
|
||||
currentFolder = currentFolder.parent ? await Folder.findById(currentFolder.parent) : null;
|
||||
}
|
||||
|
||||
return '/' + pathSegments.join('/');
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the folder ID associated with the specified secret path in the given workspace and environment.
|
||||
@param workspaceId - The ID of the workspace to search in.
|
||||
@param environment - The environment to search in.
|
||||
@param secretPath - The secret path to search for.
|
||||
@returns The folder ID associated with the specified secret path, or undefined if the path is at the root folder level.
|
||||
@throws Error if the specified secret path is not found.
|
||||
*/
|
||||
export const getFolderIdFromPath = async (workspaceId: string, environment: string, secretPath: string) => {
|
||||
const secretPathParts = secretPath.split("/").filter(path => path != "")
|
||||
if (secretPathParts.length <= 1) {
|
||||
return undefined // root folder, so no folder id
|
||||
}
|
||||
|
||||
const folderId = await Folder.find({ path: secretPath, workspace: workspaceId, environment: environment })
|
||||
if (!folderId) {
|
||||
throw Error("Secret path not found")
|
||||
}
|
||||
|
||||
return folderId
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up a path by removing empty parts, duplicate slashes,
|
||||
* and ensuring it starts with ROOT_FOLDER_PATH.
|
||||
* @param path - The input path to clean up.
|
||||
* @returns The cleaned-up path string.
|
||||
*/
|
||||
export const normalizePath = (path: string) => {
|
||||
if (path == undefined || path == "" || path == ROOT_FOLDER_PATH) {
|
||||
return ROOT_FOLDER_PATH
|
||||
}
|
||||
|
||||
const pathParts = path.split("/").filter(part => part != "")
|
||||
const cleanPathString = ROOT_FOLDER_PATH + pathParts.join("/")
|
||||
|
||||
return cleanPathString
|
||||
}
|
||||
|
||||
export const getFoldersInDirectory = async (workspaceId: string, environment: string, pathString: string) => {
|
||||
const normalizedPath = normalizePath(pathString)
|
||||
const foldersInDirectory = await Folder.find({
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parentPath: normalizedPath,
|
||||
});
|
||||
|
||||
return foldersInDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent path of the given path.
|
||||
* @param path - The input path.
|
||||
* @returns The parent path string.
|
||||
*/
|
||||
export const getParentPath = (path: string) => {
|
||||
const normalizedPath = normalizePath(path);
|
||||
const folderParts = normalizedPath.split('/').filter(part => part !== '');
|
||||
|
||||
let folderParent = ROOT_FOLDER_PATH;
|
||||
if (folderParts.length > 1) {
|
||||
folderParent = ROOT_FOLDER_PATH + folderParts.slice(0, folderParts.length - 1).join('/');
|
||||
}
|
||||
|
||||
return folderParent;
|
||||
}
|
||||
|
||||
export const validateFolderName = (folderName: string) => {
|
||||
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
|
||||
return validNameRegex.test(folderName);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
|
||||
isConnected := CheckIsConnectedToInternet()
|
||||
var secretsToReturn []models.SingleEnvironmentVariable
|
||||
var serviceTokenDetails api.GetServiceTokenDetailsResponse
|
||||
// var serviceTokenDetails api.GetServiceTokenDetailsResponse
|
||||
var errorToReturn error
|
||||
|
||||
if infisicalToken == "" {
|
||||
@@ -183,11 +183,11 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
|
||||
} else {
|
||||
log.Debug("Trying to fetch secrets using service token")
|
||||
secretsToReturn, serviceTokenDetails, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
|
||||
if serviceTokenDetails.Environment != params.Environment {
|
||||
PrintErrorMessageAndExit(fmt.Sprintf("Fetch secrets failed: token allows [%s] environment access, not [%s]. Service tokens are environment-specific; no need for --env flag.", params.Environment, serviceTokenDetails.Environment))
|
||||
}
|
||||
// if serviceTokenDetails.Environment != params.Environment {
|
||||
// PrintErrorMessageAndExit(fmt.Sprintf("Fetch secrets failed: token allows [%s] environment access, not [%s]. Service tokens are environment-specific; no need for --env flag.", params.Environment, serviceTokenDetails.Environment))
|
||||
// }
|
||||
}
|
||||
|
||||
return secretsToReturn, errorToReturn
|
||||
|
||||
@@ -64,7 +64,9 @@ These examples demonstrate how to store and fetch environment variables from [In
|
||||
### Initialize the Infisical client
|
||||
|
||||
```js
|
||||
await infisical.connect({
|
||||
import InfisicalClient from "infisical-node";
|
||||
|
||||
const client = new InfisicalClient({
|
||||
token: "your_infisical_token",
|
||||
});
|
||||
```
|
||||
@@ -72,31 +74,31 @@ These examples demonstrate how to store and fetch environment variables from [In
|
||||
### Get a value
|
||||
|
||||
```js
|
||||
const value = infisical.get("SOME_KEY");
|
||||
const value = await client.getSecret("SOME_KEY");
|
||||
```
|
||||
|
||||
### Example with Express
|
||||
|
||||
```js
|
||||
const express = require("express");
|
||||
const port = 3000;
|
||||
const infisical = require("infisical-node");
|
||||
import InfisicalClient from "infisical-node";
|
||||
import express from "express";
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
|
||||
const main = async () => {
|
||||
await infisical.connect({
|
||||
token: "st.xxx.xxx",
|
||||
});
|
||||
const client = InfisicalClient({
|
||||
token: "st.xxx.xxx",
|
||||
});
|
||||
|
||||
// your application logic
|
||||
// your application logic
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send(`Howdy, ${infisical.get("NAME")}!`);
|
||||
});
|
||||
app.get("/", async (req, res) => {
|
||||
const name = await client.getSecret("NAME");
|
||||
res.send(`Hello! My name is: ${name.secretValue}`);
|
||||
});
|
||||
|
||||
app.listen(port, async () => {
|
||||
console.log(`App listening on port ${port}`);
|
||||
});
|
||||
};
|
||||
app.listen(PORT, async () => {
|
||||
console.log(`App listening on port ${port}`);
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: "Node"
|
||||
---
|
||||
|
||||
If you're working with Node.js, the official [infisical-node](https://github.com/Infisical/infisical-node) package is the easiest way to fetch secrets for your application.
|
||||
If you're working with Node.js, the official [infisical-node](https://github.com/Infisical/infisical-node) package is the easiest way to fetch and work with secrets for your application.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -12,141 +12,174 @@ Run `npm` to add `infisical-node` to your project.
|
||||
npm install infisical-node --save
|
||||
```
|
||||
|
||||
## Initialization
|
||||
## Configuration
|
||||
|
||||
Set up the Infisical client asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect(options)`.
|
||||
|
||||
This methods fetches back all the secrets in the project and environment accessible by the token passed in `options`.
|
||||
|
||||
### infisical.connect(options)
|
||||
|
||||
Updates the global instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token).
|
||||
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="token" type="string">
|
||||
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
||||
and environment
|
||||
</ResponseField>
|
||||
<ResponseField
|
||||
name="siteURL"
|
||||
type="string"
|
||||
default="https://app.infisical.com"
|
||||
>
|
||||
Your self-hosted absolute site URL including the protocol (e.g.
|
||||
`https://app.infisical.com`)
|
||||
</ResponseField>
|
||||
<ResponseField name="debug" type="boolean" default="false">
|
||||
Whether or not debug mode is on
|
||||
</ResponseField>
|
||||
<ResponseField name="attachToProcessEnv" type="boolean" default="false">
|
||||
Whether or not to attach fetched secrets to `process.env`
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### infisical.createConnection(options)
|
||||
|
||||
Returns a local instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token).
|
||||
|
||||
This method is useful if you wish to connect to two or more Infisical projects within your app.
|
||||
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="token" type="string">
|
||||
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
||||
and environment
|
||||
</ResponseField>
|
||||
<ResponseField
|
||||
name="siteURL"
|
||||
type="string"
|
||||
default="https://app.infisical.com"
|
||||
>
|
||||
Your self-hosted absolute site URL including the protocol (e.g.
|
||||
`https://app.infisical.com`)
|
||||
</ResponseField>
|
||||
<ResponseField name="debug" type="boolean" default="false">
|
||||
Whether or not debug mode is on
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
Import the SDK and create a client instance with your Infisical token.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="ES6">
|
||||
```js
|
||||
import infisical from "infisical-node";
|
||||
import InfisicalClient from "infisical-node";
|
||||
|
||||
const client = new InfisicalClient({
|
||||
token: "your_infisical_token"
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
await infisical.connect({
|
||||
token: "your_infisical_token",
|
||||
});
|
||||
|
||||
// your app logic
|
||||
}
|
||||
|
||||
main();
|
||||
// your app logic
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="ES5">
|
||||
```js
|
||||
const infisical = require("infisical-node");
|
||||
const InfisicalClient = require("infisical-node");
|
||||
|
||||
infisical.connect({
|
||||
token: "your_infisical_token"
|
||||
})
|
||||
.then(() => {
|
||||
// your application logic
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error: ', err);
|
||||
})
|
||||
const client = new InfisicalClient({
|
||||
token: "your_infisical_token"
|
||||
});
|
||||
|
||||
// your app logic
|
||||
````
|
||||
</Tab>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
To get the value of a secret, use `infisical.get(key)`.
|
||||
|
||||
### infisical.get(key)
|
||||
|
||||
Return the value of the secret with the specified `key`. Note that the Infisical client falls back to `process.env` if `token` is `undefined` during the
|
||||
initialization step or if a value for the secret is not found in the fetched secrets.
|
||||
|
||||
<ResponseField name="key" type="string" required>
|
||||
The key of the secret
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="token" type="string">
|
||||
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
||||
and environment
|
||||
</ResponseField>
|
||||
<ResponseField
|
||||
name="siteURL"
|
||||
type="string"
|
||||
default="https://app.infisical.com"
|
||||
>
|
||||
Your self-hosted absolute site URL including the protocol (e.g.
|
||||
`https://app.infisical.com`)
|
||||
</ResponseField>
|
||||
<ResponseField name="cacheTTL" type="number" default="300">
|
||||
Time-to-live (in seconds) for refreshing cached secrets. Default: `300`.
|
||||
</ResponseField>
|
||||
<ResponseField name="debug" type="boolean" default="false">
|
||||
Whether or not debug mode is on
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Usage
|
||||
|
||||
### infisical.getSecret(secretName, options)
|
||||
|
||||
```js
|
||||
const value = infisical.get("SOME_KEY");
|
||||
const secret = await infisical.getSecret("API_KEY");
|
||||
const value = secret.secretValue; // get its value
|
||||
```
|
||||
|
||||
Retrieve a secret from Infisical.
|
||||
|
||||
By default, `getSecret()` returns a personal secret. If not found, it returns a shared secret, or tries to retrieve the value from `process.env`.
|
||||
|
||||
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The key of the secret to retrieve
|
||||
</ResponseField>
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="type" type="string">
|
||||
"personal" (default) or "shared".
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### infisical.createSecret(secretName, secretValue, options)
|
||||
|
||||
```js
|
||||
const newApiKey = await infisical.createSecret("API_KEY", "FOO");
|
||||
```
|
||||
|
||||
Create a new secret in Infisical.
|
||||
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The key of the secret to create
|
||||
</ResponseField>
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The value of the secret to create
|
||||
</ResponseField>
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="type" type="string">
|
||||
"shared" (default) or "personal". A personal secret can only be created if a shared secret with the same name exists.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### infisical.updateSecret(secretName, secretValue, options)
|
||||
|
||||
```js
|
||||
const updatedApiKey = await infisical.updateSecret("API_KEY", "BAR");
|
||||
```
|
||||
|
||||
Update an existing secret in Infisical.
|
||||
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The key of the secret to update
|
||||
</ResponseField>
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The new value of the secret
|
||||
</ResponseField>
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="type" type="string">
|
||||
"shared" (default) or "personal".
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### infisical.deleteSecret(secretName, options)
|
||||
|
||||
```js
|
||||
const deletedSecret = await infisical.deleteSecret("API_KEY");
|
||||
```
|
||||
|
||||
Delete a secret in Infisical.
|
||||
|
||||
<ResponseField name="secretName" type="string" required>
|
||||
The key of the secret to delete
|
||||
</ResponseField>
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="type" type="string">
|
||||
"shared" (default) or "personal". Note that deleting a shared secret also deletes all associated personal secrets.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Example with Express
|
||||
|
||||
```js
|
||||
const express = require("express");
|
||||
const port = 3000;
|
||||
const infisical = require("infisical-node");
|
||||
import InfisicalClient from "infisical-node";
|
||||
import express from "express";
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
|
||||
const main = async () => {
|
||||
await infisical.connect({
|
||||
token: "st.xxx.xxx",
|
||||
});
|
||||
const client = new InfisicalClient({
|
||||
token: "YOUR_INFISICAL_TOKEN"
|
||||
});
|
||||
|
||||
// your application logic
|
||||
app.get("/", async (req, res) => {
|
||||
// access value
|
||||
const name = await client.getSecret("NAME");
|
||||
res.send(`Hello! My name is: ${name.secretValue}`);
|
||||
});
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send(`Howdy, ${infisical.get("NAME")}!`);
|
||||
});
|
||||
|
||||
app.listen(port, async () => {
|
||||
console.log(`App listening on port ${port}`);
|
||||
});
|
||||
};
|
||||
app.listen(PORT, async () => {
|
||||
// initialize client
|
||||
console.log(`App listening on port ${port}`);
|
||||
});
|
||||
```
|
||||
|
||||
This example demonstrates how to use the Infisical SDK with an Express application. The application retrieves a secret named "NAME" and responds to requests with a greeting that includes the secret value.
|
||||
|
||||
<Warning>
|
||||
We do not recommend hardcoding your [Infisical
|
||||
Token](/getting-started/dashboard/token). Setting it as an environment
|
||||
|
||||
16
frontend/package-lock.json
generated
16
frontend/package-lock.json
generated
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"name": "npm-proj-1682405486465-0.42385611556033065msLhaJ",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
@@ -75,7 +75,7 @@
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"uuid": "^8.3.2",
|
||||
"uuidv4": "^6.2.13",
|
||||
"yaml": "^2.2.0",
|
||||
"yaml": "^2.2.2",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -22405,9 +22405,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
|
||||
"integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==",
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
@@ -38856,9 +38856,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"yaml": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
|
||||
"integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw=="
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"uuid": "^8.3.2",
|
||||
"uuidv4": "^6.2.13",
|
||||
"yaml": "^2.2.0",
|
||||
"yaml": "^2.2.2",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -803,8 +803,6 @@ export default function Dashboard() {
|
||||
isReadDenied: false
|
||||
};
|
||||
|
||||
console.log(124, envSlug, selectedWorkspaceEnv)
|
||||
|
||||
if (selectedWorkspaceEnv) {
|
||||
if (snapshotData) setSelectedSnapshotEnv(selectedWorkspaceEnv);
|
||||
else setSelectedEnv(selectedWorkspaceEnv);
|
||||
|
||||
@@ -125,7 +125,7 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
|
||||
|
||||
if (isSecretsLoading || isEnvListLoading) {
|
||||
return (
|
||||
<div className="container mx-auto flex h-full w-full items-center justify-center px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||
<div className="container mx-auto flex h-screen w-full items-center justify-center px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||
<img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" />
|
||||
</div>
|
||||
);
|
||||
@@ -234,14 +234,14 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="group min-w-full flex flex-row items-center mt-4">
|
||||
<div className="group flex flex-row items-center mt-4 min-w-[60.3rem]">
|
||||
<div className="w-10 h-10 px-4 flex items-center justify-center border-none"><div className='text-center w-10 text-xs text-transparent'>0</div></div>
|
||||
<div className="flex flex-row justify-between items-center min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
|
||||
<span className="text-transparent">0</span>
|
||||
<button type="button" className='mr-2 text-transparent'>1</button>
|
||||
</div>
|
||||
{userAvailableEnvs?.map(env => {
|
||||
return <div key={`button-${env.slug}`} className="flex flex-row w-full justify-center h-10 items-center border-none mb-1 mx-2 min-w-[10rem]">
|
||||
return <div key={`button-${env.slug}`} className="flex flex-row w-full justify-center h-10 items-center border-none mb-1 mx-2 min-w-[11rem]">
|
||||
<Button
|
||||
onClick={() => onEnvChange(env.slug)}
|
||||
// router.push(`${router.asPath }?env=${env.slug}`)
|
||||
|
||||
@@ -28,7 +28,7 @@ const DashboardInput = ({ isOverridden, isSecretValueHidden, isReadOnly, secret,
|
||||
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
||||
};
|
||||
|
||||
return <td key={`row-${secret?.key || ''}--`} className={`flex cursor-default flex-row w-full min-w-[11rem] justify-center h-10 items-center ${!(secret?.value || secret?.value === '') ? "bg-red-400/10" : "bg-mineshaft-900/30"}`}>
|
||||
return <td key={`row-${secret?.key || ''}--`} className={`flex cursor-default flex-row w-full justify-center h-10 items-center ${!(secret?.value || secret?.value === '') ? "bg-red-400/10" : "bg-mineshaft-900/30"}`}>
|
||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full cursor-default">
|
||||
<input
|
||||
// {...register(`secrets.${index}.valueOverride`)}
|
||||
@@ -118,8 +118,8 @@ export const EnvComparisonRow = ({
|
||||
<tr className="group min-w-full flex flex-row items-center hover:bg-bunker-700">
|
||||
<td className="w-10 h-10 px-4 flex items-center justify-center border-none"><div className='text-center w-10 text-xs text-bunker-400'>{index + 1}</div></td>
|
||||
<td className="flex flex-row justify-between items-center h-full min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
|
||||
<div className="flex flex-row items-center h-8 cursor-default">{secrets![0].key || ''}</div>
|
||||
<button type="button" className='mr-2 text-bunker-400 hover:text-bunker-300 invisible group-hover:visible' onClick={() => setAreValuesHiddenThisRow(!areValuesHiddenThisRow)}>
|
||||
<div className="flex truncate flex-row items-center h-8 cursor-default">{secrets![0].key || ''}</div>
|
||||
<button type="button" className='mr-1 ml-2 text-bunker-400 hover:text-bunker-300 invisible group-hover:visible' onClick={() => setAreValuesHiddenThisRow(!areValuesHiddenThisRow)}>
|
||||
<FontAwesomeIcon icon={areValuesHiddenThisRow ? faEye : faEyeSlash} />
|
||||
</button>
|
||||
</td>
|
||||
|
||||
380
i18n/README.hi.md
Normal file
380
i18n/README.hi.md
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user