Compare commits

..

1 Commits

Author SHA1 Message Date
amk-dev
f862b4aaae fix: fix actions not triggered when sidebar is not open 2023-12-19 15:17:03 +05:30
62 changed files with 475 additions and 671 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "hoppscotch-backend", "name": "hoppscotch-backend",
"version": "2023.12.2", "version": "2023.12.0-1",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,

View File

@@ -28,13 +28,6 @@ export const JSON_INVALID = 'json_invalid';
*/ */
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified'; export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
/**
* Auth Provider not specified
* (Auth)
*/
export const AUTH_PROVIDER_NOT_CONFIGURED =
'auth/provider_not_configured_correctly';
/** /**
* Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file * Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file
*/ */
@@ -683,16 +676,9 @@ export const INFRA_CONFIG_RESET_FAILED = 'infra_config/reset_failed' as const;
*/ */
export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const; export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const;
/**
* Infra Config service (auth provider/mailer/audit logs) not configured
* (InfraConfigService)
*/
export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED =
'infra_config/service_not_configured' as const;
/** /**
* Error message for when the database table does not exist * Error message for when the database table does not exist
* (InfraConfigService) * (InfraConfigService)
*/ */
export const DATABASE_TABLE_NOT_EXIST = export const DATABASE_TABLE_NOT_EXIST =
'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations'; 'Database migration not performed. Please check the FAQ for assistance: https://docs.hoppscotch.io/support/getting-started/faq';

View File

@@ -1,33 +1,10 @@
import { AuthProvider } from 'src/auth/helper';
import { AUTH_PROVIDER_NOT_CONFIGURED } from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwErr } from 'src/utils';
export enum ServiceStatus { export enum ServiceStatus {
ENABLE = 'ENABLE', ENABLE = 'ENABLE',
DISABLE = 'DISABLE', DISABLE = 'DISABLE',
} }
const AuthProviderConfigurations = {
[AuthProvider.GOOGLE]: [
InfraConfigEnum.GOOGLE_CLIENT_ID,
InfraConfigEnum.GOOGLE_CLIENT_SECRET,
],
[AuthProvider.GITHUB]: [
InfraConfigEnum.GITHUB_CLIENT_ID,
InfraConfigEnum.GITHUB_CLIENT_SECRET,
],
[AuthProvider.MICROSOFT]: [
InfraConfigEnum.MICROSOFT_CLIENT_ID,
InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
],
[AuthProvider.EMAIL]: [
InfraConfigEnum.MAILER_SMTP_URL,
InfraConfigEnum.MAILER_ADDRESS_FROM,
],
};
/** /**
* Load environment variables from the database and set them in the process * Load environment variables from the database and set them in the process
* *
@@ -65,42 +42,3 @@ export function stopApp() {
process.kill(process.pid, 'SIGTERM'); process.kill(process.pid, 'SIGTERM');
}, 5000); }, 5000);
} }
/**
* Get the configured SSO providers
* @returns Array of configured SSO providers
*/
export function getConfiguredSSOProviders() {
const allowedAuthProviders: string[] =
process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(',');
let configuredAuthProviders: string[] = [];
const addProviderIfConfigured = (provider) => {
const configParameters: string[] = AuthProviderConfigurations[provider];
const isConfigured = configParameters.every((configParameter) => {
return process.env[configParameter];
});
if (isConfigured) configuredAuthProviders.push(provider);
};
allowedAuthProviders.forEach((provider) => addProviderIfConfigured(provider));
if (configuredAuthProviders.length === 0) {
throwErr(AUTH_PROVIDER_NOT_CONFIGURED);
} else if (allowedAuthProviders.length !== configuredAuthProviders.length) {
const unConfiguredAuthProviders = allowedAuthProviders.filter(
(provider) => {
return !configuredAuthProviders.includes(provider);
},
);
console.log(
`${unConfiguredAuthProviders.join(
',',
)} SSO auth provider(s) are not configured properly. Do configure them from Admin Dashboard.`,
);
}
return configuredAuthProviders.join(',');
}

View File

@@ -15,13 +15,11 @@ import {
INFRA_CONFIG_NOT_LISTED, INFRA_CONFIG_NOT_LISTED,
INFRA_CONFIG_RESET_FAILED, INFRA_CONFIG_RESET_FAILED,
INFRA_CONFIG_UPDATE_FAILED, INFRA_CONFIG_UPDATE_FAILED,
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
} from 'src/errors'; } from 'src/errors';
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils'; import { throwErr, validateEmail, validateSMTPUrl } from 'src/utils';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper'; import { ServiceStatus, stopApp } from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args'; import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper';
@Injectable() @Injectable()
export class InfraConfigService implements OnModuleInit { export class InfraConfigService implements OnModuleInit {
@@ -71,7 +69,7 @@ export class InfraConfigService implements OnModuleInit {
}, },
{ {
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: getConfiguredSSOProviders(), value: process.env.VITE_ALLOWED_AUTH_PROVIDERS.toLocaleUpperCase(),
}, },
]; ];
@@ -126,23 +124,10 @@ export class InfraConfigService implements OnModuleInit {
cast(dbInfraConfig: DBInfraConfig) { cast(dbInfraConfig: DBInfraConfig) {
return <InfraConfig>{ return <InfraConfig>{
name: dbInfraConfig.name, name: dbInfraConfig.name,
value: dbInfraConfig.value ?? '', value: dbInfraConfig.value,
}; };
} }
/**
* Get all the InfraConfigs as map
* @returns InfraConfig map
*/
async getInfraConfigsMap() {
const infraConfigs = await this.prisma.infraConfig.findMany();
const infraConfigMap: Record<string, string> = {};
infraConfigs.forEach((config) => {
infraConfigMap[config.name] = config.value;
});
return infraConfigMap;
}
/** /**
* Update InfraConfig by name * Update InfraConfig by name
* @param name Name of the InfraConfig * @param name Name of the InfraConfig
@@ -197,32 +182,6 @@ export class InfraConfigService implements OnModuleInit {
} }
} }
/**
* Check if the service is configured or not
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
* @param configMap Map of all the infra configs
* @returns Either true or false
*/
isServiceConfigured(
service: AuthProvider,
configMap: Record<string, string>,
) {
switch (service) {
case AuthProvider.GOOGLE:
return configMap.GOOGLE_CLIENT_ID && configMap.GOOGLE_CLIENT_SECRET;
case AuthProvider.GITHUB:
return configMap.GITHUB_CLIENT_ID && configMap.GITHUB_CLIENT_SECRET;
case AuthProvider.MICROSOFT:
return (
configMap.MICROSOFT_CLIENT_ID && configMap.MICROSOFT_CLIENT_SECRET
);
case AuthProvider.EMAIL:
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
default:
return false;
}
}
/** /**
* Enable or Disable SSO for login/signup * Enable or Disable SSO for login/signup
* @param provider Auth Provider to enable or disable * @param provider Auth Provider to enable or disable
@@ -236,14 +195,8 @@ export class InfraConfigService implements OnModuleInit {
let updatedAuthProviders = allowedAuthProviders; let updatedAuthProviders = allowedAuthProviders;
const infraConfigMap = await this.getInfraConfigsMap();
providerInfo.forEach(({ provider, status }) => { providerInfo.forEach(({ provider, status }) => {
if (status === ServiceStatus.ENABLE) { if (status === ServiceStatus.ENABLE) {
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
if (!isConfigured) {
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
}
updatedAuthProviders.push(provider); updatedAuthProviders.push(provider);
} else if (status === ServiceStatus.DISABLE) { } else if (status === ServiceStatus.DISABLE) {
updatedAuthProviders = updatedAuthProviders.filter( updatedAuthProviders = updatedAuthProviders.filter(
@@ -333,9 +286,6 @@ export class InfraConfigService implements OnModuleInit {
} }
} }
/**
* Validate the values of the InfraConfigs
*/
validateEnvValues( validateEnvValues(
infraConfigs: { infraConfigs: {
name: InfraConfigEnumForClient | InfraConfigEnum; name: InfraConfigEnumForClient | InfraConfigEnum;
@@ -349,27 +299,9 @@ export class InfraConfigService implements OnModuleInit {
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM: case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
const isValidEmail = validateSMTPEmail(infraConfigs[i].value); const isValidEmail = validateEmail(infraConfigs[i].value);
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GITHUB_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GITHUB_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
default: default:
break; break;
} }

View File

@@ -17,8 +17,7 @@ async function bootstrap() {
console.log(`Port: ${configService.get('PORT')}`); console.log(`Port: ${configService.get('PORT')}`);
checkEnvironmentAuthProvider( checkEnvironmentAuthProvider(
configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS') ?? configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
); );
app.use( app.use(

View File

@@ -131,28 +131,6 @@ export const validateEmail = (email: string) => {
).test(email); ).test(email);
}; };
// Regular expressions for supported address object formats by nodemailer
// check out for more info https://nodemailer.com/message/addresses
const emailRegex1 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const emailRegex2 =
/^[\w\s]* <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
const emailRegex3 =
/^"[\w\s]+" <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
/**
* Checks to see if the SMTP email is valid or not
* @param email
* @returns A Boolean depending on the format of the email
*/
export const validateSMTPEmail = (email: string) => {
// Check if the input matches any of the formats
return (
emailRegex1.test(email) ||
emailRegex2.test(email) ||
emailRegex3.test(email)
);
};
/** /**
* Checks to see if the URL is valid or not * Checks to see if the URL is valid or not
* @param url The URL to validate * @param url The URL to validate

View File

@@ -1,6 +1,6 @@
{ {
"name": "@hoppscotch/cli", "name": "@hoppscotch/cli",
"version": "0.5.1", "version": "0.5.0",
"description": "A CLI to run Hoppscotch test scripts in CI environments.", "description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io", "homepage": "https://hoppscotch.io",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -118,15 +118,6 @@ describe("Test 'hopp test <file> --env <file>' command:", () => {
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Correctly resolves environment variables referenced in the request body", async () => {
const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json");
const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
}); });
describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => { describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {

View File

@@ -1,30 +0,0 @@
{
"v": 2,
"name": "Test environment variables in request body",
"folders": [],
"requests": [
{
"v": "1",
"name": "test-request",
"endpoint": "https://echo.hoppscotch.io",
"method": "POST",
"headers": [],
"params": [],
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"contentType": "application/json",
"body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}"
},
"preRequestScript": "",
"testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully resolves environments recursively\", ()=> {\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n});\n\npw.test(\"Successfully resolves environments referenced in the request body\", () => {\n const expectedId = \"7\"\n const expectedFirstName = \"John\"\n const expectedLastName = \"Doe\"\n const expectedFullName = `${expectedFirstName} ${expectedLastName}`\n const expectedGreetText = `Hello, ${expectedFullName}`\n\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n\n const { id, firstName, lastName, fullName, greetText } = JSON.parse(pw.response.body.data)\n\n pw.expect(id).toBe(expectedId)\n pw.expect(expectedFirstName).toBe(firstName)\n pw.expect(expectedLastName).toBe(lastName)\n pw.expect(fullName).toBe(expectedFullName)\n pw.expect(greetText).toBe(expectedGreetText)\n});"
}
],
"auth": {
"authType": "none",
"authActive": true
},
"headers": []
}

View File

@@ -1,37 +0,0 @@
{
"name": "Response body sample",
"variables": [
{
"key": "firstName",
"value": "John"
},
{
"key": "lastName",
"value": "Doe"
},
{
"key": "id",
"value": "7"
},
{
"key": "fullName",
"value": "<<firstName>> <<lastName>>"
},
{
"key": "recursiveVarX",
"value": "<<recursiveVarY>>"
},
{
"key": "recursiveVarY",
"value": "<<salutation>>"
},
{
"key": "salutation",
"value": "Hello"
},
{
"key": "greetText",
"value": "<<salutation>> <<fullName>>"
}
]
}

View File

@@ -22,10 +22,12 @@ export const trimAnsi = (target: string) => {
export const getErrorCode = (out: string) => { export const getErrorCode = (out: string) => {
const ansiTrimmedStr = trimAnsi(out); const ansiTrimmedStr = trimAnsi(out);
return ansiTrimmedStr.split(" ")[0]; return ansiTrimmedStr.split(" ")[0];
}; };
export const getTestJsonFilePath = (file: string) => { export const getTestJsonFilePath = (file: string) => {
const filePath = resolve(__dirname, `../../src/__tests__/samples/${file}`); const filePath = `${process.cwd()}/src/__tests__/samples/${file}`;
return filePath; return filePath;
}; };

View File

@@ -37,7 +37,8 @@ export async function parseEnvsData(path: string) {
envPairs.push({ key, value }); envPairs.push({ key, value });
} }
} else if (HoppEnvExportObjectResult.success) { } else if (HoppEnvExportObjectResult.success) {
envPairs.push(...HoppEnvExportObjectResult.data.variables); const { key, value } = HoppEnvExportObjectResult.data.variables[0];
envPairs.push({ key, value });
} }
return <HoppEnvs>{ global: [], selected: envPairs }; return <HoppEnvs>{ global: [], selected: envPairs };

View File

@@ -121,7 +121,7 @@
"generate_token": "Generate Token", "generate_token": "Generate Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Include in URL", "include_in_url": "Include in URL",
"inherited_from": "Inherited {auth} from parent collection {collection} ", "inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Learn how", "learn": "Learn how",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
@@ -295,7 +295,6 @@
"incorrect_email": "Incorrect email", "incorrect_email": "Incorrect email",
"invalid_link": "Invalid link", "invalid_link": "Invalid link",
"invalid_link_description": "The link you clicked is invalid or expired.", "invalid_link_description": "The link you clicked is invalid or expired.",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "Invalid JSON", "json_parsing_failed": "Invalid JSON",
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again", "json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
@@ -314,12 +313,10 @@
"export": { "export": {
"as_json": "Export as JSON", "as_json": "Export as JSON",
"create_secret_gist": "Create secret Gist", "create_secret_gist": "Create secret Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist",
"failed": "Something went wrong while exporting", "failed": "Something went wrong while exporting",
"secret_gist_success": "Successfully exported as secret Gist", "gist_created": "Gist created",
"require_github": "Login with GitHub to create secret gist", "require_github": "Login with GitHub to create secret gist",
"title": "Export", "title": "Export"
"success": "Successfully exported"
}, },
"filter": { "filter": {
"all": "All", "all": "All",

View File

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

View File

@@ -9,10 +9,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { HoppCollection } from "@hoppscotch/data"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { PropType, computed, ref } from "vue"
import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource" import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource"
import { UrlSource } from "~/helpers/import-export/import/import-sources/UrlSource" import { UrlSource } from "~/helpers/import-export/import/import-sources/UrlSource"
@@ -27,9 +24,11 @@ import {
} from "~/helpers/import-export/import/importers" } from "~/helpers/import-export/import/importers"
import { defineStep } from "~/composables/step-components" import { defineStep } from "~/composables/step-components"
import { PropType, computed, ref } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { HoppCollection } from "@hoppscotch/data"
import { appendRESTCollections, restCollections$ } from "~/newstore/collections" import { appendRESTCollections, restCollections$ } from "~/newstore/collections"
import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue" import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue"
import { GetMyTeamsQuery } from "~/helpers/backend/graphql" import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
@@ -49,7 +48,7 @@ import { getTeamCollectionJSON } from "~/helpers/backend/helpers"
import { platform } from "~/platform" import { platform } from "~/platform"
import { initializeDownloadCollection } from "~/helpers/import-export/export" import { initializeDownloadCollection } from "~/helpers/import-export/export"
import { gistExporter } from "~/helpers/import-export/export/gist" import { collectionsGistExporter } from "~/helpers/import-export/export/gistExport"
import { myCollectionsExporter } from "~/helpers/import-export/export/myCollections" import { myCollectionsExporter } from "~/helpers/import-export/export/myCollections"
import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections" import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections"
@@ -84,8 +83,6 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser() platform.auth.getCurrentUser()
) )
const myCollections = useReadonlyStream(restCollections$, [])
const showImportFailedError = () => { const showImportFailedError = () => {
toast.error(t("import.failed")) toast.error(t("import.failed"))
} }
@@ -471,13 +468,8 @@ const HoppGistCollectionsExporter: ImporterOrExporter = {
icon: IconGithub, icon: IconGithub,
disabled: !currentUser.value disabled: !currentUser.value
? true ? true
: currentUser.value?.provider !== "github.com", : currentUser.value.provider !== "github.com",
title: title: t("export.create_secret_gist"),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
currentUser?.value?.provider === "github.com"
? "export.create_secret_gist_tooltip_text"
: "export.require_github",
applicableTo: ["personal-workspace", "team-workspace"], applicableTo: ["personal-workspace", "team-workspace"],
isLoading: isHoppGistCollectionExporterInProgress, isLoading: isHoppGistCollectionExporterInProgress,
}, },
@@ -494,27 +486,13 @@ const HoppGistCollectionsExporter: ImporterOrExporter = {
} }
if (E.isRight(collectionJSON)) { if (E.isRight(collectionJSON)) {
if (!JSON.parse(collectionJSON.right).length) { collectionsGistExporter(collectionJSON.right, accessToken)
isHoppGistCollectionExporterInProgress.value = false
return toast.error(t("error.no_collections_to_export"))
}
const res = await gistExporter(collectionJSON.right, accessToken)
if (E.isLeft(res)) {
toast.error(t("export.failed"))
return
}
toast.success(t("export.secret_gist_success"))
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION", type: "HOPP_EXPORT_COLLECTION",
exporter: "gist", exporter: "gist",
platform: "rest", platform: "rest",
}) })
platform.io.openExternalLink(res.right)
} }
isHoppGistCollectionExporterInProgress.value = false isHoppGistCollectionExporterInProgress.value = false
@@ -582,6 +560,8 @@ const selectedTeamID = computed(() => {
: undefined : undefined
}) })
const myCollections = useReadonlyStream(restCollections$, [])
const getCollectionJSON = async () => { const getCollectionJSON = async () => {
if ( if (
props.collectionsType.type === "team-collections" && props.collectionsType.type === "team-collections" &&

View File

@@ -9,16 +9,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { HoppCollection } from "@hoppscotch/data"
import * as E from "fp-ts/Either"
import { ref } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { HoppCollection } from "@hoppscotch/data"
import { ImporterOrExporter } from "~/components/importExport/types" import { ImporterOrExporter } from "~/components/importExport/types"
import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource" import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource"
import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource" import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource"
import * as E from "fp-ts/Either"
import IconFolderPlus from "~icons/lucide/folder-plus" import IconFolderPlus from "~icons/lucide/folder-plus"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import { initializeDownloadCollection } from "~/helpers/import-export/export" import { initializeDownloadCollection } from "~/helpers/import-export/export"
@@ -31,7 +30,7 @@ import {
} from "~/newstore/collections" } from "~/newstore/collections"
import { hoppGqlCollectionsImporter } from "~/helpers/import-export/import/hoppGql" import { hoppGqlCollectionsImporter } from "~/helpers/import-export/import/hoppGql"
import { gqlCollectionsExporter } from "~/helpers/import-export/export/gqlCollections" import { gqlCollectionsExporter } from "~/helpers/import-export/export/gqlCollections"
import { gistExporter } from "~/helpers/import-export/export/gist" import { gqlCollectionsGistExporter } from "~/helpers/import-export/export/gqlCollectionsGistExporter"
import { computed } from "vue" import { computed } from "vue"
import { hoppGQLImporter } from "~/helpers/import-export/import/hopp" import { hoppGQLImporter } from "~/helpers/import-export/import/hopp"
@@ -43,10 +42,6 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser() platform.auth.getCurrentUser()
) )
const gqlCollections = useReadonlyStream(graphqlCollections$, [])
const isGqlCollectionGistExportInProgress = ref(false)
const GqlCollectionsHoppImporter: ImporterOrExporter = { const GqlCollectionsHoppImporter: ImporterOrExporter = {
metadata: { metadata: {
id: "import.from_json", id: "import.from_json",
@@ -124,6 +119,8 @@ const GqlCollectionsGistImporter: ImporterOrExporter = {
}), }),
} }
const gqlCollections = useReadonlyStream(graphqlCollections$, [])
const GqlCollectionsHoppExporter: ImporterOrExporter = { const GqlCollectionsHoppExporter: ImporterOrExporter = {
metadata: { metadata: {
id: "export.as_json", id: "export.as_json",
@@ -162,35 +159,29 @@ const GqlCollectionsGistExporter: ImporterOrExporter = {
metadata: { metadata: {
id: "export.as_gist", id: "export.as_gist",
name: "export.create_secret_gist", name: "export.create_secret_gist",
title: title: !currentUser
// eslint-disable-next-line @typescript-eslint/ban-ts-comment ? "export.require_github"
// @ts-ignore : // eslint-disable-next-line @typescript-eslint/ban-ts-comment
currentUser?.value?.provider === "github.com" // @ts-ignore
? "export.create_secret_gist_tooltip_text" currentUser.provider !== "github.com"
: "export.require_github", ? `export.require_github`
: "export.create_secret_gist",
icon: IconUser, icon: IconUser,
disabled: !currentUser.value disabled: !currentUser.value
? true ? true
: currentUser.value?.provider !== "github.com", : currentUser.value.provider !== "github.com",
applicableTo: ["personal-workspace"], applicableTo: ["personal-workspace"],
isLoading: isGqlCollectionGistExportInProgress,
}, },
action: async () => { action: async () => {
if (!gqlCollections.value.length) {
return toast.error(t("error.no_collections_to_export"))
}
if (!currentUser.value) { if (!currentUser.value) {
toast.error(t("profile.no_permission")) toast.error(t("profile.no_permission"))
return return
} }
isGqlCollectionGistExportInProgress.value = true
const accessToken = currentUser.value?.accessToken const accessToken = currentUser.value?.accessToken
if (accessToken) { if (accessToken) {
const res = await gistExporter( const res = await gqlCollectionsGistExporter(
JSON.stringify(gqlCollections.value), JSON.stringify(gqlCollections.value),
accessToken accessToken
) )
@@ -200,7 +191,7 @@ const GqlCollectionsGistExporter: ImporterOrExporter = {
return return
} }
toast.success(t("export.secret_gist_success")) toast.success(t("export.success"))
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_EXPORT_COLLECTION", type: "HOPP_EXPORT_COLLECTION",
@@ -210,8 +201,6 @@ const GqlCollectionsGistExporter: ImporterOrExporter = {
platform.io.openExternalLink(res.right) platform.io.openExternalLink(res.right)
} }
isGqlCollectionGistExportInProgress.value = false
}, },
} }

View File

@@ -70,11 +70,7 @@
v-model:option-tab="selectedOptionTab" v-model:option-tab="selectedOptionTab"
:properties="properties" :properties="properties"
/> />
<HttpResponse <HttpResponse :document="tab.document" :is-embed="true" />
v-if="tab.document.response"
:document="tab.document"
:is-embed="true"
/>
</div> </div>
</template> </template>
@@ -92,19 +88,18 @@ import { runRESTRequest$ } from "~/helpers/RequestRunner"
import { HoppTab } from "~/services/tab" import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document" import { HoppRESTDocument } from "~/helpers/rest/document"
import IconSave from "~icons/lucide/save" import IconSave from "~icons/lucide/save"
import { RESTOptionTabs } from "../http/RequestOptions.vue"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
const props = defineProps<{ const props = defineProps<{
modelTab: HoppTab<HoppRESTDocument> modelTab: HoppTab<HoppRESTDocument>
properties: RESTOptionTabs[] properties: string[]
sharedRequestID: string sharedRequestID: string
}>() }>()
const tab = useModel(props, "modelTab") const tab = useModel(props, "modelTab")
const selectedOptionTab = ref<RESTOptionTabs>(props.properties[0]) const selectedOptionTab = ref(props.properties[0])
const requestCancelFunc: Ref<(() => void) | null> = ref(null) const requestCancelFunc: Ref<(() => void) | null> = ref(null)

View File

@@ -9,17 +9,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Environment } from "@hoppscotch/data"
import * as E from "fp-ts/Either"
import { ref } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { Environment } from "@hoppscotch/data"
import { ImporterOrExporter } from "~/components/importExport/types" import { ImporterOrExporter } from "~/components/importExport/types"
import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource" import { FileSource } from "~/helpers/import-export/import/import-sources/FileSource"
import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource" import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource"
import { hoppEnvImporter } from "~/helpers/import-export/import/hoppEnv" import { hoppEnvImporter } from "~/helpers/import-export/import/hoppEnv"
import * as E from "fp-ts/Either"
import { import {
appendEnvironments, appendEnvironments,
addGlobalEnvVariable, addGlobalEnvVariable,
@@ -41,7 +39,7 @@ import { initializeDownloadCollection } from "~/helpers/import-export/export"
import { computed } from "vue" import { computed } from "vue"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
import { environmentsExporter } from "~/helpers/import-export/export/environments" import { environmentsExporter } from "~/helpers/import-export/export/environments"
import { gistExporter } from "~/helpers/import-export/export/gist" import { environmentsGistExporter } from "~/helpers/import-export/export/environmentsGistExport"
import { platform } from "~/platform" import { platform } from "~/platform"
const t = useI18n() const t = useI18n()
@@ -60,8 +58,6 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser() platform.auth.getCurrentUser()
) )
const isEnvironmentGistExportInProgress = ref(false)
const isTeamEnvironment = computed(() => { const isTeamEnvironment = computed(() => {
return props.environmentType === "TEAM_ENV" return props.environmentType === "TEAM_ENV"
}) })
@@ -266,44 +262,35 @@ const HoppEnvironmentsGistExporter: ImporterOrExporter = {
title: title:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
currentUser?.value?.provider === "github.com" currentUser?.provider === "github.com"
? "export.create_secret_gist_tooltip_text" ? "export.create_secret_gist"
: "export.require_github", : "export.require_github",
icon: IconUser, icon: IconUser,
disabled: !currentUser.value disabled: !currentUser.value
? true ? true
: currentUser.value?.provider !== "github.com", : currentUser.value.provider !== "github.com",
applicableTo: ["personal-workspace", "team-workspace"], applicableTo: ["personal-workspace", "team-workspace"],
isLoading: isEnvironmentGistExportInProgress,
}, },
action: async () => { action: async () => {
if (!environmentJson.value.length) {
return toast.error(t("error.no_environments_to_export"))
}
if (!currentUser.value) { if (!currentUser.value) {
toast.error(t("profile.no_permission")) toast.error(t("profile.no_permission"))
return return
} }
isEnvironmentGistExportInProgress.value = true
const accessToken = currentUser.value?.accessToken const accessToken = currentUser.value?.accessToken
if (accessToken) { if (accessToken) {
const res = await gistExporter( const res = await environmentsGistExporter(
JSON.stringify(environmentJson.value), JSON.stringify(environmentJson.value),
accessToken, accessToken
"hoppscotch-environment.json"
) )
if (E.isLeft(res)) { if (E.isLeft(res)) {
toast.error(t("export.failed")) toast.error(t("export.failed"))
isEnvironmentGistExportInProgress.value = false
return return
} }
toast.success(t("export.secret_gist_success")) toast.success(t("export.success"))
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_EXPORT_ENVIRONMENT", type: "HOPP_EXPORT_ENVIRONMENT",
@@ -312,8 +299,6 @@ const HoppEnvironmentsGistExporter: ImporterOrExporter = {
platform.io.openExternalLink(res.right) platform.io.openExternalLink(res.right)
} }
isEnvironmentGistExportInProgress.value = false
}, },
} }

View File

@@ -140,10 +140,7 @@ const runQuery = async (
const runVariables = clone(request.value.variables) const runVariables = clone(request.value.variables)
const runAuth = const runAuth =
request.value.auth.authType === "inherit" && request.value.auth.authActive request.value.auth.authType === "inherit" && request.value.auth.authActive
? clone( ? clone(tabs.currentActiveTab.value.document.inheritedProperties?.auth)
tabs.currentActiveTab.value.document.inheritedProperties?.auth
.inheritedAuth
)
: clone(request.value.auth) : clone(request.value.auth)
const inheritedHeaders = const inheritedHeaders =

View File

@@ -187,7 +187,7 @@ import IconClock from "~icons/lucide/clock"
import IconCopy from "~icons/lucide/copy" import IconCopy from "~icons/lucide/copy"
import IconBox from "~icons/lucide/box" import IconBox from "~icons/lucide/box"
import { computed, nextTick, reactive, ref } from "vue" import { computed, nextTick, reactive, ref } from "vue"
import { GraphQLField, GraphQLType, getNamedType } from "graphql" import { GraphQLField, GraphQLType } from "graphql"
import { refAutoReset } from "@vueuse/core" import { refAutoReset } from "@vueuse/core"
import { useCodemirror } from "@composables/codemirror" import { useCodemirror } from "@composables/codemirror"
import { copyToClipboard } from "@helpers/utils/clipboard" import { copyToClipboard } from "@helpers/utils/clipboard"
@@ -260,6 +260,12 @@ function getFilteredGraphqlTypes(filterText: string, types: GraphQLType[]) {
}) })
} }
function resolveRootType(type: GraphQLType) {
let t: any = type
while (t.ofType) t = t.ofType
return t
}
const toast = useToast() const toast = useToast()
const downloadSchemaIcon = refAutoReset<typeof IconDownload | typeof IconCheck>( const downloadSchemaIcon = refAutoReset<typeof IconDownload | typeof IconCheck>(
@@ -325,7 +331,7 @@ const handleJumpToType = async (type: GraphQLType) => {
selectedGqlTab.value = "types" selectedGqlTab.value = "types"
await nextTick() await nextTick()
const rootTypeName = getNamedType(type).name const rootTypeName = resolveRootType(type).name
const target = document.getElementById(`type_${rootTypeName}`) const target = document.getElementById(`type_${rootTypeName}`)
if (target) { if (target) {
target.scrollIntoView({ block: "center", behavior: "smooth" }) target.scrollIntoView({ block: "center", behavior: "smooth" })

View File

@@ -8,7 +8,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { GraphQLScalarType, GraphQLType, getNamedType } from "graphql" import { GraphQLScalarType, GraphQLType } from "graphql"
import { computed } from "vue" import { computed } from "vue"
const props = defineProps<{ const props = defineProps<{
@@ -21,9 +21,15 @@ const emit = defineEmits<{
const typeString = computed(() => `${props.gqlType}`) const typeString = computed(() => `${props.gqlType}`)
const isScalar = computed(() => { const isScalar = computed(() => {
return getNamedType(props.gqlType) instanceof GraphQLScalarType return resolveRootType(props.gqlType) instanceof GraphQLScalarType
}) })
function resolveRootType(type: GraphQLType) {
let t = type as any
while (t.ofType !== null) t = t.ofType
return t
}
function jumpToType() { function jumpToType() {
if (isScalar.value) return if (isScalar.value) return
emit("jump-to-type", props.gqlType) emit("jump-to-type", props.gqlType)

View File

@@ -99,7 +99,6 @@ useCodemirror(
linter, linter,
completer, completer,
environmentHighlights: false, environmentHighlights: false,
contextMenuEnabled: false,
}) })
) )

View File

@@ -237,7 +237,7 @@ import { useReadonlyStream, useStreamSubscriber } from "@composables/stream"
import { useToast } from "@composables/toast" import { useToast } from "@composables/toast"
import { useVModel } from "@vueuse/core" import { useVModel } from "@vueuse/core"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { Ref, computed, ref, onUnmounted } from "vue" import { Ref, computed, onBeforeUnmount, ref } from "vue"
import { defineActionHandler, invokeAction } from "~/helpers/actions" import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { runMutation } from "~/helpers/backend/GQLClient" import { runMutation } from "~/helpers/backend/GQLClient"
import { UpdateRequestDocument } from "~/helpers/backend/graphql" import { UpdateRequestDocument } from "~/helpers/backend/graphql"
@@ -322,10 +322,6 @@ const userHistories = computed(() => {
return history.value.map((history) => history.request.endpoint).slice(0, 10) return history.value.map((history) => history.request.endpoint).slice(0, 10)
}) })
const inspectionService = useService(InspectionService)
const tabs = useService(RESTTabService)
const newSendRequest = async () => { const newSendRequest = async () => {
if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) { if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) {
toast.error(`${t("empty.endpoint")}`) toast.error(`${t("empty.endpoint")}`)
@@ -426,17 +422,6 @@ function isCURL(curl: string) {
return curl.includes("curl ") return curl.includes("curl ")
} }
const currentTabID = tabs.currentTabID.value
onUnmounted(() => {
//check if current tab id exist in the current tab id lists
const isCurrentTabRemoved = !tabs
.getActiveTabs()
.value.some((tab) => tab.id === currentTabID)
if (isCurrentTabRemoved) cancelRequest()
})
const cancelRequest = () => { const cancelRequest = () => {
loading.value = false loading.value = false
requestCancelFunc.value?.() requestCancelFunc.value?.()
@@ -568,6 +553,10 @@ const saveRequest = () => {
const request = ref<HoppRESTRequest | null>(null) const request = ref<HoppRESTRequest | null>(null)
onBeforeUnmount(() => {
if (loading.value) cancelRequest()
})
defineActionHandler("request.send-cancel", () => { defineActionHandler("request.send-cancel", () => {
if (!loading.value) newSendRequest() if (!loading.value) newSendRequest()
else cancelRequest() else cancelRequest()
@@ -618,5 +607,8 @@ const isCustomMethod = computed(() => {
const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT") const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT")
const inspectionService = useService(InspectionService)
const tabs = useService(RESTTabService)
const tabResults = inspectionService.getResultViewFor(tabs.currentTabID.value) const tabResults = inspectionService.getResultViewFor(tabs.currentTabID.value)
</script> </script>

View File

@@ -5,7 +5,7 @@
render-inactive-tabs render-inactive-tabs
> >
<HoppSmartTab <HoppSmartTab
v-if="properties ? properties.includes('params') : true" v-if="properties ? properties.includes('parameters') : true"
:id="'params'" :id="'params'"
:label="`${t('tab.parameters')}`" :label="`${t('tab.parameters')}`"
:info="`${newActiveParamsCount$}`" :info="`${newActiveParamsCount$}`"
@@ -13,7 +13,7 @@
<HttpParameters v-model="request.params" /> <HttpParameters v-model="request.params" />
</HoppSmartTab> </HoppSmartTab>
<HoppSmartTab <HoppSmartTab
v-if="properties ? properties.includes('bodyParams') : true" v-if="properties ? properties.includes('body') : true"
:id="'bodyParams'" :id="'bodyParams'"
:label="`${t('tab.body')}`" :label="`${t('tab.body')}`"
> >

View File

@@ -95,7 +95,6 @@ useCodemirror(
linter, linter,
completer, completer,
environmentHighlights: false, environmentHighlights: false,
contextMenuEnabled: false,
}) })
) )

View File

@@ -103,7 +103,7 @@ const widgets: Widget[] = [
}, },
] ]
type Tabs = "params" | "bodyParams" | "headers" | "authorization" type Tabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = { type EmbedOption = {
selectedTab: Tabs selectedTab: Tabs
@@ -116,15 +116,15 @@ type EmbedOption = {
} }
const embedOption = ref<EmbedOption>({ const embedOption = ref<EmbedOption>({
selectedTab: "params", selectedTab: "parameters",
tabs: [ tabs: [
{ {
value: "params", value: "parameters",
label: t("tab.parameters"), label: t("tab.parameters"),
enabled: true, enabled: true,
}, },
{ {
value: "bodyParams", value: "body",
label: t("tab.body"), label: t("tab.body"),
enabled: true, enabled: true,
}, },

View File

@@ -225,10 +225,10 @@ const props = defineProps({
embedOptions: { embedOptions: {
type: Object as PropType<EmbedOption>, type: Object as PropType<EmbedOption>,
default: () => ({ default: () => ({
selectedTab: "params", selectedTab: "parameters",
tabs: [ tabs: [
{ {
value: "params", value: "parameters",
label: "shared_requests.parameters", label: "shared_requests.parameters",
enabled: true, enabled: true,
}, },
@@ -290,7 +290,7 @@ const widgets: Widget[] = [
}, },
] ]
type EmbedTabs = "params" | "bodyParams" | "headers" | "authorization" type EmbedTabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = { type EmbedOption = {
selectedTab: EmbedTabs selectedTab: EmbedTabs

View File

@@ -56,7 +56,7 @@ import { useI18n } from "~/composables/i18n"
const t = useI18n() const t = useI18n()
type EmbedTabs = "params" | "bodyParams" | "headers" | "authorization" type EmbedTabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = { type EmbedOption = {
selectedTab: EmbedTabs selectedTab: EmbedTabs
@@ -93,15 +93,15 @@ const props = defineProps({
embedOptions: { embedOptions: {
type: Object as PropType<EmbedOption>, type: Object as PropType<EmbedOption>,
default: () => ({ default: () => ({
selectedTab: "params", selectedTab: "parameters",
tabs: [ tabs: [
{ {
value: "params", value: "parameters",
label: "shared_requests.parameters", label: "shared_requests.parameters",
enabled: true, enabled: true,
}, },
{ {
value: "bodyParams", value: "body",
label: "shared_requests.body", label: "shared_requests.body",
enabled: true, enabled: true,
}, },

View File

@@ -21,7 +21,7 @@
/> />
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<div v-if="loading" class="flex flex-col items-center justify-center p-4"> <div v-if="loading" class="flex flex-col items-center justify-center">
<HoppSmartSpinner class="mb-4" /> <HoppSmartSpinner class="mb-4" />
<span class="text-secondaryLight">{{ t("state.loading") }}</span> <span class="text-secondaryLight">{{ t("state.loading") }}</span>
</div> </div>
@@ -136,15 +136,15 @@ const shareRequestCreatingLoading = ref(false)
const requestToShare = ref<HoppRESTRequest | null>(null) const requestToShare = ref<HoppRESTRequest | null>(null)
const embedOptions = ref<EmbedOption>({ const embedOptions = ref<EmbedOption>({
selectedTab: "params", selectedTab: "parameters",
tabs: [ tabs: [
{ {
value: "params", value: "parameters",
label: t("tab.parameters"), label: t("tab.parameters"),
enabled: false, enabled: false,
}, },
{ {
value: "bodyParams", value: "body",
label: t("tab.body"), label: t("tab.body"),
enabled: false, enabled: false,
}, },
@@ -208,7 +208,7 @@ const currentUser = useReadonlyStream(
const step = ref(1) const step = ref(1)
type EmbedTabs = "params" | "bodyParams" | "headers" | "authorization" type EmbedTabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = { type EmbedOption = {
selectedTab: EmbedTabs selectedTab: EmbedTabs
@@ -249,15 +249,7 @@ const loading = computed(
onLoggedIn(() => { onLoggedIn(() => {
try { try {
// wait for a bit to let the auth token to be set adapter.initialize()
// because in some race conditions, the token is not set this fixes that
const initLoadTimeout = setTimeout(() => {
adapter.initialize()
}, 10)
return () => {
clearTimeout(initLoadTimeout)
}
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
@@ -321,15 +313,15 @@ const displayCustomizeRequestModal = (
info: t("shared_requests.button_info"), info: t("shared_requests.button_info"),
} }
embedOptions.value = { embedOptions.value = {
selectedTab: "params", selectedTab: "parameters",
tabs: [ tabs: [
{ {
value: "params", value: "parameters",
label: t("tab.parameters"), label: t("tab.parameters"),
enabled: false, enabled: false,
}, },
{ {
value: "bodyParams", value: "body",
label: t("tab.body"), label: t("tab.body"),
enabled: false, enabled: false,
}, },
@@ -459,7 +451,7 @@ const getErrorMessage = (err: GQLError<string>) => {
} }
switch (err.error) { switch (err.error) {
case "shortcode/not_found": case "shortcode/not_found":
return t("shared_requests.not_found") return t("shared_request.not_found")
default: default:
return t("error.something_went_wrong") return t("error.something_went_wrong")
} }

View File

@@ -57,7 +57,7 @@ import { computed } from "vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
type Tabs = "params" | "bodyParams" | "headers" | "authorization" type Tabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = { type EmbedOption = {
selectedTab: Tabs selectedTab: Tabs

View File

@@ -37,7 +37,7 @@
v-if="currentSuggestionIndex === index" v-if="currentSuggestionIndex === index"
class="hidden items-center text-secondary md:flex" class="hidden items-center text-secondary md:flex"
> >
<kbd class="shortcut-key">Enter</kbd> <kbd class="shortcut-key">TAB</kbd>
<span class="ml-2 truncate">to select</span> <span class="ml-2 truncate">to select</span>
</div> </div>
</li> </li>
@@ -79,7 +79,6 @@ const props = withDefaults(
readonly?: boolean readonly?: boolean
autoCompleteSource?: string[] autoCompleteSource?: string[]
inspectionResults?: InspectorResult[] | undefined inspectionResults?: InspectorResult[] | undefined
contextMenuEnabled?: boolean
}>(), }>(),
{ {
modelValue: "", modelValue: "",
@@ -92,7 +91,6 @@ const props = withDefaults(
autoCompleteSource: undefined, autoCompleteSource: undefined,
inspectionResult: undefined, inspectionResult: undefined,
inspectionResults: undefined, inspectionResults: undefined,
contextMenuEnabled: true,
} }
) )
@@ -169,41 +167,36 @@ watch(
) )
const handleKeystroke = (ev: KeyboardEvent) => { const handleKeystroke = (ev: KeyboardEvent) => {
if (!props.autoCompleteSource) return if (["ArrowDown", "ArrowUp", "Enter", "Tab", "Escape"].includes(ev.key)) {
if (["ArrowDown", "ArrowUp", "Enter", "Escape"].includes(ev.key)) {
ev.preventDefault() ev.preventDefault()
} }
if (["Escape", "Tab", "Shift"].includes(ev.key)) { if (ev.shiftKey) {
showSuggestionPopover.value = false showSuggestionPopover.value = false
return
} }
if (ev.key === "Enter") { showSuggestionPopover.value = true
if (suggestions.value.length > 0 && currentSuggestionIndex.value > -1) {
updateModelValue(suggestions.value[currentSuggestionIndex.value])
currentSuggestionIndex.value = -1
//used to set codemirror cursor at the end of the line after selecting a suggestion if (
nextTick(() => { ["Enter", "Tab"].includes(ev.key) &&
view.value?.dispatch({ suggestions.value.length > 0 &&
selection: EditorSelection.create([ currentSuggestionIndex.value > -1
EditorSelection.range( ) {
props.modelValue.length, updateModelValue(suggestions.value[currentSuggestionIndex.value])
props.modelValue.length currentSuggestionIndex.value = -1
),
]), //used to set codemirror cursor at the end of the line after selecting a suggestion
}) nextTick(() => {
view.value?.dispatch({
selection: EditorSelection.create([
EditorSelection.range(
props.modelValue.length,
props.modelValue.length
),
]),
}) })
} })
if (showSuggestionPopover.value) {
showSuggestionPopover.value = false
} else {
emit("enter", ev)
}
} else {
showSuggestionPopover.value = true
} }
if (ev.key === "ArrowDown") { if (ev.key === "ArrowDown") {
@@ -228,6 +221,15 @@ const handleKeystroke = (ev: KeyboardEvent) => {
emit("keyup", ev) emit("keyup", ev)
} }
if (ev.key === "Enter") {
emit("enter", ev)
showSuggestionPopover.value = false
}
if (ev.key === "Escape") {
showSuggestionPopover.value = false
}
// used to scroll to the first suggestion when left arrow is pressed // used to scroll to the first suggestion when left arrow is pressed
if (ev.key === "ArrowLeft") { if (ev.key === "ArrowLeft") {
if (suggestions.value.length > 0) { if (suggestions.value.length > 0) {
@@ -357,11 +359,8 @@ const initView = (el: any) => {
handleTextSelection() handleTextSelection()
}, 140) }, 140)
// Only add event listeners if context menu is enabled in the component el.addEventListener("mouseup", debounceFn)
if (props.contextMenuEnabled) { el.addEventListener("keyup", debounceFn)
el.addEventListener("mouseup", debounceFn)
el.addEventListener("keyup", debounceFn)
}
const extensions: Extension = [ const extensions: Extension = [
EditorView.contentAttributes.of({ "aria-label": props.placeholder }), EditorView.contentAttributes.of({ "aria-label": props.placeholder }),
@@ -397,7 +396,7 @@ const initView = (el: any) => {
ev.preventDefault() ev.preventDefault()
}, },
scroll(event) { scroll(event) {
if (event.target && props.contextMenuEnabled) { if (event.target) {
handleTextSelection() handleTextSelection()
} }
}, },
@@ -406,6 +405,7 @@ const initView = (el: any) => {
class { class {
update(update: ViewUpdate) { update(update: ViewUpdate) {
if (props.readonly) return if (props.readonly) return
if (update.docChanged) { if (update.docChanged) {
const prevValue = clone(cachedValue.value) const prevValue = clone(cachedValue.value)
@@ -436,17 +436,6 @@ const initView = (el: any) => {
clipboardEv = null clipboardEv = null
pastedValue = null pastedValue = null
} }
if (props.contextMenuEnabled) {
// close the context menu if text is being updated in the editor
invokeAction("contextmenu.open", {
position: {
top: 0,
left: 0,
},
text: null,
})
}
} }
} }
} }

View File

@@ -124,8 +124,6 @@
E.isRight(pendingInvites.data) && E.isRight(pendingInvites.data) &&
pendingInvites.data.right.team?.teamInvitations.length === 0 pendingInvites.data.right.team?.teamInvitations.length === 0
" "
:src="`/images/states/${colorMode.value}/add_group.svg`"
:alt="t('empty.pending_invites')"
:text="t('empty.pending_invites')" :text="t('empty.pending_invites')"
/> />
<div <div

View File

@@ -17,7 +17,7 @@
v-if="props.showCount && props.teamMembers.length > maxMembersSoftLimit" v-if="props.showCount && props.teamMembers.length > maxMembersSoftLimit"
v-tippy="{ theme: 'tooltip', allowHTML: true }" v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="remainingSlicedMembers" :title="remainingSlicedMembers"
class="text-[8px] z-10 inline-flex h-5 w-5 cursor-pointer items-center justify-center rounded-full bg-dividerDark text-secondaryDark ring-2 ring-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primaryDark" class="font- text-8px z-10 inline-flex h-5 w-5 cursor-pointer items-center justify-center rounded-full bg-dividerDark text-secondaryDark ring-2 ring-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primaryDark"
tabindex="0" tabindex="0"
@click="handleClick()" @click="handleClick()"
> >

View File

@@ -33,7 +33,7 @@
</HoppSmartPlaceholder> </HoppSmartPlaceholder>
<div v-else-if="!loading" class="flex flex-col"> <div v-else-if="!loading" class="flex flex-col">
<div <div
class="sticky top-0 z-10 mb-2 flex items-center justify-between bg-popover py-2 pl-2" class="sticky -top-2 top-0 z-10 mb-2 flex items-center justify-between bg-popover py-2 pl-2"
> >
<div class="flex items-center px-2 font-semibold text-secondaryLight"> <div class="flex items-center px-2 font-semibold text-secondaryLight">
{{ t("team.title") }} {{ t("team.title") }}

View File

@@ -63,8 +63,6 @@ type CodeMirrorOptions = {
additionalExts?: Extension[] additionalExts?: Extension[]
contextMenuEnabled?: boolean
// callback on editor update // callback on editor update
onUpdate?: (view: ViewUpdate) => void onUpdate?: (view: ViewUpdate) => void
} }
@@ -210,9 +208,6 @@ export function useCodemirror(
): { cursor: Ref<{ line: number; ch: number }> } { ): { cursor: Ref<{ line: number; ch: number }> } {
const { subscribeToStream } = useStreamSubscriber() const { subscribeToStream } = useStreamSubscriber()
// Set default value for contextMenuEnabled if not provided
options.contextMenuEnabled = options.contextMenuEnabled ?? true
const additionalExts = new Compartment() const additionalExts = new Compartment()
const language = new Compartment() const language = new Compartment()
const lineWrapping = new Compartment() const lineWrapping = new Compartment()
@@ -277,11 +272,8 @@ export function useCodemirror(
handleTextSelection() handleTextSelection()
}, 140) }, 140)
// Only add event listeners if context menu is enabled in the editor el.addEventListener("mouseup", debounceFn)
if (options.contextMenuEnabled) { el.addEventListener("keyup", debounceFn)
el.addEventListener("mouseup", debounceFn)
el.addEventListener("keyup", debounceFn)
}
if (options.onUpdate) { if (options.onUpdate) {
options.onUpdate(update) options.onUpdate(update)
@@ -320,7 +312,7 @@ export function useCodemirror(
), ),
EditorView.domEventHandlers({ EditorView.domEventHandlers({
scroll(event) { scroll(event) {
if (event.target && options.contextMenuEnabled) { if (event.target) {
handleTextSelection() handleTextSelection()
} }
}, },

View File

@@ -154,9 +154,6 @@ export function runRESTRequest$(
) )
if (E.isRight(runResult)) { if (E.isRight(runResult)) {
// set the response in the tab so that multiple tabs can run request simultaneously
tab.value.document.response = res
tab.value.document.testResults = translateToSandboxTestResults( tab.value.document.testResults = translateToSandboxTestResults(
runResult.right runResult.right
) )

View File

@@ -1,24 +1,13 @@
import * as O from "fp-ts/Option" import * as O from "fp-ts/Option"
import { flow } from "fp-ts/function" import { flow } from "fp-ts/function"
type SafeParseJSON = {
(str: string, convertToArray: true): O.Option<Array<unknown>>
(str: string, convertToArray?: false): O.Option<Record<string, unknown>>
}
/** /**
* Checks and Parses JSON string * Checks and Parses JSON string
* @param str Raw JSON data to be parsed * @param str Raw JSON data to be parsed
* @returns Option type with some(JSON data) or none * @returns Option type with some(JSON data) or none
*/ */
export const safeParseJSON: SafeParseJSON = (str, convertToArray = false) => export const safeParseJSON = (str: string): O.Option<object> =>
O.tryCatch(() => { O.tryCatch(() => JSON.parse(str))
const data = JSON.parse(str)
if (convertToArray) {
return Array.isArray(data) ? data : [data]
}
return data
})
/** /**
* Checks if given string is a JSON string * Checks if given string is a JSON string

View File

@@ -27,7 +27,7 @@ export const getDefaultGQLRequest = (): HoppGQLRequest => ({
}`, }`,
query: DEFAULT_QUERY, query: DEFAULT_QUERY,
auth: { auth: {
authType: "none", authType: "inherit",
authActive: true, authActive: true,
}, },
}) })

View File

@@ -0,0 +1,18 @@
import * as E from "fp-ts/Either"
import { createGist } from "~/helpers/gist"
export const environmentsGistExporter = async (
environmentsJSON: string,
accessToken: string
) => {
const res = await createGist(
environmentsJSON,
"hoppscotch-collections.json",
accessToken
)()
if (E.isLeft(res)) {
return E.left(res.left)
}
return E.right(res.right.data.html_url as string)
}

View File

@@ -1,19 +0,0 @@
import * as E from "fp-ts/Either"
import { createGist } from "~/helpers/gist"
export const gistExporter = async (
JSONFileContents: string,
accessToken: string,
fileName = "hoppscotch-collections.json"
) => {
if (!accessToken) {
return E.left("Invalid User")
}
const res = await createGist(JSONFileContents, fileName, accessToken)()
if (E.isLeft(res)) {
return E.left(res.left)
}
return E.right(res.right.data.html_url as string)
}

View File

@@ -0,0 +1,22 @@
import { createGist } from "~/helpers/gist"
import * as E from "fp-ts/Either"
export const collectionsGistExporter = async (
collectionJSON: string,
accessToken: string
) => {
if (!accessToken) {
return E.left("Invalid User")
}
const res = await createGist(
collectionJSON,
"hoppscotch-collections.json",
accessToken
)()
if (E.isLeft(res)) {
return E.left(res.left)
}
return E.right(true)
}

View File

@@ -0,0 +1,18 @@
import * as E from "fp-ts/Either"
import { createGist } from "~/helpers/gist"
export const gqlCollectionsGistExporter = async (
gqlCollectionsJSON: string,
accessToken: string
) => {
const res = await createGist(
gqlCollectionsJSON,
"hoppscotch-collections.json",
accessToken
)()
if (E.isLeft(res)) {
return E.left(res.left)
}
return E.right(res.right.data.html_url as string)
}

View File

@@ -18,7 +18,7 @@ const hoppEnvSchema = z.object({
}) })
export const hoppEnvImporter = (content: string) => { export const hoppEnvImporter = (content: string) => {
const parsedContent = safeParseJSON(content, true) const parsedContent = safeParseJSON(content)
// parse json from the environments string // parse json from the environments string
if (O.isNone(parsedContent)) { if (O.isNone(parsedContent)) {

View File

@@ -549,19 +549,13 @@ const convertPathToHoppReqs = (
), ),
// Construct request object // Construct request object
RA.map(({ method, info }) => { RA.map(({ method, info }) =>
const openAPIUrl = parseOpenAPIUrl(doc) makeRESTRequest({
const openAPIPath = replaceOpenApiPathTemplating(pathName)
const endpoint =
openAPIUrl.endsWith("/") && openAPIPath.startsWith("/")
? openAPIUrl + openAPIPath.slice(1)
: openAPIUrl + openAPIPath
return makeRESTRequest({
name: info.operationId ?? info.summary ?? "Untitled Request", name: info.operationId ?? info.summary ?? "Untitled Request",
method: method.toUpperCase(), method: method.toUpperCase(),
endpoint, endpoint: `${parseOpenAPIUrl(doc)}${replaceOpenApiPathTemplating(
pathName
)}`,
// We don't need to worry about reference types as the Dereferencing pass should remove them // We don't need to worry about reference types as the Dereferencing pass should remove them
params: parseOpenAPIParams( params: parseOpenAPIParams(
@@ -578,7 +572,7 @@ const convertPathToHoppReqs = (
preRequestScript: "", preRequestScript: "",
testScript: "", testScript: "",
}) })
}), ),
// Disable Readonly // Disable Readonly
RA.toArray RA.toArray

View File

@@ -156,7 +156,6 @@ export default class ShortcodeListAdapter {
const [shortcodeCreated$, shortcodeCreatedSub] = runAuthOnlyGQLSubscription( const [shortcodeCreated$, shortcodeCreatedSub] = runAuthOnlyGQLSubscription(
{ {
query: ShortcodeCreatedDocument, query: ShortcodeCreatedDocument,
variables: {},
} }
) )
@@ -173,7 +172,6 @@ export default class ShortcodeListAdapter {
const [shortcodeRevoked$, shortcodeRevokedSub] = runAuthOnlyGQLSubscription( const [shortcodeRevoked$, shortcodeRevokedSub] = runAuthOnlyGQLSubscription(
{ {
query: ShortcodeDeletedDocument, query: ShortcodeDeletedDocument,
variables: {},
} }
) )
@@ -190,7 +188,6 @@ export default class ShortcodeListAdapter {
const [shortcodeUpdated$, shortcodeUpdatedSub] = runAuthOnlyGQLSubscription( const [shortcodeUpdated$, shortcodeUpdatedSub] = runAuthOnlyGQLSubscription(
{ {
query: ShortcodeUpdatedDocument, query: ShortcodeUpdatedDocument,
variables: {},
} }
) )

View File

@@ -1034,11 +1034,6 @@ export default class NewTeamCollectionAdapter {
} }
} }
/**
* Used to obtain the inherited auth and headers for a given folder path, used for both REST and GraphQL team collections
* @param folderPath the path of the folder to cascade the auth from
* @returns the inherited auth and headers for the given folder path
*/
public cascadeParentCollectionForHeaderAuth(folderPath: string) { public cascadeParentCollectionForHeaderAuth(folderPath: string) {
let auth: HoppInheritedProperty["auth"] = { let auth: HoppInheritedProperty["auth"] = {
parentID: folderPath ?? "", parentID: folderPath ?? "",
@@ -1085,7 +1080,7 @@ export default class NewTeamCollectionAdapter {
authType: "inherit", authType: "inherit",
authActive: true, authActive: true,
} }
auth.parentID = path.slice(0, i + 1).join("/") auth.parentID = [...path.slice(0, i + 1)].join("/")
auth.parentName = parentFolder.title auth.parentName = parentFolder.title
} }
@@ -1094,12 +1089,9 @@ export default class NewTeamCollectionAdapter {
const parentFolderAuth = data.auth const parentFolderAuth = data.auth
const parentFolderHeaders = data.headers const parentFolderHeaders = data.headers
if ( if (parentFolderAuth?.authType === "inherit" && path.length === 1) {
parentFolderAuth?.authType === "inherit" &&
path.slice(0, i + 1).length === 1
) {
auth = { auth = {
parentID: path.slice(0, i + 1).join("/"), parentID: [...path.slice(0, i + 1)].join("/"),
parentName: parentFolder.title, parentName: parentFolder.title,
inheritedAuth: auth.inheritedAuth, inheritedAuth: auth.inheritedAuth,
} }
@@ -1107,7 +1099,7 @@ export default class NewTeamCollectionAdapter {
if (parentFolderAuth?.authType !== "inherit") { if (parentFolderAuth?.authType !== "inherit") {
auth = { auth = {
parentID: path.slice(0, i + 1).join("/"), parentID: [...path.slice(0, i + 1)].join("/"),
parentName: parentFolder.title, parentName: parentFolder.title,
inheritedAuth: parentFolderAuth, inheritedAuth: parentFolderAuth,
} }
@@ -1120,7 +1112,7 @@ export default class NewTeamCollectionAdapter {
const index = headers.findIndex( const index = headers.findIndex(
(h) => h.inheritedHeader?.key === header.key (h) => h.inheritedHeader?.key === header.key
) )
const currentPath = path.slice(0, i + 1).join("/") const currentPath = [...path.slice(0, i + 1)].join("/")
if (index !== -1) { if (index !== -1) {
// Replace the existing header with the same key // Replace the existing header with the same key
headers[index] = { headers[index] = {

View File

@@ -4,7 +4,6 @@ import {
HoppRESTRequest, HoppRESTRequest,
HoppCollection, HoppCollection,
makeCollection, makeCollection,
HoppGQLAuth,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import DispatchingStore, { defineDispatchers } from "./DispatchingStore" import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
import { cloneDeep } from "lodash-es" import { cloneDeep } from "lodash-es"
@@ -12,9 +11,6 @@ import { resolveSaveContextOnRequestReorder } from "~/helpers/collection/request
import { getService } from "~/modules/dioc" import { getService } from "~/modules/dioc"
import { RESTTabService } from "~/services/tab/rest" import { RESTTabService } from "~/services/tab/rest"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { HoppRESTAuth } from "@hoppscotch/data"
import { HoppRESTHeaders } from "@hoppscotch/data"
import { HoppGQLHeader } from "~/helpers/graphql"
const defaultRESTCollectionState = { const defaultRESTCollectionState = {
state: [ state: [
@@ -67,12 +63,6 @@ export function navigateToFolderWithIndexPath(
return target !== undefined ? target : null return target !== undefined ? target : null
} }
/**
* Used to obtain the inherited auth and headers for a given folder path, used for both REST and GraphQL personal collections
* @param folderPath the path of the folder to cascade the auth from
* @param type the type of collection
* @returns the inherited auth and headers for the given folder path
*/
export function cascadeParentCollectionForHeaderAuth( export function cascadeParentCollectionForHeaderAuth(
folderPath: string | undefined, folderPath: string | undefined,
type: "rest" | "graphql" type: "rest" | "graphql"
@@ -113,16 +103,10 @@ export function cascadeParentCollectionForHeaderAuth(
return { auth, headers } return { auth, headers }
} }
const parentFolderAuth = parentFolder.auth as HoppRESTAuth | HoppGQLAuth const parentFolderAuth = parentFolder.auth
const parentFolderHeaders = parentFolder.headers as const parentFolderHeaders = parentFolder.headers
| HoppRESTHeaders
| HoppGQLHeader[]
// check if the parent folder has authType 'inherit' and if it is the root folder // check if the parent folder has authType 'inherit' and if it is the root folder
if ( if (parentFolderAuth?.authType === "inherit" && path.length === 1) {
parentFolderAuth?.authType === "inherit" &&
[...path.slice(0, i + 1)].length === 1
) {
auth = { auth = {
parentID: [...path.slice(0, i + 1)].join("/"), parentID: [...path.slice(0, i + 1)].join("/"),
parentName: parentFolder.name, parentName: parentFolder.name,

View File

@@ -1,27 +1,7 @@
<template> <template>
<div class="flex flex-col justify-center"> <div class="flex flex-col flex-1 w-full">
<div
v-if="sharedRequestDetails.loading"
class="flex justify-center items-center py-5"
>
<HoppSmartSpinner />
</div>
<div
v-else-if="E.isLeft(sharedRequestDetails.data) || invalidLink"
class="flex flex-1 flex-col items-center justify-center p-8"
>
<icon-lucide-alert-triangle class="svg-icons mb-2 opacity-75" />
<h1 class="heading text-center">
{{ t("error.invalid_link") }}
</h1>
<p class="mt-2 text-center">
{{ t("error.invalid_embed_link") }}
</p>
</div>
<Embeds <Embeds
v-else-if="tab" v-if="tab"
v-model:modelTab="tab" v-model:modelTab="tab"
:properties="properties" :properties="properties"
:shared-request-i-d="sharedRequestID" :shared-request-i-d="sharedRequestID"
@@ -48,9 +28,6 @@ import {
import { HoppTab } from "~/services/tab" import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document" import { HoppRESTDocument } from "~/helpers/rest/document"
import { applySetting } from "~/newstore/settings" import { applySetting } from "~/newstore/settings"
import { useI18n } from "~/composables/i18n"
const t = useI18n()
const route = useRoute() const route = useRoute()

View File

@@ -244,13 +244,9 @@ export class PersistenceService extends Service {
private setupSettingsPersistence() { private setupSettingsPersistence() {
const settingsKey = "settings" const settingsKey = "settings"
let settingsData = JSON.parse( let settingsData = JSON.parse(
window.localStorage.getItem(settingsKey) ?? "null" window.localStorage.getItem(settingsKey) || "{}"
) )
if (!settingsData) {
settingsData = getDefaultSettings()
}
// Validate data read from localStorage // Validate data read from localStorage
const result = SETTINGS_SCHEMA.safeParse(settingsData) const result = SETTINGS_SCHEMA.safeParse(settingsData)
if (result.success) { if (result.success) {

View File

@@ -36,7 +36,7 @@ const SettingsDefSchema = z.object({
httpUser: z.boolean(), httpUser: z.boolean(),
httpPassword: z.boolean(), httpPassword: z.boolean(),
bearerToken: z.boolean(), bearerToken: z.boolean(),
oauth2Token: z.optional(z.boolean()), oauth2Token: z.boolean(),
}), }),
THEME_COLOR: ThemeColorSchema, THEME_COLOR: ThemeColorSchema,
BG_COLOR: BgColorSchema, BG_COLOR: BgColorSchema,
@@ -103,10 +103,13 @@ export const LOCAL_STATE_SCHEMA = z.union([
.strict(), .strict(),
]) ])
export const SETTINGS_SCHEMA = SettingsDefSchema.extend({ export const SETTINGS_SCHEMA = z.union([
EXTENSIONS_ENABLED: z.optional(z.boolean()), z.object({}).strict(),
PROXY_ENABLED: z.optional(z.boolean()), SettingsDefSchema.extend({
}) EXTENSIONS_ENABLED: z.optional(z.boolean()),
PROXY_ENABLED: z.optional(z.boolean()),
}),
])
export const REST_HISTORY_ENTRY_SCHEMA = z export const REST_HISTORY_ENTRY_SCHEMA = z
.object({ .object({
@@ -205,7 +208,7 @@ export const MQTT_REQUEST_SCHEMA = z.nullable(
z z
.object({ .object({
endpoint: z.string(), endpoint: z.string(),
clientID: z.optional(z.string()), clientID: z.string(),
}) })
.strict() .strict()
) )

View File

@@ -1,7 +1,7 @@
{ {
"name": "@hoppscotch/selfhost-desktop", "name": "@hoppscotch/selfhost-desktop",
"private": true, "private": true,
"version": "2023.12.2", "version": "2023.12.0-1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev:vite": "vite", "dev:vite": "vite",
@@ -76,6 +76,8 @@
"vite-plugin-pwa": "^0.13.1", "vite-plugin-pwa": "^0.13.1",
"vite-plugin-static-copy": "^0.12.0", "vite-plugin-static-copy": "^0.12.0",
"vite-plugin-vue-layouts": "^0.7.0", "vite-plugin-vue-layouts": "^0.7.0",
"vue-tsc": "^1.0.11" "vite-plugin-windicss": "^1.8.8",
"vue-tsc": "^1.0.11",
"windicss": "^3.5.6"
} }
} }

View File

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

View File

@@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "Hoppscotch", "productName": "Hoppscotch",
"version": "23.12.2" "version": "23.12.0-1"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@@ -8,6 +8,7 @@ import VueI18n from "@intlify/vite-plugin-vue-i18n"
import Components from "unplugin-vue-components/vite" import Components from "unplugin-vue-components/vite"
import Icons from "unplugin-icons/vite" import Icons from "unplugin-icons/vite"
import Inspect from "vite-plugin-inspect" import Inspect from "vite-plugin-inspect"
import WindiCSS from "vite-plugin-windicss"
import { VitePWA } from "vite-plugin-pwa" import { VitePWA } from "vite-plugin-pwa"
import Pages from "vite-plugin-pages" import Pages from "vite-plugin-pages"
import Layouts from "vite-plugin-vue-layouts" import Layouts from "vite-plugin-vue-layouts"
@@ -104,6 +105,9 @@ export default defineConfig({
compositionOnly: true, compositionOnly: true,
include: [path.resolve(__dirname, "locales")], include: [path.resolve(__dirname, "locales")],
}), }),
WindiCSS({
root: path.resolve(__dirname, "../hoppscotch-common"),
}),
Components({ Components({
dts: "../hoppscotch-common/src/components.d.ts", dts: "../hoppscotch-common/src/components.d.ts",
dirs: [ dirs: [

View File

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

View File

@@ -114,7 +114,6 @@ async function setInitialUser() {
} else { } else {
setUser(null) setUser(null)
isGettingInitialUser.value = false isGettingInitialUser.value = false
await logout()
} }
return return
@@ -147,26 +146,22 @@ async function setInitialUser() {
} }
async function refreshToken() { async function refreshToken() {
try { const res = await axios.get(
const res = await axios.get( `${import.meta.env.VITE_BACKEND_API_URL}/auth/refresh`,
`${import.meta.env.VITE_BACKEND_API_URL}/auth/refresh`, {
{ withCredentials: true,
withCredentials: true,
}
)
const isSuccessful = res.status === 200
if (isSuccessful) {
authEvents$.next({
event: "token_refresh",
})
} }
)
return isSuccessful const isSuccessful = res.status === 200
} catch (error) {
return false if (isSuccessful) {
authEvents$.next({
event: "token_refresh",
})
} }
return isSuccessful
} }
async function sendMagicLink(email: string) { async function sendMagicLink(email: string) {

View File

@@ -24,7 +24,7 @@
## **Built with** ## **Built with**
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) - [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Tailwind CSS](https://tailwindcss.com) - [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Windi CSS](https://windicss.org)
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) - [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
- [TypeScript](https://www.typescriptlang.org) - [TypeScript](https://www.typescriptlang.org)
- [Vue](https://vuejs.org) - [Vue](https://vuejs.org)

View File

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

View File

@@ -1,9 +1,9 @@
// generated by unplugin-vue-components // generated by unplugin-vue-components
// We suggest you to commit this file into source control // We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core' import '@vue/runtime-core';
export {} export {};
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
@@ -13,6 +13,8 @@ declare module '@vue/runtime-core' {
AppModal: typeof import('./components/app/Modal.vue')['default'] AppModal: typeof import('./components/app/Modal.vue')['default']
AppSidebar: typeof import('./components/app/Sidebar.vue')['default'] AppSidebar: typeof import('./components/app/Sidebar.vue')['default']
AppToast: typeof import('./components/app/Toast.vue')['default'] AppToast: typeof import('./components/app/Toast.vue')['default']
ButtonPrimary: typeof import('./../../hoppscotch-ui/src/components/button/Primary.vue')['default']
ButtonSecondary: typeof import('./../../hoppscotch-ui/src/components/button/Secondary.vue')['default']
DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default'] DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default']
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
@@ -21,7 +23,6 @@ declare module '@vue/runtime-core' {
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'] HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'] HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
@@ -29,13 +30,42 @@ declare module '@vue/runtime-core' {
HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable'] HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable']
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'] IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default'] SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default']
SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default'] SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default']
SettingsReset: typeof import('./components/settings/Reset.vue')['default'] SettingsReset: typeof import('./components/settings/Reset.vue')['default']
SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default'] SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default']
SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default'] SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default']
SmartAnchor: typeof import('./../../hoppscotch-ui/src/components/smart/Anchor.vue')['default']
SmartAutoComplete: typeof import('./../../hoppscotch-ui/src/components/smart/AutoComplete.vue')['default']
SmartCheckbox: typeof import('./../../hoppscotch-ui/src/components/smart/Checkbox.vue')['default']
SmartConfirmModal: typeof import('./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue')['default']
SmartExpand: typeof import('./../../hoppscotch-ui/src/components/smart/Expand.vue')['default']
SmartFileChip: typeof import('./../../hoppscotch-ui/src/components/smart/FileChip.vue')['default']
SmartInput: typeof import('./../../hoppscotch-ui/src/components/smart/Input.vue')['default']
SmartIntersection: typeof import('./../../hoppscotch-ui/src/components/smart/Intersection.vue')['default']
SmartItem: typeof import('./../../hoppscotch-ui/src/components/smart/Item.vue')['default']
SmartLink: typeof import('./../../hoppscotch-ui/src/components/smart/Link.vue')['default']
SmartModal: typeof import('./../../hoppscotch-ui/src/components/smart/Modal.vue')['default']
SmartPicture: typeof import('./../../hoppscotch-ui/src/components/smart/Picture.vue')['default']
SmartPlaceholder: typeof import('./../../hoppscotch-ui/src/components/smart/Placeholder.vue')['default']
SmartProgressRing: typeof import('./../../hoppscotch-ui/src/components/smart/ProgressRing.vue')['default']
SmartRadio: typeof import('./../../hoppscotch-ui/src/components/smart/Radio.vue')['default']
SmartRadioGroup: typeof import('./../../hoppscotch-ui/src/components/smart/RadioGroup.vue')['default']
SmartSelectWrapper: typeof import('./../../hoppscotch-ui/src/components/smart/SelectWrapper.vue')['default']
SmartSlideOver: typeof import('./../../hoppscotch-ui/src/components/smart/SlideOver.vue')['default']
SmartSpinner: typeof import('./../../hoppscotch-ui/src/components/smart/Spinner.vue')['default']
SmartTab: typeof import('./../../hoppscotch-ui/src/components/smart/Tab.vue')['default']
SmartTable: typeof import('./../../hoppscotch-ui/src/components/smart/Table.vue')['default']
SmartTabs: typeof import('./../../hoppscotch-ui/src/components/smart/Tabs.vue')['default']
SmartToggle: typeof import('./../../hoppscotch-ui/src/components/smart/Toggle.vue')['default']
SmartTree: typeof import('./../../hoppscotch-ui/src/components/smart/Tree.vue')['default']
SmartTreeBranch: typeof import('./../../hoppscotch-ui/src/components/smart/TreeBranch.vue')['default']
SmartWindow: typeof import('./../../hoppscotch-ui/src/components/smart/Window.vue')['default']
SmartWindows: typeof import('./../../hoppscotch-ui/src/components/smart/Windows.vue')['default']
TeamsAdd: typeof import('./components/teams/Add.vue')['default'] TeamsAdd: typeof import('./components/teams/Add.vue')['default']
TeamsDetails: typeof import('./components/teams/Details.vue')['default'] TeamsDetails: typeof import('./components/teams/Details.vue')['default']
TeamsInvite: typeof import('./components/teams/Invite.vue')['default'] TeamsInvite: typeof import('./components/teams/Invite.vue')['default']
@@ -46,6 +76,27 @@ declare module '@vue/runtime-core' {
UsersDetails: typeof import('./components/users/Details.vue')['default'] UsersDetails: typeof import('./components/users/Details.vue')['default']
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default'] UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']
UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default'] UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default']
AppHeader: typeof import('./components/app/Header.vue')['default'];
AppLogin: typeof import('./components/app/Login.vue')['default'];
AppLogout: typeof import('./components/app/Logout.vue')['default'];
AppModal: typeof import('./components/app/Modal.vue')['default'];
AppSidebar: typeof import('./components/app/Sidebar.vue')['default'];
AppToast: typeof import('./components/app/Toast.vue')['default'];
DashboardMetricsCard: typeof import('./components/dashboard/MetricsCard.vue')['default'];
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'];
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'];
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'];
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'];
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'];
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'];
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'];
IconLucideInbox: typeof import('~icons/lucide/inbox')['default'];
TeamsAdd: typeof import('./components/teams/Add.vue')['default'];
TeamsDetails: typeof import('./components/teams/Details.vue')['default'];
TeamsInvite: typeof import('./components/teams/Invite.vue')['default'];
TeamsMembers: typeof import('./components/teams/Members.vue')['default'];
TeamsPendingInvites: typeof import('./components/teams/PendingInvites.vue')['default'];
Tippy: typeof import('vue-tippy')['Tippy'];
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default'];
} }
} }

View File

@@ -12,7 +12,7 @@
:class="isOpen ? '' : '-translate-x-full ease-in'" :class="isOpen ? '' : '-translate-x-full ease-in'"
class="sidebar-container transform !md:translate-x-0 ease-out" class="sidebar-container transform !md:translate-x-0 ease-out"
> >
<div :class="isExpanded ? 'w-56' : 'w-full'"> <div :class="isExpanded ? 'w-80' : 'w-full'">
<div class="flex items-center justify-start px-4 my-4"> <div class="flex items-center justify-start px-4 my-4">
<div class="flex items-center"> <div class="flex items-center">
<HoppSmartLink class="flex items-center space-x-4" to="/dashboard"> <HoppSmartLink class="flex items-center space-x-4" to="/dashboard">
@@ -26,6 +26,7 @@
</HoppSmartLink> </HoppSmartLink>
</div> </div>
</div> </div>
<nav class="my-5"> <nav class="my-5">
<HoppSmartLink <HoppSmartLink
v-for="(navigation, index) in primaryNavigations" v-for="(navigation, index) in primaryNavigations"
@@ -38,32 +39,19 @@
:to="navigation.to" :to="navigation.to"
tabindex="0" tabindex="0"
:exact="navigation.exact" :exact="navigation.exact"
class="nav-link"
:class=" :class="
!isExpanded !isExpanded
? 'flex items-center justify-center' ? 'flex items-center justify-center'
: 'flex items-center' : 'flex items-center'
" "
@click="setActiveTab(navigation.label)"
> >
<div <div v-if="navigation.icon">
class="flex p-5 w-full font-bold" <component :is="navigation.icon" class="svg-icons" />
:class="
activeTab === navigation.label
? 'bg-primaryDark text-secondaryDark border-l-2 border-l-emerald-600'
: 'bg-primary hover:bg-primaryLight hover:text-secondaryDark focus-visible:text-secondaryDark focus-visible:bg-primaryLight focus-visible:outline-none'
"
>
<div
v-if="navigation.icon"
class="svg-icons"
:class="isExpanded ? 'mr-3' : 'mx-auto'"
>
<component :is="navigation.icon" />
</div>
<span v-if="isExpanded" class="nav-title">
{{ navigation.label }}
</span>
</div> </div>
<span v-if="isExpanded" class="nav-title">
{{ navigation.label }}
</span>
</HoppSmartLink> </HoppSmartLink>
</nav> </nav>
</div> </div>
@@ -72,27 +60,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, type Component } from 'vue'; import { HoppSmartLink } from '@hoppscotch/ui';
import { useI18n } from '~/composables/i18n';
import { useSidebar } from '~/composables/useSidebar'; import { useSidebar } from '~/composables/useSidebar';
import IconDashboard from '~icons/lucide/layout-dashboard'; import IconDashboard from '~icons/lucide/layout-dashboard';
import IconSettings from '~icons/lucide/settings';
import IconUser from '~icons/lucide/user'; import IconUser from '~icons/lucide/user';
import IconUsers from '~icons/lucide/users'; import IconUsers from '~icons/lucide/users';
import IconSettings from '~icons/lucide/settings';
import { useI18n } from '~/composables/i18n';
const t = useI18n(); const t = useI18n();
const { isOpen, isExpanded } = useSidebar(); const { isOpen, isExpanded } = useSidebar();
type NavigationItem = { const primaryNavigations = [
label: string;
icon: Component;
to: string;
exact: boolean;
};
const primaryNavigations: NavigationItem[] = [
{ {
label: t('metrics.dashboard'), label: t('metrics.dashboard'),
icon: IconDashboard, icon: IconDashboard,
@@ -118,12 +98,6 @@ const primaryNavigations: NavigationItem[] = [
exact: true, exact: true,
}, },
]; ];
const activeTab = ref('Dashboard');
const setActiveTab = (tab: string) => {
activeTab.value = tab;
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -132,4 +106,54 @@ const setActiveTab = (tab: string) => {
@apply transition duration-300; @apply transition duration-300;
@apply flex overflow-y-auto bg-primary border-r border-divider; @apply flex overflow-y-auto bg-primary border-r border-divider;
} }
.nav-link {
@apply relative;
@apply p-4;
@apply flex flex-1;
@apply items-center;
@apply space-x-4;
@apply hover:bg-primaryDark hover:text-secondaryDark;
@apply focus-visible:text-secondaryDark;
@apply after:absolute;
@apply after:inset-x-0;
@apply after:md:inset-x-auto;
@apply after:md:inset-y-0;
@apply after:bottom-0;
@apply after:md:bottom-auto;
@apply after:md:left-0;
@apply after:z-10;
@apply after:h-0.5;
@apply after:md:h-full;
@apply after:w-full;
@apply after:md:w-0.5;
@apply after:content-[''];
@apply focus:after:bg-divider;
.svg-icons {
@apply opacity-75;
}
&.router-link-active {
@apply text-secondaryDark;
@apply bg-primaryLight;
@apply hover:text-secondaryDark;
@apply after:bg-accent;
.svg-icons {
@apply opacity-100;
}
}
&.exact-active-link {
@apply text-secondaryDark;
@apply bg-primaryLight;
@apply hover:text-secondaryDark;
@apply after:bg-accent;
.svg-icons {
@apply opacity-100;
}
}
}
</style> </style>

114
pnpm-lock.yaml generated
View File

@@ -1063,9 +1063,15 @@ importers:
vite-plugin-vue-layouts: vite-plugin-vue-layouts:
specifier: ^0.7.0 specifier: ^0.7.0
version: 0.7.0(vite@4.5.0)(vue-router@4.2.5)(vue@3.3.9) version: 0.7.0(vite@4.5.0)(vue-router@4.2.5)(vue@3.3.9)
vite-plugin-windicss:
specifier: ^1.8.8
version: 1.9.1(vite@4.5.0)
vue-tsc: vue-tsc:
specifier: ^1.0.11 specifier: ^1.0.11
version: 1.8.8(typescript@4.9.5) version: 1.8.8(typescript@4.9.5)
windicss:
specifier: ^3.5.6
version: 3.5.6
packages/hoppscotch-selfhost-web: packages/hoppscotch-selfhost-web:
dependencies: dependencies:
@@ -4231,7 +4237,7 @@ packages:
peerDependencies: peerDependencies:
vue: 3.3.9 vue: 3.3.9
dependencies: dependencies:
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
/@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.0.3): /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.0.3):
resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==} resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==}
@@ -7053,7 +7059,7 @@ packages:
lodash-es: 4.17.21 lodash-es: 4.17.21
path: 0.12.7 path: 0.12.7
vite-plugin-eslint: 1.8.1(eslint@8.55.0)(vite@3.2.4) vite-plugin-eslint: 1.8.1(eslint@8.55.0)(vite@3.2.4)
vue: 3.3.9(typescript@4.9.3) vue: 3.3.9(typescript@4.9.5)
vuedraggable-es: 4.1.1(vue@3.3.9) vuedraggable-es: 4.1.1(vue@3.3.9)
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/composition-api' - '@vue/composition-api'
@@ -7092,7 +7098,7 @@ packages:
peerDependencies: peerDependencies:
vue: 3.3.9 vue: 3.3.9
dependencies: dependencies:
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
/@humanwhocodes/config-array@0.11.10: /@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
@@ -7394,7 +7400,7 @@ packages:
dependencies: dependencies:
'@intlify/bundle-utils': 7.4.0(vue-i18n@9.2.2) '@intlify/bundle-utils': 7.4.0(vue-i18n@9.2.2)
'@intlify/shared': 9.4.1 '@intlify/shared': 9.4.1
'@rollup/pluginutils': 5.0.3(rollup@2.79.1) '@rollup/pluginutils': 5.0.3(rollup@3.29.4)
'@vue/compiler-sfc': 3.3.10 '@vue/compiler-sfc': 3.3.10
debug: 4.3.4(supports-color@9.2.2) debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1 fast-glob: 3.3.1
@@ -8987,6 +8993,7 @@ packages:
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 2.3.1 picomatch: 2.3.1
rollup: 2.79.1 rollup: 2.79.1
dev: true
/@rollup/pluginutils@5.0.3(rollup@3.29.4): /@rollup/pluginutils@5.0.3(rollup@3.29.4):
resolution: {integrity: sha512-hfllNN4a80rwNQ9QCxhxuHCGHMAvabXqxNdaChUSSadMre7t4iEUI6fFAhBOn/eIYTgYVhBv7vCLsAJ4u3lf3g==} resolution: {integrity: sha512-hfllNN4a80rwNQ9QCxhxuHCGHMAvabXqxNdaChUSSadMre7t4iEUI6fFAhBOn/eIYTgYVhBv7vCLsAJ4u3lf3g==}
@@ -9001,7 +9008,6 @@ packages:
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 2.3.1 picomatch: 2.3.1
rollup: 3.29.4 rollup: 3.29.4
dev: true
/@rollup/pluginutils@5.1.0(rollup@2.79.1): /@rollup/pluginutils@5.1.0(rollup@2.79.1):
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
@@ -10619,7 +10625,7 @@ packages:
regenerator-runtime: 0.13.11 regenerator-runtime: 0.13.11
systemjs: 6.14.2 systemjs: 6.14.2
terser: 5.24.0 terser: 5.24.0
vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0) vite: 3.2.4(@types/node@17.0.27)(terser@5.24.0)
/@vitejs/plugin-legacy@2.3.0(terser@5.24.0)(vite@4.5.0): /@vitejs/plugin-legacy@2.3.0(terser@5.24.0)(vite@4.5.0):
resolution: {integrity: sha512-Bh62i0gzQvvT8AeAAb78nOnqSYXypkRmQmOTImdPZ39meHR9e2une3AIFmVo4s1SDmcmJ6qj18Sa/lRc/14KaA==} resolution: {integrity: sha512-Bh62i0gzQvvT8AeAAb78nOnqSYXypkRmQmOTImdPZ39meHR9e2une3AIFmVo4s1SDmcmJ6qj18Sa/lRc/14KaA==}
@@ -11137,7 +11143,7 @@ packages:
'@types/web-bluetooth': 0.0.14 '@types/web-bluetooth': 0.0.14
'@vueuse/metadata': 8.7.5 '@vueuse/metadata': 8.7.5
'@vueuse/shared': 8.7.5(vue@3.3.9) '@vueuse/shared': 8.7.5(vue@3.3.9)
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
vue-demi: 0.14.6(vue@3.3.9) vue-demi: 0.14.6(vue@3.3.9)
/@vueuse/core@9.12.0(vue@3.3.9): /@vueuse/core@9.12.0(vue@3.3.9):
@@ -11196,7 +11202,7 @@ packages:
vue: vue:
optional: true optional: true
dependencies: dependencies:
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
vue-demi: 0.14.6(vue@3.3.9) vue-demi: 0.14.6(vue@3.3.9)
/@vueuse/shared@9.12.0(vue@3.3.9): /@vueuse/shared@9.12.0(vue@3.3.9):
@@ -11416,6 +11422,30 @@ packages:
tslib: 2.6.2 tslib: 2.6.2
dev: true dev: true
/@windicss/config@1.9.1:
resolution: {integrity: sha512-MjutTiS9XIteriwkH9D+que+bILbpulekYzjJGQDg3Sb2H87aOcO30f7N11ZiHF5OYoZn4yJz4lDbB3A6IuXfQ==}
dependencies:
debug: 4.3.4(supports-color@9.2.2)
jiti: 1.19.3
windicss: 3.5.6
transitivePeerDependencies:
- supports-color
dev: true
/@windicss/plugin-utils@1.9.1:
resolution: {integrity: sha512-sz/Z2sxUZIkJ2nVeTmtYTtXhWxe/yTTkM5nqU6eKhP0n6waipTCJJdLvWoZcgzQBbBCL/JLRQd/9BYsBqKuLDQ==}
dependencies:
'@antfu/utils': 0.7.6
'@windicss/config': 1.9.1
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
magic-string: 0.30.4
micromatch: 4.0.5
windicss: 3.5.6
transitivePeerDependencies:
- supports-color
dev: true
/@xtuc/ieee754@1.2.0: /@xtuc/ieee754@1.2.0:
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
dev: true dev: true
@@ -13584,7 +13614,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
rxjs: 7.8.1 rxjs: 7.8.1
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
dev: false dev: false
/dir-glob@3.0.1: /dir-glob@3.0.1:
@@ -24132,7 +24162,7 @@ packages:
dependencies: dependencies:
fast-glob: 3.3.2 fast-glob: 3.3.2
unplugin: 1.5.1 unplugin: 1.5.1
vite: 4.5.0(@types/node@17.0.27)(sass@1.69.5)(terser@5.24.0) vite: 4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.24.0)
dev: true dev: true
/unplugin-icons@0.14.9(@vue/compiler-sfc@3.2.45)(vite@3.2.4): /unplugin-icons@0.14.9(@vue/compiler-sfc@3.2.45)(vite@3.2.4):
@@ -24742,7 +24772,7 @@ packages:
'@types/eslint': 8.44.3 '@types/eslint': 8.44.3
eslint: 8.55.0 eslint: 8.55.0
rollup: 2.79.1 rollup: 2.79.1
vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0) vite: 3.2.4(@types/node@17.0.27)(terser@5.24.0)
/vite-plugin-eslint@1.8.1(eslint@8.55.0)(vite@4.5.0): /vite-plugin-eslint@1.8.1(eslint@8.55.0)(vite@4.5.0):
resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
@@ -24773,7 +24803,7 @@ packages:
peerDependencies: peerDependencies:
vite: '>=2.0.0' vite: '>=2.0.0'
dependencies: dependencies:
vite: 4.5.0(@types/node@17.0.27)(sass@1.69.5)(terser@5.24.0) vite: 4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.24.0)
dev: true dev: true
/vite-plugin-inspect@0.7.38(rollup@2.79.1)(vite@4.5.0): /vite-plugin-inspect@0.7.38(rollup@2.79.1)(vite@4.5.0):
@@ -25036,6 +25066,54 @@ packages:
- supports-color - supports-color
dev: true dev: true
/vite-plugin-windicss@1.9.1(vite@4.5.0):
resolution: {integrity: sha512-CWm1b/tXVCJTbEGn4oB8B7Gev9xDuY9k4E/KiJqDuLYspBUFQyZKPF2mSZ3DfNdojsfqgzxu9ervqvlb9jJ7fw==}
peerDependencies:
vite: ^2.0.1 || ^3.0.0 || ^4.0.0
dependencies:
'@windicss/plugin-utils': 1.9.1
debug: 4.3.4(supports-color@9.2.2)
kolorist: 1.8.0
vite: 4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.24.0)
windicss: 3.5.6
transitivePeerDependencies:
- supports-color
dev: true
/vite@3.2.4(@types/node@17.0.27)(terser@5.24.0):
resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 17.0.27
esbuild: 0.15.15
postcss: 8.4.32
resolve: 1.22.4
rollup: 2.79.1
terser: 5.24.0
optionalDependencies:
fsevents: 2.3.3
/vite@3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0): /vite@3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0):
resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@@ -25458,7 +25536,7 @@ packages:
'@vue/composition-api': '@vue/composition-api':
optional: true optional: true
dependencies: dependencies:
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
/vue-eslint-parser@9.3.1(eslint@8.47.0): /vue-eslint-parser@9.3.1(eslint@8.47.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
@@ -25542,7 +25620,7 @@ packages:
vue: 3.3.9 vue: 3.3.9
dependencies: dependencies:
'@vue/devtools-api': 6.5.1 '@vue/devtools-api': 6.5.1
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
/vue-template-compiler@2.7.14: /vue-template-compiler@2.7.14:
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
@@ -25654,7 +25732,7 @@ packages:
vue: 3.3.9 vue: 3.3.9
dependencies: dependencies:
sortablejs: 1.14.0 sortablejs: 1.14.0
vue: 3.3.9(typescript@5.3.2) vue: 3.3.9(typescript@4.9.5)
/w3c-hr-time@1.0.2: /w3c-hr-time@1.0.2:
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
@@ -25925,6 +26003,12 @@ packages:
dependencies: dependencies:
string-width: 4.2.3 string-width: 4.2.3
/windicss@3.5.6:
resolution: {integrity: sha512-P1mzPEjgFMZLX0ZqfFht4fhV/FX8DTG7ERG1fBLiWvd34pTLVReS5CVsewKn9PApSgXnVfPWwvq+qUsRwpnwFA==}
engines: {node: '>= 12'}
hasBin: true
dev: true
/windows-release@4.0.0: /windows-release@4.0.0:
resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==}
engines: {node: '>=10'} engines: {node: '>=10'}

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine3.19 as base_builder FROM node:18-alpine3.16 as base_builder
WORKDIR /usr/src/app WORKDIR /usr/src/app