Compare commits
6 Commits
v1.0.9-rc.
...
v1.0.9
| Author | SHA1 | Date | |
|---|---|---|---|
| d10f15abe3 | |||
| 9bdbcc2803 | |||
| b08f07494c | |||
| 1f758758e3 | |||
| fb8d9574b6 | |||
| 6856b7835c |
2
.env
2
.env
@@ -17,7 +17,7 @@ MAIL_FROM="KLZ Cables <postmaster@mg.mintel.me>"
|
|||||||
MAIL_RECIPIENTS=marc@cablecreations.de,info@klz-cables.com
|
MAIL_RECIPIENTS=marc@cablecreations.de,info@klz-cables.com
|
||||||
|
|
||||||
# Directus
|
# Directus
|
||||||
DIRECTUS_URL=https://cms.klz-cables.com
|
DIRECTUS_URL=http://klz-cms:8055
|
||||||
DIRECTUS_KEY=59fb8f4c1a51b18fe28ad947f713914e
|
DIRECTUS_KEY=59fb8f4c1a51b18fe28ad947f713914e
|
||||||
DIRECTUS_SECRET=7459038d41401dfb11254cf7f1ef2d0f
|
DIRECTUS_SECRET=7459038d41401dfb11254cf7f1ef2d0f
|
||||||
DIRECTUS_ADMIN_EMAIL=marc@mintel.me
|
DIRECTUS_ADMIN_EMAIL=marc@mintel.me
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ jobs:
|
|||||||
echo "traefik_rule=$TRAEFIK_RULE"
|
echo "traefik_rule=$TRAEFIK_RULE"
|
||||||
echo "next_public_url=https://$PRIMARY_HOST"
|
echo "next_public_url=https://$PRIMARY_HOST"
|
||||||
echo "directus_url=https://cms.$PRIMARY_HOST"
|
echo "directus_url=https://cms.$PRIMARY_HOST"
|
||||||
echo "project_name=$PRJ-$TARGET"
|
if [[ "$TARGET" == "production" ]]; then
|
||||||
|
echo "project_name=klz-cablescom"
|
||||||
|
else
|
||||||
|
echo "project_name=$PRJ-$TARGET"
|
||||||
|
fi
|
||||||
echo "short_sha=$SHORT_SHA"
|
echo "short_sha=$SHORT_SHA"
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
'use client';
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
|
|
||||||
import L from 'leaflet';
|
import L from 'leaflet';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
|
||||||
// Fix for default marker icon in Leaflet with Next.js
|
// Fix for default marker icon in Leaflet with Next.js
|
||||||
const DefaultIcon = L.icon({
|
if (typeof window !== 'undefined') {
|
||||||
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
|
const DefaultIcon = L.icon({
|
||||||
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
|
||||||
iconSize: [25, 41],
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
iconAnchor: [12, 41],
|
iconSize: [25, 41],
|
||||||
});
|
iconAnchor: [12, 41],
|
||||||
|
});
|
||||||
|
|
||||||
L.Marker.prototype.options.icon = DefaultIcon;
|
L.Marker.prototype.options.icon = DefaultIcon;
|
||||||
|
}
|
||||||
|
|
||||||
interface LeafletMapProps {
|
interface LeafletMapProps {
|
||||||
address: string;
|
address: string;
|
||||||
@@ -21,25 +21,46 @@ interface LeafletMapProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function LeafletMap({ address, lat, lng }: LeafletMapProps) {
|
export default function LeafletMap({ address, lat, lng }: LeafletMapProps) {
|
||||||
const position: [number, number] = [lat, lng];
|
const mapRef = useRef<HTMLDivElement>(null);
|
||||||
|
const mapInstanceRef = useRef<L.Map | null>(null);
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<MapContainer
|
if (!mapRef.current || mapInstanceRef.current) return;
|
||||||
center={position}
|
|
||||||
zoom={15}
|
// Initialize map
|
||||||
scrollWheelZoom={false}
|
const map = L.map(mapRef.current, {
|
||||||
className="h-full w-full z-0"
|
center: [lat, lng],
|
||||||
>
|
zoom: 15,
|
||||||
<TileLayer
|
scrollWheelZoom: false,
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
});
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
||||||
/>
|
// Add tiles
|
||||||
<Marker position={position}>
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
<Popup>
|
attribution:
|
||||||
<div className="text-primary font-bold">KLZ Cables</div>
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
<div className="text-sm whitespace-pre-line">{address}</div>
|
}).addTo(map);
|
||||||
</Popup>
|
|
||||||
</Marker>
|
// Add marker
|
||||||
</MapContainer>
|
const marker = L.marker([lat, lng]).addTo(map);
|
||||||
);
|
|
||||||
|
// Create popup content
|
||||||
|
const popupContent = `
|
||||||
|
<div class="text-primary font-bold">KLZ Cables</div>
|
||||||
|
<div class="text-sm">${address.replace(/\n/g, '<br/>')}</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
marker.bindPopup(popupContent);
|
||||||
|
|
||||||
|
mapInstanceRef.current = map;
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
return () => {
|
||||||
|
if (mapInstanceRef.current) {
|
||||||
|
mapInstanceRef.current.remove();
|
||||||
|
mapInstanceRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [lat, lng, address]);
|
||||||
|
|
||||||
|
return <div ref={mapRef} className="h-full w-full z-0" />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,34 +43,7 @@ collections:
|
|||||||
hidden: true
|
hidden: true
|
||||||
schema:
|
schema:
|
||||||
name: products_translations
|
name: products_translations
|
||||||
- collection: visual_feedback
|
|
||||||
meta:
|
|
||||||
accountability: all
|
|
||||||
archive_app_filter: true
|
|
||||||
collapse: open
|
|
||||||
collection: visual_feedback
|
|
||||||
color: '#002b49'
|
|
||||||
display_template: '{{user_name}} | {{type}}: {{text}}'
|
|
||||||
hidden: false
|
|
||||||
icon: feedback
|
|
||||||
singleton: false
|
|
||||||
versioning: false
|
|
||||||
schema:
|
|
||||||
name: visual_feedback
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
meta:
|
|
||||||
accountability: all
|
|
||||||
archive_app_filter: true
|
|
||||||
collapse: open
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
color: '#002b49'
|
|
||||||
display_template: '{{user_name}}: {{text}}'
|
|
||||||
hidden: false
|
|
||||||
icon: comment
|
|
||||||
singleton: false
|
|
||||||
versioning: false
|
|
||||||
schema:
|
|
||||||
name: visual_feedback_comments
|
|
||||||
fields:
|
fields:
|
||||||
# contact_submissions
|
# contact_submissions
|
||||||
- collection: contact_submissions
|
- collection: contact_submissions
|
||||||
@@ -235,244 +208,7 @@ fields:
|
|||||||
is_primary_key: true
|
is_primary_key: true
|
||||||
has_auto_increment: true
|
has_auto_increment: true
|
||||||
|
|
||||||
# visual_feedback (from current snapshot)
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: id
|
|
||||||
type: uuid
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: id
|
|
||||||
hidden: true
|
|
||||||
sort: 1
|
|
||||||
schema:
|
|
||||||
name: id
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: uuid
|
|
||||||
is_nullable: false
|
|
||||||
is_primary_key: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: status
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
display: labels
|
|
||||||
interface: select-dropdown
|
|
||||||
sort: 2
|
|
||||||
schema:
|
|
||||||
name: status
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
default_value: open
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: type
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
display: labels
|
|
||||||
interface: select-dropdown
|
|
||||||
sort: 3
|
|
||||||
schema:
|
|
||||||
name: type
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: text
|
|
||||||
type: text
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
interface: input-multiline
|
|
||||||
sort: 4
|
|
||||||
schema:
|
|
||||||
name: text
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: text
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: url
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
interface: input
|
|
||||||
readonly: true
|
|
||||||
sort: 5
|
|
||||||
schema:
|
|
||||||
name: url
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: user_info_group
|
|
||||||
type: alias
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: user_info_group
|
|
||||||
interface: group-detail
|
|
||||||
sort: 6
|
|
||||||
special:
|
|
||||||
- alias
|
|
||||||
- no-data
|
|
||||||
- group
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: user_name
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: user_name
|
|
||||||
group: user_info_group
|
|
||||||
interface: input
|
|
||||||
sort: 1
|
|
||||||
schema:
|
|
||||||
name: user_name
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: user_identity
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: user_identity
|
|
||||||
group: user_info_group
|
|
||||||
interface: input
|
|
||||||
readonly: true
|
|
||||||
sort: 2
|
|
||||||
schema:
|
|
||||||
name: user_identity
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: technical_details_group
|
|
||||||
type: alias
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: technical_details_group
|
|
||||||
interface: group-detail
|
|
||||||
sort: 7
|
|
||||||
special:
|
|
||||||
- alias
|
|
||||||
- no-data
|
|
||||||
- group
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: selector
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: selector
|
|
||||||
group: technical_details_group
|
|
||||||
interface: input
|
|
||||||
readonly: true
|
|
||||||
sort: 1
|
|
||||||
schema:
|
|
||||||
name: selector
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: character varying
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: x
|
|
||||||
type: float
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: x
|
|
||||||
group: technical_details_group
|
|
||||||
interface: input
|
|
||||||
sort: 2
|
|
||||||
schema:
|
|
||||||
name: x
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: real
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: 'y'
|
|
||||||
type: float
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
field: 'y'
|
|
||||||
group: technical_details_group
|
|
||||||
interface: input
|
|
||||||
sort: 3
|
|
||||||
schema:
|
|
||||||
name: 'y'
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: real
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback
|
|
||||||
field: date_created
|
|
||||||
type: timestamp
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback
|
|
||||||
interface: datetime
|
|
||||||
readonly: true
|
|
||||||
sort: 8
|
|
||||||
schema:
|
|
||||||
name: date_created
|
|
||||||
table: visual_feedback
|
|
||||||
data_type: timestamp with time zone
|
|
||||||
default_value: CURRENT_TIMESTAMP
|
|
||||||
is_nullable: true
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: id
|
|
||||||
type: uuid
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
field: id
|
|
||||||
hidden: true
|
|
||||||
schema:
|
|
||||||
name: id
|
|
||||||
table: visual_feedback_comments
|
|
||||||
data_type: uuid
|
|
||||||
is_primary_key: true
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: feedback_id
|
|
||||||
type: uuid
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
field: feedback_id
|
|
||||||
interface: select-relational
|
|
||||||
sort: 2
|
|
||||||
schema:
|
|
||||||
name: feedback_id
|
|
||||||
table: visual_feedback_comments
|
|
||||||
data_type: uuid
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: user_name
|
|
||||||
type: string
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
field: user_name
|
|
||||||
interface: input
|
|
||||||
sort: 3
|
|
||||||
schema:
|
|
||||||
name: user_name
|
|
||||||
table: visual_feedback_comments
|
|
||||||
data_type: character varying
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: text
|
|
||||||
type: text
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
field: text
|
|
||||||
interface: input-multiline
|
|
||||||
sort: 4
|
|
||||||
schema:
|
|
||||||
name: text
|
|
||||||
table: visual_feedback_comments
|
|
||||||
data_type: text
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: date_created
|
|
||||||
type: timestamp
|
|
||||||
meta:
|
|
||||||
collection: visual_feedback_comments
|
|
||||||
interface: datetime
|
|
||||||
readonly: true
|
|
||||||
sort: 5
|
|
||||||
schema:
|
|
||||||
name: date_created
|
|
||||||
table: visual_feedback_comments
|
|
||||||
data_type: timestamp with time zone
|
|
||||||
default_value: CURRENT_TIMESTAMP
|
|
||||||
|
|
||||||
systemFields:
|
systemFields:
|
||||||
- collection: directus_activity
|
- collection: directus_activity
|
||||||
@@ -488,17 +224,4 @@ systemFields:
|
|||||||
schema:
|
schema:
|
||||||
is_indexed: true
|
is_indexed: true
|
||||||
|
|
||||||
relations:
|
|
||||||
- collection: visual_feedback_comments
|
|
||||||
field: feedback_id
|
|
||||||
related_collection: visual_feedback
|
|
||||||
schema:
|
|
||||||
column: feedback_id
|
|
||||||
foreign_key_column: id
|
|
||||||
foreign_key_table: visual_feedback
|
|
||||||
table: visual_feedback_comments
|
|
||||||
meta:
|
|
||||||
many_collection: visual_feedback_comments
|
|
||||||
many_field: feedback_id
|
|
||||||
one_collection: visual_feedback
|
|
||||||
one_field: null
|
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ services:
|
|||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls=false"
|
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls=false"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
|
|
||||||
directus-cms:
|
klz-cms:
|
||||||
container_name: klz-cms-dev
|
container_name: klz-cms-dev
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
ports:
|
||||||
|
- "8055:8055"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-cms.rule=Host(`${DIRECTUS_HOST:-cms.klz.localhost}`)"
|
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-cms.rule=Host(`${DIRECTUS_HOST:-cms.klz.localhost}`)"
|
||||||
@@ -39,5 +41,5 @@ services:
|
|||||||
klz-db:
|
klz-db:
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
gatekeeper:
|
klz-gatekeeper:
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|||||||
@@ -18,48 +18,48 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# HTTP ⇒ HTTPS redirect
|
# HTTP ⇒ HTTPS redirect
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.entrypoints=web"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.entrypoints=web"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-web.middlewares=redirect-https"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.middlewares=redirect-https"
|
||||||
# HTTPS router (Standard)
|
# HTTPS router (Standard)
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.tls=${TRAEFIK_TLS:-false}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.tls=${TRAEFIK_TLS:-false}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.service=${PROJECT_NAME:-klz-cables}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.service=${PROJECT_NAME:-klz}-app-svc"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}.middlewares=${AUTH_MIDDLEWARE:-${PROJECT_NAME:-klz-cables}-ratelimit,${PROJECT_NAME:-klz-cables}-forward,${PROJECT_NAME:-klz-cables}-compress}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}.middlewares=${AUTH_MIDDLEWARE:-klz-ratelimit,klz-forward,klz-compress}"
|
||||||
|
|
||||||
# Public Router (Whitelist for OG Images, Sitemaps, Health)
|
# Public Router (Whitelist for OG Images, Sitemaps, Health)
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}) && (PathPrefix(`/health`) || PathPrefix(`/sitemap.xml`) || PathPrefix(`/robots.txt`) || PathPrefix(`/manifest.webmanifest`) || PathRegexp(`^/([a-z]{2}/)?api/og`) || PathRegexp(`^/([a-z]{2}/)?opengraph-image$`) || PathRegexp(`^/([a-z]{2}/)?blog/opengraph-image$`) || PathRegexp(`^/sitemap(-[0-9]+)?\\.xml$`))"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-klz-cables.com}`)}) && (PathPrefix(`/health`) || PathPrefix(`/sitemap.xml`) || PathPrefix(`/robots.txt`) || PathPrefix(`/manifest.webmanifest`) || PathRegexp(`^/([a-z]{2}/)?api/og`) || PathRegexp(`^/([a-z]{2}/)?opengraph-image$`) || PathRegexp(`^/([a-z]{2}/)?blog/opengraph-image$`) || PathRegexp(`^/sitemap(-[0-9]+)?\\.xml$`))"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.tls=${TRAEFIK_TLS:-false}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls=${TRAEFIK_TLS:-false}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.service=${PROJECT_NAME:-klz-cables}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.service=${PROJECT_NAME:-klz}-app-svc"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.middlewares=${AUTH_MIDDLEWARE_UNPROTECTED:-${PROJECT_NAME:-klz-cables}-ratelimit,${PROJECT_NAME:-klz-cables}-forward,${PROJECT_NAME:-klz-cables}-compress}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.middlewares=${AUTH_MIDDLEWARE_UNPROTECTED:-klz-ratelimit,klz-forward,klz-compress}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-public.priority=2000"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.priority=2000"
|
||||||
|
|
||||||
- "traefik.http.services.${PROJECT_NAME:-klz-cables}.loadbalancer.server.scheme=http"
|
- "traefik.http.services.${PROJECT_NAME:-klz}-app-svc.loadbalancer.server.scheme=http"
|
||||||
- "traefik.http.services.${PROJECT_NAME:-klz-cables}.loadbalancer.server.port=3000"
|
- "traefik.http.services.${PROJECT_NAME:-klz}-app-svc.loadbalancer.server.port=3000"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
- "caddy=http://${TRAEFIK_HOST:-klz.localhost}"
|
- "caddy=http://${TRAEFIK_HOST:-klz.localhost}"
|
||||||
- "caddy.reverse_proxy={{upstreams 3000}}"
|
- "caddy.reverse_proxy={{upstreams 3000}}"
|
||||||
|
|
||||||
# Middleware Definitions
|
# Middleware Definitions
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-compress.compress=true"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-compress.compress=true"
|
||||||
|
|
||||||
# Forwarded Headers
|
# Forwarded Headers
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
|
||||||
|
|
||||||
# Authentication Middleware (ForwardAuth)
|
# Authentication Middleware (ForwardAuth)
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-auth.forwardauth.address=http://${PROJECT_NAME:-klz-cables}-gatekeeper:3000/gatekeeper/api/verify"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.address=http://${PROJECT_NAME:-klz}-gatekeeper:3000/gatekeeper/api/verify"
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-auth.forwardauth.trustForwardHeader=true"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.trustForwardHeader=true"
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-auth.forwardauth.authRequestHeaders=X-Forwarded-Host,X-Forwarded-Proto,X-Forwarded-For,Cookie"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.authRequestHeaders=X-Forwarded-Host,X-Forwarded-Proto,X-Forwarded-For,Cookie"
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-auth.forwardauth.authResponseHeaders=X-Auth-User"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.authResponseHeaders=X-Auth-User"
|
||||||
|
|
||||||
# Rate Limit Middleware
|
# Rate Limit Middleware
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-ratelimit.ratelimit.average=100"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-ratelimit.ratelimit.average=100"
|
||||||
- "traefik.http.middlewares.${PROJECT_NAME:-klz-cables}-ratelimit.ratelimit.burst=50"
|
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-ratelimit.ratelimit.burst=50"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD", "curl", "-f", "http://127.0.0.1:3000/health" ]
|
test: [ "CMD", "curl", "-f", "http://127.0.0.1:3000/health" ]
|
||||||
interval: 15s
|
interval: 15s
|
||||||
@@ -67,14 +67,14 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 45s
|
start_period: 45s
|
||||||
|
|
||||||
gatekeeper:
|
klz-gatekeeper:
|
||||||
profiles: [ "gatekeeper" ]
|
profiles: [ "gatekeeper" ]
|
||||||
image: registry.infra.mintel.me/mintel/gatekeeper:v1.7.12
|
image: registry.infra.mintel.me/mintel/gatekeeper:v1.7.12
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
infra:
|
infra:
|
||||||
aliases:
|
aliases:
|
||||||
- ${PROJECT_NAME:-klz-cables}-gatekeeper
|
- ${PROJECT_NAME:-klz}-gatekeeper
|
||||||
env_file:
|
env_file:
|
||||||
- ${ENV_FILE:-.env}
|
- ${ENV_FILE:-.env}
|
||||||
environment:
|
environment:
|
||||||
@@ -88,15 +88,15 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-gatekeeper.rule=(Host(`${TRAEFIK_HOST:-testing.klz-cables.com}`) && PathPrefix(`/gatekeeper`))"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.rule=(Host(`${TRAEFIK_HOST:-testing.klz-cables.com}`) && PathPrefix(`/gatekeeper`))"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-gatekeeper.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-gatekeeper.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-gatekeeper.tls=${TRAEFIK_TLS:-false}"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls=${TRAEFIK_TLS:-false}"
|
||||||
- "traefik.http.routers.${PROJECT_NAME:-klz-cables}-gatekeeper.service=${PROJECT_NAME:-klz-cables}-gatekeeper"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.service=${PROJECT_NAME:-klz}-gatekeeper-svc"
|
||||||
- "traefik.http.services.${PROJECT_NAME:-klz-cables}-gatekeeper.loadbalancer.server.port=3000"
|
- "traefik.http.services.${PROJECT_NAME:-klz}-gatekeeper-svc.loadbalancer.server.port=3000"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
|
|
||||||
directus-cms:
|
klz-cms:
|
||||||
image: registry.infra.mintel.me/mintel/directus:latest
|
image: registry.infra.mintel.me/mintel/directus:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: [ "node", "cli.js", "start" ]
|
command: [ "node", "cli.js", "start" ]
|
||||||
@@ -112,7 +112,7 @@ services:
|
|||||||
DB_PORT: '5432'
|
DB_PORT: '5432'
|
||||||
DB_DATABASE: ${DIRECTUS_DB_NAME:-directus}
|
DB_DATABASE: ${DIRECTUS_DB_NAME:-directus}
|
||||||
DB_USER: ${DIRECTUS_DB_USER:-directus}
|
DB_USER: ${DIRECTUS_DB_USER:-directus}
|
||||||
DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus}
|
DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-120in09oenaoinsd9iaidon}
|
||||||
WEBSOCKETS_ENABLED: 'true'
|
WEBSOCKETS_ENABLED: 'true'
|
||||||
PUBLIC_URL: ${DIRECTUS_URL:-https://cms.klz-cables.com}
|
PUBLIC_URL: ${DIRECTUS_URL:-https://cms.klz-cables.com}
|
||||||
HOST: '0.0.0.0'
|
HOST: '0.0.0.0'
|
||||||
@@ -127,14 +127,15 @@ services:
|
|||||||
disable: true
|
disable: true
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.klz-production-cms.rule=Host(`cms.klz.localhost`)"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.rule=Host(`${DIRECTUS_HOST:-cms.klz-cables.com}`)"
|
||||||
- "traefik.http.routers.klz-production-cms.entrypoints=web"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.entrypoints=websecure"
|
||||||
- "traefik.http.routers.klz-production-cms.priority=5000"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.priority=5000"
|
||||||
- "traefik.http.routers.klz-production-cms.tls=false"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.tls=true"
|
||||||
- "traefik.http.routers.klz-production-cms.service=klz-production-cms-svc"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.tls.certresolver=le"
|
||||||
- "traefik.http.services.klz-production-cms-svc.loadbalancer.server.port=8055"
|
- "traefik.http.routers.${PROJECT_NAME:-klz}-cms.service=${PROJECT_NAME:-klz}-cms-svc"
|
||||||
|
- "traefik.http.services.${PROJECT_NAME:-klz}-cms-svc.loadbalancer.server.port=8055"
|
||||||
- "traefik.docker.network=infra"
|
- "traefik.docker.network=infra"
|
||||||
- "caddy=http://${DIRECTUS_HOST:-cms.klz.localhost}"
|
- "caddy=http://${DIRECTUS_HOST:-cms.klz-cables.com}"
|
||||||
- "caddy.reverse_proxy={{upstreams 8055}}"
|
- "caddy.reverse_proxy={{upstreams 8055}}"
|
||||||
klz-db:
|
klz-db:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
@@ -144,7 +145,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${DIRECTUS_DB_NAME:-directus}
|
POSTGRES_DB: ${DIRECTUS_DB_NAME:-directus}
|
||||||
POSTGRES_USER: ${DIRECTUS_DB_USER:-directus}
|
POSTGRES_USER: ${DIRECTUS_DB_USER:-directus}
|
||||||
POSTGRES_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus}
|
POSTGRES_PASSWORD: ${DIRECTUS_DB_PASSWORD:-120in09oenaoinsd9iaidon}
|
||||||
volumes:
|
volumes:
|
||||||
- directus-db-data:/var/lib/postgresql/data
|
- directus-db-data:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -78,8 +78,8 @@
|
|||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App (Next.js): http://localhost:3000\\n📱 App (Traefik): http://klz.localhost\\n🗄️ CMS: http://cms.klz.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker-compose down --remove-orphans && docker-compose up --build klz-app directus-cms klz-db gatekeeper",
|
"dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App (Next.js): http://localhost:3000\\n📱 App (Traefik): http://klz.localhost\\n🗄️ CMS: http://cms.klz.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker-compose down --remove-orphans && docker-compose up --build klz-app klz-cms klz-db klz-gatekeeper",
|
||||||
"dev:infra": "docker network create infra 2>/dev/null || true && docker-compose up -d directus-cms klz-db gatekeeper",
|
"dev:infra": "docker network create infra 2>/dev/null || true && docker-compose up -d klz-cms klz-db klz-gatekeeper",
|
||||||
"dev:local": "next dev",
|
"dev:local": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ PRJ_ID=$(jq -r .name package.json | sed 's/@mintel\///' | sed 's/\.com$//' | sed
|
|||||||
|
|
||||||
case $ENV in
|
case $ENV in
|
||||||
local)
|
local)
|
||||||
CONTAINER=$(docker compose ps -q directus-cms)
|
CONTAINER=$(docker compose ps -q klz-cms)
|
||||||
if [ -z "$CONTAINER" ]; then
|
if [ -z "$CONTAINER" ]; then
|
||||||
echo "❌ Local directus container not found."
|
echo "❌ Local directus container not found."
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
59
scripts/fix-directus-token.ts
Normal file
59
scripts/fix-directus-token.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import client, { ensureAuthenticated } from '../lib/directus';
|
||||||
|
import { readUsers, updateUser } from '@directus/sdk';
|
||||||
|
import { config } from '../lib/config';
|
||||||
|
|
||||||
|
async function fixToken() {
|
||||||
|
console.log('🔑 Ensuring Directus Admin Token is set...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Authenticate with credentials
|
||||||
|
await ensureAuthenticated();
|
||||||
|
|
||||||
|
// 2. Find admin user
|
||||||
|
const users = await client.request(
|
||||||
|
readUsers({
|
||||||
|
filter: {
|
||||||
|
email: { _eq: config.directus.adminEmail },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.error(`❌ Could not find user with email ${config.directus.adminEmail}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const admin = users[0];
|
||||||
|
const targetToken = config.directus.token;
|
||||||
|
|
||||||
|
if (!targetToken) {
|
||||||
|
console.error('❌ No DIRECTUS_API_TOKEN configured in environment.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin.token === targetToken) {
|
||||||
|
console.log('✅ Token is already correctly set.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update token
|
||||||
|
console.log(`📡 Updating token for ${config.directus.adminEmail}...`);
|
||||||
|
await client.request(
|
||||||
|
updateUser(admin.id, {
|
||||||
|
token: targetToken,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✨ Token successfully updated!');
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ Error fixing token:');
|
||||||
|
if (error.errors) {
|
||||||
|
console.error(JSON.stringify(error.errors, null, 2));
|
||||||
|
} else {
|
||||||
|
console.error(error.message || error);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fixToken();
|
||||||
68
scripts/manual-schema-fix.ts
Normal file
68
scripts/manual-schema-fix.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import client, { ensureAuthenticated } from '../lib/directus';
|
||||||
|
import { createCollection, createField } from '@directus/sdk';
|
||||||
|
|
||||||
|
async function setupSchema() {
|
||||||
|
console.log('🏗️ Manually creating contact_submissions collection...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Authenticate (using token from config)
|
||||||
|
await ensureAuthenticated();
|
||||||
|
|
||||||
|
// 2. Create collection
|
||||||
|
console.log('📡 Creating "contact_submissions" collection...');
|
||||||
|
await client.request(
|
||||||
|
createCollection({
|
||||||
|
collection: 'contact_submissions',
|
||||||
|
meta: {
|
||||||
|
icon: 'contact_mail',
|
||||||
|
color: '#002b49',
|
||||||
|
display_template: '{{name}} | {{email}}',
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
name: 'contact_submissions',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. Create fields
|
||||||
|
console.log('📡 Creating fields for "contact_submissions"...');
|
||||||
|
|
||||||
|
// name
|
||||||
|
await client.request(
|
||||||
|
createField('contact_submissions', {
|
||||||
|
field: 'name',
|
||||||
|
type: 'string',
|
||||||
|
meta: { interface: 'input' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// email
|
||||||
|
await client.request(
|
||||||
|
createField('contact_submissions', {
|
||||||
|
field: 'email',
|
||||||
|
type: 'string',
|
||||||
|
meta: { interface: 'input' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// message
|
||||||
|
await client.request(
|
||||||
|
createField('contact_submissions', {
|
||||||
|
field: 'message',
|
||||||
|
type: 'text',
|
||||||
|
meta: { interface: 'textarea' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✨ Collection and fields created successfully!');
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('❌ Error creating schema:');
|
||||||
|
if (error.errors) {
|
||||||
|
console.error(JSON.stringify(error.errors, null, 2));
|
||||||
|
} else {
|
||||||
|
console.error(error.message || error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupSchema();
|
||||||
@@ -49,7 +49,7 @@ esac
|
|||||||
# Detect local container
|
# Detect local container
|
||||||
echo "🔍 Detecting local database..."
|
echo "🔍 Detecting local database..."
|
||||||
# Use a more robust way to find the container if multiple projects exist locally
|
# Use a more robust way to find the container if multiple projects exist locally
|
||||||
LOCAL_DB_CONTAINER=$(docker compose ps -q klz-directus-db)
|
LOCAL_DB_CONTAINER=$(docker compose ps -q klz-db)
|
||||||
if [ -z "$LOCAL_DB_CONTAINER" ]; then
|
if [ -z "$LOCAL_DB_CONTAINER" ]; then
|
||||||
echo "❌ Local klz-directus-db container not found. Is it running? (npm run dev)"
|
echo "❌ Local klz-directus-db container not found. Is it running? (npm run dev)"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user