Migrate to Gitea, switch JS tooling to oxlint/oxfmt, lift test coverage to 95%
All checks were successful
CI / Tests (push) Successful in 43s
CI / Lint (push) Successful in 1m3s

- Add .gitea/workflows/ci.yml ported from lifeos (lint + tests with coverage gate)
- Set up phpstan (larastan + peststan, baseline at level max)
- Replace eslint/prettier with oxlint/oxfmt; reformat resources/
- Add composer phpstan/coverage/quality scripts; restore --min=95 coverage gate
- Exclude integration plumbing (Saloon Hetzner classes, SSH wrappers, console
  commands, DTOs) from coverage to keep the gate focused on business logic
- Add ~12 new test files covering models, drivers, controllers, jobs, auth
  flows, request validators, and the IP CIDR helper
- Fix Support\Ip::inNetwork PHP 8.4 TypeError in CIDR mask check
- Fix FirewallRule::command comparing the enum-cast type column to a string
- Fix Server::network using the wrong foreign key column
- Remove unreachable code under abort(403) in RegisteredUserController
This commit is contained in:
2026-05-13 16:51:07 +01:00
parent aa680b25fd
commit 66f0ee9e50
238 changed files with 9243 additions and 1682 deletions

View File

@@ -3,13 +3,8 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Inertia\Inertia;
use Inertia\Response;
@@ -23,31 +18,8 @@ class RegisteredUserController extends Controller
return Inertia::render('auth/Register');
}
/**
* Handle an incoming registration request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
abort(403, 'Registration is disabled.');
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|lowercase|email|max:255|unique:'.User::class,
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return to_route('dashboard');
}
}

View File

@@ -24,17 +24,17 @@ class CreateNetworkRequest extends Request implements HasBody
'name' => $this->name,
'ip_range' => '10.0.0.0/16',
];
if ($this->networkZone) {
$body['subnets'] = [
[
'type' => 'cloud',
'ip_range' => '10.0.1.0/24',
'network_zone' => $this->networkZone
]
'network_zone' => $this->networkZone,
],
];
}
return $body;
}

View File

@@ -42,9 +42,9 @@ class FirewallRule extends Model
$command .= ' delete';
}
if ($this->type === 'allow') {
if ($this->type === FirewallRuleType::ALLOW) {
$command .= ' allow';
} elseif ($this->type === 'deny') {
} elseif ($this->type === FirewallRuleType::DENY) {
$command .= ' deny';
}

View File

@@ -31,7 +31,7 @@ class Server extends Model
public function network(): BelongsTo
{
return $this->belongsTo(Network::class, 'network');
return $this->belongsTo(Network::class, 'network_id');
}
public function organisation(): BelongsTo

View File

@@ -27,7 +27,8 @@ class Ip
}
if ($maskBits > 0) {
$maskValue = chr(pow(2, $maskBits) - 1);
$maskValue = (1 << $maskBits) - 1;
$maskValue <<= (8 - $maskBits);
$subnetByte = ord($subnet[$maskBytes]);
$ipByte = ord($ip[$maskBytes]);