From 194d1b4d9e003766fbdf28257b54bb2d891ea938 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Tue, 17 Aug 2021 14:35:41 +0800 Subject: [PATCH 1/4] fix(tracker-assist):3.0.3 hack to enable audio from iframe --- tracker/tracker-assist/package.json | 2 +- tracker/tracker-assist/src/CallWindow.ts | 14 ++++++++++-- tracker/tracker-assist/src/index.ts | 28 ++++++++++++------------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/tracker/tracker-assist/package.json b/tracker/tracker-assist/package.json index 6064782c2..42f0358f0 100644 --- a/tracker/tracker-assist/package.json +++ b/tracker/tracker-assist/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-assist", "description": "Tracker plugin for screen assistance through the WebRTC", - "version": "3.0.2", + "version": "3.0.3", "keywords": [ "WebRTC", "assistance", diff --git a/tracker/tracker-assist/src/CallWindow.ts b/tracker/tracker-assist/src/CallWindow.ts index 279daeb81..860753aa7 100644 --- a/tracker/tracker-assist/src/CallWindow.ts +++ b/tracker/tracker-assist/src/CallWindow.ts @@ -101,15 +101,22 @@ export default class CallWindow { } + private aRemote: HTMLAudioElement | null = null; private localStream: MediaStream | null = null; private remoteStream: MediaStream | null = null; private setLocalVideoStream: (MediaStream) => void = () => {}; private videoRequested: boolean = true; // TODO: green camera light private _trySetStreams() { - if (this.vRemote && this.remoteStream) { + if (this.vRemote && !this.vRemote.srcObject && this.remoteStream) { this.vRemote.srcObject = this.remoteStream; + // Hack for audio (doesen't work in iframe because of some magical reasons) + this.aRemote = document.createElement("audio"); + this.aRemote.autoplay = true; + this.aRemote.style.display = "none" + this.aRemote.srcObject = this.remoteStream; + document.body.appendChild(this.aRemote) } - if (this.vLocal && this.localStream) { + if (this.vLocal && !this.vLocal.srcObject && this.localStream) { this.vLocal.srcObject = this.localStream; } } @@ -195,6 +202,9 @@ export default class CallWindow { if (this.iframe.parentElement) { document.body.removeChild(this.iframe); } + if (this.aRemote && this.aRemote.parentElement) { + document.body.removeChild(this.aRemote); + } } } \ No newline at end of file diff --git a/tracker/tracker-assist/src/index.ts b/tracker/tracker-assist/src/index.ts index 273907687..3ec90b141 100644 --- a/tracker/tracker-assist/src/index.ts +++ b/tracker/tracker-assist/src/index.ts @@ -164,20 +164,20 @@ export default function(opts: Partial = {}) { }); call.on('stream', function(rStream) { callUI.setRemoteStream(rStream); - dataConn.on('data', (data: any) => { - if (data === "call_end") { - //console.log('receiving callend on call') - onCallEnd(); - return; - } - if (data && typeof data.name === 'string') { - //console.log("name",data) - callUI.setAssistentName(data.name); - } - if (data && typeof data.x === 'number' && typeof data.y === 'number') { - mouse.move(data); - } - }); + }); + dataConn.on('data', (data: any) => { + if (data === "call_end") { + //console.log('receiving callend on call') + onCallEnd(); + return; + } + if (data && typeof data.name === 'string') { + //console.log("name",data) + callUI.setAssistentName(data.name); + } + if (data && typeof data.x === 'number' && typeof data.y === 'number') { + mouse.move(data); + } }); } From b1dbd977091761eb5ab7f770d0ecb29a64b4e9df Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 19 Aug 2021 13:23:24 +0530 Subject: [PATCH 2/4] fix(ui) - bookmarking --- .../components/shared/Bookmark/Bookmark.tsx | 26 +++++++++++++++++++ .../components/shared/Bookmark/bookmark.css | 14 ++++++++++ .../app/components/shared/Bookmark/index.js | 1 + .../shared/SessionItem/SessionItem.js | 21 +++------------ .../shared/SessionItem/sessionItem.css | 24 ++++++----------- frontend/app/duck/sessions.js | 17 +++++++----- 6 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 frontend/app/components/shared/Bookmark/Bookmark.tsx create mode 100644 frontend/app/components/shared/Bookmark/bookmark.css create mode 100644 frontend/app/components/shared/Bookmark/index.js diff --git a/frontend/app/components/shared/Bookmark/Bookmark.tsx b/frontend/app/components/shared/Bookmark/Bookmark.tsx new file mode 100644 index 000000000..ed75e6c7e --- /dev/null +++ b/frontend/app/components/shared/Bookmark/Bookmark.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react' +import stl from './bookmark.css' +import { Icon } from 'UI' +import { toggleFavorite } from 'Duck/sessions' +import { connect } from 'react-redux' +// import Session from 'Types/session'; + +interface Props { + toggleFavorite: (session) => void, + favorite: Boolean, + sessionId: any +} +function Bookmark({ toggleFavorite, sessionId, favorite } : Props ) { + + return ( +
toggleFavorite(sessionId) } + data-favourite={ favorite } + > + +
+ ) +} + +export default connect(null, { toggleFavorite })(Bookmark) diff --git a/frontend/app/components/shared/Bookmark/bookmark.css b/frontend/app/components/shared/Bookmark/bookmark.css new file mode 100644 index 000000000..9e44e8115 --- /dev/null +++ b/frontend/app/components/shared/Bookmark/bookmark.css @@ -0,0 +1,14 @@ +.favoriteWrapper { + cursor: pointer; + display: flex; + align-items: center; + /* opacity: 0; */ + margin: 0 15px; + + &[data-favourite=true] { + opacity: 1; + & svg { + fill: $teal; + } + } +} \ No newline at end of file diff --git a/frontend/app/components/shared/Bookmark/index.js b/frontend/app/components/shared/Bookmark/index.js new file mode 100644 index 000000000..4110151af --- /dev/null +++ b/frontend/app/components/shared/Bookmark/index.js @@ -0,0 +1 @@ +export { default } from './Bookmark' \ No newline at end of file diff --git a/frontend/app/components/shared/SessionItem/SessionItem.js b/frontend/app/components/shared/SessionItem/SessionItem.js index 36a7f186a..301bf4463 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.js +++ b/frontend/app/components/shared/SessionItem/SessionItem.js @@ -15,7 +15,7 @@ import { session as sessionRoute } from 'App/routes'; import { durationFormatted, formatTimeOrDate } from 'App/date'; import stl from './sessionItem.css'; import LiveTag from 'Shared/LiveTag'; -import { session } from '../../../routes'; +import Bookmark from 'Shared/Bookmark'; import Counter from './Counter' const Label = ({ label = '', color = 'color-gray-medium'}) => ( @@ -25,15 +25,6 @@ const Label = ({ label = '', color = 'color-gray-medium'}) => ( timezone: state.getIn(['sessions', 'timezone']) }), { toggleFavorite }) export default class SessionItem extends React.PureComponent { - state = { favouriting: false }; - - toggleFavorite = () => { - this.setState({ favouriting: true }); - this.props.toggleFavorite(this.props.session).then(() => { - this.setState({ favouriting: false }); - }); - } - // eslint-disable-next-line complexity render() { const { @@ -114,14 +105,8 @@ export default class SessionItem extends React.PureComponent { { live && } -
-
- -
+
+
diff --git a/frontend/app/components/shared/SessionItem/sessionItem.css b/frontend/app/components/shared/SessionItem/sessionItem.css index 853e70bea..8f9824bec 100644 --- a/frontend/app/components/shared/SessionItem/sessionItem.css +++ b/frontend/app/components/shared/SessionItem/sessionItem.css @@ -20,13 +20,20 @@ align-items: center; border: solid thin #EEEEEE; + & .favorite { + opacity: 0; + &[data-favourite=true] { + opacity: 1; + } + } + &:hover { & .playLink { transition: all 0.4s; opacity: 1; } - & .favoriteWrapper { + & .favorite { transition: all 0.4s; opacity: 1; } @@ -81,21 +88,6 @@ } } -.favoriteWrapper { - cursor: pointer; - display: flex; - align-items: center; - opacity: 0; - margin: 0 15px; - - &[data-favourite=true] { - opacity: 1; - & svg { - fill: $teal; - } - } -} - .playLink { display: flex; align-items: center; diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index 9089490c4..271a590ec 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -120,9 +120,10 @@ const reducer = (state = initialState, action = {}) => { return state .set('list', list) .set('sessionIds', list.map(({ sessionId }) => sessionId ).toJS()) + .set('favoriteList', list.filter(({ favorite }) => favorite)) .set('total', total) .set('keyMap', keyMap) - .set('wdTypeCount', wdTypeCount); + .set('wdTypeCount', wdTypeCount); case SET_AUTOPLAY_VALUES: { const sessionIds = state.get('sessionIds') const currentSessionId = state.get('current').sessionId @@ -179,13 +180,15 @@ const reducer = (state = initialState, action = {}) => { .set('visitedEvents', visitedEvents) .set('host', visitedEvents[0] && visitedEvents[0].host); } - case FETCH_FAVORITE_LIST.SUCCESS: + case FETCH_FAVORITE_LIST.SUCCESS: return state .set('favoriteList', List(action.data).map(Session)); case TOGGLE_FAVORITE.SUCCESS: { - const id = action.session.sessionId; + const id = action.sessionId; + const session = state.get('list').find(({sessionId}) => sessionId === id) const wasInFavorite = state .get('favoriteList').findIndex(({ sessionId }) => sessionId === id) > -1; + return state .update('list', list => list .map(session => (session.sessionId === id @@ -193,7 +196,7 @@ const reducer = (state = initialState, action = {}) => { : session))) .update('favoriteList', list => (wasInFavorite ? list.filter(({ sessionId }) => sessionId !== id) - : list.push(action.session.set('favorite', true)))) + : list.push(session.set('favorite', true)))) .update('current', session => (session.sessionId === id ? session.set('favorite', !wasInFavorite) : session)); @@ -283,11 +286,11 @@ export const fetch = (sessionId) => (dispatch, getState) => { }); } -export function toggleFavorite(session) { +export function toggleFavorite(sessionId) { return { types: TOGGLE_FAVORITE.toArray(), - call: client => client.get(`/sessions2/${ session.sessionId }/favorite`), - session, + call: client => client.get(`/sessions2/${ sessionId }/favorite`), + sessionId, }; } From 1f3394f5085172abb05706a161c44e645ce7029f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 19 Aug 2021 13:27:38 +0530 Subject: [PATCH 3/4] fix(ui) - bookmarking --- .../components/Session_/PlayerBlockHeader.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/frontend/app/components/Session_/PlayerBlockHeader.js b/frontend/app/components/Session_/PlayerBlockHeader.js index 0d0294ea3..dee980e00 100644 --- a/frontend/app/components/Session_/PlayerBlockHeader.js +++ b/frontend/app/components/Session_/PlayerBlockHeader.js @@ -53,21 +53,16 @@ export default class PlayerBlockHeader extends React.PureComponent { ); backHandler = () => { - const { funnelRef, history, siteId } = this.props; + const { history, siteId } = this.props; if (history.action !== 'POP') history.goBack(); else - history.push(withSiteId(SESSIONS_ROUTE), siteId); - // if (funnelRef) { - // history.push(withSiteId(funnelIssueRoute(funnelRef.funnelId, funnelRef.issueId), funnelRef.siteId)); - // } else { - // history.push(withSiteId(SESSIONS_ROUTE), siteId); - // } + history.push(withSiteId(SESSIONS_ROUTE), siteId); } toggleFavorite = () => { const { session } = this.props; - this.props.toggleFavorite(session); + this.props.toggleFavorite(session.sessionId); } render() { @@ -123,8 +118,7 @@ export default class PlayerBlockHeader extends React.PureComponent { tooltip="Bookmark" onClick={ this.toggleFavorite } loading={ loading } - icon={ favorite ? 'star-solid' : 'star' } - // label={ favorite ? 'Favourited' : 'Favourite' } + icon={ favorite ? 'star-solid' : 'star' } plain /> } From 10629a6b4fb187d5aa2375e8089baf8253c2a108 Mon Sep 17 00:00:00 2001 From: Kraiem Taha Yassine Date: Fri, 20 Aug 2021 18:22:37 +0100 Subject: [PATCH 4/4] Api changes (#161) * fix(api): EE set password after invitation * feat(api): FOS&EE return tracker version with the list of projects * feat(api): FOS&EE signup changes * feat(api): EE signup email existance error message --- api/chalicelib/blueprints/bp_core_dynamic.py | 15 ++-- api/chalicelib/core/projects.py | 3 +- api/chalicelib/core/signup.py | 60 ++-------------- api/chalicelib/core/tenants.py | 6 +- .../chalicelib/blueprints/bp_core_dynamic.py | 19 ++--- ee/api/chalicelib/core/projects.py | 3 +- ee/api/chalicelib/core/signup.py | 72 ++++++------------- ee/api/chalicelib/core/tenants.py | 6 +- ee/api/chalicelib/core/users.py | 4 +- 9 files changed, 48 insertions(+), 140 deletions(-) diff --git a/api/chalicelib/blueprints/bp_core_dynamic.py b/api/chalicelib/blueprints/bp_core_dynamic.py index 14fa6de86..af674f762 100644 --- a/api/chalicelib/blueprints/bp_core_dynamic.py +++ b/api/chalicelib/blueprints/bp_core_dynamic.py @@ -22,13 +22,6 @@ app = Blueprint(__name__) _overrides.chalice_app(app) -@app.route('/signedups', methods=['GET'], authorizer=None) -def signed_ups(): - return { - 'data': tenants.get_tenants() - } - - @app.route('/login', methods=['POST'], authorizer=None) def login(): data = app.current_request.json_body @@ -52,7 +45,7 @@ def login(): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) c["smtp"] = helper.has_smtp() return { 'jwt': r.pop('jwt'), @@ -83,7 +76,7 @@ def get_account(context): @app.route('/projects', methods=['GET']) def get_projects(context): return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True, - stack_integrations=True)} + stack_integrations=True, version=True)} @app.route('/projects', methods=['POST', 'PUT']) @@ -127,7 +120,7 @@ def get_client(context): if r is not None: r.pop("createdAt") r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'data': r } @@ -148,7 +141,7 @@ def put_client(context): @app.route('/signup', methods=['GET'], authorizer=None) def get_all_signup(): - return {"data": signup.get_signed_ups()} + return {"data": tenants.tenants_exists()} @app.route('/signup', methods=['POST', 'PUT'], authorizer=None) diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index 01c87468b..a9e1cdf92 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -41,7 +41,7 @@ def __create(tenant_id, name): @dev.timed -def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False): +def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False,version=False): with pg_client.PostgresClient() as cur: cur.execute(f"""\ SELECT @@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st {',s.gdpr' if gdpr else ''} {',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''} {',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''} + {',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''} FROM public.projects AS s {'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''} where s.deleted_at IS NULL diff --git a/api/chalicelib/core/signup.py b/api/chalicelib/core/signup.py index a92289140..50fc6e41a 100644 --- a/api/chalicelib/core/signup.py +++ b/api/chalicelib/core/signup.py @@ -1,39 +1,31 @@ from chalicelib.utils import helper from chalicelib.utils import pg_client -from chalicelib.core import users, telemetry +from chalicelib.core import users, telemetry, tenants from chalicelib.utils import captcha import json from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.helper import environ -def get_signed_ups(): - with pg_client.PostgresClient() as cur: - cur.execute("SELECT tenant_id, name FROM public.tenants;") - rows = cur.fetchall() - return helper.list_to_camel_case(rows) - - def create_step1(data): print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC") errors = [] + if tenants.tenants_exists(): + return {"errors": ["tenants already registered"]} email = data.get("email") print(f"=====================> {email}") password = data.get("password") print("Verifying email validity") - email_exists = False if email is None or len(email) < 5 or not helper.is_valid_email(email): errors.append("Invalid email address.") else: print("Verifying email existance") if users.email_exists(email): - # errors.append("Email address already in use.") - email_exists = True + errors.append("Email address already in use.") if users.get_deleted_user_by_email(email) is not None: - # errors.append("Email address previously deleted.") - email_exists = True + errors.append("Email address previously deleted.") print("Verifying captcha") if helper.allow_captcha() and not captcha.is_valid(data["g-recaptcha-response"]): @@ -57,12 +49,6 @@ def create_step1(data): project_name = data.get("projectName") if project_name is None or len(project_name) < 1: project_name = "my first project" - signed_ups = get_signed_ups() - if len(signed_ups) > 0 and data.get("tenantId") is None: - errors.append("Tenant already exists, please select it from dropdown") - elif len(signed_ups) == 0 and data.get("tenantId") is not None \ - or len(signed_ups) > 0 and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]: - errors.append("Tenant not found") if len(errors) > 0: print("==> error") @@ -77,41 +63,7 @@ def create_step1(data): "organizationName": company_name, "versionNumber": environ["version_number"] } - if data.get("tenantId") is not None: - update_user = """ - u AS ( - UPDATE public.users - SET name = %(fullname)s, deleted_at=NULL - WHERE email=%(email)s - RETURNING user_id,email, role, name - ) - UPDATE public.basic_authentication - SET password= crypt(%(password)s, gen_salt('bf', 12)) - WHERE user_id = (SELECT user_id FROM u)""" - insert_user = """ - a AS ( - UPDATE public.users - SET role='admin' - WHERE role ='owner' - ), - u AS ( - INSERT INTO public.users (email, role, name, data) - VALUES (%(email)s, 'owner', %(fullname)s,%(data)s) - RETURNING user_id,email,role,name - ) - INSERT INTO public.basic_authentication (user_id, password, generated_password) - VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE)""" - query = f"""\ - WITH t AS ( - UPDATE public.tenants - SET name = %(organizationName)s, - version_number = %(versionNumber)s - RETURNING api_key - ), - {update_user if email_exists else insert_user} - RETURNING (SELECT api_key FROM t) AS api_key,(SELECT project_id FROM projects LIMIT 1) AS project_id;""" - else: - query = f"""\ + query = f"""\ WITH t AS ( INSERT INTO public.tenants (name, version_number, edition) VALUES (%(organizationName)s, %(versionNumber)s, 'fos') diff --git a/api/chalicelib/core/tenants.py b/api/chalicelib/core/tenants.py index 4b439cfef..054b3f5d5 100644 --- a/api/chalicelib/core/tenants.py +++ b/api/chalicelib/core/tenants.py @@ -77,7 +77,7 @@ def update(tenant_id, user_id, data): return edit_client(tenant_id=tenant_id, changes=changes) -def get_tenants(): +def tenants_exists(): with pg_client.PostgresClient() as cur: - cur.execute(f"SELECT name FROM public.tenants") - return helper.list_to_camel_case(cur.fetchall()) + cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)") + return cur.fetchone()["exists"] diff --git a/ee/api/chalicelib/blueprints/bp_core_dynamic.py b/ee/api/chalicelib/blueprints/bp_core_dynamic.py index 3eeff250e..9abd4cab8 100644 --- a/ee/api/chalicelib/blueprints/bp_core_dynamic.py +++ b/ee/api/chalicelib/blueprints/bp_core_dynamic.py @@ -22,13 +22,6 @@ app = Blueprint(__name__) _overrides.chalice_app(app) -@app.route('/signedups', methods=['GET'], authorizer=None) -def signed_ups(): - return { - 'data': tenants.get_tenants() - } - - @app.route('/login', methods=['POST'], authorizer=None) def login(): data = app.current_request.json_body @@ -54,7 +47,7 @@ def login(): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'jwt': r.pop('jwt'), 'data': { @@ -85,7 +78,7 @@ def get_account(context): @app.route('/projects', methods=['GET']) def get_projects(context): return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True, - stack_integrations=True)} + stack_integrations=True, version=True)} @app.route('/projects', methods=['POST', 'PUT']) @@ -129,7 +122,7 @@ def get_client(context): if r is not None: r.pop("createdAt") r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'data': r } @@ -148,10 +141,9 @@ def put_client(context): return tenants.update(tenant_id=context["tenantId"], user_id=context["userId"], data=data) -# TODO: delete this for production; it is used for dev only @app.route('/signup', methods=['GET'], authorizer=None) def get_all_signup(): - return {"data": signup.get_signed_ups()} + return {"data": tenants.tenants_exists()} @app.route('/signup', methods=['POST', 'PUT'], authorizer=None) @@ -391,7 +383,8 @@ def change_password_by_invitation(): if user["expiredChange"]: return {"errors": ["expired change, please re-use the invitation link"]} - return users.set_password_invitation(new_password=data["password"], user_id=user["userId"]) + return users.set_password_invitation(new_password=data["password"], user_id=user["userId"], + tenant_id=user["tenantId"]) @app.route('/client/members/{memberId}', methods=['PUT', 'POST']) diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index dc9cbfd23..cb1e7b1de 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -40,7 +40,7 @@ def __create(tenant_id, name): return get_project(tenant_id=tenant_id, project_id=project_id, include_gdpr=True) -def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False): +def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False, version=False): with pg_client.PostgresClient() as cur: cur.execute( cur.mogrify(f"""\ @@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st {',s.gdpr' if gdpr else ''} {',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''} {',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''} + {',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''} FROM public.projects AS s {'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''} where s.tenant_id =%(tenant_id)s diff --git a/ee/api/chalicelib/core/signup.py b/ee/api/chalicelib/core/signup.py index fbfd4f831..652867c25 100644 --- a/ee/api/chalicelib/core/signup.py +++ b/ee/api/chalicelib/core/signup.py @@ -1,22 +1,17 @@ from chalicelib.utils import helper from chalicelib.utils import pg_client -from chalicelib.core import users, telemetry +from chalicelib.core import users, telemetry, tenants from chalicelib.utils import captcha import json from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.helper import environ -def get_signed_ups(): - with pg_client.PostgresClient() as cur: - cur.execute("SELECT tenant_id, name FROM public.tenants;") - rows = cur.fetchall() - return helper.list_to_camel_case(rows) - - def create_step1(data): print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC") errors = [] + if tenants.tenants_exists(): + return {"errors":["tenants already registered"]} email = data.get("email") print(f"=====================> {email}") @@ -54,64 +49,37 @@ def create_step1(data): project_name = data.get("projectName") if project_name is None or len(project_name) < 1: project_name = "my first project" - signed_ups = get_signed_ups() - if len(signed_ups) == 0 and data.get("tenantId") is not None \ - or len(signed_ups) > 0 and data.get("tenantId") is not None\ - and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]: - errors.append("Tenant not found") if len(errors) > 0: print("==> error") print(errors) return {"errors": errors} print("No errors detected") print("Decomposed infos") - tenant_id = data.get("tenantId") params = {"email": email, "password": password, "fullname": fullname, "companyName": company_name, "projectName": project_name, "versionNumber": environ["version_number"], "data": json.dumps({"lastAnnouncementView": TimeUTC.now()})} - if tenant_id is not None: - query = """\ - WITH t AS ( - UPDATE public.tenants - SET name = %(companyName)s, - version_number = %(versionNumber)s - WHERE tenant_id=%(tenant_id)s + query = """\ + WITH t AS ( + INSERT INTO public.tenants (name, version_number, edition) + VALUES (%(companyName)s, %(versionNumber)s, 'ee') RETURNING tenant_id, api_key - ), - u AS ( - UPDATE public.users - SET email = %(email)s, - name = %(fullname)s, - WHERE role ='owner' AND tenant_id=%(tenant_id)s - RETURNING user_id,email, role, name - ) - UPDATE public.basic_authentication - SET password= crypt(%(password)s, gen_salt('bf', 12)) - WHERE user_id = (SELECT user_id FROM u) - RETURNING %(tenant_id)s AS tenant_id""" - else: - query = """\ - WITH t AS ( - INSERT INTO public.tenants (name, version_number, edition) - VALUES (%(companyName)s, %(versionNumber)s, 'ee') - RETURNING tenant_id, api_key - ), - u AS ( - INSERT INTO public.users (tenant_id, email, role, name, data) - VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s) - RETURNING user_id,email,role,name - ), - au AS ( - INSERT INTO public.basic_authentication (user_id, password, generated_password) - VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE) - ) - INSERT INTO public.projects (tenant_id, name, active) - VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE) - RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;""" + ), + u AS ( + INSERT INTO public.users (tenant_id, email, role, name, data) + VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s) + RETURNING user_id,email,role,name + ), + au AS ( + INSERT INTO public.basic_authentication (user_id, password, generated_password) + VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE) + ) + INSERT INTO public.projects (tenant_id, name, active) + VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE) + RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;""" with pg_client.PostgresClient() as cur: cur.execute(cur.mogrify(query, params)) diff --git a/ee/api/chalicelib/core/tenants.py b/ee/api/chalicelib/core/tenants.py index 7855e2e81..eb827d827 100644 --- a/ee/api/chalicelib/core/tenants.py +++ b/ee/api/chalicelib/core/tenants.py @@ -101,7 +101,7 @@ def update(tenant_id, user_id, data): return edit_client(tenant_id=tenant_id, changes=changes) -def get_tenants(): +def tenants_exists(): with pg_client.PostgresClient() as cur: - cur.execute(f"SELECT name FROM public.tenants") - return helper.list_to_camel_case(cur.fetchall()) + cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)") + return cur.fetchone()["exists"] diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index 4c6032fc2..034a9549d 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -440,11 +440,11 @@ def change_password(tenant_id, user_id, email, old_password, new_password): "jwt": authenticate(email, new_password)["jwt"]} -def set_password_invitation(user_id, new_password): +def set_password_invitation(tenant_id, user_id, new_password): changes = {"password": new_password, "generatedPassword": False, "invitationToken": None, "invitedAt": None, "changePwdExpireAt": None, "changePwdToken": None} - user = update(tenant_id=-1, user_id=user_id, changes=changes) + user = update(tenant_id=tenant_id, user_id=user_id, changes=changes) r = authenticate(user['email'], new_password) tenant_id = r.pop("tenantId")