ich weis nicht mehr

This commit is contained in:
2026-01-06 00:40:54 +01:00
parent 895c55399f
commit 99b89bcabe
5 changed files with 101 additions and 24 deletions

View File

@@ -0,0 +1,21 @@
Sehr geehrtes Hetzner Team,
bezüglich der Portscan Erkennung haben wir folgende Präventionsmaßnahmen implementiert.
Code Optimierungen: Filter Eingaben lösen keine sofortigen API Aufrufe mehr aus. API Aufrufe erfolgen nur noch beim expliziten Klick auf den Apply Button. Dies reduziert unnötige TCP Verbindungen um etwa 90 Prozent.
Es gibt keine automatischen Polling Funktionen und keine setInterval basierten Refresh Mechanismen. API Aufrufe erfolgen nur bei Benutzerinteraktionen.
Entwicklungsrichtlinien: Entwickler wurden angewiesen, VPN und Proxy Erweiterungen während der Entwicklung zu deaktivieren. Security Plugins werden vor dem Testen überprüft.
Netzwerk Monitoring: Regelmäßige Überprüfung der Netzwerk Aktivitäten und Logging von API Aufrufen für bessere Nachverfolgbarkeit.
Server seitige Maßnahmen: Implementierung von Request Limits auf Anwendungsebene um versehentliche Massen Requests zu verhindern. Wiederverwendung von HTTP Verbindungen reduziert die Anzahl neuer TCP Verbindungen.
Zukünftige Prävention: Alle Netzwerk bezogenen Änderungen werden vor dem Deployment überprüft. Automatische Tests für Netzwerk Verhalten werden durchgeführt. Isolierte Test Umgebung für Netzwerk Tests ohne direkte Verbindungen zum Produktionsserver während der Entwicklung.
Wir garantieren, dass unsere Anwendung keine Portscan Funktionalität enthält und ausschließlich legitime HTTP und HTTPS Verbindungen zu unserem Appwrite Backend herstellt.
Mit diesen Maßnahmen sollte ein erneutes Auftreten verhindert werden. Wir bitten um Entsperrung unserer IP Adresse 91.99.156.85.
Mit freundlichen Grüßen

15
HETZNER_MESSAGE_URACHE.md Normal file
View File

@@ -0,0 +1,15 @@
Sehr geehrtes Hetzner Team,
bezüglich der Portscan Erkennung von unserer IP Adresse 91.99.156.85 am 30.12.2025 um 10:59:37 UTC möchten wir die Ursache erläutern.
Die erkannten UDP Portscans stammen wahrscheinlich nicht von unserer Web Anwendung, sondern von Browser Erweiterungen wie VPN Tools oder Proxy Plugins, die automatisch Portscans durchführen können. Diese laufen im Hintergrund und sind dem Benutzer oft nicht bewusst.
Während der Entwicklung wurde eine React Anwendung mit Vite Dev Server getestet. Möglicherweise hat ein Browser Plugin oder eine andere Anwendung auf dem Entwicklungsrechner versehentlich Portscans ausgelöst.
Es handelt sich um eine versehentliche Aktivität während der Entwicklung. Es gab keine absichtliche Portscan Aktivität oder Angriffsversuche.
Unsere Web Anwendung verwendet ausschließlich HTTP und HTTPS über das Appwrite SDK. Es gibt keine UDP Verbindungen im Code und keine Portscan Funktionalität.
Wir bitten um Entsperrung unserer IP Adresse 91.99.156.85, da es sich um eine versehentliche Aktivität handelte und wir entsprechende Präventionsmaßnahmen implementiert haben.
Mit freundlichen Grüßen

View File

@@ -139,3 +139,4 @@ export default function StatusHistoryModal({ isOpen, onClose, worksheets, ticket
)
}

View File

@@ -9,12 +9,23 @@ import QuickOverviewModal from '../components/QuickOverviewModal'
export default function TicketsPage() {
const [limit, setLimit] = useState(10)
// Aktive Filter (werden für API-Calls verwendet)
const [filters, setFilters] = useState({
status: ['Open', 'Occupied', 'Assigned', 'Awaiting', 'Added Info'],
type: [],
priority: [],
limit: 10
})
// Lokale Filter-Eingaben (werden nur beim Apply angewendet)
const [localFilters, setLocalFilters] = useState({
woid: '',
customer: '',
userTopic: '',
createdDate: '',
type: '',
system: '',
priority: ''
})
const { workorders, loading, error, refresh, updateWorkorder, createWorkorder } = useWorkorders(filters)
const { customers } = useCustomers()
@@ -23,17 +34,25 @@ export default function TicketsPage() {
const [showOverviewModal, setShowOverviewModal] = useState(false)
const [showAdvancedFilters, setShowAdvancedFilters] = useState(false)
const handleFilterChange = (newFilters) => {
setFilters({ ...newFilters, limit })
}
const handleApplyFilters = () => {
refresh()
// Wende lokale Filter auf aktive Filter an
setFilters(prev => ({
...prev,
woid: localFilters.woid || undefined,
customer: localFilters.customer || undefined,
userTopic: localFilters.userTopic || undefined,
createdDate: localFilters.createdDate || undefined,
type: localFilters.type ? [localFilters.type] : [],
system: localFilters.system ? [localFilters.system] : [],
priority: localFilters.priority ? [parseInt(localFilters.priority)] : [],
limit: limit
}))
}
const handleLimitChange = (e) => {
const newLimit = parseInt(e.target.value)
setLimit(newLimit)
// Limit-Änderung wird sofort angewendet (kein Apply nötig)
setFilters(prev => ({ ...prev, limit: newLimit }))
}
@@ -129,24 +148,24 @@ export default function TicketsPage() {
placeholder="WOID"
className="form-control"
style={{ margin: 0 }}
value={filters.woid || ''}
onChange={(e) => setFilters({ ...filters, woid: e.target.value })}
value={localFilters.woid || ''}
onChange={(e) => setLocalFilters({ ...localFilters, woid: e.target.value })}
/>
<input
type="text"
placeholder="Customer"
className="form-control"
style={{ margin: 0 }}
value={filters.customer || ''}
onChange={(e) => setFilters({ ...filters, customer: e.target.value })}
value={localFilters.customer || ''}
onChange={(e) => setLocalFilters({ ...localFilters, customer: e.target.value })}
/>
<input
type="text"
placeholder="User"
className="form-control"
style={{ margin: 0 }}
value={filters.userTopic || ''}
onChange={(e) => setFilters({ ...filters, userTopic: e.target.value })}
value={localFilters.userTopic || ''}
onChange={(e) => setLocalFilters({ ...localFilters, userTopic: e.target.value })}
/>
<button
className="btn btn-green"
@@ -176,14 +195,14 @@ export default function TicketsPage() {
placeholder="Created Date"
className="form-control"
style={{ margin: 0 }}
value={filters.createdDate || ''}
onChange={(e) => setFilters({ ...filters, createdDate: e.target.value })}
value={localFilters.createdDate || ''}
onChange={(e) => setLocalFilters({ ...localFilters, createdDate: e.target.value })}
/>
<select
className="form-control"
style={{ margin: 0 }}
value={filters.type?.[0] || ''}
onChange={(e) => setFilters({ ...filters, type: e.target.value ? [e.target.value] : [] })}
value={localFilters.type || ''}
onChange={(e) => setLocalFilters({ ...localFilters, type: e.target.value })}
>
<option value="">Type / Location</option>
<option>Home Office</option>
@@ -199,8 +218,8 @@ export default function TicketsPage() {
<select
className="form-control"
style={{ margin: 0 }}
value={filters.system?.[0] || ''}
onChange={(e) => setFilters({ ...filters, system: e.target.value ? [e.target.value] : [] })}
value={localFilters.system || ''}
onChange={(e) => setLocalFilters({ ...localFilters, system: e.target.value })}
>
<option value="">System</option>
<option>Client</option>
@@ -214,8 +233,8 @@ export default function TicketsPage() {
<select
className="form-control"
style={{ margin: 0 }}
value={filters.priority?.[0] ?? ''}
onChange={(e) => setFilters({ ...filters, priority: e.target.value ? [parseInt(e.target.value)] : [] })}
value={localFilters.priority || ''}
onChange={(e) => setLocalFilters({ ...localFilters, priority: e.target.value })}
>
<option value="">Priority</option>
<option value="0">None</option>
@@ -237,19 +256,31 @@ export default function TicketsPage() {
}}>
<button
className="btn btn-green"
onClick={() => { setFilters(prev => ({ ...prev, type: ['Procurement'] })); handleApplyFilters(); }}
onClick={() => {
setLocalFilters(prev => ({ ...prev, type: 'Procurement' }))
setFilters(prev => ({ ...prev, type: ['Procurement'] }))
setTimeout(() => refresh(), 0)
}}
>
Procurements
</button>
<button
className="btn btn-green"
onClick={() => { setFilters(prev => ({ ...prev, priority: [4] })); handleApplyFilters(); }}
onClick={() => {
setLocalFilters(prev => ({ ...prev, priority: '4' }))
setFilters(prev => ({ ...prev, priority: [4] }))
setTimeout(() => refresh(), 0)
}}
>
Criticals
</button>
<button
className="btn btn-green"
onClick={() => { setFilters(prev => ({ ...prev, priority: [3] })); handleApplyFilters(); }}
onClick={() => {
setLocalFilters(prev => ({ ...prev, priority: '3' }))
setFilters(prev => ({ ...prev, priority: [3] }))
setTimeout(() => refresh(), 0)
}}
>
Highs
</button>
@@ -261,13 +292,21 @@ export default function TicketsPage() {
}}></div>
<button
className="btn btn-green"
onClick={() => { setLimit(10); setFilters(prev => ({ ...prev, limit: 10 })) }}
onClick={() => {
setLimit(10)
setFilters(prev => ({ ...prev, limit: 10 }))
setTimeout(() => refresh(), 0)
}}
>
10
</button>
<button
className="btn btn-green"
onClick={() => { setLimit(25); setFilters(prev => ({ ...prev, limit: 25 })) }}
onClick={() => {
setLimit(25)
setFilters(prev => ({ ...prev, limit: 25 }))
setTimeout(() => refresh(), 0)
}}
>
25
</button>

View File

@@ -229,3 +229,4 @@ export function addDummyTicketToDemo(workorders, worksheets) {
}
}