Compare commits

..

7 Commits

Author SHA1 Message Date
mirarifhasan
54a7e73981 chore: shortcode seed added and revamp the script 2024-05-08 21:17:43 +06:00
mirarifhasan
2a3dce6621 fix: generate fake email in lower case 2024-05-08 20:14:20 +06:00
mirarifhasan
260f526edc fix: dist folder being unsupported 2024-05-07 13:02:44 +06:00
mirarifhasan
20b9b94b53 build: seed file added
run nestjs app in dev mode, prod mode has an error
2024-05-07 11:30:32 +06:00
Andrew Bastin
391e5a20f5 chore: bump versions to 2024.3.3 2024-05-06 22:57:33 +05:30
Andrew Bastin
4b8f3bd8da fix: code generate modal erroring out (#4033) 2024-05-06 22:44:13 +05:30
Joel Jacob Stephen
94248076e6 refactor(sh-admin): improved handling of server configurations in admin dashboard (#3971)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-05-06 21:50:31 +05:30
20 changed files with 16683 additions and 18472 deletions

View File

@@ -38,7 +38,7 @@
},
"packageExtensions": {
"httpsnippet@3.0.1": {
"peerDependencies": {
"dependencies": {
"ajv": "6.12.3"
}
}

View File

@@ -1,10 +1,13 @@
{
"name": "hoppscotch-backend",
"version": "2024.3.2",
"version": "2024.3.3",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"prisma": {
"seed": "ts-node prisma/seed.ts"
},
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
@@ -66,6 +69,7 @@
"rxjs": "7.6.0"
},
"devDependencies": {
"@faker-js/faker": "8.4.1",
"@nestjs/cli": "10.2.1",
"@nestjs/schematics": "10.0.3",
"@nestjs/testing": "10.2.7",

View File

@@ -0,0 +1,444 @@
import { faker } from '@faker-js/faker';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const createUsersAndAccounts = async (
entries = 10,
withDedicateUsers: { email: string; isAdmin: boolean }[] = [],
) => {
const createAUser = async (email?, isAdmin?) => {
try {
const newUser = await prisma.user.create({
data: {
displayName: faker.person.fullName(),
email: email ?? faker.internet.email().toLowerCase(),
photoURL: faker.image.avatar(),
isAdmin: isAdmin ?? false,
},
});
await prisma.account.create({
data: {
userId: newUser.uid,
provider: 'magic',
providerAccountId: newUser.email,
},
});
} catch (e) {
console.log('Error creating user/account:', e.message);
}
};
for (let i = 0; i < withDedicateUsers.length; i++) {
const user = withDedicateUsers[i];
await createAUser(user.email.toLowerCase(), user.isAdmin);
}
for (let i = 0; i < entries - withDedicateUsers.length; i++) {
await createAUser();
}
console.log('Users created');
};
const createInvitedUsers = async (entries = 10) => {
try {
const admin = await prisma.user.findFirst({ where: { isAdmin: true } });
for (let i = 0; i < entries; i++) {
await prisma.invitedUsers.create({
data: {
adminUid: admin.uid,
adminEmail: admin.email,
inviteeEmail: faker.internet.email().toLowerCase(),
},
});
}
console.log('Invited users created');
} catch (e) {
console.log('Error creating invited users:', e.message);
}
};
const createUserCollections = async (
parentCollEntries = 10,
childCollEntriesOnEachParent = 10,
) => {
const createACollection = async (userUid, parentID, orderIndex) => {
return prisma.userCollection.create({
data: {
parentID,
title: faker.vehicle.vehicle(),
orderIndex,
type: 'REST',
userUid,
},
});
};
try {
const users = await prisma.user.findMany();
for (let u = 0; u < users.length; u++) {
const user = users[u];
for (let i = 0; i < parentCollEntries; i++) {
const parentCollection = await createACollection(user.uid, null, i + 1);
for (let j = 0; j < childCollEntriesOnEachParent; j++) {
await createACollection(user.uid, parentCollection.id, j + 1);
}
}
}
console.log('User collections created');
} catch (e) {
console.log('Error creating user collections:', e.message);
}
};
const createUserRequests = async (entriesPerCollection = 10) => {
try {
const collections = await prisma.userCollection.findMany();
for (let i = 0; i < collections.length; i++) {
const collection = collections[i];
for (let j = 0; j < entriesPerCollection; j++) {
const requestTitle = faker.git.branch();
await prisma.userRequest.create({
data: {
collectionID: collection.id,
userUid: collection.userUid,
title: requestTitle,
request: {
v: '4',
auth: { authType: 'inherit', authActive: true },
body: { body: null, contentType: null },
name: requestTitle,
method: 'GET',
params: [],
headers: [],
endpoint: 'https://echo.hoppscotch.io',
testScript: '',
preRequestScript: '',
requestVariables: [],
},
type: collection.type,
orderIndex: j + 1,
},
});
}
}
console.log('User requests created');
} catch (e) {
console.log('Error creating user requests:', e.message);
}
};
const createUserEnvironments = async () => {
try {
const users = await prisma.user.findMany();
for (let i = 0; i < users.length; i++) {
const user = users[i];
const environments = await prisma.userEnvironment.findMany({
where: { userUid: user.uid },
});
if (environments.length > 1) continue; // skip if already created. (assuming default GLOBAL environments are created by APP itself)
await prisma.userEnvironment.createMany({
data: [
{
userUid: user.uid,
name: 'production',
variables: [
{
key: 'product_id',
value: faker.number.int({ max: 1000 }),
secret: false,
},
],
isGlobal: false,
},
{
userUid: user.uid,
name: 'development',
variables: [
{
key: 'product_id',
value: faker.number.int({ max: 1000 }),
secret: false,
},
],
isGlobal: false,
},
],
});
}
console.log('User environments created');
} catch (e) {
console.log('Error creating user environment:', e.message);
}
};
const createTeamsAndTeamMembers = async (
entries = 10,
memberCountInATeam = 4,
) => {
const createATeam = async (ownerUid) => {
return prisma.team.create({
data: {
name: faker.airline.airplane().name,
members: { create: { userUid: ownerUid, role: 'OWNER' } },
},
});
};
const createATeamMember = async (teamID, userUid) => {
try {
return prisma.teamMember.create({
data: {
teamID,
userUid,
role: +faker.number.binary() === 1 ? 'EDITOR' : 'VIEWER',
},
});
} catch (e) {
console.log(e.message);
}
};
try {
const users = await prisma.user.findMany();
for (let i = 0; i < entries; i++) {
const ownerIndex = faker.number.int({ min: 0, max: users.length - 1 });
const team = await createATeam(users[ownerIndex].uid); // create a team with owner
for (let j = 0; j < Math.min(memberCountInATeam, users.length) - 1; ) {
const memberIndex = faker.number.int({ min: 0, max: users.length - 1 });
// check if user already added
const existingTeamMember = await prisma.teamMember.findFirst({
where: {
teamID: team.id,
userUid: users[memberIndex].uid,
},
});
if (existingTeamMember) continue;
await createATeamMember(team.id, users[memberIndex].uid);
j++;
}
}
console.log('Teams and TeamMembers created');
} catch (e) {
console.log('Error creating teams and team members:', e.message);
}
};
const createTeamEnvironments = async () => {
try {
const teams = await prisma.team.findMany();
for (let i = 0; i < teams.length; i++) {
const team = teams[i];
const environments = await prisma.teamEnvironment.findMany({
where: { teamID: team.id },
});
if (environments.length > 1) continue; // skip if already created. (assuming default GLOBAL environments are created by APP itself)
await prisma.teamEnvironment.createMany({
data: [
{
teamID: team.id,
name: 'team_env_production',
variables: [
{
key: 'category_id',
value: faker.number.int({ max: 1000 }).toString(),
secret: false,
},
],
},
{
teamID: team.id,
name: 'team_env_development',
variables: [
{
key: 'category_id',
value: faker.number.int({ max: 1000 }).toString(),
secret: false,
},
],
},
],
});
}
console.log('Team environments created');
} catch (e) {
console.log('Error creating team environments: ', e.message);
}
};
const createTeamCollections = async (
parentCollEntries = 10,
childCollEntriesOnEachParent = 10,
) => {
const createACollection = async (teamID, parentID, orderIndex) => {
return prisma.teamCollection.create({
data: { parentID, title: faker.vehicle.vehicle(), orderIndex, teamID },
});
};
try {
const teams = await prisma.team.findMany();
for (let t = 0; t < teams.length; t++) {
const team = teams[t];
for (let i = 0; i < parentCollEntries; i++) {
const parentCollection = await createACollection(team.id, null, i + 1);
for (let j = 0; j < childCollEntriesOnEachParent; j++) {
await createACollection(team.id, parentCollection.id, j + 1);
}
}
}
console.log('Team collections created');
} catch (e) {
console.log('Error creating team collection:', e.message);
}
};
const createTeamRequests = async (entriesPerCollection = 10) => {
try {
const collections = await prisma.teamCollection.findMany();
for (let i = 0; i < collections.length; i++) {
const collection = collections[i];
for (let j = 0; j < entriesPerCollection; j++) {
const requestTitle = faker.git.branch();
await prisma.teamRequest.create({
data: {
collectionID: collection.id,
teamID: collection.teamID,
title: requestTitle,
request: {
v: '4',
auth: { authType: 'inherit', authActive: true },
body: { body: null, contentType: null },
name: requestTitle,
method: 'GET',
params: [],
headers: [],
endpoint: 'https://echo.hoppscotch.io',
testScript: '',
preRequestScript: '',
requestVariables: [],
},
orderIndex: j + 1,
},
});
}
}
console.log('Team requests created');
} catch (e) {
console.log('Error creating team requests:', e.message);
}
};
async function createShortcodes(entriesPerUser = 2) {
try {
const users = await prisma.user.findMany();
for (let i = 0; i < users.length; i++) {
const user = users[i];
for (let j = 0; j < entriesPerUser; j++) {
const userRequests = await prisma.userRequest.findMany({
where: { userUid: user.uid },
take: entriesPerUser,
});
let shortCodeRequestObj = {
v: '4',
id: userRequests[j].id,
auth: { authType: 'inherit', authActive: true },
body: { body: null, contentType: null },
name: 'driver-hack',
method: 'GET',
params: [],
headers: [],
endpoint: 'https://echo.hoppscotch.io',
testScript: '',
preRequestScript: '',
requestVariables: [],
};
await prisma.shortcode.create({
data: {
id: faker.string.alphanumeric(12),
creatorUid: user.uid,
request: shortCodeRequestObj,
embedProperties: null,
},
});
}
}
console.log('Shortcodes created');
} catch (e) {
console.log('Error creating shortcodes:', e.message);
}
}
async function clearAllData() {
try {
// Get all model names
const modelNames = Object.keys(prisma).filter(
(str) => !str.startsWith('_') && !str.startsWith('$'),
);
// Iterate through each model and delete all data
for (let i = 0; i < modelNames.length; i++) {
await prisma[modelNames[i]].deleteMany({});
}
console.log('All data cleared');
} catch (e) {
console.log('Error in clearing data:', e.message);
}
}
(async () => {
await clearAllData();
await createUsersAndAccounts(2, [
{ email: 'admin@gmail.com', isAdmin: true },
{ email: 'user@gmail.com', isAdmin: false },
]);
await createInvitedUsers(10);
// `userSettings` can be created by APP itself
await createUserCollections(10, 10);
await createUserRequests(10);
// `userHistory` can be created by APP itself
await createUserEnvironments();
await createShortcodes(3);
await createTeamsAndTeamMembers(10, 4);
// `teamInvitation` can be created by APP itself
await createTeamEnvironments();
await createTeamCollections(5, 5);
await createTeamRequests(3);
})();

View File

@@ -1,4 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
"exclude": ["node_modules", "test", "dist", "**/*spec.ts", "prisma/seed.ts"]
}

View File

@@ -1,7 +1,7 @@
{
"name": "@hoppscotch/common",
"private": true,
"version": "2024.3.2",
"version": "2024.3.3",
"scripts": {
"dev": "pnpm exec npm-run-all -p -l dev:*",
"test": "vitest --run",

View File

@@ -1,7 +1,7 @@
{
"name": "@hoppscotch/selfhost-desktop",
"private": true,
"version": "2024.3.2",
"version": "2024.3.3",
"type": "module",
"scripts": {
"dev:vite": "vite",

View File

@@ -1,6 +1,6 @@
[package]
name = "hoppscotch-desktop"
version = "24.3.2"
version = "24.3.3"
description = "A Tauri App"
authors = ["you"]
license = ""

View File

@@ -8,7 +8,7 @@
},
"package": {
"productName": "Hoppscotch",
"version": "24.3.2"
"version": "24.3.3"
},
"tauri": {
"allowlist": {

View File

@@ -1,7 +1,7 @@
{
"name": "@hoppscotch/selfhost-web",
"private": true,
"version": "2024.3.2",
"version": "2024.3.3",
"type": "module",
"scripts": {
"dev:vite": "vite",

View File

@@ -52,7 +52,8 @@
"title": "Server is restarting"
},
"save_changes": "Save Changes",
"title": "Configurations"
"title": "Configurations",
"update_failure": "Failed to update server configurations"
},
"data_sharing": {
"description": "Share anonymous data usage to improve Hoppscotch",

View File

@@ -1,7 +1,7 @@
{
"name": "hoppscotch-sh-admin",
"private": true,
"version": "2024.3.2",
"version": "2024.3.3",
"type": "module",
"scripts": {
"dev": "pnpm exec npm-run-all -p -l dev:*",

View File

@@ -69,18 +69,18 @@
import { useVModel } from '@vueuse/core';
import { reactive } from 'vue';
import { useI18n } from '~/composables/i18n';
import { Config, SsoAuthProviders } from '~/composables/useConfigHandler';
import { ServerConfigs, SsoAuthProviders } from '~/helpers/configs';
import IconEye from '~icons/lucide/eye';
import IconEyeOff from '~icons/lucide/eye-off';
const t = useI18n();
const props = defineProps<{
config: Config;
config: ServerConfigs;
}>();
const emit = defineEmits<{
(e: 'update:config', v: Config): void;
(e: 'update:config', v: ServerConfigs): void;
}>();
const workingConfigs = useVModel(props, 'config', emit);
@@ -93,7 +93,7 @@ const capitalize = (text: string) =>
type ProviderFieldKeys = keyof ProviderFields;
type ProviderFields = {
[Field in keyof Config['providers'][SsoAuthProviders]['fields']]: boolean;
[Field in keyof ServerConfigs['providers'][SsoAuthProviders]['fields']]: boolean;
} & Partial<{ tenant: boolean }>;
type ProviderFieldMetadata = {

View File

@@ -9,14 +9,14 @@
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { Config } from '~/composables/useConfigHandler';
import { ServerConfigs } from '~/helpers/configs';
const props = defineProps<{
config: Config;
config: ServerConfigs;
}>();
const emit = defineEmits<{
(e: 'update:config', v: Config): void;
(e: 'update:config', v: ServerConfigs): void;
}>();
const workingConfigs = useVModel(props, 'config', emit);

View File

@@ -38,17 +38,17 @@
import { useVModel } from '@vueuse/core';
import { computed } from 'vue';
import { useI18n } from '~/composables/i18n';
import { Config } from '~/composables/useConfigHandler';
import { ServerConfigs } from '~/helpers/configs';
import IconShieldQuestion from '~icons/lucide/shield-question';
const t = useI18n();
const props = defineProps<{
config: Config;
config: ServerConfigs;
}>();
const emit = defineEmits<{
(e: 'update:config', v: Config): void;
(e: 'update:config', v: ServerConfigs): void;
}>();
const workingConfigs = useVModel(props, 'config', emit);

View File

@@ -17,20 +17,21 @@ import { useMutation } from '@urql/vue';
import { onMounted, ref } from 'vue';
import { useI18n } from '~/composables/i18n';
import { useToast } from '~/composables/toast';
import { Config, useConfigHandler } from '~/composables/useConfigHandler';
import { useConfigHandler } from '~/composables/useConfigHandler';
import {
EnableAndDisableSsoDocument,
ResetInfraConfigsDocument,
UpdateInfraConfigsDocument,
ToggleAnalyticsCollectionDocument,
UpdateInfraConfigsDocument,
} from '~/helpers/backend/graphql';
import { ServerConfigs } from '~/helpers/configs';
const t = useI18n();
const toast = useToast();
const props = withDefaults(
defineProps<{
workingConfigs?: Config;
workingConfigs?: ServerConfigs;
reset?: boolean;
}>(),
{

View File

@@ -58,18 +58,18 @@
import { useVModel } from '@vueuse/core';
import { computed, reactive } from 'vue';
import { useI18n } from '~/composables/i18n';
import { Config } from '~/composables/useConfigHandler';
import { ServerConfigs } from '~/helpers/configs';
import IconEye from '~icons/lucide/eye';
import IconEyeOff from '~icons/lucide/eye-off';
const t = useI18n();
const props = defineProps<{
config: Config;
config: ServerConfigs;
}>();
const emit = defineEmits<{
(e: 'update:config', v: Config): void;
(e: 'update:config', v: ServerConfigs): void;
}>();
const workingConfigs = useVModel(props, 'config', emit);
@@ -87,7 +87,7 @@ const smtpConfigs = computed({
// Mask sensitive fields
type Field = {
name: string;
key: keyof Config['mailConfigs']['fields'];
key: keyof ServerConfigs['mailConfigs']['fields'];
};
const smtpConfigFields = reactive<Field[]>([
@@ -100,10 +100,10 @@ const maskState = reactive<Record<string, boolean>>({
mailer_from_address: true,
});
const toggleMask = (fieldKey: keyof Config['mailConfigs']['fields']) => {
const toggleMask = (fieldKey: keyof ServerConfigs['mailConfigs']['fields']) => {
maskState[fieldKey] = !maskState[fieldKey];
};
const isMasked = (fieldKey: keyof Config['mailConfigs']['fields']) =>
const isMasked = (fieldKey: keyof ServerConfigs['mailConfigs']['fields']) =>
maskState[fieldKey];
</script>

View File

@@ -1,83 +1,39 @@
import { AnyVariables, UseMutationResponse } from '@urql/vue';
import { cloneDeep } from 'lodash-es';
import { computed, onMounted, ref } from 'vue';
import { onMounted, ref } from 'vue';
import { useI18n } from '~/composables/i18n';
import {
AllowedAuthProvidersDocument,
AuthProvider,
EnableAndDisableSsoArgs,
EnableAndDisableSsoMutation,
InfraConfigArgs,
InfraConfigEnum,
InfraConfigsDocument,
ResetInfraConfigsMutation,
ServiceStatus,
ToggleAnalyticsCollectionMutation,
UpdateInfraConfigsMutation,
} from '~/helpers/backend/graphql';
import {
ALL_CONFIGS,
ConfigSection,
ConfigTransform,
GITHUB_CONFIGS,
GOOGLE_CONFIGS,
MAIL_CONFIGS,
MICROSOFT_CONFIGS,
ServerConfigs,
UpdatedConfigs,
} from '~/helpers/configs';
import { useToast } from './toast';
import { useClientHandler } from './useClientHandler';
// Types
export type SsoAuthProviders = 'google' | 'microsoft' | 'github';
export type Config = {
providers: {
google: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
};
};
github: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
};
};
microsoft: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
tenant: string;
};
};
};
mailConfigs: {
name: string;
enabled: boolean;
fields: {
mailer_smtp_url: string;
mailer_from_address: string;
};
};
dataSharingConfigs: {
name: string;
enabled: boolean;
};
};
type UpdatedConfigs = {
name: string;
value: string;
};
/** Composable that handles all operations related to server configurations
* @param updatedConfigs A Config Object contatining the updated configs
*/
export function useConfigHandler(updatedConfigs?: Config) {
export function useConfigHandler(updatedConfigs?: ServerConfigs) {
const t = useI18n();
const toast = useToast();
@@ -90,24 +46,9 @@ export function useConfigHandler(updatedConfigs?: Config) {
} = useClientHandler(
InfraConfigsDocument,
{
configNames: [
'GOOGLE_CLIENT_ID',
'GOOGLE_CLIENT_SECRET',
'GOOGLE_CALLBACK_URL',
'GOOGLE_SCOPE',
'MICROSOFT_CLIENT_ID',
'MICROSOFT_CLIENT_SECRET',
'MICROSOFT_CALLBACK_URL',
'MICROSOFT_SCOPE',
'MICROSOFT_TENANT',
'GITHUB_CLIENT_ID',
'GITHUB_CLIENT_SECRET',
'GITHUB_CALLBACK_URL',
'GITHUB_SCOPE',
'MAILER_SMTP_URL',
'MAILER_ADDRESS_FROM',
'ALLOW_ANALYTICS_COLLECTION',
] as InfraConfigEnum[],
configNames: ALL_CONFIGS.flat().map(
({ name }) => name
) as InfraConfigEnum[],
},
(x) => x.infraConfigs
);
@@ -125,14 +66,14 @@ export function useConfigHandler(updatedConfigs?: Config) {
);
// Current and working configs
const currentConfigs = ref<Config>();
const workingConfigs = ref<Config>();
const currentConfigs = ref<ServerConfigs>();
const workingConfigs = ref<ServerConfigs>();
onMounted(async () => {
await fetchInfraConfigs();
await fetchAllowedAuthProviders();
const getFieldValue = (name: string) =>
const getFieldValue = (name: InfraConfigEnum) =>
infraConfigs.value.find((x) => x.name === name)?.value ?? '';
// Transforming the fetched data into a Configs object
@@ -140,42 +81,42 @@ export function useConfigHandler(updatedConfigs?: Config) {
providers: {
google: {
name: 'google',
enabled: allowedAuthProviders.value.includes('GOOGLE'),
enabled: allowedAuthProviders.value.includes(AuthProvider.Google),
fields: {
client_id: getFieldValue('GOOGLE_CLIENT_ID'),
client_secret: getFieldValue('GOOGLE_CLIENT_SECRET'),
callback_url: getFieldValue('GOOGLE_CALLBACK_URL'),
scope: getFieldValue('GOOGLE_SCOPE'),
client_id: getFieldValue(InfraConfigEnum.GoogleClientId),
client_secret: getFieldValue(InfraConfigEnum.GoogleClientSecret),
callback_url: getFieldValue(InfraConfigEnum.GoogleCallbackUrl),
scope: getFieldValue(InfraConfigEnum.GoogleScope),
},
},
github: {
name: 'github',
enabled: allowedAuthProviders.value.includes('GITHUB'),
enabled: allowedAuthProviders.value.includes(AuthProvider.Github),
fields: {
client_id: getFieldValue('GITHUB_CLIENT_ID'),
client_secret: getFieldValue('GITHUB_CLIENT_SECRET'),
callback_url: getFieldValue('GITHUB_CALLBACK_URL'),
scope: getFieldValue('GITHUB_SCOPE'),
client_id: getFieldValue(InfraConfigEnum.GithubClientId),
client_secret: getFieldValue(InfraConfigEnum.GithubClientSecret),
callback_url: getFieldValue(InfraConfigEnum.GoogleCallbackUrl),
scope: getFieldValue(InfraConfigEnum.GithubScope),
},
},
microsoft: {
name: 'microsoft',
enabled: allowedAuthProviders.value.includes('MICROSOFT'),
enabled: allowedAuthProviders.value.includes(AuthProvider.Microsoft),
fields: {
client_id: getFieldValue('MICROSOFT_CLIENT_ID'),
client_secret: getFieldValue('MICROSOFT_CLIENT_SECRET'),
callback_url: getFieldValue('MICROSOFT_CALLBACK_URL'),
scope: getFieldValue('MICROSOFT_SCOPE'),
tenant: getFieldValue('MICROSOFT_TENANT'),
client_id: getFieldValue(InfraConfigEnum.MicrosoftClientId),
client_secret: getFieldValue(InfraConfigEnum.MicrosoftClientSecret),
callback_url: getFieldValue(InfraConfigEnum.MicrosoftCallbackUrl),
scope: getFieldValue(InfraConfigEnum.MicrosoftScope),
tenant: getFieldValue(InfraConfigEnum.MicrosoftTenant),
},
},
},
mailConfigs: {
name: 'email',
enabled: allowedAuthProviders.value.includes('EMAIL'),
enabled: allowedAuthProviders.value.includes(AuthProvider.Email),
fields: {
mailer_smtp_url: getFieldValue('MAILER_SMTP_URL'),
mailer_from_address: getFieldValue('MAILER_ADDRESS_FROM'),
mailer_smtp_url: getFieldValue(InfraConfigEnum.MailerSmtpUrl),
mailer_from_address: getFieldValue(InfraConfigEnum.MailerAddressFrom),
},
},
dataSharingConfigs: {
@@ -191,138 +132,13 @@ export function useConfigHandler(updatedConfigs?: Config) {
workingConfigs.value = cloneDeep(currentConfigs.value);
});
// Transforming the working configs back into the format required by the mutations
const updatedInfraConfigs = computed(() => {
let config: UpdatedConfigs[] = [
{
name: '',
value: '',
},
];
/*
Check if any of the config fields are empty
*/
if (updatedConfigs?.providers.google.enabled) {
config.push(
{
name: 'GOOGLE_CLIENT_ID',
value: updatedConfigs?.providers.google.fields.client_id ?? '',
},
{
name: 'GOOGLE_CLIENT_SECRET',
value: updatedConfigs?.providers.google.fields.client_secret ?? '',
},
{
name: 'GOOGLE_CALLBACK_URL',
value: updatedConfigs?.providers.google.fields.callback_url ?? '',
},
{
name: 'GOOGLE_SCOPE',
value: updatedConfigs?.providers.google.fields.scope ?? '',
}
);
} else {
config = config.filter(
(item) =>
item.name !== 'GOOGLE_CLIENT_ID' &&
item.name !== 'GOOGLE_CLIENT_SECRET' &&
item.name !== 'GOOGLE_CALLBACK_URL' &&
item.name !== 'GOOGLE_SCOPE'
);
}
if (updatedConfigs?.providers.microsoft.enabled) {
config.push(
{
name: 'MICROSOFT_CLIENT_ID',
value: updatedConfigs?.providers.microsoft.fields.client_id ?? '',
},
{
name: 'MICROSOFT_CLIENT_SECRET',
value: updatedConfigs?.providers.microsoft.fields.client_secret ?? '',
},
{
name: 'MICROSOFT_CALLBACK_URL',
value: updatedConfigs?.providers.microsoft.fields.callback_url ?? '',
},
{
name: 'MICROSOFT_SCOPE',
value: updatedConfigs?.providers.microsoft.fields.scope ?? '',
},
{
name: 'MICROSOFT_TENANT',
value: updatedConfigs?.providers.microsoft.fields.tenant ?? '',
}
);
} else {
config = config.filter(
(item) =>
item.name !== 'MICROSOFT_CLIENT_ID' &&
item.name !== 'MICROSOFT_CLIENT_SECRET' &&
item.name !== 'MICROSOFT_CALLBACK_URL' &&
item.name !== 'MICROSOFT_SCOPE' &&
item.name !== 'MICROSOFT_TENANT'
);
}
if (updatedConfigs?.providers.github.enabled) {
config.push(
{
name: 'GITHUB_CLIENT_ID',
value: updatedConfigs?.providers.github.fields.client_id ?? '',
},
{
name: 'GITHUB_CLIENT_SECRET',
value: updatedConfigs?.providers.github.fields.client_secret ?? '',
},
{
name: 'GITHUB_CALLBACK_URL',
value: updatedConfigs?.providers.github.fields.callback_url ?? '',
},
{
name: 'GITHUB_SCOPE',
value: updatedConfigs?.providers.github.fields.scope ?? '',
}
);
} else {
config = config.filter(
(item) =>
item.name !== 'GITHUB_CLIENT_ID' &&
item.name !== 'GITHUB_CLIENT_SECRET' &&
item.name !== 'GITHUB_CALLBACK_URL' &&
item.name !== 'GITHUB_SCOPE'
);
}
if (updatedConfigs?.mailConfigs.enabled) {
config.push(
{
name: 'MAILER_SMTP_URL',
value: updatedConfigs?.mailConfigs.fields.mailer_smtp_url ?? '',
},
{
name: 'MAILER_ADDRESS_FROM',
value: updatedConfigs?.mailConfigs.fields.mailer_from_address ?? '',
}
);
} else {
config = config.filter(
(item) =>
item.name !== 'MAILER_SMTP_URL' && item.name !== 'MAILER_ADDRESS_FROM'
);
}
config = config.filter((item) => item.name !== '');
return config;
});
// Checking if any of the config fields are empty
const isFieldEmpty = (field: string) => field.trim() === '';
type ConfigSection = {
enabled: boolean;
fields: Record<string, string>;
};
const AreAnyConfigFieldsEmpty = (config: Config): boolean => {
const AreAnyConfigFieldsEmpty = (config: ServerConfigs): boolean => {
const sections: Array<ConfigSection> = [
config.providers.github,
config.providers.google,
@@ -337,28 +153,44 @@ export function useConfigHandler(updatedConfigs?: Config) {
};
// Transforming the working configs back into the format required by the mutations
const updatedAllowedAuthProviders = computed(() => {
return [
const transformInfraConfigs = () => {
const updatedWorkingConfigs: ConfigTransform[] = [
{
provider: 'GOOGLE',
status: updatedConfigs?.providers.google.enabled ? 'ENABLE' : 'DISABLE',
config: GOOGLE_CONFIGS,
enabled: updatedConfigs?.providers.google.enabled,
fields: updatedConfigs?.providers.google.fields,
},
{
provider: 'MICROSOFT',
status: updatedConfigs?.providers.microsoft.enabled
? 'ENABLE'
: 'DISABLE',
config: GITHUB_CONFIGS,
enabled: updatedConfigs?.providers.github.enabled,
fields: updatedConfigs?.providers.github.fields,
},
{
provider: 'GITHUB',
status: updatedConfigs?.providers.github.enabled ? 'ENABLE' : 'DISABLE',
config: MICROSOFT_CONFIGS,
enabled: updatedConfigs?.providers.microsoft.enabled,
fields: updatedConfigs?.providers.microsoft.fields,
},
{
provider: 'EMAIL',
status: updatedConfigs?.mailConfigs.enabled ? 'ENABLE' : 'DISABLE',
config: MAIL_CONFIGS,
enabled: updatedConfigs?.mailConfigs.enabled,
fields: updatedConfigs?.mailConfigs.fields,
},
];
});
const transformedConfigs: UpdatedConfigs[] = [];
updatedWorkingConfigs.forEach(({ config, enabled, fields }) => {
config.forEach(({ name, key }) => {
if (enabled && fields) {
const value =
typeof fields === 'string' ? fields : String(fields[key]);
transformedConfigs.push({ name, value });
}
});
});
return transformedConfigs;
};
// Generic function to handle mutation execution and error handling
const executeMutation = async <T, V>(
@@ -379,27 +211,59 @@ export function useConfigHandler(updatedConfigs?: Config) {
// Updating the auth provider configurations
const updateAuthProvider = (
updateProviderStatus: UseMutationResponse<EnableAndDisableSsoMutation>
) =>
executeMutation(
) => {
const updatedAllowedAuthProviders: EnableAndDisableSsoArgs[] = [
{
provider: AuthProvider.Google,
status: updatedConfigs?.providers.google.enabled
? ServiceStatus.Enable
: ServiceStatus.Disable,
},
{
provider: AuthProvider.Microsoft,
status: updatedConfigs?.providers.microsoft.enabled
? ServiceStatus.Enable
: ServiceStatus.Disable,
},
{
provider: AuthProvider.Github,
status: updatedConfigs?.providers.github.enabled
? ServiceStatus.Enable
: ServiceStatus.Disable,
},
{
provider: AuthProvider.Email,
status: updatedConfigs?.mailConfigs.enabled
? ServiceStatus.Enable
: ServiceStatus.Disable,
},
];
return executeMutation(
updateProviderStatus,
{
providerInfo:
updatedAllowedAuthProviders.value as EnableAndDisableSsoArgs[],
providerInfo: updatedAllowedAuthProviders,
},
'configs.auth_providers.update_failure'
);
};
// Updating the infra configurations
const updateInfraConfigs = (
updateInfraConfigsMutation: UseMutationResponse<UpdateInfraConfigsMutation>
) =>
executeMutation(
) => {
const infraConfigs: InfraConfigArgs[] = updatedConfigs
? transformInfraConfigs()
: [];
return executeMutation(
updateInfraConfigsMutation,
{
infraConfigs: updatedInfraConfigs.value as InfraConfigArgs[],
infraConfigs,
},
'configs.mail_configs.update_failure'
'configs.update_failure'
);
};
// Resetting the infra configurations
const resetInfraConfigs = (
@@ -411,7 +275,6 @@ export function useConfigHandler(updatedConfigs?: Config) {
'configs.reset.failure'
);
// Updating the data sharing configurations
const updateDataSharingConfigs = (
toggleDataSharingMutation: UseMutationResponse<ToggleAnalyticsCollectionMutation>
) =>
@@ -419,8 +282,8 @@ export function useConfigHandler(updatedConfigs?: Config) {
toggleDataSharingMutation,
{
status: updatedConfigs?.dataSharingConfigs.enabled
? 'ENABLE'
: 'DISABLE',
? ServiceStatus.Enable
: ServiceStatus.Disable,
},
'configs.data_sharing.update_failure'
);
@@ -428,8 +291,6 @@ export function useConfigHandler(updatedConfigs?: Config) {
return {
currentConfigs,
workingConfigs,
updatedInfraConfigs,
updatedAllowedAuthProviders,
updateAuthProvider,
updateDataSharingConfigs,
updateInfraConfigs,

View File

@@ -0,0 +1,160 @@
import { InfraConfigEnum } from './backend/graphql';
export type SsoAuthProviders = 'google' | 'microsoft' | 'github';
export type ServerConfigs = {
providers: {
google: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
};
};
github: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
};
};
microsoft: {
name: SsoAuthProviders;
enabled: boolean;
fields: {
client_id: string;
client_secret: string;
callback_url: string;
scope: string;
tenant: string;
};
};
};
mailConfigs: {
name: string;
enabled: boolean;
fields: {
mailer_smtp_url: string;
mailer_from_address: string;
};
};
dataSharingConfigs: {
name: string;
enabled: boolean;
};
};
export type UpdatedConfigs = {
name: InfraConfigEnum;
value: string;
};
export type ConfigTransform = {
config: Config[];
enabled?: boolean;
fields?: Record<string, string | boolean> | string;
};
export type ConfigSection = {
enabled: boolean;
fields: Record<string, string>;
};
export type Config = {
name: InfraConfigEnum;
key: string;
};
export const GOOGLE_CONFIGS: Config[] = [
{
name: InfraConfigEnum.GoogleClientId,
key: 'client_id',
},
{
name: InfraConfigEnum.GoogleClientSecret,
key: 'client_secret',
},
{
name: InfraConfigEnum.GoogleCallbackUrl,
key: 'callback_url',
},
{
name: InfraConfigEnum.GoogleScope,
key: 'scope',
},
];
export const MICROSOFT_CONFIGS: Config[] = [
{
name: InfraConfigEnum.MicrosoftClientId,
key: 'client_id',
},
{
name: InfraConfigEnum.MicrosoftClientSecret,
key: 'client_secret',
},
{
name: InfraConfigEnum.MicrosoftCallbackUrl,
key: 'callback_url',
},
{
name: InfraConfigEnum.MicrosoftScope,
key: 'scope',
},
{
name: InfraConfigEnum.MicrosoftTenant,
key: 'tenant',
},
];
export const GITHUB_CONFIGS: Config[] = [
{
name: InfraConfigEnum.GithubClientId,
key: 'client_id',
},
{
name: InfraConfigEnum.GithubClientSecret,
key: 'client_secret',
},
{
name: InfraConfigEnum.GithubCallbackUrl,
key: 'callback_url',
},
{
name: InfraConfigEnum.GithubScope,
key: 'scope',
},
];
export const MAIL_CONFIGS: Config[] = [
{
name: InfraConfigEnum.MailerSmtpUrl,
key: 'mailer_smtp_url',
},
{
name: InfraConfigEnum.MailerAddressFrom,
key: 'mailer_from_address',
},
];
const DATA_SHARING_CONFIGS: Omit<Config, 'key'>[] = [
{
name: InfraConfigEnum.AllowAnalyticsCollection,
},
];
export const ALL_CONFIGS = [
GOOGLE_CONFIGS,
MICROSOFT_CONFIGS,
GITHUB_CONFIGS,
MAIL_CONFIGS,
DATA_SHARING_CONFIGS,
];

View File

@@ -208,13 +208,14 @@ const deleteUserMutation = async (id: string | null) => {
if (result.error) {
toast.error(t('state.delete_user_failure'));
} else {
const deletedUsers = result.data?.removeUsersByAdmin || [];
const deletedUser = result.data?.removeUsersByAdmin || [];
handleUserDeletion(deletedUser);
handleUserDeletion(deletedUsers);
const { isDeleted } = deletedUser[0];
if (isDeleted) router.push('/users');
}
confirmDeletion.value = false;
deleteUserUID.value = null;
!result.error && router.push('/users');
};
</script>

34107
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff