diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3062612 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +vendor +storage/*.key +.env diff --git a/.gitea/workflows/deploy-alpha.yml b/.gitea/workflows/deploy-alpha.yml new file mode 100644 index 0000000..67e10a8 --- /dev/null +++ b/.gitea/workflows/deploy-alpha.yml @@ -0,0 +1,70 @@ +name: Build and Deploy to k3s +on: + push: + tags: + - 'PRE_ALPHA*' + +jobs: + build-and-deploy: + env: + KUBECONFIG: ~/.kube/config + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '22' + + + - name: Install dependencies + run: npm ci + + - name: Build Angular app + run: npm run build --prod + + - name: Build Docker image + run: | + docker build -t ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:${{ github.sha }} . + docker tag ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:${{ github.sha }} ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:latest + + - name: Login to Container Registry + run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ secrets.REGISTRY_URL }} -u "${{ secrets.REGISTRY_USER }}" --password-stdin + + - name: Push Docker image + run: | + docker push ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:${{ github.sha }} + docker push ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:latest + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + with: + version: 'latest' + + - name: Configure kubectl + run: | + mkdir -p ~/.kube + echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config + chmod 600 ~/.kube/config + + - name: Check Config + run: | + cat ~/.kube/config + + - name: Validate kubeconfig + run: | + if ! kubectl version --client && kubectl cluster-info --kubeconfig ~/.kube/config; then + echo "❌ Failed to connect to cluster" + exit 1 + fi + + + - name: Deploy to k3s + run: | + kubectl apply -k deploy/k3s/alpha --kubeconfig ~/.kube/config + kubectl set image deployment/hosting-frontend \ + hosting-frontend=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend:${{ github.sha }} \ + -n hosting-alpha --kubeconfig ~/.kube/config + kubectl rollout status deployment/hosting-frontend -n hosting-alpha --kubeconfig ~/.kube/config diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml new file mode 100644 index 0000000..daa7090 --- /dev/null +++ b/.gitea/workflows/deploy-prod.yml @@ -0,0 +1,67 @@ +name: Build and Deploy to k3s +on: + push: + tags: + - 'PROD*' + +jobs: + build-and-deploy: + env: + KUBECONFIG: ~/.kube/config + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '22' + + + - name: Install dependencies + run: npm ci + + - name: Build Angular app + run: npm run build --prod + + - name: Build Docker image + run: | + docker build -t ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:${{ github.sha }} . + docker tag ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:${{ github.sha }} ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:latest + + - name: Login to Container Registry + run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ secrets.REGISTRY_URL }} -u "${{ secrets.REGISTRY_USER }}" --password-stdin + + - name: Push Docker image + run: | + docker push ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:${{ github.sha }} + docker push ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:latest + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + with: + version: 'latest' + + - name: Configure kubectl + run: | + mkdir -p ~/.kube + echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config + chmod 600 ~/.kube/config + + + - name: Validate kubeconfig + run: | + if ! kubectl version --client && kubectl cluster-info --kubeconfig ~/.kube/config; then + echo "❌ Failed to connect to cluster" + exit 1 + fi + + + - name: Deploy to k3s + run: | + kubectl apply -k deploy/k3s/prod --kubeconfig ~/.kube/config + kubectl set image deployment/hosting-frontend \ + hosting-frontend=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/hosting-frontend-prod:${{ github.sha }} \ + -n hosting --kubeconfig ~/.kube/config + kubectl rollout status deployment/hosting-frontend -n hosting --kubeconfig ~/.kube/config diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e213ee1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,69 @@ +# Stage 1: Build with Composer +FROM php:8.2-cli-alpine AS build + +WORKDIR /app + +# Install dependencies required for Composer and Laravel +RUN apk add --no-cache \ + libzip-dev \ + zip \ + unzip \ + curl \ + git \ + oniguruma-dev \ + libxml2-dev + +# Install PHP extensions +RUN docker-php-ext-install zip mbstring xml + +# Install Composer +RUN curl -sS https://getcomposer.org/installer | php && \ + mv composer.phar /usr/local/bin/composer + +# Copy application files and install dependencies +COPY . . +RUN composer install --no-dev --optimize-autoloader --no-interaction + +# Stage 2: Final image +FROM php:8.2-fpm-alpine + +# Set working directory +WORKDIR /var/www + +# Install system dependencies and PHP extensions +RUN apk add --no-cache \ + nginx \ + supervisor \ + bash \ + mysql-client \ + libpng-dev \ + libjpeg-turbo-dev \ + freetype-dev \ + libxml2-dev \ + oniguruma-dev \ + zip \ + unzip \ + curl \ + shadow + +# Install PHP extensions +RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \ + docker-php-ext-install pdo pdo_mysql mbstring gd xml + +# Copy files from build stage +COPY --from=build /app /var/www + +# Fix permissions +RUN adduser -D -g '' www && \ + chown -R www:www /var/www && \ + chmod -R 755 /var/www/storage /var/www/bootstrap/cache + +# Copy custom nginx and supervisord config +COPY deploy/nginx.conf /etc/nginx/nginx.conf +COPY deploy/supervisord.conf /etc/supervisord.conf + +# Expose port +EXPOSE 80 + +# Run both services +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] diff --git a/config/sanctum.php b/config/sanctum.php index b03f249..4d2b1ac 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -11,7 +11,7 @@ return [ | | Requests from the following domains / hosts will receive stateful API | authentication cookies. Typically, these should include your local - | and production domains which access your API via a frontend SPA. + | and production domains which access your API via a backend SPA. | */ diff --git a/deploy/k3s/alpha/backend/deployment.yml b/deploy/k3s/alpha/backend/deployment.yml new file mode 100644 index 0000000..e523c8f --- /dev/null +++ b/deploy/k3s/alpha/backend/deployment.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hosting-backend + namespace: hosting-alpha +spec: + replicas: 1 + selector: + matchLabels: + app: hosting-backend + template: + metadata: + labels: + app: hosting-backend + spec: + containers: + - name: hosting-backend + image: gitea.vidoks.fr/sortifal/hosting-backend-alpha:latest + ports: + - containerPort: 80 diff --git a/deploy/k3s/alpha/backend/ingress.yml b/deploy/k3s/alpha/backend/ingress.yml new file mode 100644 index 0000000..dafefa0 --- /dev/null +++ b/deploy/k3s/alpha/backend/ingress.yml @@ -0,0 +1,21 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hosting-ingress + namespace: hosting-alpha + annotations: + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/router.entrypoints: web + traefik.ingress.kubernetes.io/router.middlewares: alpha-allow-local@kubernetescrd +spec: + rules: + - host: alpha.portfolio-host.com + http: + paths: + - path: /api + pathType: Prefix + backend: + service: + name: hosting-backend-service + port: + number: 80 diff --git a/deploy/k3s/alpha/backend/kustomization.yml b/deploy/k3s/alpha/backend/kustomization.yml new file mode 100644 index 0000000..d32ed33 --- /dev/null +++ b/deploy/k3s/alpha/backend/kustomization.yml @@ -0,0 +1,6 @@ +namespace: hosting-alpha +resources: + - deployment.yml + - service.yml + - ingress.yml + - middleware.yml diff --git a/deploy/k3s/alpha/backend/middleware.yml b/deploy/k3s/alpha/backend/middleware.yml new file mode 100644 index 0000000..4fdb549 --- /dev/null +++ b/deploy/k3s/alpha/backend/middleware.yml @@ -0,0 +1,10 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: allow-local + namespace: hosting-alpha +spec: + ipWhiteList: + sourceRange: + - 127.0.0.1/32 + - 192.168.1.0/24 diff --git a/deploy/k3s/alpha/backend/service.yml b/deploy/k3s/alpha/backend/service.yml new file mode 100644 index 0000000..8cae374 --- /dev/null +++ b/deploy/k3s/alpha/backend/service.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: hosting-backend-service + namespace: hosting-alpha +spec: + selector: + app: hosting-backend + ports: + - port: 80 + targetPort: 80 + type: ClusterIP diff --git a/deploy/k3s/alpha/kustomization.yml b/deploy/k3s/alpha/kustomization.yml new file mode 100644 index 0000000..7e59929 --- /dev/null +++ b/deploy/k3s/alpha/kustomization.yml @@ -0,0 +1,8 @@ +--- + +kind: Kustomization +namespace: hosting-alpha + +resources: + - namespace.yml + - backend diff --git a/deploy/k3s/alpha/namespace.yml b/deploy/k3s/alpha/namespace.yml new file mode 100644 index 0000000..548de4b --- /dev/null +++ b/deploy/k3s/alpha/namespace.yml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: hosting-alpha diff --git a/deploy/k3s/prod/backend/deployment.yml b/deploy/k3s/prod/backend/deployment.yml new file mode 100644 index 0000000..b6efe57 --- /dev/null +++ b/deploy/k3s/prod/backend/deployment.yml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hosting-backend + namespace: hosting +spec: + replicas: 1 + selector: + matchLabels: + app: hosting-backend + template: + metadata: + labels: + app: hosting-backend + spec: + containers: + - name: hosting-backend + image: gitea.vidoks.fr/sortifal/hosting-backend-prod:latest + ports: + - containerPort: 80 + + + diff --git a/deploy/k3s/prod/backend/ingress.yml b/deploy/k3s/prod/backend/ingress.yml new file mode 100644 index 0000000..213ade7 --- /dev/null +++ b/deploy/k3s/prod/backend/ingress.yml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hosting-ingress + namespace: hosting + annotations: + kubernetes.io/ingress.class: traefik +spec: + rules: + - host: api.portfolio-host.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: hosting-backend-service + port: + number: 80 diff --git a/deploy/k3s/prod/backend/kustomization.yml b/deploy/k3s/prod/backend/kustomization.yml new file mode 100644 index 0000000..68a16cd --- /dev/null +++ b/deploy/k3s/prod/backend/kustomization.yml @@ -0,0 +1,5 @@ +namespace: hosting +resources: + - deployment.yml + - service.yml + - ingress.yml diff --git a/deploy/k3s/prod/backend/service.yml b/deploy/k3s/prod/backend/service.yml new file mode 100644 index 0000000..d31a130 --- /dev/null +++ b/deploy/k3s/prod/backend/service.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: hosting-backend-service + namespace: hosting +spec: + selector: + app: hosting-backend + ports: + - port: 8000 + targetPort: 8000 + type: ClusterIP diff --git a/deploy/k3s/prod/kustomization.yml b/deploy/k3s/prod/kustomization.yml new file mode 100644 index 0000000..8031750 --- /dev/null +++ b/deploy/k3s/prod/kustomization.yml @@ -0,0 +1,8 @@ +--- + +kind: Kustomization +namespace: hosting + +resources: + - namespace.yml + - backend diff --git a/deploy/k3s/prod/namespace.yml b/deploy/k3s/prod/namespace.yml new file mode 100644 index 0000000..d038236 --- /dev/null +++ b/deploy/k3s/prod/namespace.yml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: hosting diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..60c80b9 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,34 @@ +worker_processes 1; + +events { worker_connections 1024; } + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + server { + listen 80; + server_name localhost; + + root /var/www/public; + + index index.php index.html; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + + location ~ /\.ht { + deny all; + } + } +} diff --git a/deploy/supervisord.conf b/deploy/supervisord.conf new file mode 100644 index 0000000..7dd8855 --- /dev/null +++ b/deploy/supervisord.conf @@ -0,0 +1,8 @@ +[supervisord] +nodaemon=true + +[program:php-fpm] +command=/usr/local/sbin/php-fpm + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" diff --git a/routes/api.php b/routes/api.php index aeef36d..ee4755e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -10,3 +10,6 @@ Route::post('/auth/login', [AuthController::class, 'login']); Route::middleware('auth:sanctum')->get('/me', function (Request $request) { return $request->user(); }); + + +Route::get('/ping', function () {return 'pong';});