Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-09-02 12:08:11 +00:00
commit 0f978b487d
20 changed files with 865 additions and 454 deletions

View File

@ -25,9 +25,11 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -43,6 +45,8 @@ public class MySqlPlugin extends BasePlugin {
private static final String PASSWORD = "password"; private static final String PASSWORD = "password";
private static final int VALIDITY_CHECK_TIMEOUT = 5; private static final int VALIDITY_CHECK_TIMEOUT = 5;
private static final String DATE_COLUMN_TYPE_NAME = "date";
public MySqlPlugin(PluginWrapper wrapper) { public MySqlPlugin(PluginWrapper wrapper) {
super(wrapper); super(wrapper);
} }
@ -88,11 +92,38 @@ public class MySqlPlugin extends BasePlugin {
ResultSetMetaData metaData = resultSet.getMetaData(); ResultSetMetaData metaData = resultSet.getMetaData();
int colCount = metaData.getColumnCount(); int colCount = metaData.getColumnCount();
while (resultSet.next()) { while (resultSet.next()) {
Map<String, Object> row = new HashMap<>(colCount); // Use `LinkedHashMap` here so that the column ordering is preserved in the response.
for (int i = 1; i <= colCount; i++) { Map<String, Object> row = new LinkedHashMap<>(colCount);
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
rowsList.add(row); rowsList.add(row);
for (int i = 1; i <= colCount; i++) {
Object value;
final String typeName = metaData.getColumnTypeName(i);
if (resultSet.getObject(i) == null) {
value = null;
} else if (DATE_COLUMN_TYPE_NAME.equalsIgnoreCase(typeName)) {
value = DateTimeFormatter.ISO_DATE.format(resultSet.getDate(i).toLocalDate());
} else if ("datetime".equalsIgnoreCase(typeName) || "timestamp".equalsIgnoreCase(typeName)) {
value = DateTimeFormatter.ISO_DATE_TIME.format(
LocalDateTime.of(
resultSet.getDate(i).toLocalDate(),
resultSet.getTime(i).toLocalTime()
)
) + "Z";
} else if ("year".equalsIgnoreCase(typeName)) {
value = resultSet.getDate(i).toLocalDate().getYear();
} else {
value = resultSet.getObject(i);
}
row.put(metaData.getColumnLabel(i), value);
}
} }
} else { } else {

View File

@ -1,6 +1,13 @@
package com.external.plugins; package com.external.plugins;
import com.appsmith.external.models.*; import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.Endpoint;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import lombok.extern.log4j.Log4j; import lombok.extern.log4j.Log4j;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
@ -10,21 +17,31 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set; import java.util.Set;
import static org.junit.Assert.*; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@Log4j @Log4j
public class MySqlPluginTest { public class MySqlPluginTest {
MySqlPlugin.MySqlPluginExecutor pluginExecutor = new MySqlPlugin.MySqlPluginExecutor(); MySqlPlugin.MySqlPluginExecutor pluginExecutor = new MySqlPlugin.MySqlPluginExecutor();
@SuppressWarnings("rawtypes") // The type parameter for the container type is just itself and is pseudo-optional.
@ClassRule @ClassRule
public static MySQLContainer mySQLContainer = new MySQLContainer() public static MySQLContainer mySQLContainer = new MySQLContainer()
.withUsername("mysql").withPassword("password").withDatabaseName("mysql"); .withUsername("mysql")
.withPassword("password")
.withDatabaseName("mysql");
String address; String address;
Integer port; Integer port;
@ -34,11 +51,67 @@ public class MySqlPluginTest {
@Before @Before
public void setUp() { public void setUp() {
if (address != null) {
return;
}
address = mySQLContainer.getContainerIpAddress(); address = mySQLContainer.getContainerIpAddress();
port = mySQLContainer.getFirstMappedPort(); port = mySQLContainer.getFirstMappedPort();
username = mySQLContainer.getUsername(); username = mySQLContainer.getUsername();
password = mySQLContainer.getPassword(); password = mySQLContainer.getPassword();
createDatasourceConfiguration(); createDatasourceConfiguration();
Properties properties = new Properties();
properties.putAll(Map.of(
"user", username,
"password", password
));
try (Connection connection = DriverManager.getConnection(
"jdbc:mysql://" + address + ":" + port + "/" + username,
properties
)) {
try (Statement statement = connection.createStatement()) {
statement.execute("DROP TABLE IF EXISTS users");
}
try (Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE users (\n" +
" id serial PRIMARY KEY,\n" +
" username VARCHAR (50) UNIQUE NOT NULL,\n" +
" password VARCHAR (50) NOT NULL,\n" +
" email VARCHAR (355) UNIQUE NOT NULL,\n" +
" spouse_dob DATE,\n" +
" dob DATE NOT NULL,\n" +
" yob YEAR NOT NULL,\n" +
" time1 TIME NOT NULL,\n" +
" created_on TIMESTAMP NOT NULL,\n" +
" updated_on DATETIME NOT NULL\n" +
")");
}
try (Statement statement = connection.createStatement()) {
statement.execute(
"INSERT INTO users VALUES (" +
"1, 'Jack', 'jill', 'jack@exemplars.com', NULL, '2018-12-31', 2018," +
" '18:32:45'," +
" '2018-11-30 20:45:15', '2018-11-30 20:45:15'" +
")");
}
try (Statement statement = connection.createStatement()) {
statement.execute(
"INSERT INTO users VALUES (" +
"2, 'Jill', 'jack', 'jill@exemplars.com', NULL, '2019-12-31', 2019," +
" '15:45:30'," +
" '2019-11-30 23:59:59', '2019-11-30 23:59:59'" +
")");
}
} catch (SQLException throwable) {
throwable.printStackTrace();
}
} }
private DatasourceConfiguration createDatasourceConfiguration() { private DatasourceConfiguration createDatasourceConfiguration() {
@ -137,7 +210,79 @@ public class MySqlPluginTest {
} }
}) })
.verifyComplete(); .verifyComplete();
}
@Test
public void testAliasColumnNames() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
Mono<Object> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setBody("SELECT id as user_id FROM users WHERE id = 1");
Mono<ActionExecutionResult> executeMono = dsConnectionMono
.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
StepVerifier.create(executeMono)
.assertNext(result -> {
final JsonNode node = ((ArrayNode) result.getBody()).get(0);
assertArrayEquals(
new String[]{
"user_id"
},
new ObjectMapper()
.convertValue(node, LinkedHashMap.class)
.keySet()
.toArray()
);
})
.verifyComplete();
}
@Test
public void testExecuteDataTypes() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
Mono<Object> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setBody("SELECT * FROM users WHERE id = 1");
Mono<ActionExecutionResult> executeMono = dsConnectionMono
.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
StepVerifier.create(executeMono)
.assertNext(result -> {
assertNotNull(result);
assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody());
final JsonNode node = ((ArrayNode) result.getBody()).get(0);
assertEquals("2018-12-31", node.get("dob").asText());
assertEquals("2018", node.get("yob").asText());
assertTrue(node.get("time1").asText().matches("\\d{2}:\\d{2}:\\d{2}"));
assertTrue(node.get("created_on").asText().matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z"));
assertTrue(node.get("updated_on").asText().matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z"));
assertArrayEquals(
new String[]{
"id",
"username",
"password",
"email",
"spouse_dob",
"dob",
"yob",
"time1",
"created_on",
"updated_on"
},
new ObjectMapper()
.convertValue(node, LinkedHashMap.class)
.keySet()
.toArray()
);
})
.verifyComplete();
} }
} }

View File

@ -47,9 +47,10 @@ public class PostgresPlugin extends BasePlugin {
private static final String USER = "user"; private static final String USER = "user";
private static final String PASSWORD = "password"; private static final String PASSWORD = "password";
private static final String SSL = "ssl"; private static final String SSL = "ssl";
private static final String DATE_COLUMN_TYPE_NAME = "date";
private static final int VALIDITY_CHECK_TIMEOUT = 5; private static final int VALIDITY_CHECK_TIMEOUT = 5;
private static final String DATE_COLUMN_TYPE_NAME = "date";
public PostgresPlugin(PluginWrapper wrapper) { public PostgresPlugin(PluginWrapper wrapper) {
super(wrapper); super(wrapper);
} }

View File

@ -38,6 +38,7 @@ public class PostgresPluginTest {
PostgresPlugin.PostgresPluginExecutor pluginExecutor = new PostgresPlugin.PostgresPluginExecutor(); PostgresPlugin.PostgresPluginExecutor pluginExecutor = new PostgresPlugin.PostgresPluginExecutor();
@SuppressWarnings("rawtypes") // The type parameter for the container type is just itself and is pseudo-optional.
@ClassRule @ClassRule
public static final PostgreSQLContainer pgsqlContainer = new PostgreSQLContainer<>("postgres:alpine") public static final PostgreSQLContainer pgsqlContainer = new PostgreSQLContainer<>("postgres:alpine")
.withExposedPorts(5432) .withExposedPorts(5432)
@ -114,8 +115,8 @@ public class PostgresPluginTest {
")"); ")");
} }
} catch (SQLException throwables) { } catch (SQLException throwable) {
throwables.printStackTrace(); throwable.printStackTrace();
} }
} }
@ -151,6 +152,33 @@ public class PostgresPluginTest {
.verifyComplete(); .verifyComplete();
} }
@Test
public void testAliasColumnNames() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
Mono<Object> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setBody("SELECT id as user_id FROM users WHERE id = 1");
Mono<ActionExecutionResult> executeMono = dsConnectionMono
.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
StepVerifier.create(executeMono)
.assertNext(result -> {
final JsonNode node = ((ArrayNode) result.getBody()).get(0);
assertArrayEquals(
new String[]{
"user_id"
},
new ObjectMapper()
.convertValue(node, LinkedHashMap.class)
.keySet()
.toArray()
);
})
.verifyComplete();
}
@Test @Test
public void testExecute() { public void testExecute() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration(); DatasourceConfiguration dsConfig = createDatasourceConfiguration();
@ -159,12 +187,11 @@ public class PostgresPluginTest {
ActionConfiguration actionConfiguration = new ActionConfiguration(); ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setBody("SELECT * FROM users WHERE id = 1"); actionConfiguration.setBody("SELECT * FROM users WHERE id = 1");
Mono<Object> executeMono = dsConnectionMono.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration)); Mono<ActionExecutionResult> executeMono = dsConnectionMono
.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
StepVerifier.create(executeMono) StepVerifier.create(executeMono)
.assertNext(obj -> { .assertNext(result -> {
ActionExecutionResult result = (ActionExecutionResult) obj;
assertNotNull(result); assertNotNull(result);
assertTrue(result.getIsExecutionSuccess()); assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody()); assertNotNull(result.getBody());

View File

@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import reactor.core.Exceptions; import reactor.core.Exceptions;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -742,4 +743,10 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
return Mono.just(Boolean.TRUE); return Mono.just(Boolean.TRUE);
} }
@Override
public Flux<User> get(MultiValueMap<String, String> params) {
// Get All Users should not be supported. Return an error
return Flux.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
}
} }

View File

@ -7,6 +7,18 @@
"policies": [], "policies": [],
"_class": "com.appsmith.server.domains.Organization", "_class": "com.appsmith.server.domains.Organization",
"$datasources": [ "$datasources": [
{
"name": "FreshDesk API",
"$pluginPackageName": "restapi-plugin",
"datasourceConfiguration": {
"sshProxyEnabled": false,
"url": "https://appsmithhelp.freshdesk.com"
},
"invalids": [],
"deleted": false,
"policies": [],
"_class": "com.appsmith.server.domains.Datasource"
},
{ {
"name": "Mock Database", "name": "Mock Database",
"$pluginPackageName": "postgres-plugin", "$pluginPackageName": "postgres-plugin",
@ -31,18 +43,6 @@
"deleted": false, "deleted": false,
"policies": [], "policies": [],
"_class": "com.appsmith.server.domains.Datasource" "_class": "com.appsmith.server.domains.Datasource"
},
{
"name": "FreshDesk API",
"$pluginPackageName": "restapi-plugin",
"datasourceConfiguration": {
"sshProxyEnabled": false,
"url": "https://appsmithhelp.freshdesk.com"
},
"invalids": [ ],
"deleted": false,
"policies": [],
"_class": "com.appsmith.server.domains.Datasource"
} }
], ],
"$applications": [ "$applications": [
@ -114,9 +114,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -276,9 +274,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -532,9 +528,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -798,9 +792,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -1477,9 +1469,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -1567,9 +1557,7 @@
"canOutsideClickClose": true, "canOutsideClickClose": true,
"type": "MODAL_WIDGET", "type": "MODAL_WIDGET",
"canEscapeKeyClose": true, "canEscapeKeyClose": true,
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "0", "parentId": "0",
"shouldScrollContents": true, "shouldScrollContents": true,
"blueprint": { "blueprint": {
@ -1819,9 +1807,7 @@
"bottomRow": 14, "bottomRow": 14,
"isVisible": true, "isVisible": true,
"type": "BUTTON_WIDGET", "type": "BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "9pgyrkail5", "parentId": "9pgyrkail5",
"isLoading": false, "isLoading": false,
"leftColumn": 12, "leftColumn": 12,
@ -2904,9 +2890,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -2994,9 +2978,7 @@
"canOutsideClickClose": true, "canOutsideClickClose": true,
"type": "MODAL_WIDGET", "type": "MODAL_WIDGET",
"canEscapeKeyClose": true, "canEscapeKeyClose": true,
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "0", "parentId": "0",
"shouldScrollContents": true, "shouldScrollContents": true,
"blueprint": { "blueprint": {
@ -3246,9 +3228,7 @@
"bottomRow": 14, "bottomRow": 14,
"isVisible": true, "isVisible": true,
"type": "BUTTON_WIDGET", "type": "BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "9pgyrkail5", "parentId": "9pgyrkail5",
"isLoading": false, "isLoading": false,
"leftColumn": 12, "leftColumn": 12,
@ -4470,9 +4450,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -4626,9 +4604,7 @@
"parentRowSpace": 40, "parentRowSpace": 40,
"isVisible": true, "isVisible": true,
"type": "BUTTON_WIDGET", "type": "BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "6rczwfuxqd", "parentId": "6rczwfuxqd",
"isLoading": false, "isLoading": false,
"parentColumnSpace": 34.5, "parentColumnSpace": 34.5,
@ -4806,9 +4782,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -4962,9 +4936,7 @@
"parentRowSpace": 40, "parentRowSpace": 40,
"isVisible": true, "isVisible": true,
"type": "BUTTON_WIDGET", "type": "BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "6rczwfuxqd", "parentId": "6rczwfuxqd",
"isLoading": false, "isLoading": false,
"parentColumnSpace": 34.5, "parentColumnSpace": 34.5,
@ -5252,9 +5224,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -5933,9 +5903,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -6710,9 +6678,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -6904,9 +6870,7 @@
"bottomRow": 14, "bottomRow": 14,
"isVisible": true, "isVisible": true,
"type": "FORM_BUTTON_WIDGET", "type": "FORM_BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "pqa1d7o4vt", "parentId": "pqa1d7o4vt",
"isLoading": false, "isLoading": false,
"disabledWhenInvalid": true, "disabledWhenInvalid": true,
@ -7522,9 +7486,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -7716,9 +7678,7 @@
"bottomRow": 14, "bottomRow": 14,
"isVisible": true, "isVisible": true,
"type": "FORM_BUTTON_WIDGET", "type": "FORM_BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "pqa1d7o4vt", "parentId": "pqa1d7o4vt",
"isLoading": false, "isLoading": false,
"disabledWhenInvalid": true, "disabledWhenInvalid": true,
@ -8479,9 +8439,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -8673,9 +8631,7 @@
"bottomRow": 12, "bottomRow": 12,
"isVisible": true, "isVisible": true,
"type": "FORM_BUTTON_WIDGET", "type": "FORM_BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "epboelq954", "parentId": "epboelq954",
"isLoading": false, "isLoading": false,
"disabledWhenInvalid": true, "disabledWhenInvalid": true,
@ -9042,9 +8998,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -9236,9 +9190,7 @@
"bottomRow": 12, "bottomRow": 12,
"isVisible": true, "isVisible": true,
"type": "FORM_BUTTON_WIDGET", "type": "FORM_BUTTON_WIDGET",
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "epboelq954", "parentId": "epboelq954",
"isLoading": false, "isLoading": false,
"disabledWhenInvalid": true, "disabledWhenInvalid": true,
@ -9652,7 +9604,6 @@
}, },
{ {
"name": "Table Tutorial", "name": "Table Tutorial",
"organizationId": "5f2a944780ca1f6faaed4e38",
"isPublic": true, "isPublic": true,
"$pages": [ "$pages": [
{ {
@ -9785,9 +9736,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -10118,9 +10067,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -10574,9 +10521,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -10801,9 +10746,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -11131,9 +11074,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -11544,9 +11485,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -12117,9 +12056,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -12206,9 +12143,7 @@
"canOutsideClickClose": true, "canOutsideClickClose": true,
"type": "MODAL_WIDGET", "type": "MODAL_WIDGET",
"canEscapeKeyClose": true, "canEscapeKeyClose": true,
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "0", "parentId": "0",
"shouldScrollContents": true, "shouldScrollContents": true,
"blueprint": { "blueprint": {
@ -12466,9 +12401,7 @@
}, },
"text": "Update", "text": "Update",
"isDisabled": false, "isDisabled": false,
"dynamicBindings": { "dynamicBindings": {}
}
}, },
{ {
"widgetName": "Text3", "widgetName": "Text3",
@ -12841,9 +12774,7 @@
"parentRowSpace": 1, "parentRowSpace": 1,
"type": "CANVAS_WIDGET", "type": "CANVAS_WIDGET",
"canExtend": true, "canExtend": true,
"dynamicBindings": { "dynamicBindings": {},
},
"version": 5, "version": 5,
"minHeight": 1292, "minHeight": 1292,
"parentColumnSpace": 1, "parentColumnSpace": 1,
@ -12930,9 +12861,7 @@
"canOutsideClickClose": true, "canOutsideClickClose": true,
"type": "MODAL_WIDGET", "type": "MODAL_WIDGET",
"canEscapeKeyClose": true, "canEscapeKeyClose": true,
"dynamicBindings": { "dynamicBindings": {},
},
"parentId": "0", "parentId": "0",
"shouldScrollContents": true, "shouldScrollContents": true,
"blueprint": { "blueprint": {
@ -13190,9 +13119,7 @@
}, },
"text": "Update", "text": "Update",
"isDisabled": false, "isDisabled": false,
"dynamicBindings": { "dynamicBindings": {}
}
}, },
{ {
"widgetName": "Text3", "widgetName": "Text3",

View File

@ -23,6 +23,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
@ -372,5 +375,17 @@ public class UserServiceTest {
.verifyComplete(); .verifyComplete();
} }
@Test
@WithUserDetails(value = "api_user")
public void getAllUsersTest() {
Flux<User> userFlux = userService.get(CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>()));
StepVerifier.create(userFlux)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.UNSUPPORTED_OPERATION.getMessage()))
.verify();
}
} }

View File

@ -42,7 +42,7 @@ Assuming you have node (>=v12), use the following command to run the migration:
```sh ```sh
npm install npm install
node main.js 'https://localhost/api/v1/' 'mongodb://localhost:27017/mobtools' node acl-migration.js 'https://localhost/api/v1/' 'mongodb://localhost:27017/mobtools'
``` ```
The first argument should be a running API endpoint, and the second argument should be a URI to the database that this The first argument should be a running API endpoint, and the second argument should be a URI to the database that this

View File

@ -0,0 +1,206 @@
if (process.argv.length !== 5) {
console.error("Takes three arguments, the MongoDB URL (like 'mongodb://localhost:27017/mobtools'),\n" +
"\tthe encryption salt and the encryption password used by the server connecting to this DB.");
process.exit(1);
}
const MONGODB_URL = process.argv[2];
const ENCRYPTION_SALT = process.argv[3];
const ENCRYPTION_PASSWORD = process.argv[4];
const { MongoClient, ObjectID } = require("mongodb");
const fs = require("fs");
const path = require("path");
const CryptoJS = require("crypto-js");
const mongoClient = new MongoClient(MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.time("total time taken");
main()
.then(() => console.log("\nFinished Successfully."))
.catch(error => console.error(error))
.finally(() => {
mongoClient.close();
console.timeEnd("total time taken");
console.log();
});
async function main() {
const con = await mongoClient.connect();
const db = con.db();
const pluginPackageNameByIds = {};
for (const plugin of await db.collection("plugin").find().toArray()) {
pluginPackageNameByIds[plugin._id.toString()] = plugin.packageName;
}
const templateOrganizationId = (await db.collection("config").findOne({name: "template-organization"})).config.organizationId;
const organization = await db.collection("organization").findOne({_id: ObjectID(templateOrganizationId)});
const $datasources = await db.collection("datasource")
.find({organizationId: templateOrganizationId, deleted: false})
.map(datasource => {
const datasourceConfiguration = datasource.datasourceConfiguration;
if (datasourceConfiguration.authentication && datasourceConfiguration.authentication.password) {
datasourceConfiguration.authentication.password = decrypt(datasourceConfiguration.authentication.password);
}
return {
name: datasource.name,
$pluginPackageName: pluginPackageNameByIds[datasource.pluginId],
datasourceConfiguration: datasourceConfiguration,
invalids: datasource.invalids,
deleted: false,
policies: [],
_class: datasource._class,
};
})
.toArray();
const allPageIds = [];
const allDefaultPageIds = new Set();
const $applications = await db.collection("application")
.find({organizationId: templateOrganizationId, deleted: false, isPublic: true})
.map(application => {
allPageIds.push(...application.pages.map(page => ObjectID(page._id)));
allDefaultPageIds.add(application.pages.filter(page => page.isDefault)[0]._id.toString());
return {
name: application.name,
isPublic: true,
$pages: [],
pages: application.pages,
deleted: false,
policies: [],
_class: application._class,
};
})
.toArray();
const actionsByPageId = {};
for (const action of await db.collection("action").find({organizationId: templateOrganizationId, deleted: false}).toArray()) {
if (!actionsByPageId[action.pageId]) {
actionsByPageId[action.pageId] = [];
}
let $isEmbedded = typeof action.datasource._id === "undefined";
actionsByPageId[action.pageId].push({
name: action.name,
datasource: {
$isEmbedded,
name: action.datasource.name,
$pluginPackageName: pluginPackageNameByIds[action.datasource.pluginId],
datasourceConfiguration: action.datasource.datasourceConfiguration,
invalids: action.datasource.invalids,
deleted: false,
policies: [],
},
actionConfiguration: action.actionConfiguration,
pluginType: action.pluginType,
executeOnLoad: action.executeOnLoad,
dynamicBindingPathList: action.dynamicBindingPathList,
isValid: action.isValid,
invalids: action.invalids,
jsonPathKeys: action.jsonPathKeys,
deleted: false,
policies: [],
_class: action._class,
});
}
const pagesById = {};
for (const page of await db.collection("page").find({_id: {$in: allPageIds}}).toArray()) {
const pageId = page._id.toString();
for (const layout of page.layouts) {
delete layout._id;
for (const actionSet of layout.layoutOnLoadActions) {
for (const action of actionSet) {
delete action._id;
}
}
for (const actionSet of layout.publishedLayoutOnLoadActions) {
for (const action of actionSet) {
delete action._id;
}
}
}
pagesById[pageId] = {
name: page.name,
$isDefault: allDefaultPageIds.has(pageId),
$actions: actionsByPageId[pageId],
layouts: page.layouts,
deleted: false,
policies: [],
_class: page._class,
};
}
for (const application of $applications) {
application.$pages = [];
for (const page of application.pages) {
application.$pages.push(pagesById[page._id]);
}
delete application.pages;
}
const finalData = {
name: organization.name,
organizationSettings: organization.organizationSettings,
slug: organization.slug,
userRoles: [],
deleted: false,
policies: [],
_class: organization._class,
$datasources,
$applications,
};
if (finalData.slug !== "example-apps") {
console.warn("The slug of the organization in the generated dump is not `example-apps`. This might be significant.");
}
fs.writeFileSync(
findExamplesJsonPath(),
JSON.stringify(finalData, null, 2)
);
}
function findExamplesJsonPath() {
let projectDir = __dirname;
while (projectDir != null && !fs.existsSync(path.join(projectDir, "appsmith-server"))) {
projectDir = path.dirname(projectDir);
}
return path.join(projectDir, "appsmith-server", "src", "main", "resources", "examples-organization.json");
}
/*!
* Author: flohall
* date: 2019-11-05
* file: module/textEncryptor.js
* Original: <https://stackoverflow.com/a/58720652/151048>.
*/
const key = CryptoJS.PBKDF2(ENCRYPTION_PASSWORD, ENCRYPTION_SALT, {
keySize: 256 / 32,
iterations: 1024
});
const decryptConfig = {
// same as NULL_IV_GENERATOR of AesBytesEncryptor - so encryption creates always same cipher text for same input
iv: {words: [0, 0, 0, 0, 0, 0, 0, 0], sigBytes: 0},
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
};
function decrypt(text) {
return CryptoJS.AES
.decrypt({ciphertext: CryptoJS.enc.Hex.parse(text)}, key, decryptConfig)
.toString(CryptoJS.enc.Utf8);
}

View File

@ -45,6 +45,12 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true "dev": true
}, },
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==",
"dev": true
},
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",

View File

@ -3,15 +3,16 @@
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"private": true, "private": true,
"main": "main.js",
"scripts": { "scripts": {
"run": "node main.js" "acl-migration": "node acl-migration.js",
"dump-examples-org": "node dump-examples-org.js"
}, },
"keywords": [], "keywords": [],
"author": "Appsmith", "author": "Appsmith",
"devDependencies": { "devDependencies": {
"axios": "^0.19.2", "axios": "^0.19.2",
"axios-cookiejar-support": "^1.0.0", "axios-cookiejar-support": "^1.0.0",
"crypto-js": "^4.0.0",
"mongodb": "^3.5.8", "mongodb": "^3.5.8",
"tough-cookie": "^4.0.0" "tough-cookie": "^4.0.0"
} }

View File

@ -6,43 +6,65 @@ is_command_present() {
type "$1" >/dev/null 2>&1 type "$1" >/dev/null 2>&1
} }
is_mac() {
[[ $OSTYPE == darwin* ]]
}
# This function checks if the relevant ports required by Appsmith are available or not # This function checks if the relevant ports required by Appsmith are available or not
# The script should error out in case they aren't available # The script should error out in case they aren't available
check_ports_occupied() { check_ports_occupied() {
ports_occupied="$( local port_check_output
if [[ "$OSTYPE" == "darwin"* ]]; then local ports_pattern="80|443"
sudo netstat -anp tcp
else if is_mac; then
sudo netstat -tupln tcp port_check_output="$(netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /^.*\.('"$ports_pattern"')$/')"
fi | awk '$6 == "LISTEN" && $4 ~ /^.*[.:](80|443)$/' | wc -l | grep -o '[[:digit:]]\+' elif is_command_present ss; then
)" # The `ss` command seems to be a better/faster version of `netstat`, but is not available on all Linux
# distributions by default. Other distributions have `ss` but no `netstat`. So, we try for `ss` first, then
# fallback to `netstat`.
port_check_output="$(ss --all --numeric --tcp | awk '$1 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')"
elif is_command_present netstat; then
port_check_output="$(netstat --all --numeric --tcp | awk '$6 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')"
fi
if [[ -n $port_check_output ]]; then
echo "+++++++++++ ERROR ++++++++++++++++++++++"
echo "Appsmith requires ports 80 & 443 to be open. Please shut down any other service(s) that may be running on these ports."
echo "++++++++++++++++++++++++++++++++++++++++"
echo ""
bye
fi
} }
install_docker() { install_docker() {
if [[ $package_manager -eq apt-get ]];then
echo "++++++++++++++++++++++++" echo "++++++++++++++++++++++++"
echo "Setting up docker repos" echo "Setting up docker repos"
sudo $package_manager update --quiet
sudo apt-get -y --quiet install gnupg-agent if [[ $package_manager == apt-get ]]; then
apt_cmd="sudo apt-get --yes --quiet"
$apt_cmd update
$apt_cmd install gnupg-agent
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$(lsb_release -cs) \ $apt_cmd update
stable" echo "Installing docker"
$apt_cmd install docker-ce docker-ce-cli containerd.io
else else
sudo yum install -y yum-utils yum_cmd="sudo yum --assumeyes --quiet"
$yum_cmd install yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
echo "Installing docker"
$yum_cmd install docker-ce docker-ce-cli containerd.io
fi fi
sudo ${package_manager} -y update --quiet
echo "Installing docker"
sudo ${package_manager} -y install docker-ce docker-ce-cli containerd.io --quiet
} }
install_docker_compose() { install_docker_compose() {
if [ $package_manager == "apt-get" -o $package_manager == "yum" ];then if [[ $package_manager == "apt-get" || $package_manager == "yum" ]]; then
if [ ! -f /usr/bin/docker-compose ];then if [[ ! -f /usr/bin/docker-compose ]];then
echo "Installing docker-compose..." echo "Installing docker-compose..."
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
@ -67,29 +89,29 @@ start_docker() {
} }
check_os() { check_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then if is_mac; then
package_manager="brew" package_manager="brew"
desired_os=1 desired_os=1
return return
fi fi
os_name=`cat /etc/*-release | egrep "^NAME="` os_name="$(cat /etc/*-release | awk -F= '$1 == "NAME" { gsub(/"/, ""); print $2; exit }')"
os_name="${os_name#*=}"
case "${os_name}" in case "$os_name" in
\"Ubuntu*\") Ubuntu*)
desired_os=1 desired_os=1
package_manager="apt-get" package_manager="apt-get"
;; ;;
\"Red\ Hat*\") Red\ Hat*)
desired_os=0 desired_os=1
package_manager="yum" package_manager="yum"
;; ;;
\"CentOS*\") CentOS*)
desired_os=0 desired_os=1
package_manager="yum" package_manager="yum"
;; ;;
*) desired_os=0 *)
desired_os=0
esac esac
} }
@ -98,16 +120,11 @@ overwrite_file() {
local template_file="$2" local template_file="$2"
local full_path="$install_dir/$relative_path" local full_path="$install_dir/$relative_path"
if [[ -f $full_path ]]; then if [[ -f $full_path ]] && ! confirm y "File $relative_path already exists. Would you like to replace it?"; then
read -p "File $relative_path already exists. Would you like to replace it? [Y]: " value echo "You chose NOT to replace existing file: '$full_path'."
value=${value:-Y}
if ! [[ $value == "Y" || $value == "y" || $value == "yes" || $value == "Yes" ]]; then
echo "You chose not to replace existing file: '$full_path'."
rm -f "$template_file" rm -f "$template_file"
echo "File $template_file removed from source directory." echo "File $template_file removed from source directory."
echo "" echo ""
fi
else else
mv -f "$template_file" "$full_path" mv -f "$template_file" "$full_path"
echo "File $full_path moved successfully!" echo "File $full_path moved successfully!"
@ -116,30 +133,28 @@ overwrite_file() {
# This function prompts the user for an input for a non-empty Mongo root password. # This function prompts the user for an input for a non-empty Mongo root password.
read_mongo_password() { read_mongo_password() {
read -sp 'Set the mongo password: ' mongo_root_password read -srp 'Set the mongo password: ' mongo_root_password
while [[ -z $mongo_root_password ]] while [[ -z $mongo_root_password ]]; do
do
echo "" echo ""
echo "" echo ""
echo "+++++++++++ ERROR ++++++++++++++++++++++" echo "+++++++++++ ERROR ++++++++++++++++++++++"
echo "The mongo password cannot be empty. Please input a valid password string." echo "The mongo password cannot be empty. Please input a valid password string."
echo "++++++++++++++++++++++++++++++++++++++++" echo "++++++++++++++++++++++++++++++++++++++++"
echo "" echo ""
read -sp 'Set the mongo password: ' mongo_root_password read -srp 'Set the mongo password: ' mongo_root_password
done done
} }
# This function prompts the user for an input for a non-empty Mongo username. # This function prompts the user for an input for a non-empty Mongo username.
read_mongo_username() { read_mongo_username() {
read -p 'Set the mongo root user: ' mongo_root_user read -rp 'Set the mongo root user: ' mongo_root_user
while [[ -z $mongo_root_user ]] while [[ -z $mongo_root_user ]]; do
do
echo "" echo ""
echo "+++++++++++ ERROR ++++++++++++++++++++++" echo "+++++++++++ ERROR ++++++++++++++++++++++"
echo "The mongo username cannot be empty. Please input a valid username string." echo "The mongo username cannot be empty. Please input a valid username string."
echo "++++++++++++++++++++++++++++++++++++++++" echo "++++++++++++++++++++++++++++++++++++++++"
echo "" echo ""
read -p 'Set the mongo root user: ' mongo_root_user read -rp 'Set the mongo root user: ' mongo_root_user
done done
} }
@ -163,7 +178,7 @@ wait_for_containers_start() {
urlencode() { urlencode() {
# urlencode <string> # urlencode <string>
old_lc_collate=$LC_COLLATE local old_lc_collate="$LC_COLLATE"
LC_COLLATE=C LC_COLLATE=C
local length="${#1}" local length="${#1}"
@ -175,13 +190,44 @@ urlencode() {
esac esac
done done
LC_COLLATE=$old_lc_collate LC_COLLATE="$old_lc_collate"
}
generate_password() {
# Picked up the following method of generation from : https://gist.github.com/earthgecko/3089509
LC_CTYPE=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 13 | head -n 1
}
confirm() {
local default="$1" # Should be `y` or `n`.
local prompt="$2"
local options="y/N"
if [[ $default == y || $default == Y ]]; then
options="Y/n"
fi
local answer
read -n1 -rp "$prompt [$options] " answer
if [[ -z $answer ]]; then
# No answer given, the user just hit the Enter key. Take the default value as the answer.
answer="$default"
else
# An answer was given. This means the user didn't get to hit Enter so the cursor on the same line. Do an empty
# echo so the cursor moves to a new line.
echo
fi
[[ yY =~ $answer ]]
}
echo_contact_support() {
echo "Please contact <support@appsmith.com> with your OS details and version${1:-.}"
} }
bye() { # Prints a friendly good bye message and exits the script. bye() { # Prints a friendly good bye message and exits the script.
echo "" echo -e "\nExiting for now. Bye! \U1F44B\n"
echo -e "Exiting for now. Bye! \U1F44B" exit 1
exit
} }
echo -e "\U1F44B Thank you for trying out Appsmith! " echo -e "\U1F44B Thank you for trying out Appsmith! "
@ -196,32 +242,38 @@ check_os
if [[ $desired_os -eq 0 ]];then if [[ $desired_os -eq 0 ]];then
echo "" echo ""
echo "This script is currently meant to install Appsmith on Mac OS X | Ubuntu | RHEL | CentOS machines." echo "This script is currently meant to install Appsmith on Mac OS X | Ubuntu | RHEL | CentOS machines."
echo "Please contact support@appsmith.com with your OS details if you wish to extend this support" echo_contact_support " if you wish to extend this support."
bye bye
else else
echo "You're on an OS that is supported by this installation script." echo "You're on an OS that is supported by this installation script."
echo "" echo ""
fi fi
if [[ "$OSTYPE" == "darwin"* && "$EUID" -eq 0 ]]; then if [[ $EUID -eq 0 ]]; then
echo "Please do not run this script with root permissions on macOS." echo "Please do not run this script as root/sudo."
echo "Please contact support@appsmith.com with your OS details if you wish to extend this support" echo_contact_support
bye bye
fi fi
check_ports_occupied check_ports_occupied
if [[ $ports_occupied -ne 0 ]]; then read -rp 'Installation Directory [appsmith]: ' install_dir
echo "+++++++++++ ERROR ++++++++++++++++++++++" install_dir="${install_dir:-appsmith}"
echo "Appsmith requires ports 80 & 443 to be open. Please shut down any other service(s) that may be running on these ports." if [[ $install_dir != /* ]]; then
echo "++++++++++++++++++++++++++++++++++++++++" # If it's not an absolute path, prepend current working directory to it, to make it an absolute path.
echo "" install_dir="$PWD/$install_dir"
bye fi
if [[ -e "$install_dir" ]]; then
echo "The path '$install_dir' is already present. Please run the script again with a different path to install new."
echo "If you're trying to update your existing installation, that happens automatically through WatchTower."
echo_contact_support " if you're facing problems with the auto-updates."
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 # 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 ! is_command_present docker; then
if [ $package_manager == "apt-get" -o $package_manager == "yum" ];then if [[ $package_manager == "apt-get" || $package_manager == "yum" ]]; then
install_docker install_docker
else else
echo "" echo ""
@ -229,7 +281,7 @@ if ! is_command_present docker ;then
echo "Docker Desktop must be installed manually on Mac OS to proceed. Docker can only be installed automatically on Ubuntu / Redhat / Cent OS" 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 "https://docs.docker.com/docker-for-mac/install/"
echo "++++++++++++++++++++++++++++++++++++++++++++++++" echo "++++++++++++++++++++++++++++++++++++++++++++++++"
exit exit 1
fi fi
fi fi
@ -243,31 +295,12 @@ if [[ $package_manager == "yum" || $package_manager == "apt-get" ]]; then
start_docker start_docker
fi fi
read -p 'Installation Directory [appsmith]: ' install_dir echo "Installing Appsmith to '$install_dir'."
install_dir="${install_dir:-appsmith}" mkdir -p "$install_dir"
mkdir -p "$PWD/$install_dir"
install_dir="$PWD/$install_dir"
read -p 'Is this a fresh installation? [Y/n]' fresh_install
fresh_install="${fresh_install:-Y}"
echo "" echo ""
if [ $fresh_install == "N" -o $fresh_install == "n" -o $fresh_install == "no" -o $fresh_install == "No" ];then if confirm y "Is this a fresh installation?"; then
read -p 'Enter your current mongo db host: ' mongo_host echo "Appsmith needs to create a MongoDB instance."
read -p 'Enter your current mongo root user: ' mongo_root_user
read -sp 'Enter your current mongo password: ' mongo_root_password
read -p 'Enter your current mongo database name: ' mongo_database
# It is possible that this isn't the first installation.
echo ""
read -p 'Do you have any existing data in the database?[Y/n]: ' existing_encrypted_data
existing_encrypted_data=${existing_encrypted_data:-Y}
# In this case be more cautious of auto generating the encryption keys. Err on the side of not generating the encryption keys
if [ $existing_encrypted_data == "N" -o $existing_encrypted_data == "n" -o $existing_encrypted_data == "no" -o $existing_encrypted_data == "No" ];then
auto_generate_encryption="true"
else
auto_generate_encryption="false"
fi
elif [ $fresh_install == "Y" -o $fresh_install == "y" -o $fresh_install == "yes" -o $fresh_install == "Yes" ];then
echo "Appsmith needs to create a mongo db"
mongo_host="mongo" mongo_host="mongo"
mongo_database="appsmith" mongo_database="appsmith"
@ -277,12 +310,25 @@ elif [ $fresh_install == "Y" -o $fresh_install == "y" -o $fresh_install == "yes"
# 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"
else
read -rp 'Enter your current mongo db host: ' mongo_host
read -rp 'Enter your current mongo root user: ' mongo_root_user
read -srp 'Enter your current mongo password: ' mongo_root_password
read -rp 'Enter your current mongo database name: ' mongo_database
# It is possible that this isn't the first installation.
echo ""
# In this case be more cautious of auto generating the encryption keys. Err on the side of not generating the encryption keys
if confirm y "Do you have any existing data in the database?"; then
auto_generate_encryption="false"
else
auto_generate_encryption="true"
fi
fi fi
echo "" echo ""
# urlencoding the Mongo username and password # urlencoding the Mongo username and password
encoded_mongo_root_user=$( urlencode $mongo_root_user ) encoded_mongo_root_user=$(urlencode "$mongo_root_user")
encoded_mongo_root_password=$( urlencode $mongo_root_password ) encoded_mongo_root_password=$(urlencode "$mongo_root_password")
encryptionEnv=./template/encryption.env encryptionEnv=./template/encryption.env
if test -f "$encryptionEnv"; then if test -f "$encryptionEnv"; then
@ -290,7 +336,7 @@ if test -f "$encryptionEnv"; then
echo "1) No. Conserve the older encryption password and salt and continue" echo "1) No. Conserve the older encryption password and salt and continue"
echo "2) Yes. Overwrite the existing encryption (NOT SUGGESTED) with autogenerated encryption password and salt" echo "2) Yes. Overwrite the existing encryption (NOT SUGGESTED) with autogenerated encryption password and salt"
echo "3) Yes. Overwrite the existing encryption (NOT SUGGESTED) with manually entering the encryption password and salt" echo "3) Yes. Overwrite the existing encryption (NOT SUGGESTED) with manually entering the encryption password and salt"
read -p 'Enter option number [1]: ' overwrite_encryption read -rp 'Enter option number [1]: ' overwrite_encryption
overwrite_encryption=${overwrite_encryption:-1} overwrite_encryption=${overwrite_encryption:-1}
auto_generate_encryption="false" auto_generate_encryption="false"
if [[ $overwrite_encryption -eq 1 ]];then if [[ $overwrite_encryption -eq 1 ]];then
@ -309,22 +355,17 @@ fi
if [[ "$setup_encryption" = "true" ]];then if [[ "$setup_encryption" = "true" ]];then
if [[ "$auto_generate_encryption" = "false" ]];then if [[ "$auto_generate_encryption" = "false" ]];then
echo "Please enter the salt and password found in the encyption.env file of your previous appsmith installation " echo "Please enter the salt and password found in the encyption.env file of your previous appsmith installation "
read -p 'Enter your encryption password: ' user_encryption_password read -rp 'Enter your encryption password: ' user_encryption_password
read -p 'Enter your encryption salt: ' user_encryption_salt read -rp 'Enter your encryption salt: ' user_encryption_salt
elif [[ "$auto_generate_encryption" = "true" ]]; then elif [[ "$auto_generate_encryption" = "true" ]]; then
# Picked up the following method of generation from : https://gist.github.com/earthgecko/3089509 user_encryption_password=$(generate_password)
user_encryption_password=$(cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 13 | head -n 1) user_encryption_salt=$(generate_password)
user_encryption_salt=$(cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 13 | head -n 1)
fi fi
fi fi
echo "" echo ""
read -p 'Do you have a custom domain that you would like to link? (Only for cloud installations) [N/y]: ' setup_domain
setup_domain=${setup_domain:-N}
# Setting default value for the setup_ssl variable. Without this, the script errors out in the if condition later
setup_ssl='N'
if [ $setup_domain == "Y" -o $setup_domain == "y" -o $setup_domain == "yes" -o $setup_domain == "Yes" ];then if confirm n "Do you have a custom domain that you would like to link? (Only for cloud installations)"; then
echo "" echo ""
echo "+++++++++++ IMPORTANT PLEASE READ ++++++++++++++++++++++" echo "+++++++++++ IMPORTANT PLEASE READ ++++++++++++++++++++++"
echo "Please update your DNS records with your domain registrar" echo "Please update your DNS records with your domain registrar"
@ -333,12 +374,9 @@ if [ $setup_domain == "Y" -o $setup_domain == "y" -o $setup_domain == "yes" -o $
echo "+++++++++++++++++++++++++++++++++++++++++++++++" echo "+++++++++++++++++++++++++++++++++++++++++++++++"
echo "" echo ""
echo "Would you like to provision an SSL certificate for your custom domain / subdomain?" echo "Would you like to provision an SSL certificate for your custom domain / subdomain?"
read -p '(Your DNS records must be updated for us to proceed) [Y/n]: ' setup_ssl if confirm y '(Your DNS records must be updated for us to proceed)'; then
setup_ssl=${setup_ssl:-Y} read -rp 'Enter the domain or subdomain on which you want to host appsmith (example.com / app.example.com): ' custom_domain
fi fi
if [ $setup_ssl == "Y" -o $setup_ssl == "y" -o $setup_ssl == "yes" -o $setup_ssl == "Yes" ]; then
read -p 'Enter the domain or subdomain on which you want to host appsmith (example.com / app.example.com): ' custom_domain
fi fi
NGINX_SSL_CMNT="" NGINX_SSL_CMNT=""
@ -348,8 +386,11 @@ fi
echo "" echo ""
echo "Downloading the configuration templates..." echo "Downloading the configuration templates..."
mkdir -p template templates_dir="$(mktemp -d)"
( cd template mkdir -p "$templates_dir"
(
cd "$templates_dir"
curl --remote-name-all --silent --show-error \ curl --remote-name-all --silent --show-error \
https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker-compose.yml.sh \ https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/docker-compose.yml.sh \
https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/init-letsencrypt.sh.sh \ https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/init-letsencrypt.sh.sh \
@ -359,22 +400,20 @@ curl --remote-name-all --silent --show-error \
https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/encryption.env.sh https://raw.githubusercontent.com/appsmithorg/appsmith/release/deploy/template/encryption.env.sh
) )
# Role - Folder # Create needed folder structure.
for directory_name in nginx certbot/conf certbot/www mongo/db mkdir -p "$install_dir/data/"{nginx,certbot/{conf,www},mongo/db}
do
mkdir -p "$install_dir/data/$directory_name"
done
echo "" echo ""
echo "Generating the configuration files from the templates" echo "Generating the configuration files from the templates"
. ./template/nginx_app.conf.sh bash "$templates_dir/nginx_app.conf.sh" "$NGINX_SSL_CMNT" "$custom_domain" > nginx_app.conf
. ./template/docker-compose.yml.sh bash "$templates_dir/docker-compose.yml.sh" "$mongo_root_user" "$mongo_root_password" "$mongo_database" > docker-compose.yml
. ./template/mongo-init.js.sh bash "$templates_dir/mongo-init.js.sh" "$mongo_root_user" "$mongo_root_password" > mongo-init.js
. ./template/init-letsencrypt.sh.sh bash "$templates_dir"/init-letsencrypt.sh.sh "$custom_domain" > init-letsencrypt.sh
. ./template/docker.env.sh bash "$templates_dir/docker.env.sh" "$encoded_mongo_root_user" "$encoded_mongo_root_password" "$mongo_host" > docker.env
if [[ "$setup_encryption" = "true" ]]; then if [[ "$setup_encryption" = "true" ]]; then
. ./template/encryption.env.sh bash "$templates_dir/encryption.env.sh" "$user_encryption_password" "$user_encryption_salt" > encryption.env
fi fi
rm -rf "$templates_dir"
chmod 0755 init-letsencrypt.sh chmod 0755 init-letsencrypt.sh
overwrite_file "data/nginx/app.conf.template" "nginx_app.conf" overwrite_file "data/nginx/app.conf.template" "nginx_app.conf"
@ -430,5 +469,4 @@ else
echo "Join our Discord server https://discord.com/invite/rBTTVJp" echo "Join our Discord server https://discord.com/invite/rBTTVJp"
fi fi
echo "" echo -e "\nPeace out \U1F596\n"
echo -e "Peace out \U1F596\n"

View File

@ -1,10 +1,12 @@
#!/bin/sh #!/bin/bash
if [ ! -f docker-compose.yml ]; then set -o nounset
touch docker-compose.yml
fi
cat >| docker-compose.yml << EOF mongo_root_user="$1"
mongo_root_password="$2"
mongo_database="$3"
cat <<EOF
version: "3.7" version: "3.7"
services: services:
@ -21,6 +23,8 @@ services:
command: "/bin/sh -c 'while :; do sleep 6h & wait \$\${!}; nginx -s reload; done & /start-nginx.sh'" command: "/bin/sh -c 'while :; do sleep 6h & wait \$\${!}; nginx -s reload; done & /start-nginx.sh'"
depends_on: depends_on:
- appsmith-internal-server - appsmith-internal-server
labels:
com.centurylinklabs.watchtower.enable: "true"
networks: networks:
- appsmith - appsmith
@ -44,6 +48,8 @@ services:
- mongo - mongo
depends_on: depends_on:
- mongo - mongo
labels:
com.centurylinklabs.watchtower.enable: "true"
networks: networks:
- appsmith - appsmith
@ -52,7 +58,7 @@ services:
expose: expose:
- "27017" - "27017"
environment: environment:
- MONGO_INITDB_DATABASE=appsmith - MONGO_INITDB_DATABASE=$mongo_database
- MONGO_INITDB_ROOT_USERNAME=$mongo_root_user - MONGO_INITDB_ROOT_USERNAME=$mongo_root_user
- MONGO_INITDB_ROOT_PASSWORD=$mongo_root_password - MONGO_INITDB_ROOT_PASSWORD=$mongo_root_password
volumes: volumes:
@ -68,8 +74,16 @@ services:
networks: networks:
- appsmith - appsmith
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# Update check interval in seconds.
command: --interval 300 --label-enable
networks:
- appsmith
networks: networks:
appsmith: appsmith:
driver: bridge driver: bridge
EOF EOF

View File

@ -1,10 +1,12 @@
#!/bin/sh #!/bin/bash
if [ ! -f docker-compose.yml ]; then set -o nounset
touch docker-compose.yml
fi
cat >| docker.env << EOF encoded_mongo_root_user="$1"
encoded_mongo_root_password="$2"
mongo_host="$3"
cat << EOF
# Read our documentation on how to configure these features # Read our documentation on how to configure these features
# https://docs.appsmith.com/v/v1.1/enabling-3p-services # https://docs.appsmith.com/v/v1.1/enabling-3p-services

View File

@ -1,11 +1,11 @@
#!/bin/sh #!/bin/bash
if [ ! -f encryption.env ]; then set -o nounset
touch encryption.env
fi
cat >| encryption.env << EOF user_encryption_password="$1"
user_encryption_salt="$2"
cat <<EOF
APPSMITH_ENCRYPTION_PASSWORD=$user_encryption_password APPSMITH_ENCRYPTION_PASSWORD=$user_encryption_password
APPSMITH_ENCRYPTION_SALT=$user_encryption_salt APPSMITH_ENCRYPTION_SALT=$user_encryption_salt
EOF EOF

View File

@ -1,12 +1,10 @@
#!/bin/sh #!/bin/bash
if [ ! -f init-letsencrypt.sh ]; then set -o nounset
touch init-letsencrypt.sh
fi
custom_domain="$1"
cat << EOF
cat >| init-letsencrypt.sh << EOF
#!/bin/bash #!/bin/bash
if ! [ -x "\$(command -v docker-compose)" ]; then if ! [ -x "\$(command -v docker-compose)" ]; then

View File

@ -1,12 +1,11 @@
#!/bin/sh #!/bin/bash
if [ ! -f mongo-init.js ]; then set -o nounset
touch mongo-init.js
fi
mongo_root_user="$1"
mongo_root_password="$2"
cat << EOF
cat >| mongo-init.js << EOF
let error = false let error = false
print("**** Going to start Mongo seed ****") print("**** Going to start Mongo seed ****")
@ -1534,7 +1533,4 @@ printjson(res)
if (error) { if (error) {
print('Error occurred while inserting the records') print('Error occurred while inserting the records')
} }
EOF EOF

View File

@ -1,15 +1,16 @@
#!/bin/sh #!/bin/bash
if [ ! -f nginx_app.conf ]; then set -o nounset
touch nginx_app.conf
fi
# This template file is different from the others because of the sub_filter commands in the Nginx configuration # In the config file, there's three types of variables, all represented with the syntax `$name`. The ones that are not
# Those variables are substituted inside the Docker container for appsmith-editor during bootup. # escaped with a backslash are rendered within this script. Among the ones that are escaped with a backslash, the ones
# Hence we wish to prevent environment substitution here. # starting with `APPSMITH_` will be rendered at boot-up time by appsmith-editor docker container. The rest (like $scheme
# Relevant variables will be replaced at the end of this file via sed command # and $host) are for nginx to work out.
content=' NGINX_SSL_CMNT="$1"
custom_domain="$2"
cat <<EOF
server { server {
listen 80; listen 80;
$NGINX_SSL_CMNT server_name $custom_domain ; $NGINX_SSL_CMNT server_name $custom_domain ;
@ -24,29 +25,29 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
root /var/www/certbot; root /var/www/certbot;
} }
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Host \$host;
location / { location / {
try_files $uri /index.html =404; try_files \$uri /index.html =404;
sub_filter __APPSMITH_SENTRY_DSN__ '\''${APPSMITH_SENTRY_DSN}'\''; sub_filter __APPSMITH_SENTRY_DSN__ '\${APPSMITH_SENTRY_DSN}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '\''${APPSMITH_SMART_LOOK_ID}'\''; sub_filter __APPSMITH_SMART_LOOK_ID__ '\${APPSMITH_SMART_LOOK_ID}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\''${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}'\''; sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\''${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}'\''; sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\''${APPSMITH_MARKETPLACE_ENABLED}'\''; sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\${APPSMITH_MARKETPLACE_ENABLED}';
sub_filter __APPSMITH_SEGMENT_KEY__ '\''${APPSMITH_SEGMENT_KEY}'\''; sub_filter __APPSMITH_SEGMENT_KEY__ '\${APPSMITH_SEGMENT_KEY}';
sub_filter __APPSMITH_OPTIMIZELY_KEY__ '\''${APPSMITH_OPTIMIZELY_KEY}'\''; sub_filter __APPSMITH_OPTIMIZELY_KEY__ '\${APPSMITH_OPTIMIZELY_KEY}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '\''${APPSMITH_ALGOLIA_API_ID}'\''; sub_filter __APPSMITH_ALGOLIA_API_ID__ '\${APPSMITH_ALGOLIA_API_ID}';
sub_filter __APPSMITH_ALGOLIA_SEARCH_INDEX_NAME__ '\''${APPSMITH_ALGOLIA_SEARCH_INDEX_NAME}'\''; sub_filter __APPSMITH_ALGOLIA_SEARCH_INDEX_NAME__ '\${APPSMITH_ALGOLIA_SEARCH_INDEX_NAME}';
sub_filter __APPSMITH_ALGOLIA_API_KEY__ '\''${APPSMITH_ALGOLIA_API_KEY}'\''; sub_filter __APPSMITH_ALGOLIA_API_KEY__ '\${APPSMITH_ALGOLIA_API_KEY}';
sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\''${APPSMITH_CLIENT_LOG_LEVEL}'\''; sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\${APPSMITH_CLIENT_LOG_LEVEL}';
sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\''${APPSMITH_GOOGLE_MAPS_API_KEY}'\''; sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\${APPSMITH_GOOGLE_MAPS_API_KEY}';
sub_filter __APPSMITH_TNC_PP__ '\''${APPSMITH_TNC_PP}'\''; sub_filter __APPSMITH_TNC_PP__ '\${APPSMITH_TNC_PP}';
sub_filter __APPSMITH_VERSION_ID__ '\''${APPSMITH_VERSION_ID}'\''; sub_filter __APPSMITH_VERSION_ID__ '\${APPSMITH_VERSION_ID}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\''${APPSMITH_VERSION_RELEASE_DATE}'\''; sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\${APPSMITH_VERSION_RELEASE_DATE}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '\''${APPSMITH_INTERCOM_APP_ID}'\''; sub_filter __APPSMITH_INTERCOM_APP_ID__ '\${APPSMITH_INTERCOM_APP_ID}';
sub_filter __APPSMITH_MAIL_ENABLED__ '\''${APPSMITH_MAIL_ENABLED}'\''; sub_filter __APPSMITH_MAIL_ENABLED__ '\${APPSMITH_MAIL_ENABLED}';
} }
location /f { location /f {
@ -76,32 +77,32 @@ $NGINX_SSL_CMNT
$NGINX_SSL_CMNT include /etc/letsencrypt/options-ssl-nginx.conf; $NGINX_SSL_CMNT include /etc/letsencrypt/options-ssl-nginx.conf;
$NGINX_SSL_CMNT ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; $NGINX_SSL_CMNT ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT proxy_set_header X-Forwarded-Proto $scheme; $NGINX_SSL_CMNT proxy_set_header X-Forwarded-Proto \$scheme;
$NGINX_SSL_CMNT proxy_set_header X-Forwarded-Host $host; $NGINX_SSL_CMNT proxy_set_header X-Forwarded-Host \$host;
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT root /var/www/appsmith; $NGINX_SSL_CMNT root /var/www/appsmith;
$NGINX_SSL_CMNT index index.html index.htm; $NGINX_SSL_CMNT index index.html index.htm;
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT location / { $NGINX_SSL_CMNT location / {
$NGINX_SSL_CMNT try_files $uri /index.html =404; $NGINX_SSL_CMNT try_files \$uri /index.html =404;
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT sub_filter __APPSMITH_SENTRY_DSN__ '\''${APPSMITH_SENTRY_DSN}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_SENTRY_DSN__ '\${APPSMITH_SENTRY_DSN}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_SMART_LOOK_ID__ '\''${APPSMITH_SMART_LOOK_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_SMART_LOOK_ID__ '\${APPSMITH_SMART_LOOK_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\''${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '\${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\''${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '\${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\''${APPSMITH_MARKETPLACE_ENABLED}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '\${APPSMITH_MARKETPLACE_ENABLED}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_SEGMENT_KEY__ '\''${APPSMITH_SEGMENT_KEY}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_SEGMENT_KEY__ '\${APPSMITH_SEGMENT_KEY}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_OPTIMIZELY_KEY__ '\''${APPSMITH_OPTIMIZELY_KEY}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_OPTIMIZELY_KEY__ '\${APPSMITH_OPTIMIZELY_KEY}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_ID__ '\''${APPSMITH_ALGOLIA_API_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_ID__ '\${APPSMITH_ALGOLIA_API_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_SEARCH_INDEX_NAME__ '\''${APPSMITH_ALGOLIA_SEARCH_INDEX_NAME}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_SEARCH_INDEX_NAME__ '\${APPSMITH_ALGOLIA_SEARCH_INDEX_NAME}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_KEY__ '\''${APPSMITH_ALGOLIA_API_KEY}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_KEY__ '\${APPSMITH_ALGOLIA_API_KEY}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\''${APPSMITH_CLIENT_LOG_LEVEL}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\${APPSMITH_CLIENT_LOG_LEVEL}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\''${APPSMITH_GOOGLE_MAPS_API_KEY}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\${APPSMITH_GOOGLE_MAPS_API_KEY}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_TNC_PP__ '\''${APPSMITH_TNC_PP}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_TNC_PP__ '\${APPSMITH_TNC_PP}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_ID__ '\''${APPSMITH_VERSION_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_ID__ '\${APPSMITH_VERSION_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\''${APPSMITH_VERSION_RELEASE_DATE}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\${APPSMITH_VERSION_RELEASE_DATE}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_INTERCOM_APP_ID__ '\''${APPSMITH_INTERCOM_APP_ID}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_INTERCOM_APP_ID__ '\${APPSMITH_INTERCOM_APP_ID}';
$NGINX_SSL_CMNT sub_filter __APPSMITH_MAIL_ENABLED__ '\''${APPSMITH_MAIL_ENABLED}'\''; $NGINX_SSL_CMNT sub_filter __APPSMITH_MAIL_ENABLED__ '\${APPSMITH_MAIL_ENABLED}';
$NGINX_SSL_CMNT } $NGINX_SSL_CMNT }
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT location /f { $NGINX_SSL_CMNT location /f {
@ -121,8 +122,4 @@ $NGINX_SSL_CMNT proxy_pass http://appsmith-internal-server:8080;
$NGINX_SSL_CMNT } $NGINX_SSL_CMNT }
$NGINX_SSL_CMNT $NGINX_SSL_CMNT
$NGINX_SSL_CMNT } $NGINX_SSL_CMNT }
' EOF
echo "$content" \
| sed -e "s/\$NGINX_SSL_CMNT/$NGINX_SSL_CMNT/g" -e "s/\$custom_domain/$custom_domain/g" \
>| nginx_app.conf