mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
Finish basic scaffolding for service token v3
This commit is contained in:
@@ -2,10 +2,12 @@ import * as secretsController from "./secretsController";
|
|||||||
import * as workspacesController from "./workspacesController";
|
import * as workspacesController from "./workspacesController";
|
||||||
import * as authController from "./authController";
|
import * as authController from "./authController";
|
||||||
import * as signupController from "./signupController";
|
import * as signupController from "./signupController";
|
||||||
|
import * as serviceTokenDataController from "./serviceTokenDataController";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
secretsController,
|
secretsController,
|
||||||
signupController,
|
signupController,
|
||||||
workspacesController,
|
workspacesController,
|
||||||
|
serviceTokenDataController
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { Request, Response } from "express";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
import { ServiceTokenDataV3 } from "../../models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/serviceTokenV3";
|
||||||
|
import { createToken } from "../../helpers/auth";
|
||||||
|
|
||||||
|
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
body: { name, workspaceId, publicKey }
|
||||||
|
} = await validateRequest(reqValidator.CreateServiceTokenV3, req);
|
||||||
|
|
||||||
|
const serviceTokenData = await new ServiceTokenDataV3({
|
||||||
|
name,
|
||||||
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
|
publicKey,
|
||||||
|
isActive: false
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
console.log("the newly created serviceTokenDataV3: ", serviceTokenData);
|
||||||
|
|
||||||
|
const token = createToken({
|
||||||
|
payload: {
|
||||||
|
_id: serviceTokenData._id.toString()
|
||||||
|
},
|
||||||
|
expiresIn: "5d",
|
||||||
|
secret: "hello" // TODO: replace with real secret
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("jwt token: ", token);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
serviceTokenData,
|
||||||
|
serviceToken: `proj_token.${token}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateServiceTokenData = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { serviceTokenDataId },
|
||||||
|
body: { name, isActive }
|
||||||
|
} = await validateRequest(reqValidator.UpdateServiceTokenV3, req);
|
||||||
|
|
||||||
|
const serviceTokenData = await ServiceTokenDataV3.findByIdAndUpdate(
|
||||||
|
serviceTokenDataId,
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
isActive
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
serviceTokenData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { serviceTokenDataId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteServiceTokenV3, req);
|
||||||
|
|
||||||
|
const serviceTokenData = await ServiceTokenDataV3.findByIdAndDelete(serviceTokenDataId);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
serviceTokenData
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { validateRequest } from "../../helpers/validation";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import { Secret } from "../../models";
|
import { Secret, ServiceTokenDataV3 } from "../../models";
|
||||||
import { SecretService } from "../../services";
|
import { SecretService } from "../../services";
|
||||||
import { getUserProjectPermissions } from "../../ee/services/ProjectRoleService";
|
import { getUserProjectPermissions } from "../../ee/services/ProjectRoleService";
|
||||||
import { UnauthorizedRequestError } from "../../utils/errors";
|
import { UnauthorizedRequestError } from "../../utils/errors";
|
||||||
@@ -101,3 +101,17 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
message: "Successfully named workspace secrets"
|
message: "Successfully named workspace secrets"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getWorkspaceServiceTokenData = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceServiceTokenDataV3, req);
|
||||||
|
|
||||||
|
const serviceTokenData = await ServiceTokenDataV3.find({
|
||||||
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
serviceTokenData
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ router.get(
|
|||||||
router.get("/redirect/github", authLimiter, (req, res, next) => {
|
router.get("/redirect/github", authLimiter, (req, res, next) => {
|
||||||
passport.authenticate("github", {
|
passport.authenticate("github", {
|
||||||
session: false,
|
session: false,
|
||||||
|
scope: [ 'user:email' ],
|
||||||
...(req.query.callback_port
|
...(req.query.callback_port
|
||||||
? {
|
? {
|
||||||
state: req.query.callback_port as string
|
state: req.query.callback_port as string
|
||||||
@@ -43,7 +44,8 @@ router.get(
|
|||||||
authLimiter,
|
authLimiter,
|
||||||
passport.authenticate("github", {
|
passport.authenticate("github", {
|
||||||
failureRedirect: "/login/provider/error",
|
failureRedirect: "/login/provider/error",
|
||||||
session: false
|
session: false,
|
||||||
|
scope: [ 'user:email' ]
|
||||||
}),
|
}),
|
||||||
ssoController.redirectSSO
|
ssoController.redirectSSO
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ import {
|
|||||||
auth as v3AuthRouter,
|
auth as v3AuthRouter,
|
||||||
secrets as v3SecretsRouter,
|
secrets as v3SecretsRouter,
|
||||||
signup as v3SignupRouter,
|
signup as v3SignupRouter,
|
||||||
workspaces as v3WorkspacesRouter
|
workspaces as v3WorkspacesRouter,
|
||||||
|
serviceTokenData as v3ServiceTokenDataRouter
|
||||||
} from "./routes/v3";
|
} from "./routes/v3";
|
||||||
import { healthCheck } from "./routes/status";
|
import { healthCheck } from "./routes/status";
|
||||||
import { getLogger } from "./utils/logger";
|
import { getLogger } from "./utils/logger";
|
||||||
@@ -188,13 +189,15 @@ const main = async () => {
|
|||||||
app.use("/api/v2/secret", v2SecretRouter); // deprecate
|
app.use("/api/v2/secret", v2SecretRouter); // deprecate
|
||||||
app.use("/api/v2/secrets", v2SecretsRouter); // note: in the process of moving to v3/secrets
|
app.use("/api/v2/secrets", v2SecretsRouter); // note: in the process of moving to v3/secrets
|
||||||
app.use("/api/v2/service-token", v2ServiceTokenDataRouter);
|
app.use("/api/v2/service-token", v2ServiceTokenDataRouter);
|
||||||
app.use("/api/v2/service-accounts", v2ServiceAccountsRouter); // new
|
// app.use("/api/v2/service-accounts", v2ServiceAccountsRouter); // new
|
||||||
|
|
||||||
// v3 routes (experimental)
|
// v3 routes (experimental)
|
||||||
app.use("/api/v3/auth", v3AuthRouter);
|
app.use("/api/v3/auth", v3AuthRouter);
|
||||||
app.use("/api/v3/secrets", v3SecretsRouter);
|
app.use("/api/v3/secrets", v3SecretsRouter);
|
||||||
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
||||||
app.use("/api/v3/signup", v3SignupRouter);
|
app.use("/api/v3/signup", v3SignupRouter);
|
||||||
|
app.use("/api/v3/service-token", v3ServiceTokenDataRouter);
|
||||||
|
|
||||||
|
|
||||||
// api docs
|
// api docs
|
||||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
||||||
|
|||||||
@@ -14,17 +14,18 @@ export * from "./tag";
|
|||||||
export * from "./folder";
|
export * from "./folder";
|
||||||
export * from "./secretImports";
|
export * from "./secretImports";
|
||||||
export * from "./secretBlindIndexData";
|
export * from "./secretBlindIndexData";
|
||||||
export * from "./serviceToken";
|
export * from "./serviceToken"; // TODO: deprecate
|
||||||
export * from "./serviceAccount";
|
export * from "./serviceAccount"; // TODO: deprecate
|
||||||
export * from "./serviceAccountKey";
|
export * from "./serviceAccountKey"; // TODO: deprecate
|
||||||
export * from "./serviceAccountOrganizationPermission";
|
export * from "./serviceAccountOrganizationPermission"; // TODO: deprecate
|
||||||
export * from "./serviceAccountWorkspacePermission";
|
export * from "./serviceAccountWorkspacePermission"; // TODO: deprecate
|
||||||
export * from "./tokenData";
|
export * from "./tokenData";
|
||||||
export * from "./user";
|
export * from "./user";
|
||||||
export * from "./userAction";
|
export * from "./userAction";
|
||||||
export * from "./workspace";
|
export * from "./workspace";
|
||||||
export * from "./serviceTokenData";
|
export * from "./serviceTokenData"; // TODO: deprecate
|
||||||
export * from "./apiKeyData";
|
export * from "./apiKeyData";
|
||||||
export * from "./loginSRPDetail";
|
export * from "./loginSRPDetail";
|
||||||
export * from "./tokenVersion";
|
export * from "./tokenVersion";
|
||||||
export * from "./webhooks";
|
export * from "./webhooks";
|
||||||
|
export * from "./serviceTokenDataV3";
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Document, Schema, Types, model } from "mongoose";
|
import { Document, Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
export interface IServiceTokenV3 extends Document {
|
export interface IServiceTokenDataV3 extends Document {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
name: string;
|
name: string;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
|
isActive: boolean;
|
||||||
|
lastUsed: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceTokenV3Schema = new Schema(
|
const serviceTokenDataV3Schema = new Schema(
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -21,8 +23,19 @@ const serviceTokenV3Schema = new Schema(
|
|||||||
publicKey: {
|
publicKey: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
lastUsed: {
|
||||||
|
type: Date,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ServiceTokenV3 = model<IServiceTokenV3>("ServiceTokenV3", serviceTokenV3Schema);
|
export const ServiceTokenDataV3 = model<IServiceTokenDataV3>("ServiceTokenDataV3", serviceTokenDataV3Schema);
|
||||||
@@ -2,10 +2,12 @@ import auth from "./auth";
|
|||||||
import secrets from "./secrets";
|
import secrets from "./secrets";
|
||||||
import workspaces from "./workspaces";
|
import workspaces from "./workspaces";
|
||||||
import signup from "./signup";
|
import signup from "./signup";
|
||||||
|
import serviceTokenData from "./serviceTokenData";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
auth,
|
auth,
|
||||||
secrets,
|
secrets,
|
||||||
signup,
|
signup,
|
||||||
workspaces,
|
workspaces,
|
||||||
|
serviceTokenData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import express from "express";
|
||||||
|
const router = express.Router();
|
||||||
|
import { requireAuth } from "../../middleware";
|
||||||
|
import { AuthMode } from "../../variables";
|
||||||
|
import { serviceTokenDataController } from "../../controllers/v3";
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
serviceTokenDataController.createServiceTokenData
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:serviceTokenDataId",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
serviceTokenDataController.updateServiceTokenData
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/:serviceTokenDataId",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
serviceTokenDataController.deleteServiceTokenData
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -34,4 +34,12 @@ router.post(
|
|||||||
|
|
||||||
// --
|
// --
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:workspaceId/service-token",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
workspacesController.getWorkspaceServiceTokenData
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import { InternalServerError, OrganizationNotFoundError } from "./errors";
|
|||||||
import { ACCEPTED, INVITED, MEMBER } from "../variables";
|
import { ACCEPTED, INVITED, MEMBER } from "../variables";
|
||||||
import { getSiteURL } from "../config";
|
import { getSiteURL } from "../config";
|
||||||
|
|
||||||
|
import { standardRequest } from "../config/request";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const GoogleStrategy = require("passport-google-oauth20").Strategy;
|
const GoogleStrategy = require("passport-google-oauth20").Strategy;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
@@ -143,9 +145,11 @@ const initializePassport = async () => {
|
|||||||
passReqToCallback: true,
|
passReqToCallback: true,
|
||||||
clientID: clientIdGitHubLogin,
|
clientID: clientIdGitHubLogin,
|
||||||
clientSecret: clientSecretGitHubLogin,
|
clientSecret: clientSecretGitHubLogin,
|
||||||
callbackURL: "/api/v1/sso/github"
|
callbackURL: "/api/v1/sso/github",
|
||||||
|
scope: [ 'user:email' ]
|
||||||
},
|
},
|
||||||
async (req : express.Request, accessToken : any, refreshToken : any, profile : any, done : any) => {
|
async (req : express.Request, accessToken : any, refreshToken : any, profile : any, done : any) => {
|
||||||
|
|
||||||
const email = profile.emails[0].value;
|
const email = profile.emails[0].value;
|
||||||
|
|
||||||
let user = await User.findOne({
|
let user = await User.findOne({
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ export * from "./organization";
|
|||||||
export * from "./secrets";
|
export * from "./secrets";
|
||||||
export * from "./serviceAccount";
|
export * from "./serviceAccount";
|
||||||
export * from "./serviceTokenData";
|
export * from "./serviceTokenData";
|
||||||
|
export * from "./serviceTokenV3";
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CreateServiceTokenV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
publicKey: z.string().trim(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateServiceTokenV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
serviceTokenDataId: z.string()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim().optional(),
|
||||||
|
isActive: z.boolean().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteServiceTokenV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
serviceTokenDataId: z.string()
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -299,3 +299,9 @@ export const NameWorkspaceSecretsV3 = z.object({
|
|||||||
.array()
|
.array()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceServiceTokenDataV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -1 +1,8 @@
|
|||||||
export { useCreateServiceToken, useDeleteServiceToken, useGetUserWsServiceTokens } from "./queries";
|
export {
|
||||||
|
useCreateServiceToken,
|
||||||
|
useDeleteServiceToken,
|
||||||
|
useGetUserWsServiceTokens,
|
||||||
|
useCreateServiceTokenV3,
|
||||||
|
useUpdateServiceTokenV3,
|
||||||
|
useDeleteServiceTokenV3
|
||||||
|
} from "./queries";
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ import {
|
|||||||
CreateServiceTokenDTO,
|
CreateServiceTokenDTO,
|
||||||
CreateServiceTokenRes,
|
CreateServiceTokenRes,
|
||||||
DeleteServiceTokenRes,
|
DeleteServiceTokenRes,
|
||||||
ServiceToken
|
ServiceToken,
|
||||||
|
ServiceTokenDataV3,
|
||||||
|
CreateServiceTokenDataV3DTO,
|
||||||
|
CreateServiceTokenDataV3Res,
|
||||||
|
UpdateServiceTokenDataV3DTO,
|
||||||
|
DeleteServiceTokenDataV3DTO
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
import { workspaceKeys } from "../workspace/queries";
|
||||||
|
|
||||||
const serviceTokenKeys = {
|
const serviceTokenKeys = {
|
||||||
getAllWorkspaceServiceToken: (workspaceID: string) => [{ workspaceID }, "service-tokens"] as const
|
getAllWorkspaceServiceToken: (workspaceID: string) => [{ workspaceID }, "service-tokens"] as const
|
||||||
@@ -32,12 +38,11 @@ export const useGetUserWsServiceTokens = ({ workspaceID }: UseGetWorkspaceServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mutation
|
// mutation
|
||||||
export const useCreateServiceToken = () => {
|
export const useCreateServiceToken = () => { // TODO: deprecate
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<CreateServiceTokenRes, {}, CreateServiceTokenDTO>({
|
return useMutation<CreateServiceTokenRes, {}, CreateServiceTokenDTO>({
|
||||||
mutationFn: async (body) => {
|
mutationFn: async (body) => {
|
||||||
console.log("useCreateServiceToken");
|
|
||||||
const { data } = await apiRequest.post("/api/v2/service-token/", body);
|
const { data } = await apiRequest.post("/api/v2/service-token/", body);
|
||||||
data.serviceToken += `.${body.randomBytes}`;
|
data.serviceToken += `.${body.randomBytes}`;
|
||||||
return data;
|
return data;
|
||||||
@@ -62,3 +67,55 @@ export const useDeleteServiceToken = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCreateServiceTokenV3 = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<CreateServiceTokenDataV3Res, {}, CreateServiceTokenDataV3DTO>({
|
||||||
|
mutationFn: async (body) => {
|
||||||
|
const { data } = await apiRequest.post("/api/v3/service-token/", body);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess: ({ serviceTokenData }) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceServiceTokenDataV3(serviceTokenData.workspace));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateServiceTokenV3 = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<ServiceTokenDataV3, {}, UpdateServiceTokenDataV3DTO>({
|
||||||
|
mutationFn: async ({
|
||||||
|
serviceTokenDataId,
|
||||||
|
name,
|
||||||
|
isActive
|
||||||
|
}) => {
|
||||||
|
const { data: { serviceTokenData } } = await apiRequest.patch(`/api/v3/service-token/${serviceTokenDataId}`, {
|
||||||
|
name,
|
||||||
|
isActive
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceTokenData;
|
||||||
|
},
|
||||||
|
onSuccess: ({ workspace }) => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceServiceTokenDataV3(workspace));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteServiceTokenV3 = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<ServiceTokenDataV3, {}, DeleteServiceTokenDataV3DTO>({
|
||||||
|
mutationFn: async ({
|
||||||
|
serviceTokenDataId
|
||||||
|
}) => {
|
||||||
|
console.log("useDeleteServiceTokenV3");
|
||||||
|
const { data: { serviceTokenData } } = await apiRequest.delete(`/api/v3/service-token/${serviceTokenDataId}`);
|
||||||
|
console.log("useDeleteServiceTokenV3 serviceTokenData: ", serviceTokenData);
|
||||||
|
return serviceTokenData;
|
||||||
|
},
|
||||||
|
onSuccess: ({ workspace }) => {
|
||||||
|
console.log("useDeleteServiceTokenV3 onSuccess: ", workspace);
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceServiceTokenDataV3(workspace));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -33,3 +33,38 @@ export type CreateServiceTokenRes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DeleteServiceTokenRes = { serviceTokenData: ServiceToken };
|
export type DeleteServiceTokenRes = { serviceTokenData: ServiceToken };
|
||||||
|
|
||||||
|
// --- v3
|
||||||
|
|
||||||
|
export type ServiceTokenDataV3 = {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
workspace: string;
|
||||||
|
isActive: boolean;
|
||||||
|
lastUsed?: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: add scopes
|
||||||
|
// TODO: encrypted key info
|
||||||
|
export type CreateServiceTokenDataV3DTO = {
|
||||||
|
name: string;
|
||||||
|
workspaceId: string;
|
||||||
|
publicKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreateServiceTokenDataV3Res = {
|
||||||
|
serviceToken: string;
|
||||||
|
serviceTokenData: ServiceTokenDataV3;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateServiceTokenDataV3DTO = {
|
||||||
|
serviceTokenDataId: string;
|
||||||
|
name?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeleteServiceTokenDataV3DTO = {
|
||||||
|
serviceTokenDataId: string;
|
||||||
|
}
|
||||||
@@ -19,4 +19,6 @@ export {
|
|||||||
useReorderWsEnvironment,
|
useReorderWsEnvironment,
|
||||||
useToggleAutoCapitalization,
|
useToggleAutoCapitalization,
|
||||||
useUpdateUserWorkspaceRole,
|
useUpdateUserWorkspaceRole,
|
||||||
useUpdateWsEnvironment} from "./queries";
|
useUpdateWsEnvironment,
|
||||||
|
useGetWorkspaceServiceTokenDataV3
|
||||||
|
} from "./queries";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { IntegrationAuth } from "../integrationAuth/types";
|
|||||||
import { TIntegration } from "../integrations/types";
|
import { TIntegration } from "../integrations/types";
|
||||||
import { EncryptedSecret } from "../secrets/types";
|
import { EncryptedSecret } from "../secrets/types";
|
||||||
import { TWorkspaceUser } from "../users/types";
|
import { TWorkspaceUser } from "../users/types";
|
||||||
|
import { ServiceTokenDataV3 } from "../serviceTokens/types";
|
||||||
import {
|
import {
|
||||||
CreateEnvironmentDTO,
|
CreateEnvironmentDTO,
|
||||||
CreateWorkspaceDTO,
|
CreateWorkspaceDTO,
|
||||||
@@ -32,7 +33,8 @@ export const workspaceKeys = {
|
|||||||
getAllUserWorkspace: ["workspaces"] as const,
|
getAllUserWorkspace: ["workspaces"] as const,
|
||||||
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const,
|
getUserWsEnvironments: (workspaceId: string) => ["workspace-env", { workspaceId }] as const,
|
||||||
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const,
|
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const,
|
||||||
getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const
|
getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const,
|
||||||
|
getWorkspaceServiceTokenDataV3: (workspaceId: string) => [{ workspaceId }, "workspace-service-token-data-v3"] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchWorkspaceById = async (workspaceId: string) => {
|
const fetchWorkspaceById = async (workspaceId: string) => {
|
||||||
@@ -361,3 +363,19 @@ export const useUpdateUserWorkspaceRole = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetWorkspaceServiceTokenDataV3 = (workspaceId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: workspaceKeys.getWorkspaceServiceTokenDataV3(workspaceId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const {
|
||||||
|
data: { serviceTokenData }
|
||||||
|
} = await apiRequest.get<{ serviceTokenData: ServiceTokenDataV3[] }>(
|
||||||
|
`/api/v3/workspaces/${workspaceId}/service-token`
|
||||||
|
);
|
||||||
|
|
||||||
|
return serviceTokenData;
|
||||||
|
},
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -49,56 +49,54 @@ export const APIKeyTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<TableContainer>
|
||||||
<TableContainer className="">
|
<Table>
|
||||||
<Table>
|
<THead>
|
||||||
<THead>
|
<Tr>
|
||||||
|
<Th className="flex-1">Name</Th>
|
||||||
|
<Th className="flex-1">Last active</Th>
|
||||||
|
<Th className="flex-1">Created</Th>
|
||||||
|
<Th className="flex-1">Expiration</Th>
|
||||||
|
<Th className="w-5" />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isLoading && <TableSkeleton columns={4} innerKey="api-keys" />}
|
||||||
|
{!isLoading &&
|
||||||
|
data &&
|
||||||
|
data.length > 0 &&
|
||||||
|
data.map(({ _id, name, createdAt, expiresAt, lastUsed }) => {
|
||||||
|
return (
|
||||||
|
<Tr className="h-10" key={`api-key-${_id}`}>
|
||||||
|
<Td>{name}</Td>
|
||||||
|
<Td>{formatDate(lastUsed)}</Td>
|
||||||
|
<Td>{formatDate(createdAt)}</Td>
|
||||||
|
<Td>{formatDate(expiresAt)}</Td>
|
||||||
|
<Td>
|
||||||
|
<IconButton
|
||||||
|
onClick={async () => {
|
||||||
|
await handleDeleteAPIKeyDataClick(_id);
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!isLoading && data && data?.length === 0 && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th className="flex-1">Name</Th>
|
<Td colSpan={5}>
|
||||||
<Th className="flex-1">Last active</Th>
|
<EmptyState title="No API Keys on file" icon={faKey} />
|
||||||
<Th className="flex-1">Created</Th>
|
</Td>
|
||||||
<Th className="flex-1">Expiration</Th>
|
|
||||||
<Th className="w-5" />
|
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
)}
|
||||||
<TBody>
|
</TBody>
|
||||||
{isLoading && <TableSkeleton columns={4} innerKey="api-keys" />}
|
</Table>
|
||||||
{!isLoading &&
|
</TableContainer>
|
||||||
data &&
|
|
||||||
data.length > 0 &&
|
|
||||||
data.map(({ _id, name, createdAt, expiresAt, lastUsed }) => {
|
|
||||||
return (
|
|
||||||
<Tr className="h-10" key={`api-key-${_id}`}>
|
|
||||||
<Td>{name}</Td>
|
|
||||||
<Td>{formatDate(lastUsed)}</Td>
|
|
||||||
<Td>{formatDate(createdAt)}</Td>
|
|
||||||
<Td>{formatDate(expiresAt)}</Td>
|
|
||||||
<Td>
|
|
||||||
<IconButton
|
|
||||||
onClick={async () => {
|
|
||||||
await handleDeleteAPIKeyDataClick(_id);
|
|
||||||
}}
|
|
||||||
size="lg"
|
|
||||||
colorSchema="danger"
|
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{!isLoading && data && data?.length === 0 && (
|
|
||||||
<Tr>
|
|
||||||
<Td colSpan={5}>
|
|
||||||
<EmptyState title="No API Keys on file" icon={faKey} />
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
)}
|
|
||||||
</TBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -161,18 +161,18 @@ export const AddAPIKeyModal = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="mt-8 flex items-center">
|
<div className="mt-8 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
size="sm"
|
size="sm"
|
||||||
type="submit"
|
type="submit"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isDisabled={isLoading}
|
isDisabled={isLoading}
|
||||||
>
|
>
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
<Button colorSchema="secondary" variant="plain">
|
<Button colorSchema="secondary" variant="plain">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { Fragment } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
|
|
||||||
import NavHeader from "@app/components/navigation/NavHeader";
|
|
||||||
|
|
||||||
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
||||||
import { ProjectServiceTokensTab } from "./components/ProjectServiceTokensTab";
|
import { ProjectServiceTokensTab } from "./components/ProjectServiceTokensTab";
|
||||||
import { WebhooksTab } from "./components/WebhooksTab";
|
import { WebhooksTab } from "./components/WebhooksTab";
|
||||||
@@ -17,12 +15,9 @@ const tabs = [
|
|||||||
export const ProjectSettingsPage = () => {
|
export const ProjectSettingsPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full justify-center bg-bunker-800 px-6 text-white">
|
<div className="flex justify-center w-full h-full bg-bunker-800 text-white">
|
||||||
<div className="w-full max-w-screen-lg">
|
<div className="max-w-7xl px-6 w-full">
|
||||||
<div className="relative right-5 ml-4">
|
<div className="my-6">
|
||||||
<NavHeader pageName={t("settings.project.title")} isProjectRelated />
|
|
||||||
</div>
|
|
||||||
<div className="my-8">
|
|
||||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.project.title")}</p>
|
<p className="text-3xl font-semibold text-gray-200">{t("settings.project.title")}</p>
|
||||||
</div>
|
</div>
|
||||||
<Tab.Group>
|
<Tab.Group>
|
||||||
|
|||||||
+182
-4
@@ -1,7 +1,185 @@
|
|||||||
export const AddServiceTokenV3Modal = () => {
|
import nacl from "tweetnacl";
|
||||||
|
import { encodeBase64 } from "tweetnacl-util";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { useWorkspace } from "@app/context";
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Input,
|
||||||
|
Button
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
import { useCreateServiceTokenV3 } from "@app/hooks/api";
|
||||||
|
|
||||||
|
const expirations = [
|
||||||
|
{ label: "1 day", value: "86400" },
|
||||||
|
{ label: "7 days", value: "604800" },
|
||||||
|
{ label: "1 month", value: "2592000" },
|
||||||
|
{ label: "6 months", value: "15552000" },
|
||||||
|
{ label: "12 months", value: "31104000" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
name: yup.string().required("ST V3 name is required"),
|
||||||
|
expiresIn: yup.string().required("ST V3 expiration window is required")
|
||||||
|
}).required();
|
||||||
|
|
||||||
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["createServiceTokenV3"]>;
|
||||||
|
handlePopUpToggle: (popUpName: keyof UsePopUpState<["createServiceTokenV3"]>, state?: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Will download a JSON
|
||||||
|
// Maybe you can set a timer at which point service token is no longer active!
|
||||||
|
// Maybe you can also set IP allowlist for it too
|
||||||
|
|
||||||
|
export const AddServiceTokenV3Modal = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpToggle
|
||||||
|
}: Props) => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { mutateAsync } = useCreateServiceTokenV3();
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
reset
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema)
|
||||||
|
});
|
||||||
|
|
||||||
|
const onFormSubmit = async ({
|
||||||
|
name,
|
||||||
|
expiresIn
|
||||||
|
}: FormData) => {
|
||||||
|
try {
|
||||||
|
if (!currentWorkspace?._id) return;
|
||||||
|
|
||||||
|
console.log("onFormSubmit name: ", name);
|
||||||
|
console.log("onFormSubmit expiresIn: ", expiresIn);
|
||||||
|
|
||||||
|
const pair = nacl.box.keyPair();
|
||||||
|
const secretKeyUint8Array = pair.secretKey;
|
||||||
|
const publicKeyUint8Array = pair.publicKey;
|
||||||
|
const privateKey = encodeBase64(secretKeyUint8Array);
|
||||||
|
const publicKey = encodeBase64(publicKeyUint8Array);
|
||||||
|
|
||||||
|
console.log("pair: ", pair);
|
||||||
|
console.log("privateKey: ", privateKey);
|
||||||
|
console.log("publicKey: ", publicKey );
|
||||||
|
const { serviceToken } = await mutateAsync({
|
||||||
|
name,
|
||||||
|
workspaceId: currentWorkspace._id,
|
||||||
|
publicKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const downloadData = {
|
||||||
|
publicKey,
|
||||||
|
privateKey,
|
||||||
|
serviceToken
|
||||||
|
};
|
||||||
|
|
||||||
|
const blob = new Blob([JSON.stringify(downloadData, null, 2)], { type: 'application/json' });
|
||||||
|
const href = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = href;
|
||||||
|
link.download = `infisical_${name}.json`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully created ST V3",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
reset();
|
||||||
|
handlePopUpToggle("createServiceTokenV3", false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to create ST V3",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Modal
|
||||||
AddServiceTokenV3Modal
|
isOpen={popUp?.createServiceTokenV3?.isOpen}
|
||||||
</div>
|
onOpenChange={(isOpen) => {
|
||||||
|
handlePopUpToggle("createServiceTokenV3", isOpen);
|
||||||
|
reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModalContent title="Create Service Token V3">
|
||||||
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="name"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="My ST V3"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="expiresIn"
|
||||||
|
defaultValue="15552000"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Expiration"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{expirations.map(({ label, value }) => (
|
||||||
|
<SelectItem value={String(value || "")} key={`api-key-expiration-${label}`}>
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-8 flex items-center">
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
// isLoading={isLoading}
|
||||||
|
// isDisabled={isLoading}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+24
-4
@@ -1,14 +1,34 @@
|
|||||||
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { AddServiceTokenV3Modal } from "./AddServiceTokenV3Modal";
|
import { AddServiceTokenV3Modal } from "./AddServiceTokenV3Modal";
|
||||||
import { ServiceTokenV3Table } from "./ServiceTokenV3Table";
|
import { ServiceTokenV3Table } from "./ServiceTokenV3Table";
|
||||||
|
import { Button } from "@app/components/v2";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const ServiceTokenV3Section = () => {
|
export const ServiceTokenV3Section = () => {
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
|
"createServiceTokenV3"
|
||||||
|
] as const);
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">
|
<div className="flex justify-between mb-8">
|
||||||
Service Tokens 2.0
|
<p className="text-xl font-semibold text-mineshaft-100">
|
||||||
</p>
|
Service Tokens 2.0
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
type="submit"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
onClick={() => handlePopUpOpen("createServiceTokenV3")}
|
||||||
|
>
|
||||||
|
Create ST V3
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<ServiceTokenV3Table />
|
<ServiceTokenV3Table />
|
||||||
<AddServiceTokenV3Modal />
|
<AddServiceTokenV3Modal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+166
-3
@@ -1,7 +1,170 @@
|
|||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faKey, faXmark, faPencil } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
EmptyState,
|
||||||
|
IconButton,
|
||||||
|
Switch,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
useGetWorkspaceServiceTokenDataV3,
|
||||||
|
useUpdateServiceTokenV3,
|
||||||
|
useDeleteServiceTokenV3
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
|
||||||
export const ServiceTokenV3Table = () => {
|
export const ServiceTokenV3Table = () => {
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { data, isLoading } = useGetWorkspaceServiceTokenDataV3(currentWorkspace?._id || "");
|
||||||
|
const { mutateAsync: updateMutateAsync } = useUpdateServiceTokenV3();
|
||||||
|
const { mutateAsync: deleteMutateAsync } = useDeleteServiceTokenV3();
|
||||||
|
|
||||||
|
console.log("data1: ", data);
|
||||||
|
|
||||||
|
const handleDeleteServiceTokenData = async (serviceTokenDataId: string) => {
|
||||||
|
try {
|
||||||
|
await deleteMutateAsync({
|
||||||
|
serviceTokenDataId
|
||||||
|
});
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully deleted service token v3",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to delete service token v3",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleToggleServiceTokenDataStatus = async ({
|
||||||
|
serviceTokenDataId,
|
||||||
|
isActive
|
||||||
|
}: {
|
||||||
|
serviceTokenDataId: string;
|
||||||
|
isActive: boolean;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await updateMutateAsync({
|
||||||
|
serviceTokenDataId,
|
||||||
|
isActive
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: `Successfully ${isActive ? "enabled" : "disabled"} service token v3`,
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
createNotification({
|
||||||
|
text: `Failed to ${isActive ? "enable" : "disable"} service token v3`,
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDate = (dateToFormat: string) => {
|
||||||
|
const date = new Date(dateToFormat);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
|
||||||
|
const formattedDate = `${day}/${month}/${year}`;
|
||||||
|
|
||||||
|
return formattedDate;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<TableContainer>
|
||||||
ServiceTokenV3Table
|
<Table>
|
||||||
</div>
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th>Name</Th>
|
||||||
|
<Th>Status</Th>
|
||||||
|
<Th>Last Active</Th>
|
||||||
|
<Th>Created</Th>
|
||||||
|
<Th>Expiration</Th>
|
||||||
|
<Th className="w-5"></Th>
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isLoading && <TableSkeleton columns={5} innerKey="service-tokens" />}
|
||||||
|
{!isLoading &&
|
||||||
|
data &&
|
||||||
|
data.length > 0 &&
|
||||||
|
data.map(({
|
||||||
|
_id,
|
||||||
|
name,
|
||||||
|
isActive,
|
||||||
|
lastUsed,
|
||||||
|
createdAt,
|
||||||
|
// expiresAt
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Tr className="h-10" key={`st-v3-${_id}`}>
|
||||||
|
<Td>{name}</Td>
|
||||||
|
<Td>
|
||||||
|
<Switch
|
||||||
|
id="test"
|
||||||
|
// id={`enable-${authMethodOpt.value}-auth`}
|
||||||
|
onCheckedChange={(value) => handleToggleServiceTokenDataStatus({
|
||||||
|
serviceTokenDataId: _id,
|
||||||
|
isActive: value
|
||||||
|
})}
|
||||||
|
isChecked={isActive}
|
||||||
|
>
|
||||||
|
<p className="w-12 mr-4">{isActive ? "Active" : "Inactive"}</p>
|
||||||
|
</Switch>
|
||||||
|
</Td>
|
||||||
|
<Td>{lastUsed ? formatDate(lastUsed) : "-"}</Td>
|
||||||
|
<Td>{formatDate(createdAt)}</Td>
|
||||||
|
<Td>{formatDate(createdAt)}</Td>
|
||||||
|
<Td className="flex justify-end">
|
||||||
|
<IconButton
|
||||||
|
onClick={async () => {
|
||||||
|
console.log("edit");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPencil} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleDeleteServiceTokenData(_id)}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="ml-4"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!isLoading && data && data?.length === 0 && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={5}>
|
||||||
|
<EmptyState title="No service token v3 on file" icon={faKey} />
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user