Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ee): One-click demo account #616

Merged
merged 14 commits into from
Aug 22, 2024
Prev Previous commit
Next Next commit
fix: one click demo
  • Loading branch information
abouolia committed Aug 19, 2024
commit cca596b4a9e09a6527c1184b7da3fea21bad1df2
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Service, Inject } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '@/api/controllers/BaseController';
import { OneClickDemoApplication } from '@/services/OneClickDemo/OneClickDemoApplication';
import { reset } from 'colorette';
import { body } from 'express-validator';

@Service()
export class OneClickDemoController extends BaseController {
Expand All @@ -16,6 +18,14 @@ export class OneClickDemoController extends BaseController {
const router = Router();

router.post('/one_click', asyncMiddleware(this.oneClickDemo.bind(this)));
router.post(
'/one_click_signin',
[
body('demo_id').exists(),
],
this.validationResult,
asyncMiddleware(this.oneClickSignIn.bind(this))
);

return router;
}
Expand All @@ -38,4 +48,26 @@ export class OneClickDemoController extends BaseController {
next(error);
}
}

/**
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
private async oneClickSignIn(
req: Request,
res: Response,
next: NextFunction
) {
const { demoId } = this.matchedBodyData(req);

try {
const data = await this.oneClickDemoApp.autoSignIn(demoId);

return res.status(200).send(data);
} catch (error) {
next(error);
}
}
}
30 changes: 26 additions & 4 deletions packages/server/src/services/OneClickDemo/CreateOneClickDemo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Inject, Service } from 'typedi';
import { faker } from '@faker-js/faker';
import uniqid from 'uniqid';
import AuthenticationApplication from '../Authentication/AuthApplication';
import OrganizationService from '../Organization/OrganizationService';
import { IAuthSignInPOJO } from '@/interfaces';
import { OneClickDemo } from '@/system/models/OneclickDemo';
import { SystemUser } from '@/system/models';

@Service()
export class CreateOneClickDemo {
Expand All @@ -16,17 +19,21 @@ export class CreateOneClickDemo {
* Creates one-click demo account.
* @returns {Promise<ICreateOneClickDemoPOJO>}
*/
async createOneClickDemo(): Promise<ICreateOneClickDemoPOJO> {
public async createOneClickDemo(): Promise<ICreateOneClickDemoPOJO> {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
const email = faker.internet.email();
const password = '123123123';
const demoId = uniqid();

await this.authApp.signUp({ firstName, lastName, email, password });

//
const signedIn = await this.authApp.signIn(email, password);
const tenantId = signedIn.tenant.id;
const userId = signedIn.user.id;

// Creates a new one-click demo.
await OneClickDemo.query().insert({ key: demoId, tenantId, userId });

const buildJob = await this.organizationService.buildRunJob(
tenantId,
Expand All @@ -40,18 +47,33 @@ export class CreateOneClickDemo {
},
signedIn.user
);
return { email, signedIn, buildJob };
return { email, demoId, signedIn, buildJob };
}

/**
* Sign-in automicatlly using the demo id one creating an account finish.
* @param {string} oneClickDemoId -
*/
async autoSignIn(oneClickDemoId: string) {}
async autoSignIn(oneClickDemoId: string) {
const foundOneclickDemo = await OneClickDemo.query()
.findOne('key', oneClickDemoId)
.throwIfNotFound();

const userId = foundOneclickDemo.userId;
const user = await SystemUser.query().findById(userId);

const email = user.email;
const password = '123123123';

const signedIn = await this.authApp.signIn(email, password);

return signedIn;
}
}

interface ICreateOneClickDemoPOJO {
email: string;
demoId: string;
signedIn: IAuthSignInPOJO;
buildJob: any;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { Inject, Service } from "typedi";
import { CreateOneClickDemo } from "./CreateOneClickDemo";

import { Inject, Service } from 'typedi';
import { CreateOneClickDemo } from './CreateOneClickDemo';

@Service()
export class OneClickDemoApplication {

@Inject()
private createOneClickDemoService: CreateOneClickDemo;


/**
*
* @returns
*/
public createOneClick() {
return this.createOneClickDemoService.createOneClickDemo();
}
}

/**
*
* @param oneClickDemoId
* @returns
*/
public autoSignIn(oneClickDemoId: string) {
return this.createOneClickDemoService.autoSignIn(oneClickDemoId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.createTable('oneclick_demos', (table) => {
table.increments('id');
table.string('key');
table.integer('tenant_id').unsigned();
table.integer('user_id').unsigned();
table.timestamps();
});
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTableIfExists('oneclick_demos');
};
17 changes: 17 additions & 0 deletions packages/server/src/system/models/OneclickDemo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SystemModel from '@/system/models/SystemModel';

export class OneClickDemo extends SystemModel {
/**
* Table name
*/
static get tableName() {
return 'oneclick_demos';
}

/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt'];
}
}
4 changes: 3 additions & 1 deletion packages/webapp/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function AppInsider({ history }) {
<Router history={history}>
<Switch>
<Route path={'/one_click_demo'}>
<OneClickDemoPage />
<EnsureAuthNotAuthenticated>
<OneClickDemoPage />
</EnsureAuthNotAuthenticated>
</Route>
<Route path={'/auth/register/verify'}>
<EnsureAuthenticated>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@


.root {
text-align: center;
display: flex;
height: 100vh;
width: 100%;
}

.inner{
margin: auto;
max-width: 450px;
}

.progressBar{
height: 5px;

:global .bp4-progress-meter{
background-color: rgba(159, 171, 188, 0.8)
}
}

.oneClickBtn {
width: 400px;
margin-top: 3rem;
}

.waitingText{
font-size: 15px;
line-height: 1.54;
color: #5F6B7C;
}
66 changes: 56 additions & 10 deletions packages/webapp/src/containers/OneClickDemo/OneClickDemoPage.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,85 @@
// @ts-nocheck
import { Button, Intent, ProgressBar, Spinner, Text } from '@blueprintjs/core';
import { useEffect, useState } from 'react';
import { Box } from '@/components';
import { useCreateOneClickDemo } from '@/hooks/query/oneclick-demo';
import { Button } from '@blueprintjs/core';
import {
useCreateOneClickDemo,
useOneClickDemoSignin,
} from '@/hooks/query/oneclick-demo';
import { Box, Icon, Stack } from '@/components';
import { useJob } from '@/hooks/query';
import style from './OneClickDemoPage.module.scss';

export default function OneClickDemoPage() {
const { mutateAsync: createOneClickDemo } = useCreateOneClickDemo();
const {
mutateAsync: createOneClickDemo,
isLoading: isCreateOneClickLoading,
} = useCreateOneClickDemo();
const { mutateAsync: oneClickDemoSignIn } = useOneClickDemoSignin();
const [buildJobId, setBuildJobId] = useState<string>('');
const [demoId, setDemoId] = useState<string>('');

// Job done state.
const [isJobDone, setIsJobDone] = useState<boolean>(false);

const {
data: { running, queued, failed, completed },
isFetching: isJobFetching,
} = useJob(buildJobId, {
refetchInterval: 2000,
enabled: !!buildJobId,
enabled: !isJobDone && !!buildJobId,
onSuccess: (res) => {
if (res.completed) {
oneClickDemoSignIn({ demoId }).then((res) => {
debugger;
});
}
},
});

useEffect(() => {
if (completed) {
setIsJobDone(true);
}
}, [completed]);
}, [completed, setIsJobDone]);

const handleCreateAccountBtnClick = () => {
createOneClickDemo({})
.then((res) => {
setBuildJobId(res?.data?.data?.build_job?.job_id)
setBuildJobId(res?.data?.data?.build_job?.job_id);
setDemoId(res?.data?.data?.demo_id);
})
.catch(() => {});
};
const isLoading = running;

return (
<Box>
{running && (<h1>Building...</h1>)}
<Button onClick={handleCreateAccountBtnClick}>One-Click Create</Button>
<Box className={style.root}>
<Box className={style.inner}>
<Stack align={'center'} spacing={40}>
<Icon icon="bigcapital" height={37} width={228} />

{isLoading && (
<Stack align={'center'} spacing={15}>
<ProgressBar stripes value={null} className={style.progressBar} />
<Text className={style.waitingText}>
We're preparing temporary environment for trial, It typically
take few seconds. Do not close or refresh the page.
</Text>
</Stack>
)}
</Stack>

{!isLoading && (
<Button
className={style.oneClickBtn}
intent={Intent.PRIMARY}
onClick={handleCreateAccountBtnClick}
loading={isCreateOneClickLoading}
>
Create Demo Account
</Button>
)}
</Box>
</Box>
);
}
2 changes: 1 addition & 1 deletion packages/webapp/src/hooks/query/authentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
/**
* Saves the response data to cookies.
*/
function setAuthLoginCookies(data) {
export function setAuthLoginCookies(data) {
setCookie('token', data.token);
setCookie('authenticated_user_id', data.user.id);
setCookie('organization_id', data.tenant.organization_id);
Expand Down
Loading
Loading