feature/6-livewire #9
29 changed files with 120 additions and 109 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,3 +22,4 @@ yarn-error.log
|
||||||
/.nova
|
/.nova
|
||||||
/.vscode
|
/.vscode
|
||||||
/.zed
|
/.zed
|
||||||
|
/.vite
|
||||||
|
|
|
||||||
8
.vite/deps/_metadata.json
Normal file
8
.vite/deps/_metadata.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"hash": "9e76e24f",
|
||||||
|
"configHash": "16a1459d",
|
||||||
|
"lockfileHash": "e3b0c442",
|
||||||
|
"browserHash": "8e8bb46e",
|
||||||
|
"optimized": {},
|
||||||
|
"chunks": {}
|
||||||
|
}
|
||||||
3
.vite/deps/package.json
Normal file
3
.vite/deps/package.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
|
|
@ -63,66 +63,54 @@ EOF
|
||||||
# Install Node development dependencies globally
|
# Install Node development dependencies globally
|
||||||
RUN npm install -g nodemon
|
RUN npm install -g nodemon
|
||||||
|
|
||||||
# Create startup script
|
# Create startup script for development (runs as host user)
|
||||||
RUN cat > /start.sh <<'EOF'
|
RUN cat > /start.sh <<'EOF'
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Ensure all required Laravel directories exist
|
# Create .env file if it doesn't exist
|
||||||
mkdir -p /app/bootstrap/cache
|
if [ ! -f ".env" ]; then
|
||||||
mkdir -p /app/storage/app/public
|
echo "Creating .env file from .env.example..."
|
||||||
mkdir -p /app/storage/framework/cache/data
|
cp .env.example .env
|
||||||
mkdir -p /app/storage/framework/sessions
|
fi
|
||||||
mkdir -p /app/storage/framework/testing
|
|
||||||
mkdir -p /app/storage/framework/views
|
|
||||||
mkdir -p /app/storage/logs
|
|
||||||
|
|
||||||
# Set permissions - use www-data user
|
# Install dependencies if volumes are empty
|
||||||
chown -R www-data:www-data /app/storage /app/bootstrap/cache
|
|
||||||
chmod -R 775 /app/storage /app/bootstrap/cache
|
|
||||||
|
|
||||||
# Create cache directories with proper permissions
|
|
||||||
mkdir -p /app/storage/framework/cache/data
|
|
||||||
chown -R www-data:www-data /app/storage/framework/cache
|
|
||||||
chmod -R 775 /app/storage/framework/cache
|
|
||||||
|
|
||||||
# Check if vendor exists in the volume
|
|
||||||
if [ ! -f "vendor/autoload.php" ]; then
|
if [ ! -f "vendor/autoload.php" ]; then
|
||||||
echo "Installing composer dependencies..."
|
echo "Installing composer dependencies..."
|
||||||
composer install
|
composer install
|
||||||
else
|
|
||||||
echo "Composer dependencies found, skipping install..."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if node_modules exists in the volume
|
# Handle node_modules with care - clean install if having issues
|
||||||
if [ ! -f "node_modules/.bin/vite" ]; then
|
if [ ! -f "node_modules/.bin/vite" ]; then
|
||||||
echo "Installing npm dependencies..."
|
echo "Installing npm dependencies..."
|
||||||
npm install
|
# Clean any remnants first
|
||||||
|
rm -rf node_modules/.* 2>/dev/null || true
|
||||||
|
rm -rf /app/.npm 2>/dev/null || true
|
||||||
|
# Fresh install with cache in tmp to avoid permission issues
|
||||||
|
npm install --cache /tmp/.npm
|
||||||
else
|
else
|
||||||
echo "Node modules found, skipping npm install..."
|
echo "Node modules already installed, skipping npm install"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run Laravel commands as www-data to avoid permission issues
|
# Clear Laravel caches
|
||||||
su -s /bin/sh www-data -c "php artisan config:clear" || true
|
php artisan config:clear || true
|
||||||
su -s /bin/sh www-data -c "php artisan cache:clear" || true
|
php artisan cache:clear || true
|
||||||
su -s /bin/sh www-data -c "php artisan route:clear" || true
|
|
||||||
su -s /bin/sh www-data -c "php artisan view:clear" || true
|
|
||||||
|
|
||||||
# Run migrations if database is ready
|
# Wait for database and run migrations
|
||||||
echo "Waiting for database..."
|
echo "Waiting for database..."
|
||||||
sleep 5
|
sleep 5
|
||||||
su -s /bin/sh www-data -c "php artisan migrate --force" || echo "Migration failed or not needed"
|
php artisan migrate --force || echo "Migration failed or not needed"
|
||||||
|
|
||||||
# Generate app key if not set
|
# Generate app key if not set
|
||||||
if [ -z "$APP_KEY" ] || [ "$APP_KEY" = "base64:YOUR_KEY_HERE" ]; then
|
if [ -z "$APP_KEY" ] || [ "$APP_KEY" = "base64:YOUR_KEY_HERE" ]; then
|
||||||
echo "Generating application key..."
|
echo "Generating application key..."
|
||||||
su -s /bin/sh www-data -c "php artisan key:generate"
|
php artisan key:generate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start Vite dev server in background for hot reload
|
# Start Vite dev server in background
|
||||||
npm run dev &
|
npm run dev &
|
||||||
|
|
||||||
# Start FrankenPHP (runs as root in dev for simplicity)
|
# Start FrankenPHP
|
||||||
exec frankenphp run --config /etc/caddy/Caddyfile
|
exec frankenphp run --config /etc/caddy/Caddyfile
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,6 @@ public function login()
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.auth.login')
|
return view('livewire.auth.login')
|
||||||
->layout('layouts.guest');
|
->layout('components.layouts.guest');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Livewire\Auth;
|
namespace App\Livewire\Auth;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\Planner;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
@ -13,38 +13,25 @@ class Register extends Component
|
||||||
#[Rule('required|string|max:255')]
|
#[Rule('required|string|max:255')]
|
||||||
public $name = '';
|
public $name = '';
|
||||||
|
|
||||||
#[Rule('required|email|unique:users,email')]
|
#[Rule('required|email|unique:planners,email')]
|
||||||
public $email = '';
|
public $email = '';
|
||||||
|
|
||||||
#[Rule('required|min:8|confirmed')]
|
#[Rule('required|min:8|confirmed')]
|
||||||
public $password = '';
|
public $password = '';
|
||||||
|
|
||||||
public $password_confirmation = '';
|
public $password_confirmation = '';
|
||||||
|
|
||||||
#[Rule('required|exists:planners,id')]
|
|
||||||
public $planner_id = '';
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
// Set default planner_id if only one exists
|
|
||||||
$planners = \App\Models\Planner::all();
|
|
||||||
if ($planners->count() === 1) {
|
|
||||||
$this->planner_id = $planners->first()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
$user = User::create([
|
$planner = Planner::create([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
'password' => Hash::make($this->password),
|
'password' => Hash::make($this->password),
|
||||||
'planner_id' => $this->planner_id,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Auth::login($user);
|
Auth::login($planner);
|
||||||
session()->regenerate();
|
session()->regenerate();
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
|
|
@ -52,8 +39,7 @@ public function register()
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.auth.register', [
|
return view('livewire.auth.register')
|
||||||
'planners' => \App\Models\Planner::all()
|
->layout('components.layouts.guest');
|
||||||
])->layout('layouts.guest');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,8 @@
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
$middleware->append(ForceJsonResponse::class);
|
// Apply ForceJsonResponse only to API routes
|
||||||
|
$middleware->api(ForceJsonResponse::class);
|
||||||
$middleware->append(StartSession::class);
|
$middleware->append(StartSession::class);
|
||||||
$middleware->append(HandleCors::class);
|
$middleware->append(HandleCors::class);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
29
build-push.sh
Executable file
29
build-push.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Build and push production image to Codeberg
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
REGISTRY="codeberg.org"
|
||||||
|
NAMESPACE="lvl0"
|
||||||
|
IMAGE_NAME="dish-planner"
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
|
||||||
|
echo "🔨 Building production image..."
|
||||||
|
podman build -f Dockerfile -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG} .
|
||||||
|
|
||||||
|
echo "📤 Pushing to Codeberg registry..."
|
||||||
|
echo "Please ensure you're logged in to Codeberg:"
|
||||||
|
echo " podman login codeberg.org"
|
||||||
|
|
||||||
|
podman push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}
|
||||||
|
|
||||||
|
echo "✅ Done! Image pushed to ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}"
|
||||||
|
echo ""
|
||||||
|
echo "To deploy in production:"
|
||||||
|
echo "1. Copy docker-compose.prod.yml to your server"
|
||||||
|
echo "2. Set required environment variables:"
|
||||||
|
echo " - APP_KEY (generate with: openssl rand -base64 32)"
|
||||||
|
echo " - APP_URL"
|
||||||
|
echo " - DB_DATABASE, DB_USERNAME, DB_PASSWORD, DB_ROOT_PASSWORD"
|
||||||
|
echo "3. Run: docker-compose -f docker-compose.prod.yml up -d"
|
||||||
3
build_and_push.sh
Executable file
3
build_and_push.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
docker build -t 192.168.178.152:50114/dishplanner-backend .
|
||||||
|
docker push 192.168.178.152:50114/dishplanner-backend
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
services:
|
|
||||||
backend:
|
|
||||||
volumes:
|
|
||||||
- '.:/var/www/html:Z'
|
|
||||||
|
|
||||||
mysql:
|
|
||||||
image: 'docker.io/library/mysql:8.0'
|
|
||||||
environment:
|
|
||||||
MYSQL_USER: '${DB_USERNAME}'
|
|
||||||
MYSQL_PASSWORD: '${DB_PASSWORD}'
|
|
||||||
volumes:
|
|
||||||
- 'sail-mysql:/var/lib/mysql:Z'
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
image: 'docker.io/library/node:22-alpine'
|
|
||||||
working_dir: /app
|
|
||||||
command: sh -c "npm install && npm run dev -- --host"
|
|
||||||
volumes:
|
|
||||||
- '../frontend:/app:Z'
|
|
||||||
ports:
|
|
||||||
- '5173:5173'
|
|
||||||
networks:
|
|
||||||
- sail
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
|
|
@ -8,6 +8,8 @@ services:
|
||||||
dockerfile: Dockerfile.dev
|
dockerfile: Dockerfile.dev
|
||||||
container_name: dishplanner_app
|
container_name: dishplanner_app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
# Remove user directive to run as root in container
|
||||||
|
# The container will handle permissions internally
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000" # Laravel app
|
- "8000:8000" # Laravel app
|
||||||
- "5173:5173" # Vite dev server
|
- "5173:5173" # Vite dev server
|
||||||
|
|
@ -38,13 +40,11 @@ services:
|
||||||
# Vite
|
# Vite
|
||||||
VITE_HOST: "0.0.0.0"
|
VITE_HOST: "0.0.0.0"
|
||||||
volumes:
|
volumes:
|
||||||
# Mount entire project for hot reload
|
# Mount entire project for hot reload with SELinux context
|
||||||
- .:/app
|
- .:/app:Z
|
||||||
# Persist vendor and node_modules for performance
|
# Named volumes for performance and permission isolation
|
||||||
- app_vendor:/app/vendor
|
- app_vendor:/app/vendor
|
||||||
- app_node_modules:/app/node_modules
|
- app_node_modules:/app/node_modules
|
||||||
# Persist storage
|
|
||||||
- app_storage:/app/storage
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
networks:
|
networks:
|
||||||
|
|
@ -101,5 +101,4 @@ networks:
|
||||||
volumes:
|
volumes:
|
||||||
db_data:
|
db_data:
|
||||||
app_vendor:
|
app_vendor:
|
||||||
app_node_modules:
|
app_node_modules:
|
||||||
app_storage:
|
|
||||||
0
public/.htaccess
Normal file → Executable file
0
public/.htaccess
Normal file → Executable file
0
public/favicon.ico
Normal file → Executable file
0
public/favicon.ico
Normal file → Executable file
0
public/index.php
Normal file → Executable file
0
public/index.php
Normal file → Executable file
0
public/robots.txt
Normal file → Executable file
0
public/robots.txt
Normal file → Executable file
|
|
@ -23,20 +23,6 @@ class="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-gray-100
|
||||||
@error('email') <span class="text-danger text-xs block -mt-2 mb-2">{{ $message }}</span> @enderror
|
@error('email') <span class="text-danger text-xs block -mt-2 mb-2">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($planners->count() > 1)
|
|
||||||
<div>
|
|
||||||
<label for="planner_id" class="block text-sm font-medium mb-2">Planner</label>
|
|
||||||
<select wire:model="planner_id"
|
|
||||||
id="planner_id"
|
|
||||||
class="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-gray-100 focus:bg-gray-900 focus:outline-none focus:border-accent-blue">
|
|
||||||
<option value="">Select a planner</option>
|
|
||||||
@foreach($planners as $planner)
|
|
||||||
<option value="{{ $planner->id }}">{{ $planner->name }}</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
@error('planner_id') <span class="text-danger text-xs block -mt-2 mb-2">{{ $message }}</span> @enderror
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="password" class="block text-sm font-medium mb-2">Password</label>
|
<label for="password" class="block text-sm font-medium mb-2">Password</label>
|
||||||
|
|
|
||||||
28
shell.nix
28
shell.nix
|
|
@ -23,26 +23,32 @@ pkgs.mkShell {
|
||||||
];
|
];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
# Export user/group IDs for Docker permission matching
|
||||||
|
export USER_ID=$(id -u)
|
||||||
|
export GROUP_ID=$(id -g)
|
||||||
|
# Use keep-id for proper permission mapping in rootless podman
|
||||||
|
export PODMAN_USERNS=keep-id
|
||||||
|
|
||||||
# Define helper functions
|
# Define helper functions
|
||||||
dev-rebuild() {
|
dev-rebuild() {
|
||||||
echo "🔨 Rebuilding development environment..."
|
echo "🔨 Rebuilding development environment..."
|
||||||
podman-compose down -v
|
PODMAN_USERNS=keep-id podman-compose down -v
|
||||||
podman-compose build --no-cache app
|
PODMAN_USERNS=keep-id podman-compose build --no-cache app
|
||||||
podman-compose up -d
|
PODMAN_USERNS=keep-id podman-compose up -d
|
||||||
echo "✅ Rebuild complete! Check logs with: dev-logs"
|
echo "✅ Rebuild complete! Check logs with: dev-logs"
|
||||||
}
|
}
|
||||||
|
|
||||||
dev-rebuild-quick() {
|
dev-rebuild-quick() {
|
||||||
echo "⚡ Quick rebuild (keeping volumes)..."
|
echo "⚡ Quick rebuild (keeping volumes)..."
|
||||||
podman-compose down
|
PODMAN_USERNS=keep-id podman-compose down
|
||||||
podman-compose build app
|
PODMAN_USERNS=keep-id podman-compose build app
|
||||||
podman-compose up -d
|
PODMAN_USERNS=keep-id podman-compose up -d
|
||||||
echo "✅ Quick rebuild complete!"
|
echo "✅ Quick rebuild complete!"
|
||||||
}
|
}
|
||||||
|
|
||||||
dev-up() {
|
dev-up() {
|
||||||
echo "🚀 Starting development environment..."
|
echo "🚀 Starting development environment..."
|
||||||
podman-compose up -d
|
PODMAN_USERNS=keep-id podman-compose up -d
|
||||||
echo "✅ Dev environment started!"
|
echo "✅ Dev environment started!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +76,13 @@ pkgs.mkShell {
|
||||||
podman-compose exec app php artisan "$@"
|
podman-compose exec app php artisan "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev-fix-permissions() {
|
||||||
|
echo "🔧 Fixing file permissions..."
|
||||||
|
echo "This will require sudo to fix Docker-created files"
|
||||||
|
sudo chown -R $(id -u):$(id -g) storage/ bootstrap/cache/ vendor/ node_modules/ 2>/dev/null || true
|
||||||
|
echo "✅ Permissions fixed!"
|
||||||
|
}
|
||||||
|
|
||||||
prod-build() {
|
prod-build() {
|
||||||
local TAG="''${1:-latest}"
|
local TAG="''${1:-latest}"
|
||||||
local REGISTRY="codeberg.org"
|
local REGISTRY="codeberg.org"
|
||||||
|
|
@ -125,6 +138,7 @@ pkgs.mkShell {
|
||||||
echo " dev-logs [svc] - Follow logs (default: all)"
|
echo " dev-logs [svc] - Follow logs (default: all)"
|
||||||
echo " dev-shell - Enter app container"
|
echo " dev-shell - Enter app container"
|
||||||
echo " dev-artisan - Run artisan commands"
|
echo " dev-artisan - Run artisan commands"
|
||||||
|
echo " dev-fix-permissions - Fix Docker-created file permissions"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Production commands:"
|
echo "Production commands:"
|
||||||
echo " prod-login - Login to Codeberg registry"
|
echo " prod-login - Login to Codeberg registry"
|
||||||
|
|
|
||||||
0
storage/app/.gitignore
vendored
Normal file → Executable file
0
storage/app/.gitignore
vendored
Normal file → Executable file
0
storage/app/private/.gitignore
vendored
Normal file → Executable file
0
storage/app/private/.gitignore
vendored
Normal file → Executable file
0
storage/app/public/.gitignore
vendored
Normal file → Executable file
0
storage/app/public/.gitignore
vendored
Normal file → Executable file
0
storage/framework/.gitignore
vendored
Normal file → Executable file
0
storage/framework/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/data/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/data/.gitignore
vendored
Normal file → Executable file
0
storage/framework/sessions/.gitignore
vendored
Normal file → Executable file
0
storage/framework/sessions/.gitignore
vendored
Normal file → Executable file
0
storage/framework/testing/.gitignore
vendored
Normal file → Executable file
0
storage/framework/testing/.gitignore
vendored
Normal file → Executable file
0
storage/framework/views/.gitignore
vendored
Normal file → Executable file
0
storage/framework/views/.gitignore
vendored
Normal file → Executable file
18
update.sh
Executable file
18
update.sh
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔄 Pulling latest backend changes..."
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
echo "📦 Installing PHP dependencies..."
|
||||||
|
composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||||
|
|
||||||
|
echo "🗄️ Running migrations..."
|
||||||
|
php artisan migrate --force
|
||||||
|
|
||||||
|
echo "🧹 Clearing and caching config..."
|
||||||
|
php artisan config:cache
|
||||||
|
php artisan route:cache
|
||||||
|
php artisan view:cache
|
||||||
|
|
||||||
|
echo "✅ Backend update complete!"
|
||||||
Loading…
Reference in a new issue