Compare commits

...

17 Commits

Author SHA1 Message Date
Andrew Bastin
cd371fc9d4 chore: bump versions to 2023.12.2 2024-01-03 16:58:51 +05:30
Jordi Been
59fef248c0 build: update node alpine version (#3660) 2024-01-03 16:49:51 +05:30
Andrew Bastin
286fcd2bb0 chore: bump selfhost-desktop version to 23.12.1-1 2023-12-24 13:45:41 +05:30
Andrew Bastin
b2d98f7b66 chore: remove windi from selfhost-desktop 2023-12-24 13:21:12 +05:30
James George
c6c220091a feat(common): support importing environments individually (#3691) 2023-12-24 12:12:02 +05:30
Andrew Bastin
8f503479b6 chore: bump package version to 2023.12.1 2023-12-22 20:49:21 +05:30
Mir Arif Hasan
54d8378ccf fix: improve smtp email validation and fix enableAndDisableSSO mutation (#3689)
Co-authored-by: Balu Babu <balub997@gmail.com>
2023-12-22 20:37:15 +05:30
James George
0df194f9c5 fix(cli): environment resolution in the single-entry export format (#3687) 2023-12-22 19:21:33 +05:30
Liyas Thomas
ddf7eb6ad6 chore: minor ui improvements 2023-12-20 18:30:16 +05:30
Nivedin
7db7b9b068 fix: fallback section for embeds if invalid link (#3673) 2023-12-19 18:37:44 +05:30
James George
3d25ef48d1 fix(persistence-service): update schemas found to differ in runtime (#3671) 2023-12-19 18:34:27 +05:30
Mir Arif Hasan
4f138beb8a chore: db migration missing message (#3672) 2023-12-19 18:42:00 +06:00
James George
3d7a76bced chore(common): Gist export flow updates (#3665) 2023-12-19 17:37:35 +05:30
Nivedin
74359ea74e fix: auth-header not inheriting properties (#3668) 2023-12-19 17:04:24 +05:30
Mir Arif Hasan
a694d3f7eb hotfix: added validation on infra config update (#3667)
* feat: added validation on infra config update

* chore: removed async keyword

* fix: feedback
2023-12-19 17:15:46 +06:00
Nivedin
58a9514b67 fix: gql history schema error (#3662) 2023-12-19 16:39:32 +05:30
Akash K
a75bfa9d9e fix: actions not working when sidebar is hidden (#3669) 2023-12-19 16:13:59 +05:30
42 changed files with 457 additions and 249 deletions

View File

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

View File

@@ -28,6 +28,13 @@ export const JSON_INVALID = 'json_invalid';
*/
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
*/
@@ -676,9 +683,16 @@ export const INFRA_CONFIG_RESET_FAILED = 'infra_config/reset_failed' 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
* (InfraConfigService)
*/
export const DATABASE_TABLE_NOT_EXIST =
'Database migration not performed. Please check the FAQ for assistance: https://docs.hoppscotch.io/support/getting-started/faq';
'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations';

View File

@@ -1,10 +1,33 @@
import { AuthProvider } from 'src/auth/helper';
import { AUTH_PROVIDER_NOT_CONFIGURED } from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwErr } from 'src/utils';
export enum ServiceStatus {
ENABLE = 'ENABLE',
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
*
@@ -42,3 +65,42 @@ export function stopApp() {
process.kill(process.pid, 'SIGTERM');
}, 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,11 +15,13 @@ import {
INFRA_CONFIG_NOT_LISTED,
INFRA_CONFIG_RESET_FAILED,
INFRA_CONFIG_UPDATE_FAILED,
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
} from 'src/errors';
import { throwErr, validateEmail, validateSMTPUrl } from 'src/utils';
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils';
import { ConfigService } from '@nestjs/config';
import { ServiceStatus, stopApp } from './helper';
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper';
@Injectable()
export class InfraConfigService implements OnModuleInit {
@@ -69,7 +71,7 @@ export class InfraConfigService implements OnModuleInit {
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: process.env.VITE_ALLOWED_AUTH_PROVIDERS.toLocaleUpperCase(),
value: getConfiguredSSOProviders(),
},
];
@@ -124,10 +126,23 @@ export class InfraConfigService implements OnModuleInit {
cast(dbInfraConfig: DBInfraConfig) {
return <InfraConfig>{
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
* @param name Name of the InfraConfig
@@ -182,6 +197,32 @@ 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
* @param provider Auth Provider to enable or disable
@@ -195,8 +236,14 @@ export class InfraConfigService implements OnModuleInit {
let updatedAuthProviders = allowedAuthProviders;
const infraConfigMap = await this.getInfraConfigsMap();
providerInfo.forEach(({ provider, status }) => {
if (status === ServiceStatus.ENABLE) {
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
if (!isConfigured) {
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
}
updatedAuthProviders.push(provider);
} else if (status === ServiceStatus.DISABLE) {
updatedAuthProviders = updatedAuthProviders.filter(
@@ -286,6 +333,9 @@ export class InfraConfigService implements OnModuleInit {
}
}
/**
* Validate the values of the InfraConfigs
*/
validateEnvValues(
infraConfigs: {
name: InfraConfigEnumForClient | InfraConfigEnum;
@@ -299,9 +349,27 @@ export class InfraConfigService implements OnModuleInit {
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
const isValidEmail = validateEmail(infraConfigs[i].value);
const isValidEmail = validateSMTPEmail(infraConfigs[i].value);
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
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:
break;
}

View File

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

View File

@@ -131,6 +131,28 @@ export const validateEmail = (email: string) => {
).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
* @param url The URL to validate

View File

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

View File

@@ -118,6 +118,15 @@ describe("Test 'hopp test <file> --env <file>' command:", () => {
const { error } = await runCLI(args);
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:", () => {

View File

@@ -0,0 +1,30 @@
{
"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

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

View File

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

View File

@@ -121,7 +121,7 @@
"generate_token": "Generate Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Include in URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "Learn how",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
@@ -295,6 +295,7 @@
"incorrect_email": "Incorrect email",
"invalid_link": "Invalid link",
"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_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.",
@@ -313,10 +314,12 @@
"export": {
"as_json": "Export as JSON",
"create_secret_gist": "Create secret Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist",
"failed": "Something went wrong while exporting",
"gist_created": "Gist created",
"secret_gist_success": "Successfully exported as secret Gist",
"require_github": "Login with GitHub to create secret gist",
"title": "Export"
"title": "Export",
"success": "Successfully exported"
},
"filter": {
"all": "All",

View File

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

View File

@@ -1,15 +1,16 @@
<template>
<Splitpanes
class="smart-splitter"
:rtl="SIDEBAR_ON_LEFT && mdAndLarger"
:class="{
'!flex-row-reverse': SIDEBAR_ON_LEFT && mdAndLarger,
'smart-splitter': SIDEBAR && hasSidebar,
'no-splitter': !(SIDEBAR && hasSidebar),
}"
:horizontal="!mdAndLarger"
@resize="setPaneEvent($event, 'vertical')"
>
<Pane
:size="PANE_MAIN_SIZE"
:size="SIDEBAR && hasSidebar ? PANE_MAIN_SIZE : 100"
min-size="65"
class="flex flex-col !overflow-auto"
>
@@ -36,9 +37,8 @@
</Splitpanes>
</Pane>
<Pane
v-if="SIDEBAR && hasSidebar"
:size="PANE_SIDEBAR_SIZE"
min-size="25"
:size="SIDEBAR && hasSidebar ? PANE_SIDEBAR_SIZE : 0"
:min-size="25"
class="flex flex-col !overflow-auto bg-primaryContrast"
>
<slot name="sidebar" />

View File

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

View File

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

View File

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

View File

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

View File

@@ -124,6 +124,8 @@
E.isRight(pendingInvites.data) &&
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')"
/>
<div

View File

@@ -17,7 +17,7 @@
v-if="props.showCount && props.teamMembers.length > maxMembersSoftLimit"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="remainingSlicedMembers"
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"
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"
tabindex="0"
@click="handleClick()"
>

View File

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

View File

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

View File

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

View File

@@ -1,18 +0,0 @@
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

@@ -0,0 +1,19 @@
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

@@ -1,22 +0,0 @@
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

@@ -1,18 +0,0 @@
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) => {
const parsedContent = safeParseJSON(content)
const parsedContent = safeParseJSON(content, true)
// parse json from the environments string
if (O.isNone(parsedContent)) {

View File

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

View File

@@ -1,7 +1,19 @@
<template>
<div class="flex flex-col flex-1 w-full">
<div class="flex flex-col justify-center">
<div
v-if="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
v-if="tab"
v-else-if="!invalidLink && tab"
v-model:modelTab="tab"
:properties="properties"
:shared-request-i-d="sharedRequestID"
@@ -28,6 +40,9 @@ import {
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { applySetting } from "~/newstore/settings"
import { useI18n } from "~/composables/i18n"
const t = useI18n()
const route = useRoute()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

114
pnpm-lock.yaml generated
View File

@@ -1063,15 +1063,9 @@ importers:
vite-plugin-vue-layouts:
specifier: ^0.7.0
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:
specifier: ^1.0.11
version: 1.8.8(typescript@4.9.5)
windicss:
specifier: ^3.5.6
version: 3.5.6
packages/hoppscotch-selfhost-web:
dependencies:
@@ -4237,7 +4231,7 @@ packages:
peerDependencies:
vue: 3.3.9
dependencies:
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
/@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==}
@@ -7059,7 +7053,7 @@ packages:
lodash-es: 4.17.21
path: 0.12.7
vite-plugin-eslint: 1.8.1(eslint@8.55.0)(vite@3.2.4)
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@4.9.3)
vuedraggable-es: 4.1.1(vue@3.3.9)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -7098,7 +7092,7 @@ packages:
peerDependencies:
vue: 3.3.9
dependencies:
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
@@ -7400,7 +7394,7 @@ packages:
dependencies:
'@intlify/bundle-utils': 7.4.0(vue-i18n@9.2.2)
'@intlify/shared': 9.4.1
'@rollup/pluginutils': 5.0.3(rollup@3.29.4)
'@rollup/pluginutils': 5.0.3(rollup@2.79.1)
'@vue/compiler-sfc': 3.3.10
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
@@ -8993,7 +8987,6 @@ packages:
estree-walker: 2.0.2
picomatch: 2.3.1
rollup: 2.79.1
dev: true
/@rollup/pluginutils@5.0.3(rollup@3.29.4):
resolution: {integrity: sha512-hfllNN4a80rwNQ9QCxhxuHCGHMAvabXqxNdaChUSSadMre7t4iEUI6fFAhBOn/eIYTgYVhBv7vCLsAJ4u3lf3g==}
@@ -9008,6 +9001,7 @@ packages:
estree-walker: 2.0.2
picomatch: 2.3.1
rollup: 3.29.4
dev: true
/@rollup/pluginutils@5.1.0(rollup@2.79.1):
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
@@ -10625,7 +10619,7 @@ packages:
regenerator-runtime: 0.13.11
systemjs: 6.14.2
terser: 5.24.0
vite: 3.2.4(@types/node@17.0.27)(terser@5.24.0)
vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0)
/@vitejs/plugin-legacy@2.3.0(terser@5.24.0)(vite@4.5.0):
resolution: {integrity: sha512-Bh62i0gzQvvT8AeAAb78nOnqSYXypkRmQmOTImdPZ39meHR9e2une3AIFmVo4s1SDmcmJ6qj18Sa/lRc/14KaA==}
@@ -11143,7 +11137,7 @@ packages:
'@types/web-bluetooth': 0.0.14
'@vueuse/metadata': 8.7.5
'@vueuse/shared': 8.7.5(vue@3.3.9)
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
vue-demi: 0.14.6(vue@3.3.9)
/@vueuse/core@9.12.0(vue@3.3.9):
@@ -11202,7 +11196,7 @@ packages:
vue:
optional: true
dependencies:
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
vue-demi: 0.14.6(vue@3.3.9)
/@vueuse/shared@9.12.0(vue@3.3.9):
@@ -11422,30 +11416,6 @@ packages:
tslib: 2.6.2
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:
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
dev: true
@@ -13614,7 +13584,7 @@ packages:
optional: true
dependencies:
rxjs: 7.8.1
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
dev: false
/dir-glob@3.0.1:
@@ -24162,7 +24132,7 @@ packages:
dependencies:
fast-glob: 3.3.2
unplugin: 1.5.1
vite: 4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.24.0)
vite: 4.5.0(@types/node@17.0.27)(sass@1.69.5)(terser@5.24.0)
dev: true
/unplugin-icons@0.14.9(@vue/compiler-sfc@3.2.45)(vite@3.2.4):
@@ -24772,7 +24742,7 @@ packages:
'@types/eslint': 8.44.3
eslint: 8.55.0
rollup: 2.79.1
vite: 3.2.4(@types/node@17.0.27)(terser@5.24.0)
vite: 3.2.4(@types/node@18.18.8)(sass@1.58.0)(terser@5.24.0)
/vite-plugin-eslint@1.8.1(eslint@8.55.0)(vite@4.5.0):
resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
@@ -24803,7 +24773,7 @@ packages:
peerDependencies:
vite: '>=2.0.0'
dependencies:
vite: 4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.24.0)
vite: 4.5.0(@types/node@17.0.27)(sass@1.69.5)(terser@5.24.0)
dev: true
/vite-plugin-inspect@0.7.38(rollup@2.79.1)(vite@4.5.0):
@@ -25066,54 +25036,6 @@ packages:
- supports-color
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):
resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -25536,7 +25458,7 @@ packages:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
/vue-eslint-parser@9.3.1(eslint@8.47.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
@@ -25620,7 +25542,7 @@ packages:
vue: 3.3.9
dependencies:
'@vue/devtools-api': 6.5.1
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
/vue-template-compiler@2.7.14:
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
@@ -25732,7 +25654,7 @@ packages:
vue: 3.3.9
dependencies:
sortablejs: 1.14.0
vue: 3.3.9(typescript@4.9.5)
vue: 3.3.9(typescript@5.3.2)
/w3c-hr-time@1.0.2:
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
@@ -26003,12 +25925,6 @@ packages:
dependencies:
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:
resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==}
engines: {node: '>=10'}

View File

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