Skip to content

Commit 4ec3229

Browse files
committed
fix(server): improve IP uniqueness validation with team-specific error messages
- Refactor server IP duplicate detection to use `first()` instead of `get()->count()` - Add team-scoped validation to distinguish between same-team and cross-team IP conflicts - Update error messages to clarify ownership: "already exists in your team" vs "in use by another team" - Apply consistent validation logic across API, boarding, and server management flows - Add comprehensive test suite for IP uniqueness enforcement across teams
1 parent 0c19464 commit 4ec3229

File tree

7 files changed

+169
-48
lines changed

7 files changed

+169
-48
lines changed

app/Http/Controllers/Api/ServersController.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,13 @@ public function create_server(Request $request)
519519
if (! $privateKey) {
520520
return response()->json(['message' => 'Private key not found.'], 404);
521521
}
522-
$allServers = ModelsServer::whereIp($request->ip)->get();
523-
if ($allServers->count() > 0) {
524-
return response()->json(['message' => 'Server with this IP already exists.'], 400);
522+
$foundServer = ModelsServer::whereIp($request->ip)->first();
523+
if ($foundServer) {
524+
if ($foundServer->team_id === $teamId) {
525+
return response()->json(['message' => 'A server with this IP/Domain already exists in your team.'], 400);
526+
}
527+
528+
return response()->json(['message' => 'A server with this IP/Domain is already in use by another team.'], 400);
525529
}
526530

527531
$proxyType = $request->proxy_type ? str($request->proxy_type)->upper() : ProxyTypes::TRAEFIK->value;

app/Livewire/Boarding/Index.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,11 @@ public function saveServer()
283283
$this->privateKey = formatPrivateKey($this->privateKey);
284284
$foundServer = Server::whereIp($this->remoteServerHost)->first();
285285
if ($foundServer) {
286-
return $this->dispatch('error', 'IP address is already in use by another team.');
286+
if ($foundServer->team_id === currentTeam()->id) {
287+
return $this->dispatch('error', 'A server with this IP/Domain already exists in your team.');
288+
}
289+
290+
return $this->dispatch('error', 'A server with this IP/Domain is already in use by another team.');
287291
}
288292
$this->createdServer = Server::create([
289293
'name' => $this->remoteServerName,

app/Livewire/Server/New/ByIp.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,13 @@ public function submit()
9797
$this->validate();
9898
try {
9999
$this->authorize('create', Server::class);
100-
if (Server::where('team_id', currentTeam()->id)
101-
->where('ip', $this->ip)
102-
->exists()) {
103-
return $this->dispatch('error', 'This IP/Domain is already in use by another server in your team.');
100+
$foundServer = Server::whereIp($this->ip)->first();
101+
if ($foundServer) {
102+
if ($foundServer->team_id === currentTeam()->id) {
103+
return $this->dispatch('error', 'A server with this IP/Domain already exists in your team.');
104+
}
105+
106+
return $this->dispatch('error', 'A server with this IP/Domain is already in use by another team.');
104107
}
105108

106109
if (is_null($this->private_key_id)) {

app/Livewire/Server/Show.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,16 @@ public function syncData(bool $toModel = false)
189189
$this->validate();
190190

191191
$this->authorize('update', $this->server);
192-
if (Server::where('team_id', currentTeam()->id)
193-
->where('ip', $this->ip)
192+
$foundServer = Server::where('ip', $this->ip)
194193
->where('id', '!=', $this->server->id)
195-
->exists()) {
194+
->first();
195+
if ($foundServer) {
196196
$this->ip = $this->server->ip;
197-
throw new \Exception('This IP/Domain is already in use by another server in your team.');
197+
if ($foundServer->team_id === currentTeam()->id) {
198+
throw new \Exception('A server with this IP/Domain already exists in your team.');
199+
}
200+
201+
throw new \Exception('A server with this IP/Domain is already in use by another team.');
198202
}
199203

200204
$this->server->name = $this->name;

templates/service-templates-latest.json

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,24 +2681,6 @@
26812681
"minversion": "0.0.0",
26822682
"port": "8065"
26832683
},
2684-
"maybe": {
2685-
"documentation": "https://github.com/maybe-finance/maybe?utm_source=coolify.io",
2686-
"slogan": "Maybe, the OS for your personal finances.",
2687-
"compose": "c2VydmljZXM6CiAgbWF5YmU6CiAgICBpbWFnZTogJ2doY3IuaW8vbWF5YmUtZmluYW5jZS9tYXliZTpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdhcHBfc3RvcmFnZTovcmFpbHMvc3RvcmFnZScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01BWUJFCiAgICAgIC0gU0VMRl9IT1NURUQ9dHJ1ZQogICAgICAtICdSQUlMU19GT1JDRV9TU0w9JHtSQUlMU19GT1JDRV9TU0w6LWZhbHNlfScKICAgICAgLSAnUkFJTFNfQVNTVU1FX1NTTD0ke1JBSUxTX0FTU1VNRV9TU0w6LWZhbHNlfScKICAgICAgLSAnR09PRF9KT0JfRVhFQ1VUSU9OX01PREU9JHtHT09EX0pPQl9FWEVDVVRJT05fTU9ERTotYXN5bmN9JwogICAgICAtICdTRUNSRVRfS0VZX0JBU0U9JHtTRVJWSUNFX0JBU0U2NF82NF9TRUNSRVRLRVlCQVNFfScKICAgICAgLSBEQl9IT1NUPXBvc3RncmVzCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LW1heWJlLWRifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHQ6JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfUByZWRpczo2Mzc5LzEnCiAgICAgIC0gJ09QRU5BSV9BQ0NFU1NfVE9LRU49JHtPUEVOQUlfQUNDRVNTX1RPS0VOfScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogIHdvcmtlcjoKICAgIGltYWdlOiAnZ2hjci5pby9tYXliZS1maW5hbmNlL21heWJlOmxhdGVzdCcKICAgIGNvbW1hbmQ6ICdidW5kbGUgZXhlYyBzaWRla2lxJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1tYXliZS1kYn0nCiAgICAgIC0gJ1NFQ1JFVF9LRVlfQkFTRT0ke1NFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVEtFWUJBU0V9JwogICAgICAtIFNFTEZfSE9TVEVEPXRydWUKICAgICAgLSAnUkFJTFNfRk9SQ0VfU1NMPSR7UkFJTFNfRk9SQ0VfU1NMOi1mYWxzZX0nCiAgICAgIC0gJ1JBSUxTX0FTU1VNRV9TU0w9JHtSQUlMU19BU1NVTUVfU1NMOi1mYWxzZX0nCiAgICAgIC0gJ0dPT0RfSk9CX0VYRUNVVElPTl9NT0RFPSR7R09PRF9KT0JfRVhFQ1VUSU9OX01PREU6LWFzeW5jfScKICAgICAgLSBEQl9IT1NUPXBvc3RncmVzCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL2RlZmF1bHQ6JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfUByZWRpczo2Mzc5LzEnCiAgICAgIC0gJ09QRU5BSV9BQ0NFU1NfVE9LRU49JHtPUEVOQUlfQUNDRVNTX1RPS0VOfScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBleGNsdWRlX2Zyb21faGM6IHRydWUKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnbWF5YmVfcG9zdGdyZXNfZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LW1heWJlLWRifScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6OC1hbHBpbmUnCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tYXBwZW5kb25seSB5ZXMgLS1yZXF1aXJlcGFzcyAke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgdm9sdW1lczoKICAgICAgLSAncmVkaXNfZGF0YTovZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdSRURJU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFJFRElTX0RCPTEKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtICctLXBhc3MnCiAgICAgICAgLSAnJHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiAzcwogICAgICByZXRyaWVzOiAzCg==",
2688-
"tags": [
2689-
"finances",
2690-
"wallets",
2691-
"coins",
2692-
"stocks",
2693-
"investments",
2694-
"open",
2695-
"source"
2696-
],
2697-
"category": "productivity",
2698-
"logo": "svgs/maybe.svg",
2699-
"minversion": "0.0.0",
2700-
"port": "3000"
2701-
},
27022684
"mealie": {
27032685
"documentation": "https://docs.mealie.io/?utm_source=coolify.io",
27042686
"slogan": "A recipe manager and meal planner.",
@@ -4548,6 +4530,22 @@
45484530
"minversion": "0.0.0",
45494531
"port": "3567"
45504532
},
4533+
"sure": {
4534+
"documentation": "https://github.com/we-promise/sure?utm_source=coolify.io",
4535+
"slogan": "An all-in-one personal finance platform.",
4536+
"compose": "c2VydmljZXM6CiAgd2ViOgogICAgaW1hZ2U6ICdnaGNyLmlvL3dlLXByb21pc2Uvc3VyZTowLjYuNycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1NVUkVfMzAwMAogICAgICAtIEFQUF9ET01BSU49JFNFUlZJQ0VfRlFETl9TVVJFCiAgICAgIC0gU0VDUkVUX0tFWV9CQVNFPSRTRVJWSUNFX0JBU0U2NF9CQVNFCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1zdXJlfScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vdmFsa2V5OjYzNzknCiAgICAgIC0gREJfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gU0VMRl9IT1NURUQ9dHJ1ZQogICAgICAtIFJBSUxTX0ZPUkNFX1NTTD1mYWxzZQogICAgICAtIFJBSUxTX0FTU1VNRV9TU0w9ZmFsc2UKICAgICAgLSAnT05CT0FSRElOR19TVEFURT0ke09OQk9BUkRJTkdfU1RBVEU6LW9wZW59JwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICB2YWxrZXk6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N1cmUtYXBwLXN0b3JhZ2U6L3JhaWxzL3N0b3JhZ2UnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogNQogIHdvcmtlcjoKICAgIGltYWdlOiAnZ2hjci5pby93ZS1wcm9taXNlL3N1cmU6MC42LjcnCiAgICBjb21tYW5kOiAnYnVuZGxlIGV4ZWMgc2lkZWtpcScKICAgIGVudmlyb25tZW50OgogICAgICAtIEFQUF9ET01BSU49JFNFUlZJQ0VfRlFETl9TVVJFCiAgICAgIC0gU0VDUkVUX0tFWV9CQVNFPSRTRVJWSUNFX0JBU0U2NF9CQVNFCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1zdXJlfScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vdmFsa2V5OjYzNzknCiAgICAgIC0gREJfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gU0VMRl9IT1NURUQ9dHJ1ZQogICAgICAtIFJBSUxTX0ZPUkNFX1NTTD1mYWxzZQogICAgICAtIFJBSUxTX0FTU1VNRV9TU0w9ZmFsc2UKICAgICAgLSAnT05CT0FSRElOR19TVEFURT0ke09OQk9BUkRJTkdfU1RBVEU6LW9wZW59JwogICAgdm9sdW1lczoKICAgICAgLSAnc3VyZS1hcHAtc3RvcmFnZTovcmFpbHMvc3RvcmFnZScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgdmFsa2V5OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogNQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N1cmUtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotc3VyZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgdmFsa2V5OgogICAgaW1hZ2U6ICd2YWxrZXkvdmFsa2V5OjgtYWxwaW5lJwogICAgY29tbWFuZDogJ3ZhbGtleS1zZXJ2ZXIgLS1hcHBlbmRvbmx5IHllcycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3N1cmUtdmFsa2V5Oi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd2YWxrZXktY2xpIHBpbmcgfCBncmVwIFBPTkcnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogM3MK",
4537+
"tags": [
4538+
"budgeting",
4539+
"budget",
4540+
"money",
4541+
"expenses",
4542+
"income"
4543+
],
4544+
"category": "finance",
4545+
"logo": "svgs/sure.png",
4546+
"minversion": "0.0.0",
4547+
"port": "3000"
4548+
},
45514549
"swetrix": {
45524550
"documentation": "https://docs.swetrix.com/selfhosting/how-to?utm_source=coolify.io",
45534551
"slogan": "Privacy-friendly and cookieless European web analytics alternative to Google Analytics.",

0 commit comments

Comments
 (0)