35 Commits

Author SHA1 Message Date
Johannes Zellner
fdd9ac34ae Fix shell script 2023-02-09 15:55:14 +01:00
Johannes Zellner
d090334699 Update test deps 2023-02-09 15:48:08 +01:00
Johannes Zellner
0c4156dce9 Create admin account on our own as it was removed upstream 2023-02-09 15:47:42 +01:00
Johannes Zellner
42321405b4 Update test deps 2023-02-06 10:05:30 +01:00
Johannes Zellner
7da7a5307b Bump version 2023-02-06 10:05:11 +01:00
Johannes Zellner
faaf81f0c4 Update test deps 2023-01-19 18:07:13 +01:00
Johannes Zellner
2754b1f075 Bump version 2023-01-19 18:06:21 +01:00
Girish Ramakrishnan
709feb60bc Version 1.2.0 2023-01-03 11:18:10 +01:00
Girish Ramakrishnan
60ec7830dd Update test packages 2023-01-03 11:08:07 +01:00
Girish Ramakrishnan
5e5c2b96f5 Update base image to 4.0.0 2023-01-03 11:07:53 +01:00
Johannes Zellner
ddd348b09a Bump version 2022-11-28 11:58:30 +01:00
Girish Ramakrishnan
a333a0cfb0 Update test packages 2022-11-28 11:01:30 +01:00
Girish Ramakrishnan
b08767dc17 Update Traccar to 5.5 2022-11-28 11:00:59 +01:00
Johannes Zellner
d7539d6585 Bump version 2022-10-03 20:40:00 +02:00
Johannes Zellner
a1d8c3b2ee Update test deps 2022-09-08 18:21:07 +02:00
Johannes Zellner
a023246104 Add TAIP port 2022-09-08 18:20:54 +02:00
Johannes Zellner
19c6c630c5 Bump version 2022-08-19 15:30:28 +02:00
Johannes Zellner
4c58a95a73 Add GPS103 port 2022-08-19 15:26:56 +02:00
Johannes Zellner
9ec91c5a44 Add LDAP email login tests 2022-08-08 15:21:40 +02:00
Johannes Zellner
4eeea53abc Add device record for proper testing 2022-08-08 15:21:24 +02:00
Johannes Zellner
3603182a84 Ensure we can also use the email to login via LDAP 2022-08-08 15:21:09 +02:00
Johannes Zellner
00f42d92c5 Add initial tests 2022-08-08 14:45:11 +02:00
Johannes Zellner
ee8db31cc6 Make version 1.0.0 2022-08-08 13:01:49 +02:00
Johannes Zellner
c9ece7726e Bump version 2022-08-08 09:16:21 +02:00
Johannes Zellner
6a8c01ccdf Bump version 2022-07-18 08:54:20 +02:00
Johannes Zellner
dd60363721 Bump version 2022-07-11 13:12:56 +02:00
Girish Ramakrishnan
2d1a59d7ca some devices notes 2022-06-09 16:39:43 -07:00
Girish Ramakrishnan
c6dc2334a7 Version 0.4.0 2022-06-09 10:32:48 -07:00
Girish Ramakrishnan
cf6ba93f35 switch to mysql
seems it may be better supported than postgres

https://www.traccar.org/forums/topic/traccar-on-aws-ec2-amazon-aurora-vs-mysql-on-amazon-rds/
2022-06-09 10:30:20 -07:00
Girish Ramakrishnan
36c07ba44f Optional sso support 2022-06-09 09:53:57 -07:00
Girish Ramakrishnan
353f85474b Version 0.3.0 2022-06-08 22:24:23 -07:00
Girish Ramakrishnan
371bd01528 log to stdout 2022-06-08 22:23:57 -07:00
Girish Ramakrishnan
beb64e256e readme 2022-06-08 22:09:10 -07:00
Girish Ramakrishnan
82e3886525 Add license 2022-06-08 22:07:56 -07:00
Girish Ramakrishnan
36a98e7021 add postinstall 2022-06-08 22:07:46 -07:00
12 changed files with 2784 additions and 32 deletions

View File

@@ -1,6 +0,0 @@
[0.1.0]
* Initial version
[0.2.0]
* Update manifest

53
CHANGELOG.md Normal file
View File

@@ -0,0 +1,53 @@
[0.1.0]
* Initial version
[0.2.0]
* Update manifest
[0.3.0]
* Log to stdout
[0.4.0]
* Switch from postgres to mysql. This will break previous installations, please start afresh
* Optional SSO
[0.4.1]
* Update traccar to 5.1
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.1)
[0.5.0]
* Update traccar to 5.2
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.2)
[0.6.0]
* Update traccar to 5.3
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.3)
[1.0.0]
* First stable release
* Add support for gl200 protocol port 5004
[1.0.1]
* Add GPS103 port 5001
[1.0.2]
* Add TAIP port 5031
[1.0.3]
* Update traccar to 5.4
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.4)
[1.1.0]
* Update traccar to 5.5
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.5)
[1.2.0]
* Update base image to 4.0.0
[1.2.1]
* Add H02 port 5013
[1.2.2]
* Update traccar to 5.6
* [Full changelog](https://github.com/traccar/traccar/releases/tag/v5.6)

View File

@@ -3,26 +3,55 @@
"title": "Traccar",
"author": "Traccar developers",
"description": "file://DESCRIPTION.md",
"changelog": "file://CHANGELOG",
"changelog": "file://CHANGELOG.md",
"tagline": "Modern GPS Tracking Platform",
"version": "0.2.0",
"upstreamVersion": "5.0",
"version": "1.2.2",
"upstreamVersion": "5.6",
"minBoxVersion": "7.1.0",
"memoryLimit": 1073741824,
"healthCheckPath": "/",
"httpPort": 8082,
"httpPorts": {
"OSMAND_DOMAIN": {
"title": "OsmAnd Domain",
"description": "Port over which OsmAnd clients can connect",
"containerPort": 5055,
"defaultValue": "osmand"
}
},
"addons": {
"ldap": {},
"sendmail": { "supportsDisplayName": false },
"localstorage": {},
"postgresql": {}
"mysql": {}
},
"optionalSso": true,
"manifestVersion": 2,
"tcpPorts": {
"OSMAND_PORT": {
"title": "OsmAnd Port",
"description": "Port over which OsmAnd clients can connect",
"defaultValue": 5055
},
"GL200_PORT": {
"title": "GL200 Protocol Port",
"description": "Port over which GL200 clients can connect",
"defaultValue": 5004
},
"GPS103_PORT": {
"title": "GPS103 Protocol Port",
"description": "Port over which GPS103 clients can connect",
"defaultValue": 5001
},
"TAIP_PORT": {
"title": "TAIP Protocol Port",
"description": "Port over which TAIP clients can connect",
"defaultValue": 5031
},
"H02_PORT": {
"title": "H02 Protocol Port",
"description": "Port over which H02 clients can connect",
"defaultValue": 5013
}
},
"website": "https://www.traccar.org",

View File

@@ -1,9 +1,9 @@
FROM cloudron/base:3.2.0@sha256:ba1d566164a67c266782545ea9809dc611c4152e27686fd14060332dd88263ea
FROM cloudron/base:4.0.0@sha256:31b195ed0662bdb06a6e8a5ddbedb6f191ce92e8bee04c03fb02dd4e9d0286df
RUN mkdir -p /app/code
WORKDIR /app/code
ARG VERSION=5.0
ARG VERSION=5.6
RUN wget https://github.com/traccar/traccar/releases/download/v${VERSION}/traccar-linux-64-${VERSION}.zip -O traccar.zip && \
unzip traccar.zip && \

9
LICENSE Normal file
View File

@@ -0,0 +1,9 @@
MIT License (MIT)
Copyright (c) 2016 Cloudron UG
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

6
POSTINSTALL.md Normal file
View File

@@ -0,0 +1,6 @@
This app is pre-setup with an admin account. The initial credentials are:
**Username**: admin@cloudron.local<br/>
**Password**: admin<br/>
Please change the admin email and password credentials immediately.

45
README.md Normal file
View File

@@ -0,0 +1,45 @@
# Traccar Cloudron App
This repository contains the Cloudron app package source for [Traccar](https://traccar.org/).
## Installation
[![Install](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.traccar.cloudronapp)
or using the [Cloudron command line tooling](https://cloudron.io/references/cli.html)
```
cloudron install --appstore-id org.traccar.cloudronapp
```
## Building
The app package can be built using the [Cloudron command line tooling](https://cloudron.io/references/cli.html).
```
cd traccar-app
cloudron build
cloudron install
```
## Testing
The e2e tests are located in the `test/` folder and require [nodejs](http://nodejs.org/). They are creating a fresh build, install the app on your Cloudron, perform tests, backup, restore and test if the repos are still ok. The tests expect port 29418 to be available.
```
cd traccar-app/test
npm install
USERNAME=<cloudron username> PASSWORD=<cloudron password> mocha --bail test.js
```
## Devices
https://www.traccar.org/devices/
Traccar client | osmand | port 5055 (http)
Tracker for Traccar | t55 | port 5005 | sends odb2 data
Owntracks | owntracks | port 5144 | https://owntracks.org/booklet/features/traccar/

View File

@@ -4,34 +4,72 @@ set -eu -o pipefail
mkdir -p /run/traccar/logs /app/data/media
echo -e "[client]\npassword=${CLOUDRON_MYSQL_PASSWORD}" > /run/traccar/mysql-extra
readonly mysql="mysql --defaults-file=/run/traccar/mysql-extra --user=${CLOUDRON_MYSQL_USERNAME} --host=${CLOUDRON_MYSQL_HOST} -P ${CLOUDRON_MYSQL_PORT} ${CLOUDRON_MYSQL_DATABASE}"
wait_for_table() {
ret=`$mysql --skip-column-names -s -e "SHOW TABLES LIKE '$1';"`
while [ "$ret" != "$1" ]; do
echo "=> Table $1 not yet created, waiting ..."
sleep 1;
ret=`$mysql --skip-column-names -s -e "SHOW TABLES LIKE '$1';"`
echo "ret was ${ret}"
done
}
disable_registration() {
echo "==> disabling registration"
PGPASSWORD=${CLOUDRON_POSTGRESQL_PASSWORD} psql -h ${CLOUDRON_POSTGRESQL_HOST} -p ${CLOUDRON_POSTGRESQL_PORT} -U ${CLOUDRON_POSTGRESQL_USERNAME} -d ${CLOUDRON_POSTGRESQL_DATABASE} -c "UPDATE tc_servers SET registration=false WHERE id=1"
wait_for_table tc_servers;
echo "==> Disabling registration"
$mysql -e "UPDATE tc_servers SET registration=0 WHERE id=1"
}
ensure_admin_account() {
wait_for_table tc_users;
echo "==> Ensure admin account"
count=`$mysql --skip-column-names -s -e "SELECT COUNT(*) FROM tc_users WHERE name='admin';"`
if [[ "$count" = "0" ]]; then
echo "==> Create initial admin account"
# Values are from https://github.com/traccar/traccar/blob/master/schema/changelog-3.3.xml#L179 which is not used anymore, but we still want the admin account
$mysql -e "INSERT INTO tc_users (name, email, hashedpassword, salt, administrator) VALUES ('admin', 'admin@cloudron.local', 'D33DCA55ABD4CC5BC76F2BC0B4E603FE2C6F61F4C1EF2D47', '000000000000000000000000000000000000000000000000', TRUE)"
fi
}
echo "=> Ensure traccar.xml config"
if [[ ! -f /app/data/traccar.xml ]]; then
cp /app/pkg/traccar.xml.template /app/data/traccar.xml
(sleep 10000; disable_registration) &
fi
echo "=> Ensure database settings"
# database (https://www.traccar.org/mysql/)
xmlstarlet ed --inplace \
--update '//properties/entry[@key="database.url"]' -v "jdbc:postgresql://${CLOUDRON_POSTGRESQL_HOST}:${CLOUDRON_POSTGRESQL_PORT}/${CLOUDRON_POSTGRESQL_DATABASE}" \
--update '//properties/entry[@key="database.user"]' -v "${CLOUDRON_POSTGRESQL_USERNAME}" \
--update '//properties/entry[@key="database.password"]' -v "${CLOUDRON_POSTGRESQL_PASSWORD}" \
--update '//properties/entry[@key="database.url"]' -v "jdbc:mysql://${CLOUDRON_MYSQL_HOST}:${CLOUDRON_MYSQL_PORT}/${CLOUDRON_MYSQL_DATABASE}?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&autoReconnect=true&useUnicode=yes&characterEncoding=UTF-8&sessionVariables=sql_mode=''" \
--update '//properties/entry[@key="database.user"]' -v "${CLOUDRON_MYSQL_USERNAME}" \
--update '//properties/entry[@key="database.password"]' -v "${CLOUDRON_MYSQL_PASSWORD}" \
/app/data/traccar.xml
# origin
xmlstarlet ed --inplace --update '//properties/entry[@key="web.url"]' -v "${CLOUDRON_APP_ORIGIN}" /app/data/traccar.xml
xmlstarlet ed --inplace \
--update '//properties/entry[@key="ldap.url"]' -v "${CLOUDRON_LDAP_URL}" \
--update '//properties/entry[@key="ldap.base"]' -v "${CLOUDRON_LDAP_USERS_BASE_DN}" \
--update '//properties/entry[@key="ldap.idAttribute"]' -v "username" \
--update '//properties/entry[@key="ldap.searchFilter"]' -v 'username=:login' \
--update '//properties/entry[@key="ldap.user"]' -v "${CLOUDRON_LDAP_BIND_DN}" \
--update '//properties/entry[@key="ldap.password"]' -v "${CLOUDRON_LDAP_BIND_PASSWORD}" \
/app/data/traccar.xml
# ldap
if [[ -n "${CLOUDRON_LDAP_URL:-}" ]]; then
echo "=> Ensure LDAP settings"
xmlstarlet ed --inplace \
--update '//properties/entry[@key="ldap.enable"]' -v "true" \
--update '//properties/entry[@key="ldap.url"]' -v "${CLOUDRON_LDAP_URL}" \
--update '//properties/entry[@key="ldap.base"]' -v "${CLOUDRON_LDAP_USERS_BASE_DN}" \
--update '//properties/entry[@key="ldap.idAttribute"]' -v "username" \
--update '//properties/entry[@key="ldap.searchFilter"]' -v '(|(username=:login)(mail=:login))' \
--update '//properties/entry[@key="ldap.user"]' -v "${CLOUDRON_LDAP_BIND_DN}" \
--update '//properties/entry[@key="ldap.password"]' -v "${CLOUDRON_LDAP_BIND_PASSWORD}" \
/app/data/traccar.xml
else
xmlstarlet ed --inplace --update '//properties/entry[@key="ldap.enable"]' -v "false" /app/data/traccar.xml
fi
# email
echo "=> Ensure mail settings"
xmlstarlet ed --inplace \
--update '//properties/entry[@key="mail.smtp.host"]' -v "${CLOUDRON_MAIL_SMTP_SERVER}" \
--update '//properties/entry[@key="mail.smtp.port"]' -v "${CLOUDRON_MAIL_SMTP_PORT}" \
@@ -42,6 +80,9 @@ xmlstarlet ed --inplace \
--update '//properties/entry[@key="mail.smtp.password"]' -v "${CLOUDRON_MAIL_SMTP_PASSWORD}" \
/app/data/traccar.xml
disable_registration &
ensure_admin_account &
chown -R cloudron /run/traccar /app/data
# https://www.traccar.org/optimization/

2383
test/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
test/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "test.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"expect.js": "^0.3.1",
"mocha": "^10.2.0",
"selenium-webdriver": "^4.8.0"
},
"dependencies": {
"chromedriver": "^110.0.0"
}
}

168
test/test.js Normal file
View File

@@ -0,0 +1,168 @@
#!/usr/bin/env node
/* jshint esversion: 8 */
/* global describe */
/* global before */
/* global after */
/* global it */
'use strict';
require('chromedriver');
var execSync = require('child_process').execSync,
expect = require('expect.js'),
path = require('path'),
{ Builder, By, Key, until } = require('selenium-webdriver'),
{ Options } = require('selenium-webdriver/chrome');
if (!process.env.EMAIL || !process.env.PASSWORD) {
console.log('EMAIL and PASSWORD env vars need to be set');
process.exit(1);
}
describe('Application life cycle test', function () {
this.timeout(0);
const LOCATION = 'test';
const TEST_TIMEOUT = 10000;
const EXEC_ARGS = { cwd: path.resolve(__dirname, '..'), stdio: 'inherit' };
const DEVICE_NAME = 'FancyDevice';
const DEVICE_IDENTIFIER = 'device1';
const ADMIN_USERNAME = 'admin@cloudron.local';
const ADMIN_PASSWORD = 'admin';
const EMAIL = process.env.EMAIL;
const PASSWORD = process.env.PASSWORD;
var browser, app;
before(function () {
const options = new Options().windowSize({ width: 1280, height: 1024 });
if (process.env.HEADLESS) options.addArguments('headless');
browser = new Builder().forBrowser('chrome').setChromeOptions(options).build();
});
after(function () {
browser.quit();
});
async function waitForElement(elem) {
await browser.wait(until.elementLocated(elem), TEST_TIMEOUT);
await browser.wait(until.elementIsVisible(browser.findElement(elem)), TEST_TIMEOUT);
}
function getAppInfo() {
var inspect = JSON.parse(execSync('cloudron inspect'));
app = inspect.apps.filter(function (a) { return a.location.indexOf(LOCATION) === 0; })[0];
expect(app).to.be.an('object');
}
async function login(emailOrUsername, password) {
await browser.get(`https://${app.fqdn}/login`);
await waitForElement(By.xpath('//input[@name="email"]'));
await browser.findElement(By.xpath('//input[@name="email"]')).sendKeys(Key.CONTROL + 'a');
await browser.findElement(By.xpath('//input[@name="email"]')).sendKeys(emailOrUsername);
await browser.findElement(By.xpath('//input[@name="password"]')).sendKeys(password);
await browser.findElement(By.xpath('//button[text()="Login"]')).click();
await waitForElement(By.xpath('//span[text()="Account"]'));
}
async function logout() {
await browser.get(`https://${app.fqdn}`);
await waitForElement(By.xpath('//span[text()="Account"]'));
await browser.findElement(By.xpath('//span[text()="Account"]')).click();
await browser.sleep(1000);
await waitForElement(By.xpath('//p[text()="Logout"]'));
await browser.findElement(By.xpath('//p[text()="Logout"]')).click();
await waitForElement(By.xpath('//input[@name="email"]'));
}
async function addDevice() {
await browser.get(`https://${app.fqdn}/settings/device`);
await waitForElement(By.id(':r3:'));
await browser.findElement(By.id(':r3:')).sendKeys(DEVICE_NAME);
await browser.findElement(By.id(':r4:')).sendKeys(DEVICE_IDENTIFIER);
await browser.findElement(By.xpath('//button[text()="Save"]')).click();
await waitForElement(By.xpath(`//span[text()="${DEVICE_NAME}"]`));
}
async function deviceExists() {
await browser.get(`https://${app.fqdn}`);
await waitForElement(By.xpath(`//span[text()="${DEVICE_NAME}"]`));
}
xit('build app', function () { execSync('cloudron build', EXEC_ARGS); });
it('install app', function () { execSync(`cloudron install --location ${LOCATION}`, EXEC_ARGS); });
it('can get app information', getAppInfo);
it('can login as admin', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('can add device', addDevice);
it('device exists', deviceExists);
it('can logout', logout);
it('can login as normal user with email', login.bind(null, process.env.EMAIL, process.env.PASSWORD));
it('can logout', logout);
it('can login as normal user with username', login.bind(null, process.env.USERNAME, process.env.PASSWORD));
it('can logout', logout);
it('can restart app', function () { execSync(`cloudron restart --app ${app.id}`); });
it('can login', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('device exists', deviceExists);
it('can logout', logout);
it('backup app', function () { execSync(`cloudron backup create --app ${app.id}`, EXEC_ARGS); });
it('restore app', function () {
const backups = JSON.parse(execSync(`cloudron backup list --raw --app ${app.id}`));
execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS);
execSync(`cloudron install --location ${LOCATION}`, EXEC_ARGS);
getAppInfo();
execSync(`cloudron restore --backup ${backups[0].id} --app ${app.id}`, EXEC_ARGS);
});
it('can login', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('device exists', deviceExists);
it('can logout', logout);
it('move to different location', async function () {
// ensure we don't hit NXDOMAIN in the mean time
await browser.get('about:blank');
execSync(`cloudron configure --location ${LOCATION}2 --app ${app.id}`, EXEC_ARGS);
});
it('can get app information', getAppInfo);
it('can login', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('device exists', deviceExists);
it('can logout', logout);
it('uninstall app', async function () {
// ensure we don't hit NXDOMAIN in the mean time
await browser.get('about:blank');
execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS);
});
// test update
it('can install app', function () { execSync(`cloudron install --appstore-id org.traccar.cloudronapp --location ${LOCATION}`, EXEC_ARGS); });
it('can get app information', getAppInfo);
it('can login', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('can add device', addDevice);
it('device exists', deviceExists);
it('can logout', logout);
it('can update', function () { execSync(`cloudron update --app ${app.id}`, EXEC_ARGS); });
it('can login', login.bind(null, ADMIN_USERNAME, ADMIN_PASSWORD));
it('device exists', deviceExists);
it('can logout', logout);
it('can login as normal user with username', login.bind(null, process.env.USERNAME, process.env.PASSWORD));
it('can logout', logout);
it('uninstall app', async function () {
// ensure we don't hit NXDOMAIN in the mean time
await browser.get('about:blank');
execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS);
});
});

View File

@@ -4,7 +4,7 @@
<properties>
<entry key='config.default'>./conf/default.xml</entry>
<entry key='config.default'>/app/code/conf/default.xml</entry>
<!--
@@ -18,10 +18,10 @@
-->
<entry key='database.driver'>org.postgresql.Driver</entry>
<entry key='database.url'>jdbc:postgresql://##CLOUDRON_POSTGRESQL_HOST##:##CLOUDRON_POSTGRESQL_PORT##/##CLOUDRON_POSTGRESQL_DATABASE##</entry>
<entry key='database.user'>##CLOUDRON_POSTGRESQL_USERNAME##</entry>
<entry key='database.password'>##CLOUDRON_POSTGRESQL_PASSWORD##</entry>
<entry key='database.driver'>com.mysql.cj.jdbc.Driver</entry>
<entry key='database.url'>jdbc:mysql://##CLOUDRON_MYSQL_HOST##:##CLOUDRON_MYSQL_PORT##/##CLOUDRON_MYSQL_DATABASE##</entry>
<entry key='database.user'>##CLOUDRON_MYSQL_USERNAME##</entry>
<entry key='database.password'>##CLOUDRON_MYSQL_PASSWORD##</entry>
<entry key='web.url'>##CLOUDRON_APP_ORIGIN##</entry>
@@ -41,6 +41,11 @@
<entry key='mail.smtp.username'>YourAddress@gmail.com</entry>
<entry key='mail.smtp.password'>YourPassword</entry>
<entry key='logger.console'>true</entry>
<entry key='logger.queries'>false</entry>
<entry key='logger.level'>config</entry>
<entry key='logger.fullStackTraces'>true</entry>
<!-- https://github.com/traccar/traccar/blob/8eecfdcf5c59f92158a6c339d1622e0e9d67968c/src/main/java/org/traccar/config/Keys.java -->
<entry key='users.defaultDeviceLimit'>-1</entry>
<entry key='media.path'>/app/data/media</entry>