mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
fix: resolve EACCES permission issue and hydration error #418
This commit is contained in:
1 parent
0026e3134d
commit
4f4a0dbd62
4 files changed
+45
-13
No files matched your search
+6
-3
@@ -37,15 +37,18 @@ RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
# Set the correct permission for prerender cache and data storage
|
||||
RUN mkdir .next data
|
||||
RUN chown nextjs:nodejs .next data
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# 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/static ./.next/static
|
||||
|
||||
# Grant write access to /app for settings persistence
|
||||
RUN chown -R nextjs:nodejs /app
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 5643
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ let connectionString = process.env.DATABASE_URL;
|
||||
|
||||
// On tente de lire la config sauvegardée via l'UI
|
||||
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)) {
|
||||
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
||||
if (config.url) {
|
||||
|
||||
@@ -31,7 +31,7 @@ function getLast12Months(): string[] {
|
||||
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 {
|
||||
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 [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
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 cellPadding = displayDensity === "compact" ? "py-1" : displayDensity === "normal" ? "py-1.5" : "py-2.5";
|
||||
|
||||
const handleRowSelectionChange = useCallback(
|
||||
(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);
|
||||
|
||||
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 (
|
||||
<div
|
||||
ref={tableContainerRef}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { Pool } from "pg";
|
||||
import fs from "fs/promises";
|
||||
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 {
|
||||
url: string;
|
||||
@@ -22,9 +23,10 @@ export async function testDatabaseConnection(url: string) {
|
||||
await client.query("SELECT 1");
|
||||
client.release();
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
console.error("Database connection test failed:", error.message);
|
||||
return { success: false, error: error.message };
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error("Database connection test failed:", errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
@@ -33,12 +35,17 @@ export async function testDatabaseConnection(url: string) {
|
||||
export async function saveDatabaseSettings(url: string) {
|
||||
try {
|
||||
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));
|
||||
console.log("Database configuration saved to:", CONFIG_FILE);
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
console.error("Failed to save database configuration:", error.message);
|
||||
return { success: false, error: error.message };
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error("Failed to save database configuration:", errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user