reverse proxy basics (#609)

This commit is contained in:
Saeed Vaziry
2025-06-10 00:14:05 +02:00
committed by GitHub
parent 4e6491a080
commit 09a9735962
7 changed files with 101 additions and 33 deletions

View File

@ -13,6 +13,7 @@
use App\Notifications\SiteInstallationSucceed; use App\Notifications\SiteInstallationSucceed;
use App\ValidationRules\DomainRule; use App\ValidationRules\DomainRule;
use Exception; use Exception;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -38,6 +39,7 @@ public function create(Server $server, array $input): Site
'type' => $input['type'], 'type' => $input['type'],
'domain' => $input['domain'], 'domain' => $input['domain'],
'aliases' => $input['aliases'] ?? [], 'aliases' => $input['aliases'] ?? [],
'port' => $input['port'] ?? null,
'user' => $user, 'user' => $user,
'path' => '/home/'.$user.'/'.$input['domain'], 'path' => '/home/'.$user.'/'.$input['domain'],
'status' => SiteStatus::INSTALLING, 'status' => SiteStatus::INSTALLING,
@ -132,6 +134,14 @@ public static function rules(Server $server, array $input): array
Rule::unique('sites', 'user')->where('server_id', $server->id), Rule::unique('sites', 'user')->where('server_id', $server->id),
Rule::notIn($server->getSshUsers()), Rule::notIn($server->getSshUsers()),
], ],
'port' => [
'nullable',
'integer',
'min:1',
'max:65535',
Rule::unique('sites', 'port')
->where(fn (Builder $query) => $query->where('server_id', $server->id)),
],
]; ];
return array_merge($rules, self::typeRules($server, $input)); return array_merge($rules, self::typeRules($server, $input));

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;
use Spatie\RouteAttributes\Attributes\Prefix;
#[Prefix('search')]
#[Middleware(['auth'])]
class SearchController extends Controller
{
#[Get('/', name: 'search')]
public function search(Request $request): JsonResponse
{
$this->validate($request, [
'query' => 'required|string|min:3',
]);
$query = $request->input('query');
$results = [
'data' => [], // Replace with actual search results
];
return response()->json($results);
}
}

View File

@ -2,6 +2,7 @@ import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, C
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { CommandIcon, SearchIcon } from 'lucide-react'; import { CommandIcon, SearchIcon } from 'lucide-react';
import CreateServer from '@/pages/servers/components/create-server';
export default function AppCommand() { export default function AppCommand() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -33,7 +34,9 @@ export default function AppCommand() {
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions"> <CommandGroup heading="Suggestions">
<CommandItem>Create server</CommandItem> <CreateServer>
<CommandItem>Create server</CommandItem>
</CreateServer>
<CommandItem>Create project</CommandItem> <CommandItem>Create project</CommandItem>
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>

View File

@ -47,7 +47,7 @@ function SheetContent({
<SheetPrimitive.Content <SheetPrimitive.Content
data-slot="sheet-content" data-slot="sheet-content"
className={cn( className={cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500', 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col overflow-y-auto shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
side === 'right' && side === 'right' &&
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm', 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
side === 'left' && side === 'left' &&

View File

@ -25,6 +25,7 @@ type CreateSiteForm = {
php_version: string; php_version: string;
source_control: string; source_control: string;
user: string; user: string;
port: string;
}; };
export default function CreateSite({ server, children }: { server?: Server; children: ReactNode }) { export default function CreateSite({ server, children }: { server?: Server; children: ReactNode }) {
@ -39,6 +40,7 @@ export default function CreateSite({ server, children }: { server?: Server; chil
php_version: '', php_version: '',
source_control: '', source_control: '',
user: '', user: '',
port: '',
}); });
const submit: FormEventHandler = (e) => { const submit: FormEventHandler = (e) => {
@ -154,6 +156,15 @@ export default function CreateSite({ server, children }: { server?: Server; chil
<InputError message={form.errors.aliases} /> <InputError message={form.errors.aliases} />
</FormField> </FormField>
<FormField>
<Label htmlFor="port">Reverse proxy port</Label>
<Input id="port" type="text" value={form.data.port} onChange={(e) => form.setData('port', e.target.value)} placeholder="3000" />
<p className="text-muted-foreground text-xs">
This port will be used for reverse proxying the site. It should be unique across all sites on the server.
</p>
<InputError message={form.errors.port} />
</FormField>
{page.props.configs.site_types_custom_fields[form.data.type].map((config) => getFormField(config))} {page.props.configs.site_types_custom_fields[form.data.type].map((config) => getFormField(config))}
<FormField> <FormField>

View File

@ -8,17 +8,21 @@
import access_log {{ $site->domain }} import access_log {{ $site->domain }}
import compression import compression
import security_headers import security_headers
@if ($site->type()->language() === 'php') @if ($site->port)
root * {{ $site->getWebDirectoryPath() }} reverse_proxy 127.0.0.1:{{ $site->port }}
@php @else
$phpSocket = "unix//var/run/php/php{$site->php_version}-fpm.sock"; @if ($site->type()->language() === 'php')
if ($site->isIsolated()) { root * {{ $site->getWebDirectoryPath() }}
$phpSocket = "unix//run/php/php{$site->php_version}-fpm-{$site->user}.sock"; @php
} $phpSocket = "unix//var/run/php/php{$site->php_version}-fpm.sock";
@endphp if ($site->isIsolated()) {
try_files {path} {path}/ /index.php?{query} $phpSocket = "unix//run/php/php{$site->php_version}-fpm-{$site->user}.sock";
php_fastcgi {{ $phpSocket }} }
file_server @endphp
try_files {path} {path}/ /index.php?{query}
php_fastcgi {{ $phpSocket }}
file_server
@endif
@endif @endif
@if ($site->type === \App\Enums\SiteType::LOAD_BALANCER) @if ($site->type === \App\Enums\SiteType::LOAD_BALANCER)
reverse_proxy { reverse_proxy {
@ -44,4 +48,4 @@
} }
@endif @endif
@include('ssh.services.webserver.caddy.redirects', ['site' => $site]) @include('ssh.services.webserver.caddy.redirects', ['site' => $site])
} }

View File

@ -51,31 +51,41 @@
charset utf-8; charset utf-8;
@if ($site->type()->language() === 'php') @if ($site->port)
@php
$phpSocket = "unix:/var/run/php/php{$site->php_version}-fpm.sock";
if ($site->isIsolated()) {
$phpSocket = "unix:/run/php/php{$site->php_version}-fpm-{$site->user}.sock";
}
@endphp
location / { location / {
try_files $uri $uri/ /index.php?$query_string; proxy_pass http://127.0.0.1:{{ $site->port }};
} proxy_set_header Host $host;
location ~ \.php$ { proxy_set_header X-Real-IP $remote_addr;
fastcgi_pass {{ $phpSocket }}; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; proxy_set_header X-Forwarded-Proto $scheme;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
} }
@else
@if ($site->type()->language() === 'php')
@php
$phpSocket = "unix:/var/run/php/php{$site->php_version}-fpm.sock";
if ($site->isIsolated()) {
$phpSocket = "unix:/run/php/php{$site->php_version}-fpm-{$site->user}.sock";
}
@endphp
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass {{ $phpSocket }};
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
@endif
@endif @endif
@if ($site->type === \App\Enums\SiteType::LOAD_BALANCER) @if ($site->type === \App\Enums\SiteType::LOAD_BALANCER)
location / { location / {
proxy_pass http://{{ $backendName }}$request_uri; proxy_pass http://{{ $backendName }}$request_uri;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
@endif @endif