diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 21b2b6d18..98bd314e9 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -316,6 +316,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $this->fail($e); throw $e; } finally { + $this->application_deployment_queue->update([ + 'finished_at' => now(), + ]); + if ($this->use_build_server) { $this->server = $this->build_server; } else { diff --git a/app/Livewire/Project/Application/Deployment/Index.php b/app/Livewire/Project/Application/Deployment/Index.php index b847c40ef..0567a6e8a 100644 --- a/app/Livewire/Project/Application/Deployment/Index.php +++ b/app/Livewire/Project/Application/Deployment/Index.php @@ -18,7 +18,7 @@ class Index extends Component public int $skip = 0; - public int $default_take = 40; + public int $default_take = 10; public bool $show_next = false; @@ -42,7 +42,7 @@ class Index extends Component if (! $application) { return redirect()->route('dashboard'); } - ['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40); + ['deployments' => $deployments, 'count' => $count] = $application->deployments(0, $this->default_take); $this->application = $application; $this->deployments = $deployments; $this->deployments_count = $count; diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php index 6a834ee6f..ee65a902f 100644 --- a/bootstrap/helpers/databases.php +++ b/bootstrap/helpers/databases.php @@ -56,14 +56,16 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth EnvironmentVariable::create([ 'key' => 'REDIS_PASSWORD', 'value' => $redis_password, - 'standalone_redis_id' => $database->id, + 'resourceable_type' => StandaloneRedis::class, + 'resourceable_id' => $database->id, 'is_shared' => false, ]); EnvironmentVariable::create([ 'key' => 'REDIS_USERNAME', 'value' => 'default', - 'standalone_redis_id' => $database->id, + 'resourceable_type' => StandaloneRedis::class, + 'resourceable_id' => $database->id, 'is_shared' => false, ]); diff --git a/database/migrations/2025_01_16_130238_add_deployment_queue_finished_at.php b/database/migrations/2025_01_16_130238_add_deployment_queue_finished_at.php new file mode 100644 index 000000000..34b652ee0 --- /dev/null +++ b/database/migrations/2025_01_16_130238_add_deployment_queue_finished_at.php @@ -0,0 +1,22 @@ +timestamp('finished_at')->nullable(); + }); + } + + public function down() + { + Schema::table('application_deployment_queues', function (Blueprint $table) { + $table->dropColumn('finished_at'); + }); + } +}; diff --git a/public/js/dayjs-plugin-relativeTime.js b/public/js/dayjs-plugin-relativeTime.js new file mode 100644 index 000000000..898eee6d0 --- /dev/null +++ b/public/js/dayjs-plugin-relativeTime.js @@ -0,0 +1 @@ +!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(r="undefined"!=typeof globalThis?globalThis:r||self).dayjs_plugin_relativeTime=e()}(this,(function(){"use strict";return function(r,e,t){r=r||{};var n=e.prototype,o={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function i(r,e,t,o){return n.fromToBase(r,e,t,o)}t.en.relativeTime=o,n.fromToBase=function(e,n,i,d,u){for(var f,a,s,l=i.$locale().relativeTime||o,h=r.thresholds||[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],m=h.length,c=0;c0,p<=y.r||!y.r){p<=1&&c>0&&(y=h[c-1]);var v=l[y.l];u&&(p=u(""+p)),a="string"==typeof v?v.replace("%d",p):v(p,n,y.l,s);break}}if(n)return a;var M=s?l.future:l.past;return"function"==typeof M?M(a):M.replace("%s",a)},n.to=function(r,e){return i(r,e,this,!0)},n.from=function(r,e){return i(r,e,this)};var d=function(r){return r.$u?t.utc():t()};n.toNow=function(r){return this.to(d(this),r)},n.fromNow=function(r){return this.from(d(this),r)}}})); \ No newline at end of file diff --git a/public/js/dayjs-plugin-utc.js b/public/js/dayjs-plugin-utc.js new file mode 100644 index 000000000..608771e95 --- /dev/null +++ b/public/js/dayjs-plugin-utc.js @@ -0,0 +1 @@ +!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_utc=i()}(this,(function(){"use strict";var t="minute",i=/[+-]\d\d(?::?\d\d)?/g,e=/([+-]|\d\d)/g;return function(s,f,n){var u=f.prototype;n.utc=function(t){var i={date:t,utc:!0,args:arguments};return new f(i)},u.utc=function(i){var e=n(this.toDate(),{locale:this.$L,utc:!0});return i?e.add(this.utcOffset(),t):e},u.local=function(){return n(this.toDate(),{locale:this.$L,utc:!1})};var o=u.parse;u.parse=function(t){t.utc&&(this.$u=!0),this.$utils().u(t.$offset)||(this.$offset=t.$offset),o.call(this,t)};var r=u.init;u.init=function(){if(this.$u){var t=this.$d;this.$y=t.getUTCFullYear(),this.$M=t.getUTCMonth(),this.$D=t.getUTCDate(),this.$W=t.getUTCDay(),this.$H=t.getUTCHours(),this.$m=t.getUTCMinutes(),this.$s=t.getUTCSeconds(),this.$ms=t.getUTCMilliseconds()}else r.call(this)};var a=u.utcOffset;u.utcOffset=function(s,f){var n=this.$utils().u;if(n(s))return this.$u?0:n(this.$offset)?a.call(this):this.$offset;if("string"==typeof s&&(s=function(t){void 0===t&&(t="");var s=t.match(i);if(!s)return null;var f=(""+s[0]).match(e)||["-",0,0],n=f[0],u=60*+f[1]+ +f[2];return 0===u?0:"+"===n?u:-u}(s),null===s))return this;var u=Math.abs(s)<=16?60*s:s,o=this;if(f)return o.$offset=u,o.$u=0===s,o;if(0!==s){var r=this.$u?this.toDate().getTimezoneOffset():-1*this.utcOffset();(o=this.local().add(u+r,t)).$offset=u,o.$x.$localOffset=r}else o=this.utc();return o};var h=u.format;u.format=function(t){var i=t||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":"");return h.call(this,i)},u.valueOf=function(){var t=this.$utils().u(this.$offset)?0:this.$offset+(this.$x.$localOffset||this.$d.getTimezoneOffset());return this.$d.valueOf()-6e4*t},u.isUTC=function(){return!!this.$u},u.toISOString=function(){return this.toDate().toISOString()},u.toString=function(){return this.toDate().toUTCString()};var l=u.toDate;u.toDate=function(t){return"s"===t&&this.$offset?n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate():l.call(this)};var c=u.diff;u.diff=function(t,i,e){if(t&&this.$u===t.$u)return c.call(this,t,i,e);var s=this.local(),f=n(t).local();return c.call(s,f,i,e)}}})); diff --git a/public/js/dayjs.min.js b/public/js/dayjs.min.js new file mode 100644 index 000000000..76f599020 --- /dev/null +++ b/public/js/dayjs.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){"use strict";var t=1e3,e=6e4,n=36e5,r="millisecond",i="second",s="minute",u="hour",a="day",o="week",c="month",f="quarter",h="year",d="date",l="Invalid Date",$=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,y=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(t){var e=["th","st","nd","rd"],n=t%100;return"["+t+(e[(n-20)%10]||e[n]||e[0])+"]"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:""+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?"+":"-")+m(r,2,"0")+":"+m(i,2,"0")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n="object"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if("string"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t) + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/svgs/lowcoder.svg b/public/svgs/lowcoder.svg new file mode 100644 index 000000000..0dfeb16f2 --- /dev/null +++ b/public/svgs/lowcoder.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/svgs/openblocks.svg b/public/svgs/openblocks.svg deleted file mode 100644 index 2de5ee88f..000000000 --- a/public/svgs/openblocks.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/resources/css/app.css b/resources/css/app.css index 6e7704eb7..f89d65d80 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -313,7 +313,7 @@ section { } .fullscreen { - @apply fixed top-0 left-0 w-full h-full z-[9999] dark:bg-coolgray-100 bg-white overflow-y-auto scrollbar pb-4; + @apply fixed top-0 left-0 w-full h-full z-[9999] dark:bg-coolgray-100 bg-white overflow-y-auto scrollbar; } .toast { @@ -322,4 +322,8 @@ section { .dz-button { @apply w-full p-4 py-10 my-4 font-bold bg-white border dark:border-coolgray-400 dark:text-white dark:bg-transparent hover:dark:bg-coolgray-400; +} + +.xterm { + @apply p-2; } \ No newline at end of file diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index d6075248f..4148a61d3 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -261,7 +261,7 @@
  • - - diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 5213479c8..aae357401 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -40,7 +40,9 @@ - + + + @endauth @section('body') diff --git a/resources/views/livewire/project/application/deployment/index.blade.php b/resources/views/livewire/project/application/deployment/index.blade.php index 9256ac73d..444dfc2d4 100644 --- a/resources/views/livewire/project/application/deployment/index.blade.php +++ b/resources/views/livewire/project/application/deployment/index.blade.php @@ -5,12 +5,11 @@

    Deployments

    - {{-- --}}

    Deployments ({{ $deployments_count }})

    - @if ($deployments_count > 0) + @if ($deployments_count > 0 && $deployments_count > $default_take) data_get($deployment, 'status') === 'cancelled-by-user', 'border-error' => data_get($deployment, 'status') === 'failed', 'border-success' => data_get($deployment, 'status') === 'finished', - ]) - wire:navigate href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}"> + ]) wire:navigate + href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}">
    No deployments found
    @endforelse - @endif
    diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index 92ed72981..02c68317d 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -58,7 +58,7 @@
    -
    +
    @@ -27,7 +27,8 @@ $database->getMorphClass() === 'App\Models\StandaloneMongodb' || $database->getMorphClass() === 'App\Models\StandaloneMysql' || $database->getMorphClass() === 'App\Models\StandaloneMariadb') - diff --git a/resources/views/livewire/project/service/navbar.blade.php b/resources/views/livewire/project/service/navbar.blade.php index 029a74e10..4629f1625 100644 --- a/resources/views/livewire/project/service/navbar.blade.php +++ b/resources/views/livewire/project/service/navbar.blade.php @@ -18,7 +18,7 @@ href="{{ route('project.service.logs', $parameters) }}"> - diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 42e6f8535..1b87852a7 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -12,7 +12,7 @@ }, toggleScroll() { this.alwaysScroll = !this.alwaysScroll; - + if (this.alwaysScroll) { this.intervalId = setInterval(() => { const screen = document.getElementById('screen'); @@ -58,34 +58,45 @@
    - - - - - +
    +
    + {{-- + --}} + + +
    +
    @if ($outputs)
    {{ $outputs }}
    @else diff --git a/resources/views/livewire/project/shared/terminal.blade.php b/resources/views/livewire/project/shared/terminal.blade.php index b15f2aaa9..26388cc0d 100644 --- a/resources/views/livewire/project/shared/terminal.blade.php +++ b/resources/views/livewire/project/shared/terminal.blade.php @@ -7,13 +7,14 @@
    -
    @script - + @endscript
    diff --git a/templates/compose/joomla-with-mariadb.yaml b/templates/compose/joomla-with-mariadb.yaml new file mode 100644 index 000000000..8569d2391 --- /dev/null +++ b/templates/compose/joomla-with-mariadb.yaml @@ -0,0 +1,48 @@ +# documentation: https://joomla.org +# slogan: Joomla! is the mobile-ready and user-friendly way to build your website. Choose from thousands of features and designs. Joomla! is free and open source. +# tags: cms, blog, content, management, mariadb +# logo: svgs/joomla.svg +# port: 80 + +services: + joomla: + image: joomla:latest + volumes: + - joomla_data:/var/www/html + environment: + - SERVICE_FQDN_JOOMLA + - JOOMLA_DB_HOST=mariadb + - JOOMLA_DB_USER=${SERVICE_USER_JOOMLA} + - JOOMLA_DB_PASSWORD=${SERVICE_PASSWORD_JOOMLA} + - JOOMLA_DB_NAME=${MYSQL_DATABASE:-joomla-db} + depends_on: + mariadb: + condition: service_healthy + healthcheck: + test: + - CMD + - curl + - '-f' + - 'http://127.0.0.1' + interval: 2s + timeout: 10s + retries: 10 + + mariadb: + image: mariadb:11 + volumes: + - joomla_mariadb_data:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_ROOT} + - MYSQL_DATABASE=${MYSQL_DATABASE:-joomla-db} + - MYSQL_USER=${SERVICE_USER_JOOMLA} + - MYSQL_PASSWORD=${SERVICE_PASSWORD_JOOMLA} + healthcheck: + test: + - CMD + - healthcheck.sh + - '--connect' + - '--innodb_initialized' + interval: 5s + timeout: 20s + retries: 10 diff --git a/templates/compose/lowcoder.yaml b/templates/compose/lowcoder.yaml new file mode 100644 index 000000000..7cf10b99f --- /dev/null +++ b/templates/compose/lowcoder.yaml @@ -0,0 +1,21 @@ +# documentation: https://docs.lowcoder.cloud/ +# slogan: Lowcoder (forked from OpenBlocks) is a self-hosted, open-source, low-code platform for building internal tools. +# tags: lowcoder,openblocks,low,code,platform,open,source,low,code +# logo: svgs/lowcoder.svg +# port: 3000 + +services: + lowcoder: + image: lowcoderorg/lowcoder-ce + environment: + - SERVICE_FQDN_LOWCODER_3000 + - LOWCODER_EMAIL_SIGNUP_ENABLED=${LOWCODER_EMAIL_SIGNUP_ENABLED:-true} + - LOWCODER_DB_ENCRYPTION_PASSWORD=${SERVICE_PASSWORD_ENCRYPTION} + - LOWCODER_DB_ENCRYPTION_SALT=${SERVICE_PASSWORD_SALT} + volumes: + - lowcoder_data:/lowcoder-stacks + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"] + interval: 5s + timeout: 20s + retries: 10 diff --git a/templates/compose/openblocks.yaml b/templates/compose/openblocks.yaml deleted file mode 100644 index 9ab85d25e..000000000 --- a/templates/compose/openblocks.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# documentation: https://openblocks.dev -# slogan: OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools. -# tags: openblocks,low,code,platform,open,source,low,code -# logo: svgs/openblocks.svg -# port: 3000 - -services: - openblocks: - image: openblocksdev/openblocks-ce - environment: - - SERVICE_FQDN_OPENBLOCKS_3000 - - ENABLE_USER_SIGN_UP=${ENABLE_USER_SIGN_UP:-true} - - ENCRYPTION_PASSWORD=$SERVICE_PASSWORD_ENCRYPTION - - ENCRYPTION_SALT=$SERVICE_PASSWORD_SALT - volumes: - - openblocks-data:/openblocks-stacks - healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"] - interval: 5s - timeout: 20s - retries: 10 diff --git a/templates/service-templates.json b/templates/service-templates.json index b30ddc7a5..4de2e70e6 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -1186,7 +1186,7 @@ "gotenberg": { "documentation": "https://gotenberg.dev/docs/getting-started/introduction?utm_source=coolify.io", "slogan": "Gotenberg is a Docker-powered stateless API for PDF files.", - "compose": "c2VydmljZXM6CiAgZ290ZW5iZXJnOgogICAgaW1hZ2U6ICdnb3RlbmJlcmcvZ290ZW5iZXJnOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HT1RFTkJFUkdfMzAwMAogICAgICAtICdHT1RFTkJFUkdfQVBJX0JBU0lDX0FVVEhfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfR09URU5CRVJHfScKICAgICAgLSAnR09URU5CRVJHX0FQSV9CQVNJQ19BVVRIX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9HT1RFTkJFUkd9JwogICAgY29tbWFuZDoKICAgICAgLSBnb3RlbmJlcmcKICAgICAgLSAnLS1hcGktZW5hYmxlLWJhc2ljLWF1dGgnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC92ZXJzaW9uJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", + "compose": "c2VydmljZXM6CiAgZ290ZW5iZXJnOgogICAgaW1hZ2U6ICdnb3RlbmJlcmcvZ290ZW5iZXJnOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9HT1RFTkJFUkdfMzAwMAogICAgICAtICdHT1RFTkJFUkdfQVBJX0JBU0lDX0FVVEhfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfR09URU5CRVJHfScKICAgICAgLSAnR09URU5CRVJHX0FQSV9CQVNJQ19BVVRIX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9HT1RFTkJFUkd9JwogICAgY29tbWFuZDoKICAgICAgLSBnb3RlbmJlcmcKICAgICAgLSAnLS1hcGktZW5hYmxlLWJhc2ljLWF1dGgnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9oZWFsdGgnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK", "tags": [ "api", "backend", @@ -1420,6 +1420,21 @@ "minversion": "0.0.0", "port": "8080" }, + "joomla-with-mariadb": { + "documentation": "https://joomla.org?utm_source=coolify.io", + "slogan": "Joomla! is the mobile-ready and user-friendly way to build your website. Choose from thousands of features and designs. Joomla! is free and open source.", + "compose": "c2VydmljZXM6CiAgam9vbWxhOgogICAgaW1hZ2U6ICdqb29tbGE6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnam9vbWxhX2RhdGE6L3Zhci93d3cvaHRtbCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9KT09NTEEKICAgICAgLSBKT09NTEFfREJfSE9TVD1tYXJpYWRiCiAgICAgIC0gJ0pPT01MQV9EQl9VU0VSPSR7U0VSVklDRV9VU0VSX0pPT01MQX0nCiAgICAgIC0gJ0pPT01MQV9EQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfSk9PTUxBfScKICAgICAgLSAnSk9PTUxBX0RCX05BTUU9JHtNWVNRTF9EQVRBQkFTRTotam9vbWxhLWRifScKICAgIGRlcGVuZHNfb246CiAgICAgIG1hcmlhZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxMAogIG1hcmlhZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTEnCiAgICB2b2x1bWVzOgogICAgICAtICdqb29tbGFfbWFyaWFkYl9kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JPT1R9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1qb29tbGEtZGJ9JwogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX0pPT01MQX0nCiAgICAgIC0gJ01ZU1FMX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9KT09NTEF9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGhlYWx0aGNoZWNrLnNoCiAgICAgICAgLSAnLS1jb25uZWN0JwogICAgICAgIC0gJy0taW5ub2RiX2luaXRpYWxpemVkJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", + "tags": [ + "cms", + "blog", + "content", + "management", + "mariadb" + ], + "logo": "svgs/joomla.svg", + "minversion": "0.0.0", + "port": "80" + }, "joplin": { "documentation": "https://github.com/laurent22/joplin/blob/dev/packages/server/README.md?utm_source=coolify.io", "slogan": "Self-hosted sync server for Joplin", @@ -1675,6 +1690,25 @@ "logo": "svgs/logto_dark.svg", "minversion": "0.0.0" }, + "lowcoder": { + "documentation": "https://docs.lowcoder.cloud/?utm_source=coolify.io", + "slogan": "Lowcoder (forked from OpenBlocks) is a self-hosted, open-source, low-code platform for building internal tools.", + "compose": "c2VydmljZXM6CiAgbG93Y29kZXI6CiAgICBpbWFnZTogbG93Y29kZXJvcmcvbG93Y29kZXItY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9MT1dDT0RFUl8zMDAwCiAgICAgIC0gJ0xPV0NPREVSX0VNQUlMX1NJR05VUF9FTkFCTEVEPSR7TE9XQ09ERVJfRU1BSUxfU0lHTlVQX0VOQUJMRUQ6LXRydWV9JwogICAgICAtICdMT1dDT0RFUl9EQl9FTkNSWVBUSU9OX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9FTkNSWVBUSU9OfScKICAgICAgLSAnTE9XQ09ERVJfREJfRU5DUllQVElPTl9TQUxUPSR7U0VSVklDRV9QQVNTV09SRF9TQUxUfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2xvd2NvZGVyX2RhdGE6L2xvd2NvZGVyLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=", + "tags": [ + "lowcoder", + "openblocks", + "low", + "code", + "platform", + "open", + "source", + "low", + "code" + ], + "logo": "svgs/lowcoder.svg", + "minversion": "0.0.0", + "port": "3000" + }, "mailpit": { "documentation": "https://mailpit.axllent.org/docs/?utm_source=coolify.io", "slogan": "Email & SMTP testing tool with API for developers", @@ -2186,24 +2220,6 @@ "minversion": "0.0.0", "port": "6610" }, - "openblocks": { - "documentation": "https://openblocks.dev?utm_source=coolify.io", - "slogan": "OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.", - "compose": "c2VydmljZXM6CiAgb3BlbmJsb2NrczoKICAgIGltYWdlOiBvcGVuYmxvY2tzZGV2L29wZW5ibG9ja3MtY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PUEVOQkxPQ0tTXzMwMDAKICAgICAgLSAnRU5BQkxFX1VTRVJfU0lHTl9VUD0ke0VOQUJMRV9VU0VSX1NJR05fVVA6LXRydWV9JwogICAgICAtIEVOQ1JZUFRJT05fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfRU5DUllQVElPTgogICAgICAtIEVOQ1JZUFRJT05fU0FMVD0kU0VSVklDRV9QQVNTV09SRF9TQUxUCiAgICB2b2x1bWVzOgogICAgICAtICdvcGVuYmxvY2tzLWRhdGE6L29wZW5ibG9ja3Mtc3RhY2tzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", - "tags": [ - "openblocks", - "low", - "code", - "platform", - "open", - "source", - "low", - "code" - ], - "logo": "svgs/openblocks.svg", - "minversion": "0.0.0", - "port": "3000" - }, "organizr": { "documentation": "https://docs.organizr.app/?utm_source=coolify.io", "slogan": "Homelab Services Organizer",