Compare commits

..

26 Commits

Author SHA1 Message Date
Unchained 57bae7ed6f fix: canonical URLs to always include locale prefix
Fixed Canonical Redirect Error in Google Search Console by ensuring
canonical URLs always include the locale prefix (/sr, /en, /de, /fr)
instead of omitting it for the default locale.

Changes:
- layout.tsx: Fixed canonical and hreflang URL generation
- page.tsx: Fixed homepage canonical URL
- contact/page.tsx: Fixed contact page canonical URL
- about/page.tsx: Fixed about page canonical URL
- products/page.tsx: Fixed products page canonical URL
- products/[slug]/page.tsx: Fixed product detail canonical URL

Before: /contact (when locale=sr)
After: /sr/contact (all locales)

Fixes: Canonical Redirect Error in Google Search Console
2026-04-06 15:32:24 +02:00
Unchained 2097280f20 fix: force no-cache build
Build and Deploy / build (push) Successful in 1s
2026-04-05 13:05:28 +02:00
Unchained bea6aba014 fix: simplify workflow with proper build args
Build and Deploy / build (push) Successful in 0s
2026-04-05 13:02:05 +02:00
Unchained 8454ffc5b3 test: trigger build with args
Build and Deploy / build (push) Has been cancelled
2026-04-05 12:59:49 +02:00
Unchained 38defdfb9b chore: remove test workflow
Build and Deploy / build (push) Has been cancelled
2026-04-05 12:59:13 +02:00
Flux CD 9c04dffa46 fix: add missing build args to workflow 2026-04-05 10:58:54 +00:00
Unchained bd1fa0d96a test: add no-cache build workflow
Build and Deploy / build (push) Successful in 59s
2026-04-05 12:53:44 +02:00
Unchained 826d1ebb46 trigger: rebuild with correct env vars
Build and Deploy / build (push) Successful in 59s
Previous build had localhost:8000 hardcoded.
This rebuild uses the fixed Dockerfile with build args.
2026-04-05 12:30:07 +02:00
Unchained 09b0614695 fix: remove kubectl annotate from build workflow
Build and Deploy / build (push) Successful in 59s
- Remove kubectl command that was causing build failures
- Flux will auto-detect new image within 5 minutes via polling
- Simpler, more reliable build process
2026-04-05 12:03:27 +02:00
Unchained 7c7611b723 fix: simplify build workflow YAML syntax
Build and Deploy / build (push) Successful in 10m0s
- Use 'command' and 'args' instead of multiline command
- Use quoted heredoc delimiter to prevent variable expansion
- Simplify clone and build scripts
2026-04-05 11:50:23 +02:00
Unchained 6563f0c966 fix: use full cluster DNS for gitea service
Build and Deploy / build (push) Has been cancelled
- Change gitea URL from http://gitea:3000 to http://gitea.gitea.svc.cluster.local:3000
- Add set -x for debugging
- Add explicit clone exit code checking
2026-04-05 11:43:57 +02:00
Unchained cdbcd8424b fix: improve git clone error handling in build workflow
Build and Deploy / build (push) Has been cancelled
2026-04-05 11:39:39 +02:00
Unchained 05b2c26634 fix: correct syntax errors in build workflow
Build and Deploy / build (push) Has been cancelled
- Fix unclosed quote on line 13
- Remove malformed git checkout command on line 39
2026-04-05 11:35:01 +02:00
Unchained bdc35ff2b4 fix: remove quotes from date command in build workflow
Build and Deploy / build (push) Failing after 0s
2026-04-05 11:31:59 +02:00
Unchained d53665d6da build: pass env vars as build args to fix localhost:8000 error
Build and Deploy / build (push) Failing after 0s
- Add ARG and ENV directives to Dockerfile for NEXT_PUBLIC_* vars
- Pass build args in buildctl command with --opt build-arg
- Fixes ERR_BLOCKED_BY_CLIENT on localhost:8000/graphql
2026-04-05 11:24:01 +02:00
Flux CD 80da03504c ci: add Gitea Actions workflow with BuildKit 2026-04-05 06:16:10 +00:00
Unchained 328bbbaaa2 ci: add Gitea Actions workflow with BuildKit
Build and Deploy / build (push) Failing after 23m52s
2026-04-05 08:13:55 +02:00
Flux CD 37d1894ad4 fix: remove image transformer, use deployment image directly 2026-04-05 05:10:32 +00:00
Flux CD 6236092d77 feat: add image policy setter marker for Flux automation 2026-04-05 05:07:17 +00:00
Flux CD 61b20beffa feat: switch to pre-built GHCR image using BuildKit 2026-04-05 05:02:51 +00:00
Unchained 29894cd555 chore: trigger Gitea Actions build
Build and Push to GHCR / build (push) Failing after 4m0s
2026-04-05 06:32:05 +02:00
Unchained c80970bcda feat(ci): add Gitea Actions workflow for building and pushing to GHCR
Build and Push to GHCR / build (push) Has been cancelled
Add .gitea/workflows/build.yaml that:
- Builds Docker image on push to master
- Pushes to ghcr.io/unchainedio/manoon-headless
- Tags with commit SHA and 'latest'
- Updates k8s/kustomization.yaml with new image tag
- Commits and pushes the tag update back to repo

Requires Gitea Actions runner to be configured.
2026-04-05 06:24:36 +02:00
Unchained 1dec08f857 Revert to working deployment while GHCR image builds
Build and Deploy / build (push) Failing after 12m23s
Will re-apply pre-built image once GitHub Actions successfully
pushes image to ghcr.io/unchainedio/manoon-headless
2026-04-05 06:15:54 +02:00
Unchained cc33d317ba fix(k8s): use latest tag for manoon-headless image
Build and Deploy / build (push) Has been cancelled
Temporary fix until GitHub Actions builds and pushes the image.
Workflow will update to specific SHA on next push.
2026-04-05 06:12:44 +02:00
Unchained 3c495f48b7 refactor(k8s): use pre-built GHCR image instead of building in pod
Build and Deploy / build (push) Has been cancelled
- Remove init containers (clone, install, build)
- Use ghcr.io/unchainedio/manoon-headless:latest image
- Faster pod startup, less resource usage
- Image built by GitHub Actions on push to master
2026-04-05 06:09:55 +02:00
Unchained a636d29f0b fix(k8s): handle existing workspace on pod restart
Build and Deploy / build (push) Has been cancelled
The clone init container was failing with 'destination path already exists'
when the pod restarted. EmptyDir volumes persist across container restarts
but init containers run again.

Now checks if workspace exists:
- If .git directory exists: fetch and reset to latest master
- If not: clean and clone fresh

This fixes the CrashLoopBackOff caused by failed clone attempts.
2026-04-05 05:17:30 +02:00
11 changed files with 98 additions and 116 deletions
+68
View File
@@ -0,0 +1,68 @@
name: Build and Deploy
on:
push:
branches: [master, main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Trigger BuildKit Build
run: |
kubectl delete job build-manoon-headless-action -n gitea --ignore-not-found=true 2>/dev/null || true
cat << 'JOBEOF' | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: build-manoon-headless-action
namespace: gitea
spec:
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: Never
initContainers:
- name: clone
image: alpine/git:latest
command: ["sh", "-c"]
args:
- git clone --depth 1 http://gitea:3000/unchained/manoon-headless.git /workspace
volumeMounts:
- name: workspace
mountPath: /workspace
containers:
- name: build
image: moby/buildkit:latest
command: ["sh", "-c"]
args:
- |
mkdir -p /root/.docker
cp /docker-config/.dockerconfigjson /root/.docker/config.json
buildctl --addr tcp://buildkit.gitea.svc.cluster.local:1234 build \
--frontend dockerfile.v0 \
--local context=/workspace \
--local dockerfile=/workspace \
--opt build-arg:NEXT_PUBLIC_SALEOR_API_URL=https://api.manoonoils.com/graphql/ \
--opt build-arg:NEXT_PUBLIC_SITE_URL=https://manoonoils.com \
--opt build-arg:NEXT_PUBLIC_OPENPANEL_CLIENT_ID=fa61f8ae-0b5d-4187-a9b1-5a04b0025674 \
--opt build-arg:NEXT_PUBLIC_RYBBIT_HOST=https://rybbit.nodecrew.me \
--opt build-arg:NEXT_PUBLIC_RYBBIT_SITE_ID=1 \
--no-cache \
--output type=image,name=ghcr.io/unchainedio/manoon-headless:latest,push=true
volumeMounts:
- name: workspace
mountPath: /workspace
- name: docker-config
mountPath: /docker-config
readOnly: true
volumes:
- name: workspace
emptyDir: {}
- name: docker-config
secret:
secretName: ghcr-pull-secret
JOBEOF
echo "Build triggered!"
+12 -5
View File
@@ -1,17 +1,25 @@
# Multi-stage build for Next.js
FROM node:20-slim AS builder
WORKDIR /app
# Copy package files
ARG NEXT_PUBLIC_SALEOR_API_URL
ARG NEXT_PUBLIC_SITE_URL
ARG NEXT_PUBLIC_OPENPANEL_CLIENT_ID
ARG NEXT_PUBLIC_RYBBIT_HOST
ARG NEXT_PUBLIC_RYBBIT_SITE_ID
ENV NEXT_PUBLIC_SALEOR_API_URL=${NEXT_PUBLIC_SALEOR_API_URL}
ENV NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL}
ENV NEXT_PUBLIC_OPENPANEL_CLIENT_ID=${NEXT_PUBLIC_OPENPANEL_CLIENT_ID}
ENV NEXT_PUBLIC_RYBBIT_HOST=${NEXT_PUBLIC_RYBBIT_HOST}
ENV NEXT_PUBLIC_RYBBIT_SITE_ID=${NEXT_PUBLIC_RYBBIT_SITE_ID}
COPY package*.json ./
RUN npm install --prefer-offline --no-audit
# Copy source and build
COPY . .
RUN npm run build
# Production stage
FROM node:20-slim AS runner
WORKDIR /app
@@ -20,7 +28,6 @@ ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
# Copy necessary files from builder
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
+3
View File
@@ -39,3 +39,6 @@ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/bui
// Auto-deploy test: 2026-03-07T09:02:49Z
// Auto-deploy test: 2026-03-07T10:33:23Z
// Auto-deploy test 2: 2026-03-07T10:37:05Z
# Trigger build Sun Apr 5 06:32:05 AM EET 2026
# Trigger build with env vars Sun Apr 5 08:45:00 AM EET 2026
# Build test Sun Apr 5 12:59:49 PM EET 2026
+7 -100
View File
@@ -13,102 +13,16 @@ spec:
labels:
app: storefront
spec:
initContainers:
- name: clone
image: alpine/git:latest
command:
- sh
- -c
- |
set -e
apk add --no-cache git
git clone --depth 1 --branch master \
http://gitea.gitea.svc.cluster.local:3000/unchained/manoon-headless.git \
/workspace
echo "Clone complete."
volumeMounts:
- name: workspace
mountPath: /workspace
securityContext:
runAsUser: 0
resources:
limits:
cpu: 500m
memory: 256Mi
- name: install
image: node:20-slim
workingDir: /workspace
command:
- sh
- -c
- |
set -e
echo "Installing dependencies..."
npm install --prefer-offline --no-audit 2>&1
echo "Dependencies installed."
volumeMounts:
- name: workspace
mountPath: /workspace
securityContext:
runAsUser: 0
resources:
limits:
cpu: 2000m
memory: 3Gi
requests:
cpu: 100m
memory: 1Gi
- name: build
image: node:20-slim
workingDir: /workspace
command:
- sh
- -c
- |
set -e
echo "Building Next.js app..."
npm run build
echo "Build complete!"
env:
- name: NODE_ENV
value: "production"
- name: NEXT_PUBLIC_SALEOR_API_URL
value: "https://api.manoonoils.com/graphql/"
- name: NEXT_PUBLIC_SITE_URL
value: "https://manoonoils.com"
- name: DASHBOARD_URL
value: "https://dashboard.manoonoils.com"
- name: NEXT_PUBLIC_OPENPANEL_CLIENT_ID
value: "fa61f8ae-0b5d-4187-a9b1-5a04b0025674"
- name: OPENPANEL_CLIENT_SECRET
value: "91126be0d1e78e657e0427df82733832.c6d30edf6ee673da9650a883604169a13ab8579a0dde70cb39b477f4cf441f90"
- name: OPENPANEL_API_URL
value: "https://op.nodecrew.me/api"
- name: MAUTIC_CLIENT_ID
value: "2_23cgmaqef8kgg8oo4kggc0w4wccwoss8o8w48o8sc40cowgkkg"
- name: MAUTIC_CLIENT_SECRET
value: "4k8367ab306co48c4c8g8sco8cgcwwww044gwccs0o0c8w4gco"
- name: MAUTIC_API_URL
value: "https://mautic.nodecrew.me"
volumeMounts:
- name: workspace
mountPath: /workspace
securityContext:
runAsUser: 0
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 100m
memory: 512Mi
imagePullSecrets:
- name: ghcr-pull-secret
containers:
- name: storefront
image: node:20-slim
workingDir: /workspace
image: ghcr.io/unchainedio/manoon-headless:latest # {"": "flux-system:manoon-headless"}
imagePullPolicy: Always
command:
- npm
- start
- node
- server.js
workingDir: /app
ports:
- containerPort: 3000
env:
@@ -169,10 +83,3 @@ spec:
port: 3000
periodSeconds: 5
failureThreshold: 3
volumeMounts:
- name: workspace
mountPath: /workspace
volumes:
- name: workspace
emptyDir:
sizeLimit: 2Gi
-3
View File
@@ -5,6 +5,3 @@ resources:
- service.yaml
- middleware.yaml
- ingress.yaml
images:
- name: ghcr.io/unchainedio/manoon-headless
newTag: 2c27fc6 # Updated by GitHub Actions
+1 -1
View File
@@ -19,7 +19,7 @@ export async function generateMetadata({ params }: AboutPageProps): Promise<Meta
const metadata = getPageMetadata(validLocale as Locale);
const keywords = getPageKeywords(validLocale as Locale, 'about');
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const localePrefix = `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/about`;
return {
+1 -1
View File
@@ -16,7 +16,7 @@ export async function generateMetadata({ params }: ContactPageProps): Promise<Me
const metadata = getPageMetadata(validLocale as Locale);
const keywords = getPageKeywords(validLocale as Locale, 'contact');
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const localePrefix = `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/contact`;
return {
+2 -2
View File
@@ -21,11 +21,11 @@ export async function generateMetadata({
}): Promise<Metadata> {
const { locale } = await params;
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${locale}`;
const localePrefix = `/${locale}`;
const languages: Record<string, string> = {};
for (const loc of SUPPORTED_LOCALES) {
const prefix = loc === DEFAULT_LOCALE ? "" : `/${loc}`;
const prefix = `/${loc}`;
languages[loc] = `${baseUrl}${prefix}`;
}
+2 -2
View File
@@ -29,8 +29,8 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s
setRequestLocale(validLocale);
// Build canonical URL
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix || '/'}`;
const localePrefix = `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}`;
return {
title: metadata.home.title,
+1 -1
View File
@@ -57,7 +57,7 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
const secondaryKeywords = keywords.secondary.map(replaceTemplate);
// Build canonical URL
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const localePrefix = `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/products/${slug}`;
// Get product image for OpenGraph
+1 -1
View File
@@ -24,7 +24,7 @@ export async function generateMetadata({ params }: ProductsPageProps): Promise<M
const keywords = getPageKeywords(validLocale as Locale, 'products');
// Build canonical URL
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const localePrefix = `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/products`;
return {