diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index 6abac7029..31d7639be 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -10,6 +10,7 @@ use App\Models\StandaloneMongodb;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Storage;
class Init extends Command
{
@@ -21,8 +22,23 @@ class Init extends Command
ray()->clearAll();
$this->cleanup_in_progress_application_deployments();
$this->cleanup_stucked_resources();
+ $this->cleanup_ssh();
}
+ private function cleanup_ssh() {
+ try {
+ $files = Storage::allFiles('ssh/keys');
+ foreach ($files as $file) {
+ Storage::delete($file);
+ }
+ $files = Storage::allFiles('ssh/mux');
+ foreach ($files as $file) {
+ Storage::delete($file);
+ }
+ } catch (\Throwable $e) {
+ echo "Error: {$e->getMessage()}\n";
+ }
+ }
private function cleanup_in_progress_application_deployments()
{
// Cleanup any failed deployments
diff --git a/app/Http/Livewire/Project/Database/BackupExecutions.php b/app/Http/Livewire/Project/Database/BackupExecutions.php
index 2f808d992..f8ec4efbe 100644
--- a/app/Http/Livewire/Project/Database/BackupExecutions.php
+++ b/app/Http/Livewire/Project/Database/BackupExecutions.php
@@ -2,6 +2,7 @@
namespace App\Http\Livewire\Project\Database;
+use Illuminate\Support\Facades\Storage;
use Livewire\Component;
class BackupExecutions extends Component
@@ -23,6 +24,29 @@ class BackupExecutions extends Component
$this->emit('success', 'Backup deleted successfully.');
$this->emit('refreshBackupExecutions');
}
+ public function download($exeuctionId)
+ {
+ try {
+ $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
+ if (is_null($execution)) {
+ $this->emit('error', 'Backup execution not found.');
+ return;
+ }
+ $filename = data_get($execution, 'filename');
+ $server = $execution->scheduledDatabaseBackup->database->destination->server;
+ $privateKeyLocation = savePrivateKeyToFs($server);
+ $disk = Storage::build([
+ 'driver' => 'sftp',
+ 'host' => $server->ip,
+ 'port' => $server->port,
+ 'username' => $server->user,
+ 'privateKey' => $privateKeyLocation,
+ ]);
+ return $disk->download($filename);
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
public function refreshBackupExecutions(): void
{
$this->executions = $this->backup->executions;
diff --git a/app/Http/Livewire/Project/Shared/Webhooks.php b/app/Http/Livewire/Project/Shared/Webhooks.php
new file mode 100644
index 000000000..a943347b1
--- /dev/null
+++ b/app/Http/Livewire/Project/Shared/Webhooks.php
@@ -0,0 +1,19 @@
+deploywebhook = generateDeployWebhook($this->resource);
+ }
+ public function render()
+ {
+ return view('livewire.project.shared.webhooks');
+ }
+}
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index f60994c61..79b214502 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -48,7 +48,7 @@ class RouteServiceProvider extends ServiceProvider
if ($request->path() === 'api/health') {
return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip());
}
- return Limit::perMinute(30)->by($request->user()?->id ?: $request->ip());
+ return Limit::perMinute(200)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('5', function (Request $request) {
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 3348ce1ae..8e8b2ec3e 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -4,7 +4,9 @@ use App\Models\Application;
use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\Service;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use App\Models\Team;
@@ -484,5 +486,18 @@ function queryResourcesByUuid(string $uuid)
if ($redis) return $redis;
$mongodb = StandaloneMongodb::whereUuid($uuid)->first();
if ($mongodb) return $mongodb;
+ $mysql = StandaloneMysql::whereUuid($uuid)->first();
+ if ($mysql) return $mysql;
+ $mariadb = StandaloneMariadb::whereUuid($uuid)->first();
+ if ($mariadb) return $mariadb;
return $resource;
}
+
+function generateDeployWebhook($resource) {
+ $baseUrl = base_url();
+ $api = Url::fromString($baseUrl) . '/api/v1';
+ $endpoint = '/deploy';
+ $uuid = data_get($resource, 'uuid');
+ $url = $api . $endpoint . "?uuid=$uuid&force=false";
+ return $url;
+}
diff --git a/composer.json b/composer.json
index 217560b57..9937ee5b9 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,7 @@
"laravel/ui": "^4.2",
"lcobucci/jwt": "^5.0.0",
"league/flysystem-aws-s3-v3": "^3.0",
+ "league/flysystem-sftp-v3": "^3.0",
"livewire/livewire": "^v2.12.3",
"lorisleiva/laravel-actions": "^2.7",
"masmerise/livewire-toaster": "^1.2",
diff --git a/composer.lock b/composer.lock
index 9f9e8d658..62d4f27ff 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "de2c45be3f03d43430549d963778dc4a",
+ "content-hash": "21ed976753483557403be75318585442",
"packages": [
{
"name": "aws/aws-crt-php",
@@ -2938,6 +2938,66 @@
],
"time": "2023-08-30T10:23:59+00:00"
},
+ {
+ "name": "league/flysystem-sftp-v3",
+ "version": "3.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-sftp-v3.git",
+ "reference": "1ba682def8e87fd7fa00883629553c0200d2e974"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/1ba682def8e87fd7fa00883629553c0200d2e974",
+ "reference": "1ba682def8e87fd7fa00883629553c0200d2e974",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem": "^3.0.14",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2",
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\PhpseclibV3\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "SFTP filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "sftp"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-sftp-v3/issues",
+ "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.16.0"
+ },
+ "funding": [
+ {
+ "url": "https://ecologi.com/frankdejonge",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-30T10:25:05+00:00"
+ },
{
"name": "league/mime-type-detection",
"version": "1.13.0",
diff --git a/config/sentry.php b/config/sentry.php
index 32e27e081..16f184229 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.101',
+ 'release' => '4.0.0-beta.102',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index d54064579..004c77c3b 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Location: {{ data_get($execution, 'filename', 'N/A') }}
-
- {{-- @if (data_get($execution, 'status') !== 'failed') --}}
- {{--
Download --}}
- {{-- @endif --}}
+ @if (data_get($execution, 'status') === 'success')
+
Download
+ @endif
Delete
diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php
index a966c56d4..933c89ecd 100644
--- a/resources/views/livewire/project/service/index.blade.php
+++ b/resources/views/livewire/project/service/index.blade.php
@@ -4,16 +4,25 @@
@@ -100,7 +109,9 @@
@foreach ($databases as $database)
@endforeach
-
+
+
+
+
+
+
diff --git a/routes/api.php b/routes/api.php
index 77c000576..ef233e37e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,6 +1,8 @@
query->get('uuid');
$force = $request->query->get('force') ?? false;
-
if (is_null($teamId)) {
return response()->json(['error' => 'Invalid token.'], 400);
}
@@ -50,29 +51,56 @@ Route::group([
);
return response()->json(['message' => 'Deployment queued.'], 200);
} else if ($type === 'App\Models\StandalonePostgresql') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartPostgresql::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
} else if ($type === 'App\Models\StandaloneRedis') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartRedis::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
} else if ($type === 'App\Models\StandaloneMongodb') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartMongodb::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
- }else if ($type === 'App\Models\Service') {
+ } else if ($type === 'App\Models\StandaloneMysql') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
+ StartMysql::run($resource);
+ $resource->update([
+ 'started_at' => now(),
+ ]);
+ return response()->json(['message' => 'Database started.'], 200);
+ } else if ($type === 'App\Models\StandaloneMariadb') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
+ StartMariadb::run($resource);
+ $resource->update([
+ 'started_at' => now(),
+ ]);
+ return response()->json(['message' => 'Database started.'], 200);
+ } else if ($type === 'App\Models\Service') {
StartService::run($resource);
- return response()->json(['message' => 'Service started.'], 200);
+ return response()->json(['message' => 'Service started. It could take a while, be patient.'], 200);
}
}
- return response()->json(['error' => 'No resource found.'], 404);
+ return response()->json(['error' => "No resource found with {$uuid}."], 404);
});
});
diff --git a/templates/compose/appsmith.yaml b/templates/compose/appsmith.yaml
index a30f4c4e5..81ac5fe3d 100644
--- a/templates/compose/appsmith.yaml
+++ b/templates/compose/appsmith.yaml
@@ -14,3 +14,5 @@ services:
- APPSMITH_SMART_LOOK_ID=
volumes:
- stacks-data:/appsmith-stacks
+ healthcheck:
+ test: ["NONE"]
diff --git a/templates/compose/n8n-with-postgresql.yaml b/templates/compose/n8n-with-postgresql.yaml
new file mode 100644
index 000000000..25955583d
--- /dev/null
+++ b/templates/compose/n8n-with-postgresql.yaml
@@ -0,0 +1,38 @@
+# documentation: https://docs.n8n.io/hosting/
+# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
+# tags: n8n,workflow,automation,open,source,low,code
+
+services:
+ n8n:
+ image: docker.n8n.io/n8nio/n8n
+ environment:
+ - SERVICE_FQDN_N8N
+ - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
+ - N8N_HOST=${SERVICE_FQDN_N8N}
+ - GENERIC_TIMEZONE="Europe/Berlin"
+ - TZ="Europe/Berlin"
+ - DB_TYPE=postgresdb
+ - DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-umami}
+ - DB_POSTGRESDB_HOST=postgresql
+ - DB_POSTGRESDB_PORT=5432
+ - DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
+ - DB_POSTGRESDB_SCHEMA=public
+ - DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ volumes:
+ - n8n-data:/home/node/.n8n
+ depends_on:
+ - postgresql
+ postgresql:
+ image: postgres:15-alpine
+ volumes:
+ - postgresql-data:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_USER=$SERVICE_USER_POSTGRES
+ - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ - POSTGRES_DB=${POSTGRES_DB:-umami}
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
+
diff --git a/templates/compose/n8n.yaml b/templates/compose/n8n.yaml
new file mode 100644
index 000000000..c8613cf03
--- /dev/null
+++ b/templates/compose/n8n.yaml
@@ -0,0 +1,15 @@
+# documentation: https://docs.n8n.io/hosting/
+# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
+# tags: n8n,workflow,automation,open,source,low,code
+
+services:
+ n8n:
+ image: docker.n8n.io/n8nio/n8n
+ environment:
+ - SERVICE_FQDN_N8N
+ - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
+ - N8N_HOST=${SERVICE_FQDN_N8N}
+ - GENERIC_TIMEZONE="Europe/Berlin"
+ - TZ="Europe/Berlin"
+ volumes:
+ - n8n-data:/home/node/.n8n
diff --git a/templates/service-templates.json b/templates/service-templates.json
index d5af2b37c..4c06f76c0 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -2,7 +2,7 @@
"appsmith": {
"documentation": "https:\/\/docs.appsmith.com",
"slogan": "Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.",
- "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycK",
+ "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQo=",
"tags": [
"lowcode",
"nocode",
@@ -110,6 +110,34 @@
"api"
]
},
+ "n8n-with-postgresql": {
+ "documentation": "https:\/\/docs.n8n.io\/hosting\/",
+ "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgICAgLSBEQl9UWVBFPXBvc3RncmVzZGIKICAgICAgLSAnREJfUE9TVEdSRVNEQl9EQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi11bWFtaX0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAo=",
+ "tags": [
+ "n8n",
+ "workflow",
+ "automation",
+ "open",
+ "source",
+ "low",
+ "code"
+ ]
+ },
+ "n8n": {
+ "documentation": "https:\/\/docs.n8n.io\/hosting\/",
+ "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ244bi1kYXRhOi9ob21lL25vZGUvLm44bicK",
+ "tags": [
+ "n8n",
+ "workflow",
+ "automation",
+ "open",
+ "source",
+ "low",
+ "code"
+ ]
+ },
"pairdrop": {
"documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md",
"slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.",
diff --git a/versions.json b/versions.json
index 4c8e06b9d..cd5da1110 100644
--- a/versions.json
+++ b/versions.json
@@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
- "version": "4.0.0-beta.101"
+ "version": "4.0.0-beta.102"
}
}
}