Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
c47e66cf0c
|
|
@ -3,7 +3,12 @@ import styled from "styled-components";
|
||||||
import HighlightedCode, {
|
import HighlightedCode, {
|
||||||
SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES,
|
SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES,
|
||||||
} from "components/editorComponents/HighlightedCode";
|
} from "components/editorComponents/HighlightedCode";
|
||||||
import { Popover, PopoverInteractionKind, Classes } from "@blueprintjs/core";
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
Classes,
|
||||||
|
Icon,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
import { CurrentValueViewer } from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
|
import { CurrentValueViewer } from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
|
||||||
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
import useClipboard from "utils/hooks/useClipboard";
|
import useClipboard from "utils/hooks/useClipboard";
|
||||||
|
|
@ -61,9 +66,15 @@ const Wrapper = styled.div<{ step: number }>`
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
& ~ span.${Classes.ICON} {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 10px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
&:before {
|
&:before {
|
||||||
content: "Copy";
|
content: "";
|
||||||
background: ${Colors.TUNDORA};
|
background: ${Colors.TUNDORA};
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -71,14 +82,11 @@ const Wrapper = styled.div<{ step: number }>`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 12px;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
text-align: right;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
& ~ span.${Classes.ICON} {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,6 +100,7 @@ const Wrapper = styled.div<{ step: number }>`
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
padding-left: ${props =>
|
padding-left: ${props =>
|
||||||
props.step * props.theme.spaces[2] + props.theme.spaces[3]}px;
|
props.step * props.theme.spaces[2] + props.theme.spaces[3]}px;
|
||||||
|
padding-right: 20px;
|
||||||
& span.token.property {
|
& span.token.property {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -214,6 +223,7 @@ export const EntityProperty = memo((props: EntityPropertyProps) => {
|
||||||
codeText={codeText}
|
codeText={codeText}
|
||||||
language={SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES.APPSMITH}
|
language={SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES.APPSMITH}
|
||||||
/>
|
/>
|
||||||
|
<Icon icon="duplicate" iconSize={14} color={Colors.ALTO} />
|
||||||
{propertyValue}
|
{propertyValue}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export const ExplorerPageEntity = memo((props: ExplorerPageEntityProps) => {
|
||||||
if (!!params.applicationId) {
|
if (!!params.applicationId) {
|
||||||
history.push(BUILDER_PAGE_URL(params.applicationId, props.page.pageId));
|
history.push(BUILDER_PAGE_URL(params.applicationId, props.page.pageId));
|
||||||
}
|
}
|
||||||
}, [props.isCurrentPage, props.page.pageId, params.applicationId]);
|
}, [props.page.pageId, params.applicationId]);
|
||||||
|
|
||||||
const contextMenu = (
|
const contextMenu = (
|
||||||
<PageContextMenu
|
<PageContextMenu
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
import React, { useLayoutEffect } from "react";
|
|
||||||
import { AppState } from "reducers";
|
|
||||||
import { withRouter, RouteComponentProps } from "react-router-dom";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { InjectedFormProps, reduxForm, Field } from "redux-form";
|
|
||||||
import { CREATE_PASSWORD_FORM_NAME } from "constants/forms";
|
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
|
||||||
import { getIsTokenValid, getIsValidatingToken } from "selectors/authSelectors";
|
|
||||||
import FormTextField from "components/editorComponents/form/FormTextField";
|
|
||||||
import FormMessage, {
|
|
||||||
FormMessageProps,
|
|
||||||
MessageAction,
|
|
||||||
} from "components/editorComponents/form/FormMessage";
|
|
||||||
import Spinner from "components/editorComponents/Spinner";
|
|
||||||
import Button from "components/editorComponents/Button";
|
|
||||||
import FormGroup from "components/editorComponents/form/FormGroup";
|
|
||||||
import StyledForm from "components/editorComponents/Form";
|
|
||||||
import { isEmptyString, isStrongPassword } from "utils/formhelpers";
|
|
||||||
|
|
||||||
import {
|
|
||||||
CreatePasswordFormValues,
|
|
||||||
createPasswordSubmitHandler,
|
|
||||||
} from "./helpers";
|
|
||||||
import {
|
|
||||||
AuthCardHeader,
|
|
||||||
AuthCardFooter,
|
|
||||||
AuthCardContainer,
|
|
||||||
AuthCardBody,
|
|
||||||
AuthCardNavLink,
|
|
||||||
FormActions,
|
|
||||||
} from "./StyledComponents";
|
|
||||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
|
||||||
|
|
||||||
import {
|
|
||||||
CREATE_PASSWORD_PAGE_PASSWORD_INPUT_LABEL,
|
|
||||||
CREATE_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER,
|
|
||||||
CREATE_PASSWORD_LOGIN_LINK_TEXT,
|
|
||||||
CREATE_PASSWORD_SUBMIT_BUTTON_TEXT,
|
|
||||||
CREATE_PASSWORD_PAGE_SUBTITLE,
|
|
||||||
CREATE_PASSWORD_PAGE_TITLE,
|
|
||||||
FORM_VALIDATION_INVALID_PASSWORD,
|
|
||||||
FORM_VALIDATION_EMPTY_PASSWORD,
|
|
||||||
CREATE_PASSWORD_EXPIRED_TOKEN,
|
|
||||||
CREATE_PASSWORD_INVALID_TOKEN,
|
|
||||||
CREATE_PASSWORD_RESET_SUCCESS,
|
|
||||||
CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK,
|
|
||||||
} from "constants/messages";
|
|
||||||
import { TncPPLinks } from "./SignUp";
|
|
||||||
|
|
||||||
const validate = (values: CreatePasswordFormValues) => {
|
|
||||||
const errors: CreatePasswordFormValues = {};
|
|
||||||
if (!values.password || isEmptyString(values.password)) {
|
|
||||||
errors.password = FORM_VALIDATION_EMPTY_PASSWORD;
|
|
||||||
} else if (!isStrongPassword(values.password)) {
|
|
||||||
errors.password = FORM_VALIDATION_INVALID_PASSWORD;
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CreatePasswordProps = InjectedFormProps<
|
|
||||||
CreatePasswordFormValues,
|
|
||||||
{
|
|
||||||
verifyToken: (token: string, email: string) => void;
|
|
||||||
isTokenValid: boolean;
|
|
||||||
validatingToken: boolean;
|
|
||||||
}
|
|
||||||
> & {
|
|
||||||
verifyToken: (token: string, email: string) => void;
|
|
||||||
isTokenValid: boolean;
|
|
||||||
validatingToken: boolean;
|
|
||||||
} & RouteComponentProps<{ email: string; token: string }>;
|
|
||||||
|
|
||||||
export const CreatePassword = (props: CreatePasswordProps) => {
|
|
||||||
const {
|
|
||||||
error,
|
|
||||||
handleSubmit,
|
|
||||||
pristine,
|
|
||||||
submitting,
|
|
||||||
submitSucceeded,
|
|
||||||
submitFailed,
|
|
||||||
initialValues,
|
|
||||||
isTokenValid,
|
|
||||||
validatingToken,
|
|
||||||
verifyToken,
|
|
||||||
valid,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (initialValues.token && initialValues.email)
|
|
||||||
verifyToken(initialValues.token, initialValues.email);
|
|
||||||
}, [initialValues.token, initialValues.email, verifyToken]);
|
|
||||||
|
|
||||||
const showInvalidMessage = !initialValues.token || !initialValues.email;
|
|
||||||
const showExpiredMessage = !isTokenValid && !validatingToken;
|
|
||||||
const showSuccessMessage = submitSucceeded && !pristine;
|
|
||||||
const showFailureMessage = submitFailed && !!error;
|
|
||||||
|
|
||||||
let message = "";
|
|
||||||
let messageActions: MessageAction[] | undefined = undefined;
|
|
||||||
|
|
||||||
if (showExpiredMessage) {
|
|
||||||
message = CREATE_PASSWORD_EXPIRED_TOKEN;
|
|
||||||
}
|
|
||||||
if (showInvalidMessage) {
|
|
||||||
message = CREATE_PASSWORD_INVALID_TOKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showSuccessMessage) {
|
|
||||||
message = CREATE_PASSWORD_RESET_SUCCESS;
|
|
||||||
messageActions = [
|
|
||||||
{
|
|
||||||
url: AUTH_LOGIN_URL,
|
|
||||||
text: CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK,
|
|
||||||
intent: "primary",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (showFailureMessage) {
|
|
||||||
message = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageTagProps: FormMessageProps = {
|
|
||||||
intent:
|
|
||||||
showInvalidMessage || showExpiredMessage || showFailureMessage
|
|
||||||
? "danger"
|
|
||||||
: "primary",
|
|
||||||
message,
|
|
||||||
actions: messageActions,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (showInvalidMessage || showExpiredMessage) {
|
|
||||||
return <FormMessage {...messageTagProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTokenValid && validatingToken) {
|
|
||||||
return <Spinner />;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<AuthCardContainer>
|
|
||||||
{(showSuccessMessage || showFailureMessage) && (
|
|
||||||
<FormMessage {...messageTagProps} />
|
|
||||||
)}
|
|
||||||
<AuthCardHeader>
|
|
||||||
<h1>{CREATE_PASSWORD_PAGE_TITLE}</h1>
|
|
||||||
<h5>{CREATE_PASSWORD_PAGE_SUBTITLE}</h5>
|
|
||||||
</AuthCardHeader>
|
|
||||||
<AuthCardBody>
|
|
||||||
<StyledForm onSubmit={handleSubmit(createPasswordSubmitHandler)}>
|
|
||||||
<FormGroup
|
|
||||||
intent={error ? "danger" : "none"}
|
|
||||||
label={CREATE_PASSWORD_PAGE_PASSWORD_INPUT_LABEL}
|
|
||||||
>
|
|
||||||
<FormTextField
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
placeholder={CREATE_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<Field type="hidden" name="email" component="input" />
|
|
||||||
<Field type="hidden" name="token" component="input" />
|
|
||||||
<FormActions>
|
|
||||||
<Button
|
|
||||||
filled
|
|
||||||
size="large"
|
|
||||||
type="submit"
|
|
||||||
text={CREATE_PASSWORD_SUBMIT_BUTTON_TEXT}
|
|
||||||
intent="primary"
|
|
||||||
disabled={pristine || !valid}
|
|
||||||
loading={submitting}
|
|
||||||
/>
|
|
||||||
</FormActions>
|
|
||||||
</StyledForm>
|
|
||||||
</AuthCardBody>
|
|
||||||
<AuthCardNavLink to={AUTH_LOGIN_URL}>
|
|
||||||
{CREATE_PASSWORD_LOGIN_LINK_TEXT}
|
|
||||||
</AuthCardNavLink>
|
|
||||||
<AuthCardFooter>
|
|
||||||
<TncPPLinks></TncPPLinks>
|
|
||||||
</AuthCardFooter>
|
|
||||||
</AuthCardContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
(state: AppState, props: CreatePasswordProps) => {
|
|
||||||
const queryParams = new URLSearchParams(props.location.search);
|
|
||||||
return {
|
|
||||||
initialValues: {
|
|
||||||
email: queryParams.get("email") || undefined,
|
|
||||||
token: queryParams.get("token") || undefined,
|
|
||||||
},
|
|
||||||
isTokenValid: getIsTokenValid(state),
|
|
||||||
validatingToken: getIsValidatingToken(state),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
(dispatch: any) => ({
|
|
||||||
verifyToken: (token: string, email: string) =>
|
|
||||||
dispatch({
|
|
||||||
type: ReduxActionTypes.VERIFY_INVITE_INIT,
|
|
||||||
payload: { token, email },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
)(
|
|
||||||
reduxForm<
|
|
||||||
CreatePasswordFormValues,
|
|
||||||
{
|
|
||||||
verifyToken: (token: string, email: string) => void;
|
|
||||||
validatingToken: boolean;
|
|
||||||
isTokenValid: boolean;
|
|
||||||
}
|
|
||||||
>({
|
|
||||||
validate,
|
|
||||||
form: CREATE_PASSWORD_FORM_NAME,
|
|
||||||
touchOnBlur: true,
|
|
||||||
})(withRouter(CreatePassword)),
|
|
||||||
);
|
|
||||||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import { reduxForm, InjectedFormProps } from "redux-form";
|
import { reduxForm, InjectedFormProps } from "redux-form";
|
||||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||||
import { SIGNUP_FORM_NAME } from "constants/forms";
|
import { SIGNUP_FORM_NAME } from "constants/forms";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, RouteComponentProps, useLocation, withRouter } from "react-router-dom";
|
||||||
import Divider from "components/editorComponents/Divider";
|
import Divider from "components/editorComponents/Divider";
|
||||||
import {
|
import {
|
||||||
AuthCardHeader,
|
AuthCardHeader,
|
||||||
|
|
@ -43,6 +43,8 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
|
||||||
import { getAppsmithConfigs } from "configs";
|
import { getAppsmithConfigs } from "configs";
|
||||||
import { SIGNUP_SUBMIT_PATH } from "constants/ApiConstants";
|
import { SIGNUP_SUBMIT_PATH } from "constants/ApiConstants";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { AppState } from "@appsmith/reducers";
|
||||||
const {
|
const {
|
||||||
enableGithubOAuth,
|
enableGithubOAuth,
|
||||||
enableGoogleOAuth,
|
enableGoogleOAuth,
|
||||||
|
|
@ -81,7 +83,9 @@ const validate = (values: SignupFormValues) => {
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignUp = (props: InjectedFormProps<SignupFormValues>) => {
|
type SignUpFormProps = InjectedFormProps<SignupFormValues> & RouteComponentProps<{ email: string }>;
|
||||||
|
|
||||||
|
export const SignUp = (props: SignUpFormProps) => {
|
||||||
const { error, submitting, pristine, valid } = props;
|
const { error, submitting, pristine, valid } = props;
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
@ -160,8 +164,20 @@ export const SignUp = (props: InjectedFormProps<SignupFormValues>) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default reduxForm<SignupFormValues>({
|
export default connect(
|
||||||
|
(state: AppState, props: SignUpFormProps) => {
|
||||||
|
const queryParams = new URLSearchParams(props.location.search);
|
||||||
|
return {
|
||||||
|
initialValues: {
|
||||||
|
email: queryParams.get("email"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
)(
|
||||||
|
reduxForm<SignupFormValues>({
|
||||||
validate,
|
validate,
|
||||||
form: SIGNUP_FORM_NAME,
|
form: SIGNUP_FORM_NAME,
|
||||||
touchOnBlur: true,
|
touchOnBlur: true,
|
||||||
})(SignUp);
|
})(withRouter(SignUp)),
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import { AuthContainer, AuthCard } from "./StyledComponents";
|
||||||
import SignUp from "./SignUp";
|
import SignUp from "./SignUp";
|
||||||
import ForgotPassword from "./ForgotPassword";
|
import ForgotPassword from "./ForgotPassword";
|
||||||
import ResetPassword from "./ResetPassword";
|
import ResetPassword from "./ResetPassword";
|
||||||
import CreatePassword from "./CreatePassword";
|
|
||||||
import AppRoute from "pages/common/AppRoute";
|
import AppRoute from "pages/common/AppRoute";
|
||||||
|
import PageNotFound from "pages/common/PageNotFound";
|
||||||
const AnimatedAuthCard = animated(AuthContainer);
|
const AnimatedAuthCard = animated(AuthContainer);
|
||||||
export const UserAuth = () => {
|
export const UserAuth = () => {
|
||||||
const { path } = useRouteMatch();
|
const { path } = useRouteMatch();
|
||||||
|
|
@ -50,10 +50,8 @@ export const UserAuth = () => {
|
||||||
name={"ForgotPassword"}
|
name={"ForgotPassword"}
|
||||||
/>
|
/>
|
||||||
<AppRoute
|
<AppRoute
|
||||||
exact
|
component={PageNotFound}
|
||||||
path={`${path}/createPassword`}
|
name={"PageNotFound"}
|
||||||
component={CreatePassword}
|
|
||||||
name={"CreatePassword"}
|
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</AuthCard>
|
</AuthCard>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ public interface Url {
|
||||||
String TEAM_URL = BASE_URL + VERSION + "/teams";
|
String TEAM_URL = BASE_URL + VERSION + "/teams";
|
||||||
String GROUP_URL = BASE_URL + VERSION + "/groups";
|
String GROUP_URL = BASE_URL + VERSION + "/groups";
|
||||||
String PERMISSION_URL = BASE_URL + VERSION + "/permissions";
|
String PERMISSION_URL = BASE_URL + VERSION + "/permissions";
|
||||||
String SIGNUP_URL = BASE_URL + VERSION + "/signup";
|
|
||||||
String COLLECTION_URL = BASE_URL + VERSION + "/collections";
|
String COLLECTION_URL = BASE_URL + VERSION + "/collections";
|
||||||
String IMPORT_URL = BASE_URL + VERSION + "/import";
|
String IMPORT_URL = BASE_URL + VERSION + "/import";
|
||||||
String PROVIDER_URL = BASE_URL + VERSION + "/providers";
|
String PROVIDER_URL = BASE_URL + VERSION + "/providers";
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,9 @@ public class User extends BaseDomain implements UserDetails, OidcUser {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return this.isEnabled;
|
// The `isEnabled` field is `Boolean` whereas we are returning `boolean` here. If `isEnabled` field value is
|
||||||
|
// `null`, this would throw a `NullPointerException`. Hence, checking equality with `Boolean.TRUE` instead.
|
||||||
|
return Boolean.TRUE.equals(this.isEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check the return value for the functions below to ensure that correct values are being returned
|
// TODO: Check the return value for the functions below to ensure that correct values are being returned
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
private static final String WELCOME_USER_EMAIL_TEMPLATE = "email/welcomeUserTemplate.html";
|
private static final String WELCOME_USER_EMAIL_TEMPLATE = "email/welcomeUserTemplate.html";
|
||||||
private static final String FORGOT_PASSWORD_EMAIL_TEMPLATE = "email/forgotPasswordTemplate.html";
|
private static final String FORGOT_PASSWORD_EMAIL_TEMPLATE = "email/forgotPasswordTemplate.html";
|
||||||
private static final String FORGOT_PASSWORD_CLIENT_URL_FORMAT = "%s/user/resetPassword?token=%s&email=%s";
|
private static final String FORGOT_PASSWORD_CLIENT_URL_FORMAT = "%s/user/resetPassword?token=%s&email=%s";
|
||||||
private static final String INVITE_USER_CLIENT_URL_FORMAT = "%s/user/createPassword?token=%s&email=%s";
|
private static final String INVITE_USER_CLIENT_URL_FORMAT = "%s/user/signup?token=%s&email=%s";
|
||||||
private static final String INVITE_USER_EMAIL_TEMPLATE = "email/inviteUserCreatorTemplate.html";
|
private static final String INVITE_USER_EMAIL_TEMPLATE = "email/inviteUserCreatorTemplate.html";
|
||||||
private static final String USER_ADDED_TO_ORGANIZATION_EMAIL_TEMPLATE = "email/inviteExistingUserToOrganizationTemplate.html";
|
private static final String USER_ADDED_TO_ORGANIZATION_EMAIL_TEMPLATE = "email/inviteExistingUserToOrganizationTemplate.html";
|
||||||
// We default the origin header to the production deployment of the client's URL
|
// We default the origin header to the production deployment of the client's URL
|
||||||
|
|
@ -229,7 +229,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
*
|
*
|
||||||
* @param email The email of the user whose password is being reset
|
* @param email The email of the user whose password is being reset
|
||||||
* @param token The one-time token provided to the user for resetting the password
|
* @param token The one-time token provided to the user for resetting the password
|
||||||
* @return
|
* @return Publishes a boolean indicating whether the given token is valid for the given email address
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> verifyPasswordResetToken(String email, String token) {
|
public Mono<Boolean> verifyPasswordResetToken(String email, String token) {
|
||||||
|
|
@ -425,7 +425,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
public Mono<User> userCreate(User user) {
|
public Mono<User> userCreate(User user) {
|
||||||
|
|
||||||
// Only encode the password if it's a form signup. For OAuth signups, we don't need password
|
// Only encode the password if it's a form signup. For OAuth signups, we don't need password
|
||||||
if (user.getIsEnabled() && LoginSource.FORM.equals(user.getSource())) {
|
if (user.isEnabled() && LoginSource.FORM.equals(user.getSource())) {
|
||||||
if (user.getPassword() == null || user.getPassword().isBlank()) {
|
if (user.getPassword() == null || user.getPassword().isBlank()) {
|
||||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_CREDENTIALS));
|
return Mono.error(new AppsmithException(AppsmithError.INVALID_CREDENTIALS));
|
||||||
}
|
}
|
||||||
|
|
@ -465,8 +465,8 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
* <p>
|
* <p>
|
||||||
* For new user invite flow, please {@link UserService#inviteUser(InviteUsersDTO, String)}
|
* For new user invite flow, please {@link UserService#inviteUser(InviteUsersDTO, String)}
|
||||||
*
|
*
|
||||||
* @param user
|
* @param user User object representing the user to be created/enabled.
|
||||||
* @return
|
* @return Publishes the user object, after having been saved.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<User> createUserAndSendEmail(User user, String originHeader) {
|
public Mono<User> createUserAndSendEmail(User user, String originHeader) {
|
||||||
|
|
@ -481,7 +481,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
// If the user doesn't exist, create the user. If the user exists, return a duplicate key exception
|
// If the user doesn't exist, create the user. If the user exists, return a duplicate key exception
|
||||||
return repository.findByEmail(user.getUsername())
|
return repository.findByEmail(user.getUsername())
|
||||||
.flatMap(savedUser -> {
|
.flatMap(savedUser -> {
|
||||||
if (!savedUser.getIsEnabled()) {
|
if (!savedUser.isEnabled()) {
|
||||||
// First enable the user
|
// First enable the user
|
||||||
savedUser.setIsEnabled(true);
|
savedUser.setIsEnabled(true);
|
||||||
|
|
||||||
|
|
@ -553,7 +553,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
* 2. User exists :
|
* 2. User exists :
|
||||||
* a. Add user to the organization
|
* a. Add user to the organization
|
||||||
* b. Add organization to the user
|
* b. Add organization to the user
|
||||||
* @return
|
* @return Publishes the invited users, after being saved with the new organization ID.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Flux<User> inviteUser(InviteUsersDTO inviteUsersDTO, String originHeader) {
|
public Flux<User> inviteUser(InviteUsersDTO inviteUsersDTO, String originHeader) {
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,54 @@ overwrite_file() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This function prompts the user for an input for a non-empty Mongo root password.
|
||||||
|
read_mongo_password() {
|
||||||
|
read -sp 'Set the mongo password: ' mongo_root_password
|
||||||
|
while [[ -z $mongo_root_password ]]
|
||||||
|
do
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||||
|
echo "The mongo password cannot be empty. Please input a valid password string."
|
||||||
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
echo ""
|
||||||
|
read -sp 'Set the mongo password: ' mongo_root_password
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# This function prompts the user for an input for a non-empty Mongo username.
|
||||||
|
read_mongo_username() {
|
||||||
|
read -p 'Set the mongo root user: ' mongo_root_user
|
||||||
|
while [[ -z $mongo_root_user ]]
|
||||||
|
do
|
||||||
|
echo ""
|
||||||
|
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||||
|
echo "The mongo username cannot be empty. Please input a valid username string."
|
||||||
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
echo ""
|
||||||
|
read -p 'Set the mongo root user: ' mongo_root_user
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_containers_start() {
|
||||||
|
timeout=$1
|
||||||
|
i=1
|
||||||
|
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
||||||
|
# The do-while loop is important because for-loops don't work for dynamic values
|
||||||
|
while [[ $i -le $timeout ]]
|
||||||
|
do
|
||||||
|
status_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/api/v1)
|
||||||
|
# echo $status_code
|
||||||
|
if [[ status_code -eq 401 ]]; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
||||||
|
fi
|
||||||
|
((i = i + 1))
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
echo -e "\U1F44B Thank you for trying out Appsmith! "
|
echo -e "\U1F44B Thank you for trying out Appsmith! "
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|
@ -132,6 +180,25 @@ if [[ $ports_occupied -ne 0 ]]; then
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check is Docker daemon is installed and available. If not, the install & start Docker for Linux machines. We cannot automatically install Docker Desktop on Mac OS
|
||||||
|
if ! is_command_present docker ;then
|
||||||
|
if [ $package_manager == "apt-get" -o $package_manager == "yum" ];then
|
||||||
|
install_docker
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
||||||
|
echo "Docker Desktop must be installed manually on Mac OS to proceed. Docker can only be installed automatically on Ubuntu / Redhat / Cent OS"
|
||||||
|
echo "https://docs.docker.com/docker-for-mac/install/"
|
||||||
|
echo "++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Starting docker service
|
||||||
|
if [ $package_manager == "yum" -o $package_manager == "apt-get" ];then
|
||||||
|
start_docker
|
||||||
|
fi
|
||||||
|
|
||||||
read -p 'Installation Directory [appsmith]: ' install_dir
|
read -p 'Installation Directory [appsmith]: ' install_dir
|
||||||
install_dir=${install_dir:-appsmith}
|
install_dir=${install_dir:-appsmith}
|
||||||
mkdir -p $PWD/$install_dir
|
mkdir -p $PWD/$install_dir
|
||||||
|
|
@ -159,8 +226,11 @@ elif [ $fresh_install == "Y" -o $fresh_install == "y" -o $fresh_install == "yes"
|
||||||
echo "Appsmith needs to create a mongo db"
|
echo "Appsmith needs to create a mongo db"
|
||||||
mongo_host="mongo"
|
mongo_host="mongo"
|
||||||
mongo_database="appsmith"
|
mongo_database="appsmith"
|
||||||
read -p 'Set the mongo root user: ' mongo_root_user
|
|
||||||
read -sp 'Set the mongo password: ' mongo_root_password
|
# We invoke functions to read the mongo credentials from the user because they MUST be non-empty
|
||||||
|
read_mongo_username
|
||||||
|
read_mongo_password
|
||||||
|
|
||||||
# Since the mongo was automatically setup, this must be the first time installation. Generate encryption credentials for this scenario
|
# Since the mongo was automatically setup, this must be the first time installation. Generate encryption credentials for this scenario
|
||||||
auto_generate_encryption="true"
|
auto_generate_encryption="true"
|
||||||
fi
|
fi
|
||||||
|
|
@ -228,6 +298,8 @@ if [[ -z $custom_domain ]]; then
|
||||||
NGINX_SSL_CMNT="#"
|
NGINX_SSL_CMNT="#"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Downloading the configuration templates ..."
|
||||||
mkdir -p template
|
mkdir -p template
|
||||||
( cd template
|
( cd template
|
||||||
curl -O --silent https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker-compose.yml.sh
|
curl -O --silent https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker-compose.yml.sh
|
||||||
|
|
@ -238,31 +310,13 @@ curl -O --silent https://raw.githubusercontent.com/appsmithorg/appsmith/release/
|
||||||
curl -O --silent https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/encryption.env.sh
|
curl -O --silent https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/encryption.env.sh
|
||||||
)
|
)
|
||||||
|
|
||||||
# Role - Docker
|
|
||||||
if ! is_command_present docker ;then
|
|
||||||
if [ $package_manager == "apt-get" -o $package_manager == "yum" ];then
|
|
||||||
install_docker
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
|
||||||
echo "Docker Desktop must be installed manually on Mac OS to proceed. Docker will be installed automatically on Ubuntu / Redhat / Cent OS"
|
|
||||||
echo "https://docs.docker.com/docker-for-mac/install/"
|
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++++++++++"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Starting docker service
|
|
||||||
if [ $package_manager == "yum" -o $package_manager == "apt-get" ];then
|
|
||||||
start_docker
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Role - Folder
|
# Role - Folder
|
||||||
for directory_name in nginx certbot/conf certbot/www mongo/db
|
for directory_name in nginx certbot/conf certbot/www mongo/db
|
||||||
do
|
do
|
||||||
mkdir -p "$install_dir/data/$directory_name"
|
mkdir -p "$install_dir/data/$directory_name"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
echo "Generating the configuration files from the templates"
|
echo "Generating the configuration files from the templates"
|
||||||
. ./template/nginx_app.conf.sh
|
. ./template/nginx_app.conf.sh
|
||||||
. ./template/docker-compose.yml.sh
|
. ./template/docker-compose.yml.sh
|
||||||
|
|
@ -295,6 +349,22 @@ echo "Pulling the latest container images"
|
||||||
sudo docker-compose pull
|
sudo docker-compose pull
|
||||||
echo "Starting the Appsmith containers"
|
echo "Starting the Appsmith containers"
|
||||||
sudo docker-compose -f docker-compose.yml up -d --remove-orphans
|
sudo docker-compose -f docker-compose.yml up -d --remove-orphans
|
||||||
|
|
||||||
|
# These echo statements are important for some reason. The script doesn't run successfully without them.
|
||||||
|
echo ""
|
||||||
|
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
||||||
|
wait_for_containers_start 60
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ $status_code -ne 401 ]]; then
|
||||||
|
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||||
|
echo "The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
||||||
|
echo ""
|
||||||
|
echo "cd $install_dir && sudo docker-compose ps -a"
|
||||||
|
echo "For troubleshooting help, please reach out to us via our Discord server: https://discord.com/invite/rBTTVJp"
|
||||||
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo "+++++++++++ SUCCESS ++++++++++++++++++++++"
|
echo "+++++++++++ SUCCESS ++++++++++++++++++++++"
|
||||||
echo "Your installation is complete. Please run the following command to ensure that all the containers are running without errors:"
|
echo "Your installation is complete. Please run the following command to ensure that all the containers are running without errors:"
|
||||||
|
|
@ -303,7 +373,9 @@ echo "cd $install_dir && sudo docker-compose ps -a"
|
||||||
echo "+++++++++++++++++++++++++++++++++++++++++++++++++"
|
echo "+++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Need help troubleshooting?"
|
echo "Need help troubleshooting?"
|
||||||
echo "Join our discord server https://discord.com/invite/rBTTVJp"
|
echo "Join our Discord server https://discord.com/invite/rBTTVJp"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Your application is running on http://localhost"
|
echo "Your application is running on http://localhost"
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "Peace out \U1F596"
|
echo -e "Peace out \U1F596"
|
||||||
Loading…
Reference in New Issue
Block a user