diff --git a/.env.example b/.env.example index 16dc835..d64e7f3 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ APP_NAME=Laravel -APP_ENV=local +APP_ENV=production APP_KEY= -APP_DEBUG=true +APP_DEBUG=false APP_URL=http://localhost APP_LOCALE=en @@ -18,18 +18,18 @@ BCRYPT_ROUNDS=12 LOG_CHANNEL=stack LOG_STACK=single LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug +LOG_LEVEL=error DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=incr DB_USERNAME=incr_user -DB_PASSWORD=incr_password +DB_PASSWORD=change_me_in_production SESSION_DRIVER=database SESSION_LIFETIME=120 -SESSION_ENCRYPT=false +SESSION_ENCRYPT=true SESSION_PATH=/ SESSION_DOMAIN=null diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index de2ddcc..5b6256d 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -21,6 +21,10 @@ class RegisteredUserController extends Controller */ public function create(): Response { + if (User::exists()) { + abort(403, 'Registration is disabled.'); + } + return Inertia::render('auth/register'); } @@ -31,16 +35,20 @@ public function create(): Response */ public function store(Request $request): RedirectResponse { - $request->validate([ + if (User::exists()) { + abort(403, 'Registration is disabled.'); + } + + $validated = $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), + $user = User::forceCreate([ + 'name' => $validated['name'], + 'email' => $validated['email'], + 'password' => Hash::make($validated['password']), ]); event(new Registered($user)); diff --git a/app/Http/Controllers/Milestones/MilestoneController.php b/app/Http/Controllers/Milestones/MilestoneController.php index ae0cae1..580cb4a 100644 --- a/app/Http/Controllers/Milestones/MilestoneController.php +++ b/app/Http/Controllers/Milestones/MilestoneController.php @@ -12,15 +12,12 @@ class MilestoneController extends Controller { public function store(Request $request): RedirectResponse { - $request->validate([ + $validated = $request->validate([ 'target' => 'required|integer|min:1', 'description' => 'required|string|max:255', ]); - Milestone::create([ - 'target' => $request->target, - 'description' => $request->description, - ]); + Milestone::create($validated); return back()->with('success', 'Milestone created successfully'); } diff --git a/app/Http/Controllers/Pricing/PricingController.php b/app/Http/Controllers/Pricing/PricingController.php index f6ed425..f7089cc 100644 --- a/app/Http/Controllers/Pricing/PricingController.php +++ b/app/Http/Controllers/Pricing/PricingController.php @@ -46,13 +46,15 @@ public function update(Request $request) public function history(Request $request): JsonResponse { - $limit = $request->get('limit', 30); + $limit = min(max(1, $request->integer('limit', 30)), 365); return response()->json(AssetPrice::history($this->user->asset_id, $limit)); } public function forDate(Request $request, string $date): JsonResponse { + validator(['date' => $date], ['date' => 'required|date_format:Y-m-d'])->validate(); + return response()->json([ 'date' => $date, 'price' => AssetPrice::forDate($date, $this->user->asset_id), diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 3af9fd4..784aa7c 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -44,7 +44,7 @@ public function share(Request $request): array 'name' => config('app.name'), 'quote' => ['message' => trim($message), 'author' => trim($author)], 'auth' => [ - 'user' => $request->user(), + 'user' => $request->user()?->only(['id', 'name', 'email']), ], 'ziggy' => fn (): array => [ ...(new Ziggy)->toArray(), diff --git a/app/Models/User.php b/app/Models/User.php index bbc50a0..e0fd5f6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Str; /** * @property int|null $asset_id @@ -26,7 +27,6 @@ class User extends Authenticatable protected $fillable = [ 'name', 'email', - 'password', 'asset_id', 'price_tracking_enabled', ]; @@ -57,10 +57,12 @@ public function asset(): BelongsTo public static function default(): self { - return self::firstOrCreate( - ['email' => 'user@incr.local'], - ['name' => 'Default User', 'password' => 'password'] - ); + return self::firstWhere('email', 'user@incr.local') + ?? self::forceCreate([ + 'email' => 'user@incr.local', + 'name' => 'Default User', + 'password' => bcrypt(Str::random(32)), + ]); } public function hasCompletedOnboarding(): bool diff --git a/docker/production/nginx.conf b/docker/production/nginx.conf index b462ad6..8364891 100644 --- a/docker/production/nginx.conf +++ b/docker/production/nginx.conf @@ -4,6 +4,12 @@ server { root /var/www/html/public; index index.php index.html; + server_tokens off; + + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "SAMEORIGIN"; + add_header Referrer-Policy "strict-origin-when-cross-origin"; + location / { try_files $uri $uri/ /index.php?$query_string; } diff --git a/docker/production/start-app.sh b/docker/production/start-app.sh index 166e047..8b31e84 100644 --- a/docker/production/start-app.sh +++ b/docker/production/start-app.sh @@ -10,7 +10,7 @@ fi # Wait for database to be ready echo "Waiting for database..." -until php artisan tinker --execute="DB::connection()->getPdo();" 2>/dev/null; do +until mysql -h"${DB_HOST:-db}" -u"${DB_USERNAME:-incr_user}" -p"${DB_PASSWORD}" -e "SELECT 1" >/dev/null 2>&1; do echo "Database not ready, waiting..." sleep 2 done diff --git a/docker/production/supervisord.conf b/docker/production/supervisord.conf index 8a7a5b6..f1f3fe1 100644 --- a/docker/production/supervisord.conf +++ b/docker/production/supervisord.conf @@ -1,6 +1,6 @@ [supervisord] nodaemon=true -user=root +user=www-data [program:nginx] command=nginx -g "daemon off;"