cloudron 8.0.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/bin/cloudron +7 -1
  2. package/package.json +1 -1
  3. package/src/actions.js +104 -34
  4. package/src/templates/flask/CloudronManifest.json +25 -0
  5. package/src/templates/flask/Dockerfile +16 -0
  6. package/src/templates/flask/app/__init__.py +5 -0
  7. package/src/templates/flask/app/views.py +5 -0
  8. package/src/templates/flask/dockerignore +5 -0
  9. package/src/templates/flask/requirements.txt +1 -0
  10. package/src/templates/flask/run.py +4 -0
  11. package/src/templates/flask/start.sh +8 -0
  12. package/src/templates/flask/supervisor/uwsgi.conf +12 -0
  13. package/src/templates/flask/template.json +3 -0
  14. package/src/templates/flask/uwsgi.ini +6 -0
  15. package/src/templates/lamp/CloudronManifest.json +26 -0
  16. package/src/templates/lamp/Dockerfile +27 -0
  17. package/src/templates/lamp/apache/app.conf +17 -0
  18. package/src/templates/lamp/apache/mpm_prefork.conf +7 -0
  19. package/src/templates/lamp/dockerignore +4 -0
  20. package/src/templates/lamp/index.php +18 -0
  21. package/src/templates/lamp/start.sh +9 -0
  22. package/src/templates/lamp/template.json +3 -0
  23. package/src/templates/lamp-composer/CloudronManifest.json +26 -0
  24. package/src/templates/lamp-composer/Dockerfile +30 -0
  25. package/src/templates/lamp-composer/apache/app.conf +17 -0
  26. package/src/templates/lamp-composer/apache/mpm_prefork.conf +7 -0
  27. package/src/templates/lamp-composer/composer.json +8 -0
  28. package/src/templates/lamp-composer/dockerignore +5 -0
  29. package/src/templates/lamp-composer/public/index.php +20 -0
  30. package/src/templates/lamp-composer/start.sh +9 -0
  31. package/src/templates/lamp-composer/template.json +3 -0
  32. package/src/templates/lemp/CloudronManifest.json +26 -0
  33. package/src/templates/lemp/Dockerfile +27 -0
  34. package/src/templates/lemp/dockerignore +4 -0
  35. package/src/templates/lemp/index.php +18 -0
  36. package/src/templates/lemp/nginx/app.conf +22 -0
  37. package/src/templates/lemp/start.sh +11 -0
  38. package/src/templates/lemp/template.json +3 -0
  39. package/src/templates/nextjs/CloudronManifest.json +25 -0
  40. package/src/templates/nextjs/Dockerfile +14 -0
  41. package/src/templates/nextjs/app/layout.js +12 -0
  42. package/src/templates/nextjs/app/page.js +7 -0
  43. package/src/templates/nextjs/dockerignore +5 -0
  44. package/src/templates/nextjs/next.config.mjs +6 -0
  45. package/src/templates/nextjs/package.json +15 -0
  46. package/src/templates/nextjs/start.sh +9 -0
  47. package/src/templates/nextjs/template.json +3 -0
  48. package/src/templates/nodejs/CloudronManifest.json +25 -0
  49. package/src/templates/nodejs/Dockerfile +10 -0
  50. package/src/templates/nodejs/dockerignore +4 -0
  51. package/src/templates/nodejs/package.json +11 -0
  52. package/src/templates/nodejs/server.js +12 -0
  53. package/src/templates/nodejs/start.sh +9 -0
  54. package/src/templates/nodejs/template.json +3 -0
  55. package/src/templates/nodejs-mongodb/CloudronManifest.json +26 -0
  56. package/src/templates/nodejs-mongodb/Dockerfile +10 -0
  57. package/src/templates/nodejs-mongodb/dockerignore +4 -0
  58. package/src/templates/nodejs-mongodb/package.json +14 -0
  59. package/src/templates/nodejs-mongodb/server.js +24 -0
  60. package/src/templates/nodejs-mongodb/start.sh +9 -0
  61. package/src/templates/nodejs-mongodb/template.json +3 -0
  62. package/src/templates/rails/CloudronManifest.json +26 -0
  63. package/src/templates/rails/Dockerfile +16 -0
  64. package/src/templates/rails/Gemfile +5 -0
  65. package/src/templates/rails/config/database.yml +4 -0
  66. package/src/templates/rails/config/puma.rb +9 -0
  67. package/src/templates/rails/dockerignore +7 -0
  68. package/src/templates/rails/public/index.html +12 -0
  69. package/src/templates/rails/start.sh +14 -0
  70. package/src/templates/rails/supervisor/puma.conf +12 -0
  71. package/src/templates/rails/template.json +3 -0
  72. package/src/templates/static/CloudronManifest.json +25 -0
  73. package/src/templates/static/Dockerfile +17 -0
  74. package/src/templates/static/apache/app.conf +13 -0
  75. package/src/templates/static/apache/mpm_prefork.conf +7 -0
  76. package/src/templates/static/dockerignore +3 -0
  77. package/src/templates/static/public/index.html +11 -0
  78. package/src/templates/static/start.sh +7 -0
  79. package/src/templates/static/template.json +3 -0
  80. package/test/test.js +1 -1
  81. /package/src/templates/{CloudronManifest.appstore.json.ejs → default/CloudronManifest.json.ejs} +0 -0
  82. /package/src/templates/{CloudronManifest.json.ejs → default/CloudronManifest.minimal.json.ejs} +0 -0
  83. /package/src/templates/{Dockerfile.ejs → default/Dockerfile.ejs} +0 -0
  84. /package/src/templates/{dockerignore.ejs → default/dockerignore.ejs} +0 -0
  85. /package/src/templates/{start.sh.ejs → default/start.sh.ejs} +0 -0
package/bin/cloudron CHANGED
@@ -264,7 +264,9 @@ program.command('inspect')
264
264
 
265
265
  program.command('init')
266
266
  .description('Creates a new CloudronManifest.json and Dockerfile')
267
- .option('--appstore', 'Appstore template')
267
+ .option('--minimal', 'Minimal template')
268
+ .option('--template <name>', 'Use a project template (use --list-templates to see all)')
269
+ .option('--list-templates', 'List available templates')
268
270
  .action(actions.init);
269
271
 
270
272
  program.command('install')
@@ -281,6 +283,8 @@ program.command('install')
281
283
  .option('--debug [cmd...]', 'Enable debug mode', false)
282
284
  .option('--readonly', 'Mount filesystem readonly. Default is read/write in debug mode.')
283
285
  .option('--env <KEY=value...>', 'Set environment variables. e.g X=1 Y=2')
286
+ .option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectBuildArgs, [])
287
+ .option('-f, --file <dockerfile>', 'Name of the Dockerfile')
284
288
  .action(actions.install);
285
289
 
286
290
  program.command('list')
@@ -405,6 +409,8 @@ program.command('update')
405
409
  .option('--image <docker image>', 'Docker image')
406
410
  .option('--no-backup', 'Skip backup [false]')
407
411
  .option('--no-force', 'Match appstore id and manifest id before updating', true)
412
+ .option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectBuildArgs, [])
413
+ .option('-f, --file <dockerfile>', 'Name of the Dockerfile')
408
414
  .action(actions.update);
409
415
 
410
416
  const versionsCommand = program.command('versions').description('Commands for publishing community packages');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "8.0.1",
3
+ "version": "8.0.2",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "type": "module",
package/src/actions.js CHANGED
@@ -224,7 +224,7 @@ async function waitForFinishInstallation(appId, taskId, options) {
224
224
 
225
225
  if (response.body.installationState !== 'installed') throw new Error(`Installation failed: ${response.body.error ? response.body.error.message : ''}`);
226
226
 
227
- await waitForHealthy(appId, options);
227
+ if (!options.debug) await waitForHealthy(appId, options);
228
228
  }
229
229
 
230
230
  async function waitForFinishBackup(appId, taskId, options) {
@@ -683,7 +683,6 @@ async function install(localOptions, cmd) {
683
683
  cmd: debugCmd
684
684
  };
685
685
  data.memoryLimit = -1;
686
- options.wait = false; // in debug mode, health check never succeeds
687
686
  }
688
687
 
689
688
  if (!options.appstoreId && !options.versionsUrl && manifest && manifest.icon) {
@@ -712,6 +711,10 @@ async function install(localOptions, cmd) {
712
711
  if (data.sso !== undefined) request.field('sso', String(data.sso));
713
712
  if (data.debugMode) request.field('debugMode', JSON.stringify(data.debugMode));
714
713
  if (data.icon) request.field('icon', data.icon);
714
+ const buildConfig = {};
715
+ if (options.buildArg && options.buildArg.length) buildConfig.buildArgs = options.buildArg;
716
+ if (options.file) buildConfig.dockerfileName = options.file;
717
+ if (Object.keys(buildConfig).length) request.field('buildConfig', JSON.stringify(buildConfig));
715
718
  request.attach('sourceArchive', sourceArchiveFilePath);
716
719
 
717
720
  response = await request;
@@ -903,6 +906,10 @@ async function update(localOptions, cmd) {
903
906
  request.field('skipNotification', String(data.skipNotification));
904
907
  request.field('force', String(data.force));
905
908
  if (data.icon) request.field('icon', data.icon);
909
+ const buildConfig = {};
910
+ if (options.buildArg && options.buildArg.length) buildConfig.buildArgs = options.buildArg;
911
+ if (options.file) buildConfig.dockerfileName = options.file;
912
+ if (Object.keys(buildConfig).length) request.field('buildConfig', JSON.stringify(buildConfig));
906
913
  request.attach('sourceArchive', sourceArchiveFilePath);
907
914
 
908
915
  response = await request;
@@ -1504,7 +1511,6 @@ function runExecSession(appId, execId, { stdin, stdout, stderr, tty }, options)
1504
1511
  if (stdout !== process.stdout && typeof stdout.end === 'function') {
1505
1512
  stdout.on('close', () => resolve());
1506
1513
  stdout.end();
1507
- stdout.resume(); // drain readable side of Duplex/Transform streams so autoDestroy emits 'close'
1508
1514
  } else {
1509
1515
  setImmediate(resolve);
1510
1516
  }
@@ -1912,52 +1918,116 @@ async function syncPull(remoteDir, localDir, localOptions, cmd) {
1912
1918
  }
1913
1919
  }
1914
1920
 
1921
+ function listTemplates() {
1922
+ const templatesDir = path.join(import.meta.dirname, 'templates');
1923
+ const entries = fs.readdirSync(templatesDir, { withFileTypes: true });
1924
+
1925
+ console.log();
1926
+ console.log('Available templates:');
1927
+ console.log();
1928
+
1929
+ for (const entry of entries) {
1930
+ if (!entry.isDirectory()) continue;
1931
+
1932
+ const metaPath = path.join(templatesDir, entry.name, 'template.json');
1933
+ if (!fs.existsSync(metaPath)) continue;
1934
+
1935
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
1936
+ console.log(` ${entry.name.padEnd(15)} ${meta.description}`);
1937
+ }
1938
+
1939
+ console.log();
1940
+ }
1941
+
1942
+ function copyTemplateDir(srcDir, destDir) {
1943
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
1944
+
1945
+ for (const entry of entries) {
1946
+ const srcPath = path.join(srcDir, entry.name);
1947
+
1948
+ if (entry.name === 'template.json') continue;
1949
+
1950
+ const destName = entry.name === 'dockerignore' ? '.dockerignore' : entry.name;
1951
+ const destPath = path.join(destDir, destName);
1952
+
1953
+ if (entry.isDirectory()) {
1954
+ if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });
1955
+ copyTemplateDir(srcPath, destPath);
1956
+ } else {
1957
+ if (fs.existsSync(destPath)) {
1958
+ console.log(`${destName} already exists, skipping`);
1959
+ continue;
1960
+ }
1961
+
1962
+ fs.copyFileSync(srcPath, destPath);
1963
+
1964
+ if (destName === 'start.sh') fs.chmodSync(destPath, 0o0775);
1965
+ }
1966
+ }
1967
+ }
1968
+
1915
1969
  function init(localOptions, cmd) {
1916
1970
  const options = cmd.optsWithGlobals();
1971
+
1972
+ if (options.listTemplates) return listTemplates();
1973
+
1974
+ if (options.template && options.minimal) return exit('--template and --minimal are mutually exclusive');
1975
+
1917
1976
  const manifestFilePath = locateManifest();
1918
1977
  if (manifestFilePath && path.dirname(manifestFilePath) === process.cwd()) return exit('CloudronManifest.json already exists in current directory');
1919
1978
 
1920
- const manifestTemplateFilename = options.appstore ? 'CloudronManifest.appstore.json.ejs' : 'CloudronManifest.json.ejs';
1979
+ if (options.template) {
1980
+ const templateDir = path.join(import.meta.dirname, 'templates', options.template);
1981
+ const metaPath = path.join(templateDir, 'template.json');
1982
+ if (!fs.existsSync(metaPath)) {
1983
+ console.log(`Unknown template "${options.template}".`);
1984
+ return listTemplates();
1985
+ }
1921
1986
 
1922
- const manifestTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/', manifestTemplateFilename), 'utf8');
1923
- const dockerfileTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/', 'Dockerfile.ejs'), 'utf8');
1924
- const dockerignoreTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/', 'dockerignore.ejs'), 'utf8');
1925
- const startShTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/', 'start.sh.ejs'), 'utf8');
1987
+ copyTemplateDir(templateDir, process.cwd());
1988
+ } else {
1989
+ const manifestTemplateFilename = !options.minimal ? 'CloudronManifest.json.ejs' : 'CloudronManifest.minimal.json.ejs';
1926
1990
 
1927
- const data = {
1928
- version: '0.1.0',
1929
- title: 'App title',
1930
- httpPort: 3000
1931
- };
1991
+ const manifestTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', manifestTemplateFilename), 'utf8');
1992
+ const dockerfileTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'Dockerfile.ejs'), 'utf8');
1993
+ const dockerignoreTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'dockerignore.ejs'), 'utf8');
1994
+ const startShTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'start.sh.ejs'), 'utf8');
1932
1995
 
1933
- const manifest = ejs.render(manifestTemplate, data);
1934
- fs.writeFileSync('CloudronManifest.json', manifest, 'utf8');
1996
+ const data = {
1997
+ version: '0.1.0',
1998
+ title: 'App title',
1999
+ httpPort: 3000
2000
+ };
1935
2001
 
1936
- if (fs.existsSync('Dockerfile')) {
1937
- console.log('Dockerfile already exists, skipping');
1938
- } else {
1939
- const dockerfile = ejs.render(dockerfileTemplate, data);
1940
- fs.writeFileSync('Dockerfile', dockerfile, 'utf8');
1941
- }
2002
+ const manifest = ejs.render(manifestTemplate, data);
2003
+ fs.writeFileSync('CloudronManifest.json', manifest, 'utf8');
1942
2004
 
1943
- if (fs.existsSync('.dockerignore')) {
1944
- console.log('.dockerignore already exists, skipping');
1945
- } else {
1946
- const dockerignore = ejs.render(dockerignoreTemplate, data);
1947
- fs.writeFileSync('.dockerignore', dockerignore, 'utf8');
1948
- }
2005
+ if (fs.existsSync('Dockerfile')) {
2006
+ console.log('Dockerfile already exists, skipping');
2007
+ } else {
2008
+ const dockerfile = ejs.render(dockerfileTemplate, data);
2009
+ fs.writeFileSync('Dockerfile', dockerfile, 'utf8');
2010
+ }
1949
2011
 
1950
- if (fs.existsSync('start.sh')) {
1951
- console.log('start.sh already exists, skipping');
1952
- } else {
1953
- const dockerignore = ejs.render(startShTemplate, {});
1954
- fs.writeFileSync('start.sh', dockerignore, 'utf8');
1955
- fs.chmodSync('start.sh', 0o0775);
2012
+ if (fs.existsSync('.dockerignore')) {
2013
+ console.log('.dockerignore already exists, skipping');
2014
+ } else {
2015
+ const dockerignore = ejs.render(dockerignoreTemplate, data);
2016
+ fs.writeFileSync('.dockerignore', dockerignore, 'utf8');
2017
+ }
2018
+
2019
+ if (fs.existsSync('start.sh')) {
2020
+ console.log('start.sh already exists, skipping');
2021
+ } else {
2022
+ const startSh = ejs.render(startShTemplate, {});
2023
+ fs.writeFileSync('start.sh', startSh, 'utf8');
2024
+ fs.chmodSync('start.sh', 0o0775);
2025
+ }
1956
2026
  }
1957
2027
 
1958
2028
  fs.copyFileSync(path.join(import.meta.dirname, 'templates/', 'logo.png'), 'logo.png');
1959
2029
 
1960
- if (options.appstore) {
2030
+ if (!options.minimal) {
1961
2031
  if (!fs.existsSync('.gitignore')) fs.writeFileSync('.gitignore', 'node_modules/\n', 'utf8');
1962
2032
  if (!fs.existsSync('DESCRIPTION.md')) fs.writeFileSync('DESCRIPTION.md', '## About\n\nThis app changes everything\n\n', 'utf8');
1963
2033
  if (!fs.existsSync('POSTINSTALL.md')) fs.writeFileSync('POSTINSTALL.md', 'Post installation information\n\n', 'utf8');
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "",
3
+ "version": "0.1.0",
4
+ "upstreamVersion": "",
5
+ "minBoxVersion": "9.0.0",
6
+ "title": "",
7
+ "author": "",
8
+ "description": "file://DESCRIPTION.md",
9
+ "tagline": "",
10
+ "website": "",
11
+ "contactEmail": "",
12
+ "iconUrl": "file://logo.png",
13
+ "healthCheckPath": "/",
14
+ "mediaLinks": [],
15
+ "httpPort": 3000,
16
+ "tags": [],
17
+ "changelog": "file://CHANGELOG",
18
+ "postInstallMessage": "file://POSTINSTALL.md",
19
+ "documentationUrl": "",
20
+ "forumUrl": "",
21
+ "addons": {
22
+ "localstorage": {}
23
+ },
24
+ "manifestVersion": 2
25
+ }
@@ -0,0 +1,16 @@
1
+ FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
2
+
3
+ RUN mkdir -p /app/code
4
+ WORKDIR /app/code
5
+
6
+ COPY . /app/code
7
+
8
+ RUN pip install -r requirements.txt
9
+
10
+ ADD supervisor/* /etc/supervisor/conf.d/
11
+ RUN ln -sf /run/supervisord.log /var/log/supervisor/supervisord.log
12
+
13
+ ADD uwsgi.ini /etc/uwsgi/apps-available/flask-uwsgi.ini
14
+ RUN ln -s /etc/uwsgi/apps-available/flask-uwsgi.ini /etc/uwsgi/apps-enabled/flask-uwsgi.ini
15
+
16
+ CMD ["/app/code/start.sh"]
@@ -0,0 +1,5 @@
1
+ from flask import Flask
2
+
3
+ app = Flask(__name__)
4
+
5
+ from app import views
@@ -0,0 +1,5 @@
1
+ from app import app
2
+
3
+ @app.route("/")
4
+ def index():
5
+ return "Hello from Flask"
@@ -0,0 +1,5 @@
1
+ .git
2
+ .gitignore
3
+ .dockerignore
4
+ node_modules
5
+ __pycache__
@@ -0,0 +1 @@
1
+ flask
@@ -0,0 +1,4 @@
1
+ from app import app
2
+
3
+ if __name__ == "__main__":
4
+ app.run()
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ set -eu
4
+
5
+ export FLASK_APP=run.py
6
+
7
+ echo "Starting supervisor"
8
+ exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf --nodaemon -i FlaskApp
@@ -0,0 +1,12 @@
1
+ [program:uwsgi]
2
+ autorestart=true
3
+ autostart=true
4
+ command=uwsgi --master --workers 2 --no-orphans --ini /etc/uwsgi/apps-enabled/flask-uwsgi.ini
5
+ stdout_logfile=/dev/stdout
6
+ stdout_logfile_maxbytes=0
7
+ stderr_logfile=/dev/stderr
8
+ stderr_logfile_maxbytes=0
9
+ killasgroup=true
10
+ stopasgroup=true
11
+ stopsignal=QUIT
12
+ priority=100
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Python Flask app with uWSGI and Supervisor"
3
+ }
@@ -0,0 +1,6 @@
1
+ [uwsgi]
2
+ plugin = python
3
+ chdir = /app/code
4
+ uid = cloudron
5
+ gid = cloudron
6
+ http-socket = 0.0.0.0:3000
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "",
3
+ "version": "0.1.0",
4
+ "upstreamVersion": "",
5
+ "minBoxVersion": "9.0.0",
6
+ "title": "",
7
+ "author": "",
8
+ "description": "file://DESCRIPTION.md",
9
+ "tagline": "",
10
+ "website": "",
11
+ "contactEmail": "",
12
+ "iconUrl": "file://logo.png",
13
+ "healthCheckPath": "/",
14
+ "mediaLinks": [],
15
+ "httpPort": 3000,
16
+ "tags": [],
17
+ "changelog": "file://CHANGELOG",
18
+ "postInstallMessage": "file://POSTINSTALL.md",
19
+ "documentationUrl": "",
20
+ "forumUrl": "",
21
+ "addons": {
22
+ "localstorage": {},
23
+ "mysql": {}
24
+ },
25
+ "manifestVersion": 2
26
+ }
@@ -0,0 +1,27 @@
1
+ FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
2
+
3
+ RUN mkdir -p /app/code
4
+ WORKDIR /app/code
5
+
6
+ RUN rm /etc/apache2/sites-enabled/*
7
+ RUN sed -e 's,^ErrorLog.*,ErrorLog "|/bin/cat",' -i /etc/apache2/apache2.conf
8
+ COPY apache/mpm_prefork.conf /etc/apache2/mods-available/mpm_prefork.conf
9
+
10
+ RUN a2disconf other-vhosts-access-log
11
+ COPY apache/app.conf /etc/apache2/sites-enabled/app.conf
12
+ RUN echo "Listen 3000" > /etc/apache2/ports.conf
13
+
14
+ RUN a2enmod php8.3
15
+ RUN crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_filesize 256M && \
16
+ crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_size 256M && \
17
+ crudini --set /etc/php/8.3/apache2/php.ini PHP post_max_size 256M && \
18
+ crudini --set /etc/php/8.3/apache2/php.ini PHP memory_limit 256M && \
19
+ crudini --set /etc/php/8.3/apache2/php.ini PHP max_execution_time 200 && \
20
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.save_path /run/app/sessions && \
21
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_probability 1 && \
22
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_divisor 100
23
+
24
+ COPY index.php start.sh /app/code/
25
+ RUN chown -R www-data.www-data /app/code
26
+
27
+ CMD [ "/app/code/start.sh" ]
@@ -0,0 +1,17 @@
1
+ <VirtualHost *:3000>
2
+ DocumentRoot /app/code
3
+
4
+ ErrorLog "|/bin/cat"
5
+ CustomLog "|/bin/cat" combined
6
+
7
+ <Directory /app/code/>
8
+ Options +FollowSymLinks
9
+ AllowOverride All
10
+ Require all granted
11
+
12
+ <Files "config.php">
13
+ Require all denied
14
+ </Files>
15
+ </Directory>
16
+
17
+ </VirtualHost>
@@ -0,0 +1,7 @@
1
+ <IfModule mpm_prefork_module>
2
+ StartServers 2
3
+ MinSpareServers 2
4
+ MaxSpareServers 3
5
+ MaxRequestWorkers 15
6
+ MaxConnectionsPerChild 100
7
+ </IfModule>
@@ -0,0 +1,4 @@
1
+ .git
2
+ .gitignore
3
+ .dockerignore
4
+ node_modules
@@ -0,0 +1,18 @@
1
+ <?php
2
+ $host = getenv("CLOUDRON_MYSQL_HOST");
3
+ $port = getenv("CLOUDRON_MYSQL_PORT");
4
+ $username = getenv("CLOUDRON_MYSQL_USERNAME");
5
+ $password = getenv("CLOUDRON_MYSQL_PASSWORD");
6
+ $database = getenv("CLOUDRON_MYSQL_DATABASE");
7
+
8
+ echo "<h1>Hello from Cloudron!</h1>\n";
9
+
10
+ try {
11
+ $dsn = "mysql:host=$host;port=$port;dbname=$database";
12
+ $pdo = new PDO($dsn, $username, $password);
13
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
14
+ echo "<p>MySQL connection successful.</p>\n";
15
+ } catch (PDOException $e) {
16
+ echo "<p>MySQL connection failed: " . htmlspecialchars($e->getMessage()) . "</p>\n";
17
+ }
18
+ ?>
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+
3
+ set -eu
4
+
5
+ mkdir -p /run/app/sessions
6
+
7
+ APACHE_CONFDIR="" source /etc/apache2/envvars
8
+ rm -f "${APACHE_PID_FILE}"
9
+ exec /usr/sbin/apache2 -DFOREGROUND
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "LAMP app with Apache, mod_php, and MySQL"
3
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "",
3
+ "version": "0.1.0",
4
+ "upstreamVersion": "",
5
+ "minBoxVersion": "9.0.0",
6
+ "title": "",
7
+ "author": "",
8
+ "description": "file://DESCRIPTION.md",
9
+ "tagline": "",
10
+ "website": "",
11
+ "contactEmail": "",
12
+ "iconUrl": "file://logo.png",
13
+ "healthCheckPath": "/",
14
+ "mediaLinks": [],
15
+ "httpPort": 3000,
16
+ "tags": [],
17
+ "changelog": "file://CHANGELOG",
18
+ "postInstallMessage": "file://POSTINSTALL.md",
19
+ "documentationUrl": "",
20
+ "forumUrl": "",
21
+ "addons": {
22
+ "localstorage": {},
23
+ "mysql": {}
24
+ },
25
+ "manifestVersion": 2
26
+ }
@@ -0,0 +1,30 @@
1
+ FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
2
+
3
+ RUN mkdir -p /app/code
4
+ WORKDIR /app/code
5
+
6
+ RUN rm /etc/apache2/sites-enabled/*
7
+ RUN sed -e 's,^ErrorLog.*,ErrorLog "|/bin/cat",' -i /etc/apache2/apache2.conf
8
+ COPY apache/mpm_prefork.conf /etc/apache2/mods-available/mpm_prefork.conf
9
+
10
+ RUN a2disconf other-vhosts-access-log
11
+ COPY apache/app.conf /etc/apache2/sites-enabled/app.conf
12
+ RUN echo "Listen 3000" > /etc/apache2/ports.conf
13
+
14
+ RUN a2enmod php8.3 rewrite
15
+ RUN crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_filesize 256M && \
16
+ crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_size 256M && \
17
+ crudini --set /etc/php/8.3/apache2/php.ini PHP post_max_size 256M && \
18
+ crudini --set /etc/php/8.3/apache2/php.ini PHP memory_limit 256M && \
19
+ crudini --set /etc/php/8.3/apache2/php.ini PHP max_execution_time 200 && \
20
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.save_path /run/app/sessions && \
21
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_probability 1 && \
22
+ crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_divisor 100
23
+
24
+ COPY . /app/code/
25
+
26
+ RUN composer install --no-dev --optimize-autoloader
27
+
28
+ RUN chown -R www-data.www-data /app/code
29
+
30
+ CMD [ "/app/code/start.sh" ]
@@ -0,0 +1,17 @@
1
+ <VirtualHost *:3000>
2
+ DocumentRoot /app/code/public
3
+
4
+ ErrorLog "|/bin/cat"
5
+ CustomLog "|/bin/cat" combined
6
+
7
+ <Directory /app/code/public/>
8
+ Options +FollowSymLinks
9
+ AllowOverride All
10
+ Require all granted
11
+ </Directory>
12
+
13
+ <Directory /app/code/>
14
+ Require all denied
15
+ </Directory>
16
+
17
+ </VirtualHost>
@@ -0,0 +1,7 @@
1
+ <IfModule mpm_prefork_module>
2
+ StartServers 2
3
+ MinSpareServers 2
4
+ MaxSpareServers 3
5
+ MaxRequestWorkers 15
6
+ MaxConnectionsPerChild 100
7
+ </IfModule>
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "app/cloudron-app",
3
+ "description": "",
4
+ "type": "project",
5
+ "require": {
6
+ "php": ">=8.3"
7
+ }
8
+ }
@@ -0,0 +1,5 @@
1
+ .git
2
+ .gitignore
3
+ .dockerignore
4
+ node_modules
5
+ vendor
@@ -0,0 +1,20 @@
1
+ <?php
2
+ require_once __DIR__ . '/../vendor/autoload.php';
3
+
4
+ $host = getenv("CLOUDRON_MYSQL_HOST");
5
+ $port = getenv("CLOUDRON_MYSQL_PORT");
6
+ $username = getenv("CLOUDRON_MYSQL_USERNAME");
7
+ $password = getenv("CLOUDRON_MYSQL_PASSWORD");
8
+ $database = getenv("CLOUDRON_MYSQL_DATABASE");
9
+
10
+ echo "<h1>Hello from Cloudron!</h1>\n";
11
+
12
+ try {
13
+ $dsn = "mysql:host=$host;port=$port;dbname=$database";
14
+ $pdo = new PDO($dsn, $username, $password);
15
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
16
+ echo "<p>MySQL connection successful.</p>\n";
17
+ } catch (PDOException $e) {
18
+ echo "<p>MySQL connection failed: " . htmlspecialchars($e->getMessage()) . "</p>\n";
19
+ }
20
+ ?>
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+
3
+ set -eu
4
+
5
+ mkdir -p /run/app/sessions
6
+
7
+ APACHE_CONFDIR="" source /etc/apache2/envvars
8
+ rm -f "${APACHE_PID_FILE}"
9
+ exec /usr/sbin/apache2 -DFOREGROUND
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "LAMP app with Apache, mod_php, Composer, and MySQL"
3
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "",
3
+ "version": "0.1.0",
4
+ "upstreamVersion": "",
5
+ "minBoxVersion": "9.0.0",
6
+ "title": "",
7
+ "author": "",
8
+ "description": "file://DESCRIPTION.md",
9
+ "tagline": "",
10
+ "website": "",
11
+ "contactEmail": "",
12
+ "iconUrl": "file://logo.png",
13
+ "healthCheckPath": "/",
14
+ "mediaLinks": [],
15
+ "httpPort": 3000,
16
+ "tags": [],
17
+ "changelog": "file://CHANGELOG",
18
+ "postInstallMessage": "file://POSTINSTALL.md",
19
+ "documentationUrl": "",
20
+ "forumUrl": "",
21
+ "addons": {
22
+ "localstorage": {},
23
+ "mysql": {}
24
+ },
25
+ "manifestVersion": 2
26
+ }