Compare commits

..

3 Commits

Author SHA1 Message Date
3ea0ebae1b Merge pull request 'development' (#8) from development into main
Reviewed-on: #8
2025-11-21 23:33:13 +00:00
2f2be739f2 chore: update gitignore and stop tracking sqlite runtime files 2025-11-21 23:56:03 +01:00
d1e9c2433c added fix for empty CSR history after uploading ones
deleted:    backend/spaces.db-shm
	deleted:    backend/spaces.db-wal
	modified:   frontend/src/App.jsx
	modified:   frontend/src/components/Sidebar.jsx
	new file:   frontend/src/hooks/usePermissions.js
	modified:   frontend/src/pages/Home.jsx
	modified:   frontend/src/pages/Profile.jsx
	modified:   frontend/src/pages/SpaceDetail.jsx
2025-11-21 23:50:12 +01:00
12 changed files with 390 additions and 32 deletions

327
.gitignore vendored
View File

@@ -1,29 +1,342 @@
# ============================================
# Certigo Addon - Comprehensive .gitignore
# ============================================
# ============================================
# Go / Backend
# ============================================
# Binaries and executables
*.exe
*.exe~
*.dll
*.so
*.dylib
backend/bin/
backend/myapp
backend/certigo-addon
backend/certigo-addon-*
/tmp/certigo-addon-*
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool
*.out
coverage.html
coverage.txt
# Go workspace file
go.work
go.work.sum
# Go module cache (optional, but recommended for CI/CD)
# .go/
# ============================================
# Node.js / Frontend
# ============================================
# Dependencies
node_modules/
frontend/node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Build outputs
dist/
dist-ssr/
frontend/dist/
backend/bin/
*.local
# Vite
.vite/
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Environment
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# ============================================
# Database
# ============================================
# SQLite databases
*.db
*.db-shm
*.db-wal
*.sqlite
*.sqlite3
backend/spaces.db
backend/*.db
# Environment variables
.env
.env.local
# Database backups
*.sql.backup
*.db.backup
# IDE
# ============================================
# Uploads & User-generated Content
# ============================================
# User uploads (avatars, files, etc.)
backend/uploads/
backend/uploads/**
!backend/uploads/.gitkeep
frontend/public/uploads/
# ============================================
# Logs
# ============================================
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
backend/*.log
# ============================================
# IDE & Editors
# ============================================
# VSCode
.vscode/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# IntelliJ IDEA / WebStorm
.idea/
*.iml
*.iws
*.ipr
out/
# Sublime Text
*.sublime-project
*.sublime-workspace
# Vim
*.swp
*.swo
*~
.vim/
# OS
# Emacs
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# ============================================
# OS Files
# ============================================
# macOS
.DS_Store
Thumbs.db
.AppleDouble
.LSOverride
Icon
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msix
*.msm
*.msp
*.lnk
# Linux
*~
.fuse_hidden*
.directory
.Trash-*
.nfs*
# ============================================
# Temporary & Cache Files
# ============================================
# Temporary files
*.tmp
*.temp
*.bak
*.backup
*.swp
*~.nib
*.orig
# Cache directories
.cache/
.parcel-cache/
.eslintcache
.stylelintcache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
.node_repl_history
.yarn-integrity
# ============================================
# Testing & Coverage
# ============================================
# Test coverage
coverage/
*.lcov
.nyc_output/
.coverage/
htmlcov/
.pytest_cache/
.tox/
# Jest
.jest/
# ============================================
# Build Tools & CI/CD
# ============================================
# Build artifacts
build/
out/
target/
.next/
.nuxt/
.cache/
# CI/CD
.github/workflows/*.yml.local
.circleci/
.travis.yml.local
# ============================================
# Security & Secrets
# ============================================
# Secrets and credentials
*.pem
*.key
*.crt
*.cert
secrets/
.secrets/
*.secret
config/secrets.*
# API keys and tokens
.env.secret
.env.production
.env.staging
# ============================================
# Documentation Build
# ============================================
# Generated documentation
docs/_build/
site/
# ============================================
# Misc
# ============================================
# Package manager lock files (optional - uncomment if you want to ignore)
# package-lock.json
# yarn.lock
# pnpm-lock.yaml
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env.development
.env.test
.env.production
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# ============================================
# Project-specific
# ============================================
# OpenAPI generated files (if any)
backend/generated/
backend/api/
# Provider test outputs
backend/test-outputs/
# Script outputs
backend/scripts/output/
# Keep directory structure but ignore contents
!backend/uploads/.gitkeep
!backend/config/providers/.gitkeep

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -1,7 +1,8 @@
import { useState } from 'react'
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import { AuthProvider, useAuth } from './contexts/AuthContext'
import { PermissionsProvider, usePermissions } from './contexts/PermissionsContext'
import { PermissionsProvider } from './contexts/PermissionsContext'
import { usePermissions } from './hooks/usePermissions'
import Sidebar from './components/Sidebar'
import Footer from './components/Footer'
import Home from './pages/Home'

View File

@@ -1,6 +1,6 @@
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { useAuth } from '../contexts/AuthContext'
import { usePermissions } from '../contexts/PermissionsContext'
import { usePermissions } from '../hooks/usePermissions'
import { useState, useEffect } from 'react'
const Sidebar = ({ isOpen, setIsOpen }) => {

View File

@@ -0,0 +1 @@
export { usePermissions } from '../contexts/PermissionsContext'

View File

@@ -1,7 +1,7 @@
import { useEffect, useState, useRef, useCallback } from 'react'
import { useLocation } from 'react-router-dom'
import { useAuth } from '../contexts/AuthContext'
import { usePermissions } from '../contexts/PermissionsContext'
import { usePermissions } from '../hooks/usePermissions'
const Home = () => {
const { authFetch } = useAuth()

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { useAuth } from '../contexts/AuthContext'
import { usePermissions } from '../contexts/PermissionsContext'
import { usePermissions } from '../hooks/usePermissions'
const Profile = () => {
const { authFetch, user } = useAuth()

View File

@@ -232,18 +232,50 @@ const SpaceDetail = () => {
if (response.ok) {
const csr = await response.json()
// Füge den neuen CSR zur History hinzu (nur wenn der Bereich bereits geöffnet ist)
if (showCSRDropdown[fqdn.id]) {
const newCsrWithFqdnId = { ...csr, fqdnId: fqdn.id }
setCsrHistory(prev => {
const filtered = prev.filter(csrItem => csrItem.fqdnId !== fqdn.id)
// Füge den neuen CSR am Anfang hinzu (neuester zuerst)
return [newCsrWithFqdnId, ...filtered]
})
}
setCsrData(csr)
setSelectedFqdn(fqdn)
// Lade die komplette CSR History neu, um den neuen CSR anzuzeigen
// WICHTIG: Warte auf die History bevor der Dropdown geöffnet wird
try {
const historyResponse = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr`)
if (historyResponse.ok) {
const history = await historyResponse.json()
// Stelle sicher, dass history ein Array ist
const historyArray = Array.isArray(history) ? history : []
// Füge fqdnId zu jedem CSR hinzu und stelle sicher dass sie immer gesetzt ist
// Auch wenn die API fqdnId bereits zurückgibt, überschreiben wir sie mit fqdn.id für Konsistenz
// Stelle sicher dass alle CSRs gültig sind und fqdnId gesetzt ist
const historyWithFqdnId = historyArray
.filter(csrItem => csrItem && csrItem.id) // Stelle sicher dass CSR gültig ist
.map(csrItem => ({
...csrItem,
fqdnId: String(fqdn.id) // Immer als String für konsistente Filterung
}))
setCsrHistory(prev => {
// Entferne alte CSRs für diesen FQDN und füge die neuen hinzu
// Verwende String-Vergleich für Robustheit
const filtered = prev.filter(csrItem => String(csrItem?.fqdnId) !== String(fqdn.id))
return [...filtered, ...historyWithFqdnId]
})
// Öffne den CSR History Dropdown NACH dem Laden der History
setShowCSRDropdown(prev => ({ ...prev, [fqdn.id]: true }))
} else {
setCsrHistory(prev => {
// Bei Fehler, entferne nur CSRs für diesen FQDN, behalte andere
return prev.filter(csrItem => String(csrItem?.fqdnId) !== String(fqdn.id))
})
}
} catch (err) {
console.error('Error fetching CSR history after upload:', err)
// Bei Fehler, entferne nur CSRs für diesen FQDN
setCsrHistory(prev => prev.filter(csrItem => String(csrItem?.fqdnId) !== String(fqdn.id)))
}
setShowCSRModal(true)
// Aktualisiere die FQDN-Liste
@@ -298,14 +330,23 @@ const SpaceDetail = () => {
const historyResponse = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr`)
if (historyResponse.ok) {
const history = await historyResponse.json()
setCsrHistory(Array.isArray(history) ? history : [])
const historyArray = Array.isArray(history) ? history : []
// Stelle sicher dass fqdnId gesetzt ist für konsistente Filterung
const historyWithFqdnId = historyArray
.filter(csr => csr && csr.id)
.map(csr => ({ ...csr, fqdnId: String(fqdn.id) }))
setCsrHistory(prev => {
const filtered = prev.filter(csr => String(csr?.fqdnId) !== String(fqdn.id))
return [...filtered, ...historyWithFqdnId]
})
} else {
setCsrHistory([])
setCsrHistory(prev => prev.filter(csr => String(csr?.fqdnId) !== String(fqdn.id)))
}
} catch (err) {
console.error('Error fetching CSR:', err)
setCsrData(null)
setCsrHistory([])
// Entferne nur CSRs für diesen FQDN, behalte andere
setCsrHistory(prev => prev.filter(csr => String(csr?.fqdnId) !== String(fqdn.id)))
}
}
@@ -319,7 +360,7 @@ const SpaceDetail = () => {
setSelectedFqdn(null)
setCsrData(null)
setCsrError('')
setCsrHistory([])
// csrHistory NICHT zurücksetzen - bleibt für Dropdown-Anzeige erhalten
}
const handleChange = (e) => {
@@ -822,11 +863,12 @@ const SpaceDetail = () => {
if (response.ok) {
const history = await response.json()
// Speichere History mit FQDN-ID als Key
const historyWithFqdnId = Array.isArray(history)
? history.map(csr => ({ ...csr, fqdnId: fqdn.id }))
: []
const historyArray = Array.isArray(history) ? history : []
const historyWithFqdnId = historyArray
.filter(csr => csr && csr.id)
.map(csr => ({ ...csr, fqdnId: String(fqdn.id) }))
setCsrHistory(prev => {
const filtered = prev.filter(csr => csr.fqdnId !== fqdn.id)
const filtered = prev.filter(csr => String(csr?.fqdnId) !== String(fqdn.id))
return [...filtered, ...historyWithFqdnId]
})
}
@@ -890,12 +932,13 @@ const SpaceDetail = () => {
<div className="border-t border-slate-600/50 bg-slate-800/50 p-4">
<h5 className="text-sm font-semibold text-slate-300 mb-3">CSR History</h5>
{(() => {
// Filtere CSRs für diesen FQDN - verwende String-Vergleich für Robustheit
const fqdnHistory = csrHistory
.filter(csr => csr.fqdnId === fqdn.id)
.filter(csr => csr && String(csr.fqdnId) === String(fqdn.id))
.sort((a, b) => {
// Sortiere nach created_at, neueste zuerst
const dateA = new Date(a.createdAt).getTime()
const dateB = new Date(b.createdAt).getTime()
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0
return dateB - dateA
})
return fqdnHistory.length > 0 ? (