fix: resolve EACCES permission issue and hydration error #418

This commit is contained in:
Michael committed 2026-02-21 12:54:39 +01:00
1 parent 0026e3134d
commit 4f4a0dbd62
4 files changed
+45 -13

No files matched your search

+6 -3
View File
@@ -37,15 +37,18 @@ RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache # Set the correct permission for prerender cache and data storage
RUN mkdir .next RUN mkdir .next data
RUN chown nextjs:nodejs .next RUN chown nextjs:nodejs .next data
# Automatically leverage output traces to reduce image size # Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing # https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Grant write access to /app for settings persistence
RUN chown -R nextjs:nodejs /app
USER nextjs USER nextjs
EXPOSE 5643 EXPOSE 5643
+1 -1
View File
@@ -8,7 +8,7 @@ let connectionString = process.env.DATABASE_URL;
// On tente de lire la config sauvegardée via l'UI // On tente de lire la config sauvegardée via l'UI
try { try {
const CONFIG_PATH = path.join(process.cwd(), ".db-config.json"); const CONFIG_PATH = path.join(process.cwd(), "data", ".db-config.json");
if (fs.existsSync(CONFIG_PATH)) { if (fs.existsSync(CONFIG_PATH)) {
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")); const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
if (config.url) { if (config.url) {
+24 -2
View File
@@ -31,7 +31,7 @@ function getLast12Months(): string[] {
return months; return months;
} }
const MONTHS_12 = getLast12Months(); // La constante est supprimée d'ici pour éviter le mismatch d'hydratation (new Date() au runtime module)
function formatMonthLabel(key: string): string { function formatMonthLabel(key: string): string {
const m = parseInt(key.slice(4, 6), 10); const m = parseInt(key.slice(4, 6), 10);
@@ -47,10 +47,17 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
const { rows, draftChanges, setDraftGamme, filters, displayDensity } = useGridStore(); const { rows, draftChanges, setDraftGamme, filters, displayDensity } = useGridStore();
const [sorting, setSorting] = useState<SortingState>([]); const [sorting, setSorting] = useState<SortingState>([]);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({}); const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [isMounted, setIsMounted] = useState(false);
const tableContainerRef = useRef<HTMLDivElement>(null); const tableContainerRef = useRef<HTMLDivElement>(null);
// Calculer les mois dynamiquement pour éviter le mismatch entre serveur et client
const MONTHS_12 = useMemo(() => getLast12Months(), []);
useEffect(() => {
setIsMounted(true);
}, []);
const rowHeight = displayDensity === "compact" ? 32 : displayDensity === "normal" ? 40 : 48; const rowHeight = displayDensity === "compact" ? 32 : displayDensity === "normal" ? 40 : 48;
const cellPadding = displayDensity === "compact" ? "py-1" : displayDensity === "normal" ? "py-1.5" : "py-2.5";
const handleRowSelectionChange = useCallback( const handleRowSelectionChange = useCallback(
(updater: React.SetStateAction<RowSelectionState>) => { (updater: React.SetStateAction<RowSelectionState>) => {
@@ -241,6 +248,21 @@ export function HeatmapGrid({ onSelectionChange }: HeatmapGridProps) {
const totalWidth = columns.reduce((s, c) => s + ((c as { size?: number }).size ?? 150), 0); const totalWidth = columns.reduce((s, c) => s + ((c as { size?: number }).size ?? 150), 0);
if (!isMounted) {
return (
<div
className="overflow-auto rounded-[12px] flex items-center justify-center"
style={{
height: "calc(100vh - 240px)",
background: "var(--bg-surface)",
border: "1px solid var(--border)",
}}
>
<div className="text-muted text-sm italic opacity-50">Chargement de la grille...</div>
</div>
);
}
return ( return (
<div <div
ref={tableContainerRef} ref={tableContainerRef}
+14 -7
View File
@@ -4,7 +4,8 @@ import { Pool } from "pg";
import fs from "fs/promises"; import fs from "fs/promises";
import path from "path"; import path from "path";
const CONFIG_FILE = path.join(process.cwd(), ".db-config.json"); const DATA_DIR = path.join(process.cwd(), "data");
const CONFIG_FILE = path.join(DATA_DIR, ".db-config.json");
export interface DbConfig { export interface DbConfig {
url: string; url: string;
@@ -22,9 +23,10 @@ export async function testDatabaseConnection(url: string) {
await client.query("SELECT 1"); await client.query("SELECT 1");
client.release(); client.release();
return { success: true }; return { success: true };
} catch (error: any) { } catch (error: unknown) {
console.error("Database connection test failed:", error.message); const errorMessage = error instanceof Error ? error.message : String(error);
return { success: false, error: error.message }; console.error("Database connection test failed:", errorMessage);
return { success: false, error: errorMessage };
} finally { } finally {
await pool.end(); await pool.end();
} }
@@ -33,12 +35,17 @@ export async function testDatabaseConnection(url: string) {
export async function saveDatabaseSettings(url: string) { export async function saveDatabaseSettings(url: string) {
try { try {
const config: DbConfig = { url }; const config: DbConfig = { url };
// S'assurer que le dossier data existe
await fs.mkdir(DATA_DIR, { recursive: true });
await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2)); await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
console.log("Database configuration saved to:", CONFIG_FILE); console.log("Database configuration saved to:", CONFIG_FILE);
return { success: true }; return { success: true };
} catch (error: any) { } catch (error: unknown) {
console.error("Failed to save database configuration:", error.message); const errorMessage = error instanceof Error ? error.message : String(error);
return { success: false, error: error.message }; console.error("Failed to save database configuration:", errorMessage);
return { success: false, error: errorMessage };
} }
} }