mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 22:57:59 -04:00
Cleanup migration page logic (#6675)
* move migration logic into organizations * simplify migration input * remove try catch and pray to nedb --------- Co-authored-by: jackkav <jackkav@gmail.com>
This commit is contained in:
@@ -1,15 +1,7 @@
|
||||
import { getAccountId, getCurrentSessionId } from '../../account/session';
|
||||
import { database } from '../../common/database';
|
||||
import * as models from '../../models';
|
||||
import { isOwnerOfOrganization, isPersonalOrganization } from '../../models/organization';
|
||||
import { Organization } from '../../models/organization';
|
||||
import { Project, RemoteProject } from '../../models/project';
|
||||
import { OrganizationsResponse } from '../../ui/routes/organization';
|
||||
import { invariant } from '../../utils/invariant';
|
||||
|
||||
let status: 'idle' | 'pending' | 'error' | 'completed' = 'idle';
|
||||
|
||||
// TODO:
|
||||
// Error handling and return type for errors
|
||||
|
||||
// Migration:
|
||||
// Team ~= Project > Workspaces
|
||||
@@ -18,6 +10,12 @@ let status: 'idle' | 'pending' | 'error' | 'completed' = 'idle';
|
||||
// Organization > TeamProject > Workspaces
|
||||
// In the new API: { _id: 'proj_team_123', remoteId: 'proj_team_123', parentId: 'team_123' }
|
||||
|
||||
// the remote id field previously tracked "team_id"
|
||||
// (remote concept for matching 1:1 with this project) which is now org_id
|
||||
// the _id field previously tracked the "proj_team_id"
|
||||
// which was a wrapper for the team_id prefixing proj_to the above id,
|
||||
// which is now the remoteId for tracking the projects within an org
|
||||
|
||||
export const shouldMigrateProjectUnderOrganization = async () => {
|
||||
const localProjectCount = await database.count<Project>(models.project.type, {
|
||||
remoteId: null,
|
||||
@@ -33,92 +31,35 @@ export const shouldMigrateProjectUnderOrganization = async () => {
|
||||
return localProjectCount > 0 || legacyRemoteProjectCount > 0;
|
||||
};
|
||||
|
||||
export const migrateProjectsIntoOrganization = async () => {
|
||||
if (status !== 'idle' && status !== 'error') {
|
||||
return;
|
||||
export const migrateProjectsIntoOrganization = async ({
|
||||
personalOrganization,
|
||||
}: {
|
||||
personalOrganization: Organization;
|
||||
}) => {
|
||||
// Legacy remote projects without organizations
|
||||
const legacyRemoteProjects = await database.find<RemoteProject>(models.project.type, {
|
||||
remoteId: { $ne: null },
|
||||
parentId: null,
|
||||
});
|
||||
// Legacy remoteId should be orgId and legacy _id should be remoteId
|
||||
for (const remoteProject of legacyRemoteProjects) {
|
||||
await models.project.update(remoteProject, {
|
||||
parentId: remoteProject.remoteId,
|
||||
remoteId: remoteProject._id,
|
||||
});
|
||||
}
|
||||
|
||||
status = 'pending';
|
||||
// Local projects without organizations except scratchpad
|
||||
const localProjects = await database.find<Project>(models.project.type, {
|
||||
remoteId: null,
|
||||
parentId: null,
|
||||
_id: { $ne: models.project.SCRATCHPAD_PROJECT_ID },
|
||||
});
|
||||
|
||||
try {
|
||||
const sessionId = getCurrentSessionId();
|
||||
invariant(sessionId, 'User must be logged in to migrate projects');
|
||||
|
||||
// local projects what if they dont have a parentId?
|
||||
// after migration: all projects have a parentId
|
||||
|
||||
// no more hostage projects
|
||||
// when will we know what org is you have?
|
||||
// already migrated: all things are globes
|
||||
// already migrated with a blank account what happens to previous account data?
|
||||
// go to whatever
|
||||
|
||||
// about to migrate: have one or more projects without parentIds/null
|
||||
// if the project doesn't have remoteId we can throw in the home org
|
||||
// if the project has a remoteId, and is in the org, we know it should have org as a parentId
|
||||
// if the project has a remoteId, and is not in my logged in org
|
||||
// go to the whatever
|
||||
|
||||
// whatever
|
||||
// export all
|
||||
// 1. show a alert describing the state of the orphaned projects and instructing export all and reimport
|
||||
// 2. show a recovery ux to move old workspaces into existing projects
|
||||
// 3. show orphaned projects in the home organization
|
||||
// 4. show disabled orgs in the sidebar from previous account where you can see the data
|
||||
|
||||
// todo
|
||||
// 1. [x] only assign parentIds and migrate old remote logic
|
||||
// 2. count orphaned projects
|
||||
// 3. decide which approach take for orphaned projects
|
||||
// 4. decide if theres no reason to keep migrateCollectionsIntoRemoteProject
|
||||
|
||||
// assign remote project parentIds to new organization structure
|
||||
// the remote id field used to track team_id (remote concept for matching 1:1 with this project) which is now org_id
|
||||
// the _id field used to track the proj_team_id which was a wrapper for the team_id prefixing proj_to the above id,
|
||||
// which is now the remoteId for tracking the projects within an org
|
||||
const legacyRemoteProjects = await database.find<RemoteProject>(models.project.type, {
|
||||
remoteId: { $ne: null },
|
||||
parentId: null,
|
||||
// Assign all local projects to personal organization
|
||||
for (const localProject of localProjects) {
|
||||
await models.project.update(localProject, {
|
||||
parentId: personalOrganization.id,
|
||||
});
|
||||
for (const remoteProject of legacyRemoteProjects) {
|
||||
await models.project.update(remoteProject, {
|
||||
parentId: remoteProject.remoteId,
|
||||
remoteId: remoteProject._id,
|
||||
});
|
||||
}
|
||||
|
||||
// Local projects without organizations except scratchpad
|
||||
const localProjects = await database.find<Project>(models.project.type, {
|
||||
remoteId: null,
|
||||
parentId: null,
|
||||
_id: { $ne: models.project.SCRATCHPAD_PROJECT_ID },
|
||||
});
|
||||
const organizationsResult = await window.main.insomniaFetch<OrganizationsResponse | void>({
|
||||
method: 'GET',
|
||||
path: '/v1/organizations',
|
||||
sessionId,
|
||||
});
|
||||
const accountId = getAccountId();
|
||||
invariant(organizationsResult, 'Failed to fetch organizations');
|
||||
invariant(accountId, 'Failed to get account id');
|
||||
const { organizations } = organizationsResult;
|
||||
const personalOrganization = organizations.filter(isPersonalOrganization)
|
||||
.find(organization =>
|
||||
isOwnerOfOrganization({
|
||||
organization,
|
||||
accountId,
|
||||
}));
|
||||
invariant(personalOrganization, 'Failed to find personal organization');
|
||||
|
||||
for (const localProject of localProjects) {
|
||||
await models.project.update(localProject, {
|
||||
parentId: personalOrganization.id,
|
||||
});
|
||||
}
|
||||
|
||||
status = 'completed';
|
||||
} catch (err) {
|
||||
console.warn('Failed to migrate projects to personal workspace', err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,10 +29,8 @@ import { AppLoadingIndicator } from './components/app-loading-indicator';
|
||||
import Auth from './routes/auth';
|
||||
import Authorize from './routes/auth.authorize';
|
||||
import Login from './routes/auth.login';
|
||||
import { Migrate } from './routes/auth.migrate';
|
||||
import { ErrorRoute } from './routes/error';
|
||||
import Onboarding from './routes/onboarding';
|
||||
import { OnboardingCloudMigration } from './routes/onboarding.cloud-migration';
|
||||
import { shouldOrganizationsRevalidate } from './routes/organization';
|
||||
import Root from './routes/root';
|
||||
import { initializeSentry } from './sentry';
|
||||
@@ -121,12 +119,6 @@ const router = createMemoryRouter(
|
||||
path: 'onboarding/*',
|
||||
element: <Onboarding />,
|
||||
},
|
||||
{
|
||||
path: 'onboarding/cloud-migration',
|
||||
loader: async (...args) => (await import('./routes/onboarding.cloud-migration')).loader(...args),
|
||||
action: async (...args) => (await import('./routes/onboarding.cloud-migration')).action(...args),
|
||||
element: <OnboardingCloudMigration />,
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
children: [
|
||||
@@ -880,7 +872,6 @@ const router = createMemoryRouter(
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
loader: async (...args) => (await import('./routes/auth.login')).loader(...args),
|
||||
action: async (...args) => (await import('./routes/auth.login')).action(...args),
|
||||
element: <Login />,
|
||||
},
|
||||
@@ -893,12 +884,6 @@ const router = createMemoryRouter(
|
||||
action: async (...args) => (await import('./routes/auth.authorize')).action(...args),
|
||||
element: <Authorize />,
|
||||
},
|
||||
{
|
||||
path: 'migrate',
|
||||
loader: async (...args) => (await import('./routes/auth.migrate')).loader(...args),
|
||||
action: async (...args) => (await import('./routes/auth.migrate')).action(...args),
|
||||
element: <Migrate />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2,8 +2,6 @@ import React, { Fragment } from 'react';
|
||||
import { Heading } from 'react-aria-components';
|
||||
import { ActionFunction, redirect, useFetcher, useFetchers, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { isLoggedIn } from '../../account/session';
|
||||
import { shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { invariant } from '../../utils/invariant';
|
||||
import { getLoginUrl, submitAuthCode } from '../auth-session-provider';
|
||||
import { Icon } from '../components/icon';
|
||||
@@ -26,10 +24,6 @@ export const action: ActionFunction = async ({
|
||||
console.log('Login successful');
|
||||
window.localStorage.setItem('hasUserLoggedInBefore', 'true');
|
||||
|
||||
if (isLoggedIn() && await shouldMigrateProjectUnderOrganization()) {
|
||||
throw redirect('/auth/migrate');
|
||||
}
|
||||
|
||||
return redirect('/organization');
|
||||
};
|
||||
|
||||
@@ -131,7 +125,7 @@ const Authorize = () => {
|
||||
}}
|
||||
disabled={isAuthenticating}
|
||||
>
|
||||
<i className={`fa fa-${isAuthenticating ? 'spin' : 'sign-in'}`} aria-hidden="true" />
|
||||
<Icon icon={isAuthenticating ? 'spinner' : 'sign-in'} className={isAuthenticating ? 'animate-spin' : ''} />
|
||||
Log in
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Button, Dialog, DialogTrigger, Heading, Modal, ModalOverlay } from 'react-aria-components';
|
||||
import { ActionFunction, Link, LoaderFunction, redirect, useFetcher, useLoaderData, useNavigate } from 'react-router-dom';
|
||||
import { ActionFunction, Link, redirect, useFetcher, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { getAppWebsiteBaseURL } from '../../common/constants';
|
||||
import { exportAllData } from '../../common/export-all-data';
|
||||
import { shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { SegmentEvent } from '../analytics';
|
||||
import { getLoginUrl } from '../auth-session-provider';
|
||||
import { Icon } from '../components/icon';
|
||||
@@ -34,18 +33,6 @@ const GoogleIcon = (props: React.ReactSVGElement['props']) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface LoaderData {
|
||||
hasProjectsToMigrate: boolean;
|
||||
}
|
||||
|
||||
export const loader: LoaderFunction = async () => {
|
||||
const hasProjectsToMigrate = await shouldMigrateProjectUnderOrganization();
|
||||
|
||||
return {
|
||||
hasProjectsToMigrate,
|
||||
};
|
||||
};
|
||||
|
||||
export const action: ActionFunction = async ({
|
||||
request,
|
||||
}) => {
|
||||
@@ -63,25 +50,17 @@ export const action: ActionFunction = async ({
|
||||
};
|
||||
|
||||
const Login = () => {
|
||||
const data = useLoaderData() as LoaderData;
|
||||
const loginFetcher = useFetcher();
|
||||
const [isMigrationModalOpen, setIsMigrationModalOpen] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState('email');
|
||||
const navigate = useNavigate();
|
||||
const { workspaceCount } = useRootLoaderData();
|
||||
|
||||
const login = (provider: string) => {
|
||||
if (data.hasProjectsToMigrate) {
|
||||
setIsMigrationModalOpen(true);
|
||||
setSelectedProvider(provider);
|
||||
} else {
|
||||
loginFetcher.submit({
|
||||
provider,
|
||||
}, {
|
||||
action: '/auth/login',
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -301,274 +280,6 @@ const Login = () => {
|
||||
</ModalOverlay>
|
||||
</DialogTrigger>
|
||||
</div>
|
||||
<ModalOverlay
|
||||
onOpenChange={setIsMigrationModalOpen}
|
||||
isOpen={isMigrationModalOpen}
|
||||
isDismissable
|
||||
className="w-full h-[--visual-viewport-height] fixed top-0 left-0 flex items-center justify-center bg-black/30"
|
||||
>
|
||||
<Modal className="max-w-4xl w-full rounded-md border border-solid border-[--hl-sm] p-[--padding-lg] max-h-full bg-[--color-bg] text-[--color-font]">
|
||||
<Dialog onClose={() => setIsMigrationModalOpen(false)} className="outline-none">
|
||||
{({ close }) => (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex gap-2 items-center justify-between'>
|
||||
<Heading className='text-2xl'>Continue with cloud synchronization</Heading>
|
||||
<Button
|
||||
className="flex flex-shrink-0 items-center justify-center aspect-square h-6 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
|
||||
onPress={close}
|
||||
>
|
||||
<Icon icon="x" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className='text-[--color-font] flex flex-col gap-4'>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<p>
|
||||
By continuing with creating an account in Insomnia, or logging into your existing one, we will activate the automated cloud synchronization capability which will synchronize all of your data to the cloud in an end-to-end encrypted format (E2EE) for which you will be required to enter your secret passphrase, or create a new one.
|
||||
</p>
|
||||
<ul className='text-left flex flex-col gap-2'>
|
||||
<li><i className="fa fa-check text-emerald-600" /> Your data in the cloud is end-to-end encrypted (E2EE) and secure.</li>
|
||||
<li><i className="fa fa-check text-emerald-600" /> With Git Sync your data can be stored on a third party Git repository.</li>
|
||||
<li><i className="fa fa-check text-emerald-600" /> In Scratch Pad your data is always stored locally.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex justify-center items-center py-6 max-h">
|
||||
<svg
|
||||
height="100px"
|
||||
viewBox="0 0 480 108"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
>
|
||||
<path
|
||||
d="M103.5 8c0-4.139-3.361-7.5-7.5-7.5H8A7.504 7.504 0 00.5 8v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M43.176 31.412h12.5l8.088 4.411 4.412 6.618v21.324h-25V31.412z"
|
||||
fill="#fff"
|
||||
fillOpacity={0.9}
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M66.584 66.583H43.667c-1.146 0-2.127-.408-2.943-1.224-.816-.816-1.224-1.797-1.224-2.942V33.25c0-1.146.408-2.127 1.224-2.943.816-.816 1.797-1.224 2.943-1.224H58.25l12.5 12.5v20.834c0 1.145-.408 2.126-1.224 2.942-.816.816-1.796 1.224-2.942 1.224zM56.167 43.667V33.25h-12.5v29.167h22.917v-18.75H56.167zm-20.833 31.25c-1.146 0-2.127-.408-2.943-1.224-.816-.816-1.224-1.797-1.224-2.943V41.583h4.167V70.75H58.25v4.167H35.334z"
|
||||
fill="url(#_Linear1)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M103.5 8c0-4.139-3.361-7.5-7.5-7.5H8A7.504 7.504 0 00.5 8v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#ab89ed"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M104 51h-1v2h1v-2zm72 2l10 4.773V46.227L176 51v2zm-72 0h73v-2h-73v2z"
|
||||
fill="url(#_Linear2)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M289.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fillOpacity={0.9}
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) matrix(1 0 0 1.39641 274 228.972)"
|
||||
d="M218 48H258V73H218z"
|
||||
/>
|
||||
<path
|
||||
d="M219.373 87.823c-1.753 0-3.253-.63-4.501-1.89-1.248-1.26-1.872-2.774-1.872-4.543V49.224c0-1.77.624-3.284 1.872-4.543 1.248-1.26 2.748-1.89 4.501-1.89h3.186v-6.433c0-4.45 1.553-8.243 4.66-11.38 3.106-3.136 6.864-4.704 11.271-4.704 4.408 0 8.165 1.569 11.272 4.705 3.106 3.136 4.66 6.929 4.66 11.379v6.433h3.186c1.752 0 3.253.63 4.5 1.89 1.248 1.26 1.872 2.774 1.872 4.543V81.39c0 1.77-.624 3.284-1.872 4.544-1.247 1.26-2.748 1.89-4.5 1.89h-38.235v-.001zm0-6.433h38.235V49.224h-38.235V81.39zm19.117-9.65c1.753 0 3.253-.63 4.501-1.89 1.248-1.26 1.872-2.774 1.872-4.543 0-1.769-.624-3.284-1.872-4.543-1.248-1.26-2.748-1.89-4.501-1.89-1.752 0-3.252.63-4.5 1.89-1.248 1.26-1.872 2.774-1.872 4.543 0 1.77.624 3.284 1.872 4.544 1.248 1.26 2.748 1.89 4.5 1.89v-.001zm-9.559-28.95h19.118v-6.432c0-2.681-.929-4.96-2.788-6.836-1.859-1.876-4.116-2.814-6.771-2.814-2.655 0-4.912.938-6.771 2.814-1.858 1.877-2.788 4.155-2.788 6.836v6.433-.001z"
|
||||
fill="url(#_Linear3)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M289.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#5d27c9"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M300 51l-10-4.773v11.547L300 53v-2zm72 1l-10-5.773v11.547L372 52zm-73 1h64v-2h-64v2z"
|
||||
fill="url(#_Linear4)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M475.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M430.287 69.949v-4.867h8.796a9.02 9.02 0 004.156-1.011 8.702 8.702 0 003.169-2.792 8.337 8.337 0 001.43-3.911 8.268 8.268 0 00-.647-4.102 8.542 8.542 0 00-2.572-3.318 8.924 8.924 0 00-3.887-1.748 9.074 9.074 0 00-4.278.238 8.84 8.84 0 00-3.654 2.166v-.122c0-3.872-1.589-7.585-4.417-10.323-2.828-2.738-6.663-4.277-10.662-4.277-4 0-7.835 1.539-10.663 4.277-2.828 2.738-4.416 6.451-4.416 10.323v.017h-5.027v-.017c-.004-4.542 1.634-8.943 4.628-12.44 2.995-3.497 7.158-5.87 11.769-6.708a20.682 20.682 0 0113.48 2.12c4.101 2.208 7.274 5.734 8.972 9.97a14.276 14.276 0 015.713.1 14.036 14.036 0 015.183 2.329 13.519 13.519 0 013.767 4.16 13.086 13.086 0 011.708 5.28 12.98 12.98 0 01-.644 5.496 13.287 13.287 0 01-2.885 4.775 13.827 13.827 0 01-4.633 3.238 14.228 14.228 0 01-5.59 1.147h-8.796z"
|
||||
fill="url(#_Linear5)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M432.795 61.154c0 10.339-8.508 18.846-18.846 18.846-10.339 0-18.846-8.507-18.846-18.846s8.507-18.846 18.846-18.846c10.338 0 18.846 8.507 18.846 18.846z"
|
||||
fill="#1e1e1e"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M429.532 61.782c0 8.546-7.037 15.583-15.583 15.583s-15.584-7.037-15.584-15.583S405.403 46.2 413.949 46.2s15.583 7.037 15.583 15.583v-.001z"
|
||||
fill="#fff"
|
||||
fillRule="nonzero"
|
||||
stroke="#5849be"
|
||||
strokeWidth="1.5px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M411.027 51.29c.93-.258 1.91-.397 2.921-.397 6.01 0 10.889 4.88 10.889 10.89 0 6.009-4.879 10.888-10.889 10.888-6.009 0-10.888-4.88-10.888-10.889 0-1.011.138-1.991.396-2.92a5.446 5.446 0 004.489 2.362c2.987-.002 5.444-2.459 5.445-5.446a5.443 5.443 0 00-2.363-4.488z"
|
||||
fill="#4000bf"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M475.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#c502bd"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="_Linear1"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(29.74 -39.219 73.242) scale(62.7833)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear2"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(-180 93 26) scale(82)"
|
||||
>
|
||||
<stop offset={0} stopColor="#5922c7" />
|
||||
<stop offset={1} stopColor="#aa89ed" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear3"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(83.3193) rotate(26.531 .761 5.544)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear4"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(-180 186 26) scale(82)"
|
||||
>
|
||||
<stop offset={0} stopColor="#c502bd" />
|
||||
<stop offset={1} stopColor="#602bca" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear5"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(43.196 159.635 517.692) scale(73.6195)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div className='flex gap-2 items-center justify-between'>
|
||||
<div>
|
||||
<p
|
||||
className='text-base'
|
||||
style={{
|
||||
color: 'rgba(var(--color-font-rgb), 0.8)',
|
||||
}}
|
||||
>
|
||||
While not needed, you can <Button
|
||||
onPress={async () => {
|
||||
const { filePaths, canceled } = await window.dialog.showOpenDialog({
|
||||
properties: ['openDirectory', 'createDirectory', 'promptToCreate'],
|
||||
buttonLabel: 'Select',
|
||||
title: 'Export All Insomnia Data',
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [dirPath] = filePaths;
|
||||
|
||||
try {
|
||||
dirPath && await exportAllData({
|
||||
dirPath,
|
||||
});
|
||||
} catch (e) {
|
||||
showAlert({
|
||||
title: 'Export Failed',
|
||||
message: 'An error occurred while exporting data. Please try again.',
|
||||
});
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
showAlert({
|
||||
title: 'Export Complete',
|
||||
message: 'All your data have been successfully exported',
|
||||
});
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.exportAllCollections,
|
||||
});
|
||||
}}
|
||||
className='focus:text-[--color-font] hover:text-[--color-font] font-bold transition-colors'
|
||||
> export your data </Button> for portability. {`(${workspaceCount} files)`}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onPress={() => {
|
||||
loginFetcher.submit({
|
||||
provider: selectedProvider,
|
||||
}, {
|
||||
action: '/auth/login',
|
||||
method: 'POST',
|
||||
});
|
||||
}}
|
||||
className='hover:no-underline font-bold bg-[#4000BF] text-sm hover:bg-opacity-90 py-2 px-3 text-[--color-font] transition-colors rounded-sm'
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Dialog>
|
||||
</Modal>
|
||||
</ModalOverlay>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button, Heading } from 'react-aria-components';
|
||||
import { ActionFunction, LoaderFunction, redirect, useFetcher } from 'react-router-dom';
|
||||
|
||||
import { getCurrentSessionId, logout } from '../../account/session';
|
||||
import { migrateProjectsIntoOrganization, shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { Icon } from '../components/icon';
|
||||
|
||||
export const loader: LoaderFunction = async () => {
|
||||
if (!shouldMigrateProjectUnderOrganization()) {
|
||||
return redirect('/organization');
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
interface MigrationActionData {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const action: ActionFunction = async () => {
|
||||
const sessionId = getCurrentSessionId();
|
||||
if (!sessionId) {
|
||||
await logout();
|
||||
return redirect('/auth/login');
|
||||
}
|
||||
|
||||
try {
|
||||
await migrateProjectsIntoOrganization();
|
||||
return redirect('/organization');
|
||||
} catch (err) {
|
||||
return {
|
||||
error: err instanceof Error ? err.message : 'Error while migrating data.',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const Migrate = () => {
|
||||
const migrationFetcher = useFetcher<MigrationActionData>();
|
||||
const logoutFetcher = useFetcher();
|
||||
|
||||
useEffect(() => {
|
||||
if (migrationFetcher.state === 'idle' && !migrationFetcher.data) {
|
||||
migrationFetcher.submit({}, {
|
||||
action: '/auth/migrate',
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
}, [migrationFetcher]);
|
||||
|
||||
const isMigrating = migrationFetcher.state !== 'idle';
|
||||
const error = migrationFetcher.data?.error;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[--padding-md] text-[--color-font]">
|
||||
<Heading className="text-2xl font-bold text-center px-3">
|
||||
Initializing Organizations
|
||||
</Heading>
|
||||
{isMigrating && (
|
||||
<div className="flex flex-col gap-3 rounded-md bg-[--hl-sm] p-[--padding-md]">
|
||||
<Heading className="text-lg flex items-center p-8 gap-8">
|
||||
<Icon icon="spinner" className="fa-spin" />
|
||||
<span>Fetching your organizations...</span>
|
||||
</Heading>
|
||||
</div>
|
||||
)}
|
||||
{error && !isMigrating && (
|
||||
<div className="flex flex-col gap-3 rounded-md bg-[--hl-sm] p-[--padding-md]">
|
||||
<Heading className="text-lg flex items-center p-8 gap-8">
|
||||
<Icon icon="exclamation-circle" />
|
||||
<span>{error}</span>
|
||||
</Heading>
|
||||
<Button
|
||||
className="px-4 py-1 font-bold flex flex-1 items-center justify-center gap-2 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
|
||||
onPress={() => {
|
||||
migrationFetcher.submit({}, {
|
||||
action: '/auth/migrate',
|
||||
method: 'post',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span>Try again</span>
|
||||
</Button>
|
||||
<Button
|
||||
className="px-4 py-1 font-bold flex flex-1 items-center justify-center gap-2 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
|
||||
onPress={() => {
|
||||
logoutFetcher.submit({}, {
|
||||
action: '/auth/logout',
|
||||
method: 'POST',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span>Log out</span>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,281 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
import { ActionFunction, LoaderFunction, redirect, useFetcher } from 'react-router-dom';
|
||||
|
||||
import { logout } from '../../account/session';
|
||||
import { exportAllData } from '../../common/export-all-data';
|
||||
import { shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { SegmentEvent } from '../analytics';
|
||||
import { InsomniaLogo } from '../components/insomnia-icon';
|
||||
import { showAlert } from '../components/modals';
|
||||
import { TrailLinesContainer } from '../components/trail-lines-container';
|
||||
import { useRootLoaderData } from './root';
|
||||
|
||||
export const action: ActionFunction = async () => {
|
||||
await logout();
|
||||
return redirect('/auth/login');
|
||||
};
|
||||
|
||||
export const loader: LoaderFunction = async () => {
|
||||
if (await shouldMigrateProjectUnderOrganization()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await logout();
|
||||
return redirect('/auth/login');
|
||||
};
|
||||
|
||||
export const OnboardingCloudMigration = () => {
|
||||
const { Form, state } = useFetcher();
|
||||
const { workspaceCount } = useRootLoaderData();
|
||||
return (
|
||||
<div className='relative h-full w-full text-left text-base flex bg-[--color-bg]'>
|
||||
<TrailLinesContainer>
|
||||
<div
|
||||
className='flex justify-center items-center flex-col h-full w-[540px] min-h-[min(450px,90%)]'
|
||||
>
|
||||
<div
|
||||
className='flex flex-col gap-[var(--padding-sm)] items-center justify-center p-[--padding-lg] pt-12 w-full h-full bg-[--hl-xs] rounded-[var(--radius-md)] border-solid border border-[--hl-sm] relative'
|
||||
>
|
||||
<InsomniaLogo
|
||||
className='transform translate-x-[-50%] translate-y-[-50%] absolute top-0 left-1/2 w-16 h-16'
|
||||
/>
|
||||
<div className='text-[--color-font] flex flex-col gap-4'>
|
||||
<h1 className='text-xl text-center'>Activating cloud synchronization</h1>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<p>
|
||||
We have detected that you have previously created data with Insomnia, which will be automatically synchronized to the cloud after logging-in or creating a new account.
|
||||
</p>
|
||||
<ul className='text-left flex flex-col gap-2'>
|
||||
<li><i className="fa fa-check text-emerald-600" /> Your data in the cloud is end-to-end encrypted (E2EE) and secure.</li>
|
||||
<li><i className="fa fa-check text-emerald-600" /> With Git Sync your data can be stored on a third party Git repository.</li>
|
||||
<li><i className="fa fa-check text-emerald-600" /> In Scratch Pad your data is always stored locally.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex justify-center items-center py-6">
|
||||
<svg
|
||||
viewBox="0 0 480 108"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
>
|
||||
<path
|
||||
d="M103.5 8c0-4.139-3.361-7.5-7.5-7.5H8A7.504 7.504 0 00.5 8v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M43.176 31.412h12.5l8.088 4.411 4.412 6.618v21.324h-25V31.412z"
|
||||
fill="#fff"
|
||||
fillOpacity={0.9}
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M66.584 66.583H43.667c-1.146 0-2.127-.408-2.943-1.224-.816-.816-1.224-1.797-1.224-2.942V33.25c0-1.146.408-2.127 1.224-2.943.816-.816 1.797-1.224 2.943-1.224H58.25l12.5 12.5v20.834c0 1.145-.408 2.126-1.224 2.942-.816.816-1.796 1.224-2.942 1.224zM56.167 43.667V33.25h-12.5v29.167h22.917v-18.75H56.167zm-20.833 31.25c-1.146 0-2.127-.408-2.943-1.224-.816-.816-1.224-1.797-1.224-2.943V41.583h4.167V70.75H58.25v4.167H35.334z"
|
||||
fill="url(#_Linear1)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M103.5 8c0-4.139-3.361-7.5-7.5-7.5H8A7.504 7.504 0 00.5 8v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#ab89ed"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M104 51h-1v2h1v-2zm72 2l10 4.773V46.227L176 51v2zm-72 0h73v-2h-73v2z"
|
||||
fill="url(#_Linear2)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M289.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fillOpacity={0.9}
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) matrix(1 0 0 1.39641 274 228.972)"
|
||||
d="M218 48H258V73H218z"
|
||||
/>
|
||||
<path
|
||||
d="M219.373 87.823c-1.753 0-3.253-.63-4.501-1.89-1.248-1.26-1.872-2.774-1.872-4.543V49.224c0-1.77.624-3.284 1.872-4.543 1.248-1.26 2.748-1.89 4.501-1.89h3.186v-6.433c0-4.45 1.553-8.243 4.66-11.38 3.106-3.136 6.864-4.704 11.271-4.704 4.408 0 8.165 1.569 11.272 4.705 3.106 3.136 4.66 6.929 4.66 11.379v6.433h3.186c1.752 0 3.253.63 4.5 1.89 1.248 1.26 1.872 2.774 1.872 4.543V81.39c0 1.77-.624 3.284-1.872 4.544-1.247 1.26-2.748 1.89-4.5 1.89h-38.235v-.001zm0-6.433h38.235V49.224h-38.235V81.39zm19.117-9.65c1.753 0 3.253-.63 4.501-1.89 1.248-1.26 1.872-2.774 1.872-4.543 0-1.769-.624-3.284-1.872-4.543-1.248-1.26-2.748-1.89-4.501-1.89-1.752 0-3.252.63-4.5 1.89-1.248 1.26-1.872 2.774-1.872 4.543 0 1.77.624 3.284 1.872 4.544 1.248 1.26 2.748 1.89 4.5 1.89v-.001zm-9.559-28.95h19.118v-6.432c0-2.681-.929-4.96-2.788-6.836-1.859-1.876-4.116-2.814-6.771-2.814-2.655 0-4.912.938-6.771 2.814-1.858 1.877-2.788 4.155-2.788 6.836v6.433-.001z"
|
||||
fill="url(#_Linear3)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M289.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#5d27c9"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M300 51l-10-4.773v11.547L300 53v-2zm72 1l-10-5.773v11.547L372 52zm-73 1h64v-2h-64v2z"
|
||||
fill="url(#_Linear4)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M475.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="#1f1f1f"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M430.287 69.949v-4.867h8.796a9.02 9.02 0 004.156-1.011 8.702 8.702 0 003.169-2.792 8.337 8.337 0 001.43-3.911 8.268 8.268 0 00-.647-4.102 8.542 8.542 0 00-2.572-3.318 8.924 8.924 0 00-3.887-1.748 9.074 9.074 0 00-4.278.238 8.84 8.84 0 00-3.654 2.166v-.122c0-3.872-1.589-7.585-4.417-10.323-2.828-2.738-6.663-4.277-10.662-4.277-4 0-7.835 1.539-10.663 4.277-2.828 2.738-4.416 6.451-4.416 10.323v.017h-5.027v-.017c-.004-4.542 1.634-8.943 4.628-12.44 2.995-3.497 7.158-5.87 11.769-6.708a20.682 20.682 0 0113.48 2.12c4.101 2.208 7.274 5.734 8.972 9.97a14.276 14.276 0 015.713.1 14.036 14.036 0 015.183 2.329 13.519 13.519 0 013.767 4.16 13.086 13.086 0 011.708 5.28 12.98 12.98 0 01-.644 5.496 13.287 13.287 0 01-2.885 4.775 13.827 13.827 0 01-4.633 3.238 14.228 14.228 0 01-5.59 1.147h-8.796z"
|
||||
fill="url(#_Linear5)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M432.795 61.154c0 10.339-8.508 18.846-18.846 18.846-10.339 0-18.846-8.507-18.846-18.846s8.507-18.846 18.846-18.846c10.338 0 18.846 8.507 18.846 18.846z"
|
||||
fill="#1e1e1e"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M429.532 61.782c0 8.546-7.037 15.583-15.583 15.583s-15.584-7.037-15.584-15.583S405.403 46.2 413.949 46.2s15.583 7.037 15.583 15.583v-.001z"
|
||||
fill="#fff"
|
||||
fillRule="nonzero"
|
||||
stroke="#5849be"
|
||||
strokeWidth="1.5px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M411.027 51.29c.93-.258 1.91-.397 2.921-.397 6.01 0 10.889 4.88 10.889 10.89 0 6.009-4.879 10.888-10.889 10.888-6.009 0-10.888-4.88-10.888-10.889 0-1.011.138-1.991.396-2.92a5.446 5.446 0 004.489 2.362c2.987-.002 5.444-2.459 5.445-5.446a5.443 5.443 0 00-2.363-4.488z"
|
||||
fill="#4000bf"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<path
|
||||
d="M475.5 8c0-4.139-3.361-7.5-7.5-7.5h-88a7.504 7.504 0 00-7.5 7.5v88c0 4.139 3.361 7.5 7.5 7.5h88c4.139 0 7.5-3.361 7.5-7.5V8z"
|
||||
fill="none"
|
||||
stroke="#c502bd"
|
||||
strokeWidth="1px"
|
||||
transform="translate(-272 -246) translate(274 248)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="_Linear1"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(29.74 -39.219 73.242) scale(62.7833)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear2"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(-180 93 26) scale(82)"
|
||||
>
|
||||
<stop offset={0} stopColor="#5922c7" />
|
||||
<stop offset={1} stopColor="#aa89ed" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear3"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(83.3193) rotate(26.531 .761 5.544)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear4"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(-180 186 26) scale(82)"
|
||||
>
|
||||
<stop offset={0} stopColor="#c502bd" />
|
||||
<stop offset={1} stopColor="#602bca" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="_Linear5"
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={1}
|
||||
y2={0}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(43.196 159.635 517.692) scale(73.6195)"
|
||||
>
|
||||
<stop offset={0} stopColor="#4000bf" />
|
||||
<stop offset={1} stopColor="#b49ce3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<Form method='POST' className='gap-2 flex justify-between items-center'>
|
||||
<div>
|
||||
<p
|
||||
className='text-base'
|
||||
style={{
|
||||
color: 'rgba(var(--color-font-rgb), 0.8)',
|
||||
}}
|
||||
>
|
||||
While not needed, you can <Button
|
||||
onPress={async () => {
|
||||
const { filePaths, canceled } = await window.dialog.showOpenDialog({
|
||||
properties: ['openDirectory', 'createDirectory', 'promptToCreate'],
|
||||
buttonLabel: 'Select',
|
||||
title: 'Export All Insomnia Data',
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [dirPath] = filePaths;
|
||||
|
||||
try {
|
||||
dirPath && await exportAllData({
|
||||
dirPath,
|
||||
});
|
||||
} catch (e) {
|
||||
showAlert({
|
||||
title: 'Export Failed',
|
||||
message: 'An error occurred while exporting data. Please try again.',
|
||||
});
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
showAlert({
|
||||
title: 'Export Complete',
|
||||
message: 'All your data have been successfully exported',
|
||||
});
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.exportAllCollections,
|
||||
});
|
||||
}}
|
||||
className='focus:text-[--color-font] hover:text-[--color-font] font-bold transition-colors'
|
||||
> export your data </Button> for portability. {`(${workspaceCount} files)`}
|
||||
</p>
|
||||
</div>
|
||||
<button disabled={state !== 'idle'} className='hover:no-underline font-bold bg-[#4000BF] text-sm hover:bg-opacity-90 py-2 px-3 text-[--color-font] transition-colors rounded-sm'>
|
||||
Continue
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TrailLinesContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -203,7 +203,7 @@ const Onboarding = () => {
|
||||
)}
|
||||
<Link
|
||||
className="hover:no-underline bg-[#4000BF] text-sm hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font] transition-colors rounded-sm"
|
||||
to="/onboarding/cloud-migration"
|
||||
to="/auth/login"
|
||||
onClick={() => window.localStorage.setItem('hasSeenOnboarding', 'true')}
|
||||
>
|
||||
Continue
|
||||
|
||||
@@ -35,7 +35,7 @@ import { isOwnerOfOrganization, isPersonalOrganization, isScratchpadOrganization
|
||||
import { isDesign, isScratchpad } from '../../models/workspace';
|
||||
import FileSystemDriver from '../../sync/store/drivers/file-system-driver';
|
||||
import { MergeConflict } from '../../sync/types';
|
||||
import { shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { migrateProjectsIntoOrganization, shouldMigrateProjectUnderOrganization } from '../../sync/vcs/migrate-projects-into-organization';
|
||||
import { getVCS, initVCS } from '../../sync/vcs/vcs';
|
||||
import { invariant } from '../../utils/invariant';
|
||||
import { SegmentEvent } from '../analytics';
|
||||
@@ -135,13 +135,6 @@ function sortOrganizations(accountId: string, organizations: Organization[]): Or
|
||||
export const indexLoader: LoaderFunction = async () => {
|
||||
const sessionId = getCurrentSessionId();
|
||||
if (sessionId) {
|
||||
// Check if there are any migrations to run before loading organizations.
|
||||
// If there are migrations, we need to log the user out and redirect them to the login page
|
||||
if (await shouldMigrateProjectUnderOrganization()) {
|
||||
await session.logout();
|
||||
return redirect('/auth/login');
|
||||
}
|
||||
|
||||
try {
|
||||
let vcs = getVCS();
|
||||
if (!vcs) {
|
||||
@@ -190,13 +183,18 @@ export const indexLoader: LoaderFunction = async () => {
|
||||
organizationsData.organizations = sortOrganizations(accountId, organizations);
|
||||
organizationsData.user = user;
|
||||
organizationsData.currentPlan = currentPlan;
|
||||
|
||||
const personalOrganization = organizations.filter(isPersonalOrganization)
|
||||
.find(organization =>
|
||||
isOwnerOfOrganization({
|
||||
organization,
|
||||
accountId,
|
||||
}));
|
||||
invariant(personalOrganization, 'Failed to find personal organization your account appears to be in an invalid state. Please contact support if this is a recurring issue.');
|
||||
if (await shouldMigrateProjectUnderOrganization()) {
|
||||
await migrateProjectsIntoOrganization({
|
||||
personalOrganization,
|
||||
});
|
||||
}
|
||||
|
||||
if (personalOrganization) {
|
||||
return redirect(`/organization/${personalOrganization.id}`);
|
||||
|
||||
Reference in New Issue
Block a user