- New /glossar page with 73 bilingual patent law terms across 5 categories (Litigation, Prosecution, UPC, EPA, General) - Client-side instant search filtering both DE and EN columns - Category filter pills for quick narrowing - Suggest-a-term button opens modal form for new term submissions - Per-term feedback icon for correction suggestions - Suggestions stored in Supabase (glossar_suggestions table with RLS) - Go API: GET /api/glossar (terms JSON), POST /api/glossar/suggest - Full DE/EN i18n support, responsive layout, print-friendly - Added to sidebar nav, landing page tools section, and build pipeline
232 lines
17 KiB
Go
232 lines
17 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"mgit.msbls.de/m/patholo/internal/auth"
|
|
)
|
|
|
|
type GlossarTerm struct {
|
|
DE string `json:"de"`
|
|
EN string `json:"en"`
|
|
Definition string `json:"definition,omitempty"`
|
|
Category string `json:"category"`
|
|
}
|
|
|
|
type GlossarSuggestion struct {
|
|
TermDE string `json:"term_de"`
|
|
TermEN string `json:"term_en"`
|
|
Definition string `json:"definition,omitempty"`
|
|
Category string `json:"category"`
|
|
SuggestionType string `json:"suggestion_type"` // "new" or "correction"
|
|
ExistingTermDE string `json:"existing_term_de,omitempty"`
|
|
}
|
|
|
|
var glossarTerms = []GlossarTerm{
|
|
// --- Litigation ---
|
|
{DE: "Patentverletzung", EN: "Patent infringement", Definition: "Benutzung einer patentgeschützten Erfindung ohne Zustimmung des Patentinhabers.", Category: "Litigation"},
|
|
{DE: "Verletzungsklage", EN: "Infringement action", Definition: "Klage des Patentinhabers gegen den Verletzer auf Unterlassung, Schadensersatz und Auskunft.", Category: "Litigation"},
|
|
{DE: "Nichtigkeitsklage", EN: "Nullity action", Definition: "Klage auf Erklärung der Nichtigkeit eines Patents, z.\u202fB. wegen fehlender Neuheit oder erfinderischer Tätigkeit.", Category: "Litigation"},
|
|
{DE: "Unterlassungsanspruch", EN: "Injunctive relief", Definition: "Anspruch auf gerichtliches Verbot der weiteren Patentverletzung.", Category: "Litigation"},
|
|
{DE: "Schadensersatz", EN: "Damages", Definition: "Geldersatz für den durch die Patentverletzung entstandenen Schaden.", Category: "Litigation"},
|
|
{DE: "Streitwert", EN: "Amount in dispute", Definition: "Geschätzter Wert des Streitgegenstands, Grundlage für Gerichts- und Anwaltsgebühren.", Category: "Litigation"},
|
|
{DE: "Einstweilige Verfügung", EN: "Preliminary injunction", Definition: "Vorläufige gerichtliche Maßnahme zur sofortigen Unterbindung einer Patentverletzung.", Category: "Litigation"},
|
|
{DE: "Berufung", EN: "Appeal", Definition: "Rechtsmittel gegen ein Urteil der ersten Instanz.", Category: "Litigation"},
|
|
{DE: "Revision", EN: "Revision (appeal on points of law)", Definition: "Rechtsmittel zum BGH, beschränkt auf Rechtsfragen.", Category: "Litigation"},
|
|
{DE: "Prozesskostensicherheit", EN: "Security for costs", Definition: "Sicherheitsleistung für die Verfahrenskosten, die von ausländischen Klägern verlangt werden kann.", Category: "Litigation"},
|
|
{DE: "Aussetzung", EN: "Stay of proceedings", Definition: "Vorübergehende Unterbrechung des Verletzungsverfahrens, oft bis zur Entscheidung über die Nichtigkeit.", Category: "Litigation"},
|
|
{DE: "Widerklage", EN: "Counterclaim", Definition: "Klage des Beklagten gegen den Kläger im selben Verfahren.", Category: "Litigation"},
|
|
{DE: "Beweissicherung", EN: "Preservation of evidence", Definition: "Gerichtliche Anordnung zur Sicherung von Beweismitteln vor deren Vernichtung.", Category: "Litigation"},
|
|
{DE: "Beschwerde", EN: "Complaint / Appeal", Definition: "Rechtsbehelf gegen gerichtliche Entscheidungen, die kein Urteil sind.", Category: "Litigation"},
|
|
{DE: "Lizenzbereitschaftserklärung", EN: "Licence of right declaration", Definition: "Erklärung des Patentinhabers, jedem eine Lizenz zu angemessenen Bedingungen zu erteilen.", Category: "Litigation"},
|
|
{DE: "Merkmalsgliederung", EN: "Feature analysis / Claim breakdown", Definition: "Zerlegung des Patentanspruchs in einzelne Merkmale zur Prüfung der Verletzung.", Category: "Litigation"},
|
|
{DE: "Äquivalente Patentverletzung", EN: "Infringement by equivalents", Definition: "Verletzung durch eine Lösung, die dem Patentanspruch zwar nicht wortlautgemäß, aber funktional gleichwertig entspricht.", Category: "Litigation"},
|
|
{DE: "Auskunftsanspruch", EN: "Right to information / Disclosure order", Definition: "Anspruch auf Auskunft über Herkunft und Vertriebswege patentverletzender Erzeugnisse.", Category: "Litigation"},
|
|
{DE: "Rechnungslegung", EN: "Rendering of accounts", Definition: "Pflicht des Verletzers, über Umsätze und Gewinne aus der Verletzung Auskunft zu erteilen.", Category: "Litigation"},
|
|
{DE: "Vernichtungsanspruch", EN: "Destruction claim", Definition: "Anspruch auf Vernichtung patentverletzender Erzeugnisse.", Category: "Litigation"},
|
|
{DE: "Rückrufanspruch", EN: "Recall claim", Definition: "Anspruch auf Rückruf patentverletzender Produkte aus dem Vertriebsweg.", Category: "Litigation"},
|
|
{DE: "Abmahnung", EN: "Cease-and-desist letter", Definition: "Außergerichtliche Aufforderung an den Verletzer, die Verletzungshandlung einzustellen.", Category: "Litigation"},
|
|
{DE: "Unterlassungsvertragsstrafe", EN: "Contractual penalty for breach of injunction", Definition: "Vertragsstrafe bei Verstoß gegen eine strafbewehrte Unterlassungserklärung.", Category: "Litigation"},
|
|
|
|
// --- Prosecution ---
|
|
{DE: "Patentanmeldung", EN: "Patent application", Definition: "Antrag auf Erteilung eines Patents beim zuständigen Amt.", Category: "Prosecution"},
|
|
{DE: "Patentanspruch", EN: "Patent claim", Definition: "Definiert den Schutzbereich des Patents. Unterscheidung zwischen unabhängigen und abhängigen Ansprüchen.", Category: "Prosecution"},
|
|
{DE: "Priorität", EN: "Priority", Definition: "Recht, die Erstanmeldung als Zeitrang für Folgeanmeldungen in anderen Ländern zu nutzen (12 Monate).", Category: "Prosecution"},
|
|
{DE: "Offenlegung", EN: "Publication / Disclosure", Definition: "Veröffentlichung der Patentanmeldung, in der Regel 18 Monate nach Anmeldedatum.", Category: "Prosecution"},
|
|
{DE: "Erteilung", EN: "Grant", Definition: "Entscheidung des Patentamts, das Patent zu erteilen.", Category: "Prosecution"},
|
|
{DE: "Neuheit", EN: "Novelty", Definition: "Patentierbarkeitsvoraussetzung: Die Erfindung darf nicht zum Stand der Technik gehören.", Category: "Prosecution"},
|
|
{DE: "Erfinderische Tätigkeit", EN: "Inventive step", Definition: "Die Erfindung darf sich für den Fachmann nicht in naheliegender Weise aus dem Stand der Technik ergeben.", Category: "Prosecution"},
|
|
{DE: "Stand der Technik", EN: "Prior art", Definition: "Gesamtheit aller vor dem Anmeldetag öffentlich zugänglichen Informationen.", Category: "Prosecution"},
|
|
{DE: "Beschreibung", EN: "Description / Specification", Definition: "Teil der Patentanmeldung, der die Erfindung so offenbart, dass ein Fachmann sie nacharbeiten kann.", Category: "Prosecution"},
|
|
{DE: "Teilanmeldung", EN: "Divisional application", Definition: "Abspaltung eines Teils der ursprünglichen Anmeldung in eine eigene, neue Anmeldung.", Category: "Prosecution"},
|
|
{DE: "Gebrauchsmuster", EN: "Utility model", Definition: "Registriertes Schutzrecht für technische Erfindungen ohne Prüfung auf erfinderische Tätigkeit (nur DE).", Category: "Prosecution"},
|
|
{DE: "Schutzdauer", EN: "Term of protection", Definition: "Maximale Laufzeit eines Patents (20 Jahre ab Anmeldedatum bei Zahlung der Jahresgebühren).", Category: "Prosecution"},
|
|
{DE: "Jahresgebühr", EN: "Annual renewal fee", Definition: "Jährliche Gebühr zur Aufrechterhaltung des Patents.", Category: "Prosecution"},
|
|
|
|
// --- UPC ---
|
|
{DE: "Einheitliches Patentgericht", EN: "Unified Patent Court (UPC)", Definition: "Supranationales Gericht für Patentstreitigkeiten in teilnehmenden EU-Mitgliedstaaten.", Category: "UPC"},
|
|
{DE: "Einheitspatent", EN: "Unitary patent", Definition: "Europäisches Patent mit einheitlicher Wirkung in allen teilnehmenden EU-Staaten.", Category: "UPC"},
|
|
{DE: "Opt-out", EN: "Opt-out", Definition: "Erklärung, mit der ein europäisches Patent von der Zuständigkeit des UPC ausgenommen wird.", Category: "UPC"},
|
|
{DE: "Lokalkammer", EN: "Local division", Definition: "UPC-Kammer mit Sitz in einem teilnehmenden Mitgliedstaat für Verletzungsklagen.", Category: "UPC"},
|
|
{DE: "Zentralkammer", EN: "Central division", Definition: "UPC-Kammer in Paris und München für Nichtigkeitsklagen und bestimmte Verletzungsklagen.", Category: "UPC"},
|
|
{DE: "Berufungskammer", EN: "Court of Appeal", Definition: "UPC-Berufungsgericht mit Sitz in Luxemburg.", Category: "UPC"},
|
|
{DE: "Vertraulichkeitsklub", EN: "Confidentiality club", Definition: "Kreis von Personen, die Zugang zu vertraulichen Informationen im UPC-Verfahren erhalten.", Category: "UPC"},
|
|
{DE: "Verfahrenssprache", EN: "Language of proceedings", Definition: "Sprache, in der das UPC-Verfahren geführt wird (abhängig von Kammer und Parteivereinbarung).", Category: "UPC"},
|
|
{DE: "Bifurkation", EN: "Bifurcation", Definition: "Trennung von Verletzungs- und Nichtigkeitsverfahren vor verschiedenen Kammern.", Category: "UPC"},
|
|
{DE: "Vorläufige Maßnahmen", EN: "Provisional measures", Definition: "Einstweilige Anordnungen des UPC zur Sicherung von Ansprüchen vor dem Hauptverfahren.", Category: "UPC"},
|
|
{DE: "Schutzschrift", EN: "Protective letter", Definition: "Vorsorglich eingereichte Verteidigungsschrift gegen einen erwarteten Antrag auf einstweilige Maßnahmen.", Category: "UPC"},
|
|
{DE: "Übergangsphase", EN: "Transitional period", Definition: "Zeitraum, in dem bestehende europäische Patente noch von der UPC-Zuständigkeit ausgenommen werden können.", Category: "UPC"},
|
|
|
|
// --- EPA ---
|
|
{DE: "Einspruch", EN: "Opposition", Definition: "Anfechtung eines erteilten europäischen Patents vor dem EPA innerhalb von 9 Monaten nach Erteilung.", Category: "EPA"},
|
|
{DE: "Einspruchsabteilung", EN: "Opposition division", Definition: "Dreiköpfiges Gremium des EPA, das über Einsprüche entscheidet.", Category: "EPA"},
|
|
{DE: "Beschwerdekammer (EPA)", EN: "Board of Appeal", Definition: "Instanz des EPA für Beschwerden gegen Entscheidungen der Prüfungs- oder Einspruchsabteilung.", Category: "EPA"},
|
|
{DE: "Große Beschwerdekammer", EN: "Enlarged Board of Appeal", Definition: "Höchste Instanz des EPA für Rechtsfragen von grundlegender Bedeutung.", Category: "EPA"},
|
|
{DE: "Prüfungsabteilung", EN: "Examining division", Definition: "Abteilung des EPA, die Patentanmeldungen auf Patentierbarkeit prüft.", Category: "EPA"},
|
|
{DE: "Recherchenbericht", EN: "Search report", Definition: "Bericht des EPA über den für die Patentanmeldung relevanten Stand der Technik.", Category: "EPA"},
|
|
{DE: "Europäisches Patentübereinkommen", EN: "European Patent Convention (EPC)", Definition: "Völkerrechtlicher Vertrag, der das europäische Patentrecht und das EPA regelt.", Category: "EPA"},
|
|
{DE: "Benennungsstaaten", EN: "Designated states", Definition: "Staaten, für die Schutz aus einer europäischen Patentanmeldung beansprucht wird.", Category: "EPA"},
|
|
{DE: "Wiedereinsetzung", EN: "Re-establishment of rights", Definition: "Antrag auf Wiedereinsetzung in eine versäumte Frist beim EPA.", Category: "EPA"},
|
|
{DE: "Weiterbehandlung", EN: "Further processing", Definition: "Verfahren zur Heilung einer Fristversäumnis beim EPA gegen Zahlung einer Gebühr.", Category: "EPA"},
|
|
{DE: "Beschränkungsverfahren", EN: "Limitation proceedings", Definition: "Verfahren zur nachträglichen Einschränkung der Patentansprüche eines erteilten europäischen Patents.", Category: "EPA"},
|
|
|
|
// --- General ---
|
|
{DE: "Patentinhaber", EN: "Patent proprietor / Patentee", Definition: "Person oder Unternehmen, dem das Patent gehört.", Category: "General"},
|
|
{DE: "Erfinder", EN: "Inventor", Definition: "Natürliche Person, die die technische Lehre des Patents entwickelt hat.", Category: "General"},
|
|
{DE: "Lizenz", EN: "Licence", Definition: "Vertragliche Erlaubnis zur Nutzung eines Patents.", Category: "General"},
|
|
{DE: "Zwangslizenz", EN: "Compulsory licence", Definition: "Vom Gericht oder Behörde erteilte Lizenz ohne Zustimmung des Patentinhabers.", Category: "General"},
|
|
{DE: "Schutzbereich", EN: "Scope of protection", Definition: "Durch die Patentansprüche definierter Umfang des Patentschutzes.", Category: "General"},
|
|
{DE: "Fachmann", EN: "Person skilled in the art", Definition: "Hypothetische Bezugsperson mit durchschnittlichem Fachwissen im relevanten Technikgebiet.", Category: "General"},
|
|
{DE: "Patentübertragung", EN: "Patent assignment", Definition: "Übertragung des Eigentums an einem Patent auf eine andere Person oder ein Unternehmen.", Category: "General"},
|
|
{DE: "Patentfamilie", EN: "Patent family", Definition: "Gesamtheit aller Patentanmeldungen und Patente, die auf dieselbe Priorität zurückgehen.", Category: "General"},
|
|
{DE: "Patentregister", EN: "Patent register", Definition: "Amtliches Verzeichnis aller erteilten Patente mit Angaben zu Inhaber, Status und Rechtsänderungen.", Category: "General"},
|
|
{DE: "Patentverletzungsgutachten", EN: "Freedom-to-operate opinion", Definition: "Rechtsgutachten zur Frage, ob ein Produkt oder Verfahren Patente Dritter verletzt.", Category: "General"},
|
|
{DE: "Ergänzendes Schutzzertifikat", EN: "Supplementary protection certificate (SPC)", Definition: "Zusätzlicher Schutz für Arzneimittel und Pflanzenschutzmittel nach Patentablauf (max. 5 Jahre).", Category: "General"},
|
|
{DE: "PCT-Anmeldung", EN: "PCT application", Definition: "Internationale Patentanmeldung nach dem Vertrag über die Internationale Zusammenarbeit auf dem Gebiet des Patentwesens.", Category: "General"},
|
|
{DE: "Schriftsatz", EN: "Written submission / Brief", Definition: "Formelles Schreiben an das Gericht oder Amt mit rechtlichem Vorbringen.", Category: "General"},
|
|
{DE: "Mündliche Verhandlung", EN: "Oral hearing / Oral proceedings", Definition: "Mündliche Anhörung vor Gericht oder dem EPA.", Category: "General"},
|
|
{DE: "Neuheitsschädlich", EN: "Novelty-destroying", Definition: "Eigenschaft einer Entgegenhaltung, die die Neuheit der beanspruchten Erfindung zerstört.", Category: "General"},
|
|
{DE: "Aufgabe-Lösungs-Ansatz", EN: "Problem-solution approach", Definition: "Vom EPA angewandte Methode zur Prüfung der erfinderischen Tätigkeit.", Category: "General"},
|
|
{DE: "Formstücke", EN: "Formal documents / Forms", Definition: "Standardformulare der Patentämter für Anträge und Mitteilungen.", Category: "General"},
|
|
}
|
|
|
|
func handleGlossarPage(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "dist/glossar.html")
|
|
}
|
|
|
|
func handleGlossarAPI(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(w, http.StatusOK, glossarTerms)
|
|
}
|
|
|
|
func handleGlossarSuggest(w http.ResponseWriter, r *http.Request) {
|
|
var suggestion GlossarSuggestion
|
|
if err := json.NewDecoder(r.Body).Decode(&suggestion); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
|
|
return
|
|
}
|
|
|
|
suggestion.TermDE = strings.TrimSpace(suggestion.TermDE)
|
|
suggestion.TermEN = strings.TrimSpace(suggestion.TermEN)
|
|
suggestion.Definition = strings.TrimSpace(suggestion.Definition)
|
|
suggestion.Category = strings.TrimSpace(suggestion.Category)
|
|
suggestion.SuggestionType = strings.TrimSpace(suggestion.SuggestionType)
|
|
suggestion.ExistingTermDE = strings.TrimSpace(suggestion.ExistingTermDE)
|
|
|
|
if suggestion.TermDE == "" || suggestion.TermEN == "" || suggestion.Category == "" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Felder term_de, term_en und category sind erforderlich."})
|
|
return
|
|
}
|
|
if suggestion.SuggestionType == "" {
|
|
suggestion.SuggestionType = "new"
|
|
}
|
|
if suggestion.SuggestionType != "new" && suggestion.SuggestionType != "correction" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "suggestion_type muss 'new' oder 'correction' sein."})
|
|
return
|
|
}
|
|
|
|
// Get user's access token for authenticated Supabase request
|
|
accessToken := ""
|
|
email := ""
|
|
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
|
|
accessToken = cookie.Value
|
|
email = extractEmailFromJWT(cookie.Value)
|
|
}
|
|
|
|
payload := map[string]string{
|
|
"term_de": suggestion.TermDE,
|
|
"term_en": suggestion.TermEN,
|
|
"definition": suggestion.Definition,
|
|
"category": suggestion.Category,
|
|
"suggestion_type": suggestion.SuggestionType,
|
|
"existing_term_de": suggestion.ExistingTermDE,
|
|
"submitted_by": email,
|
|
}
|
|
|
|
jsonBody, err := json.Marshal(payload)
|
|
if err != nil {
|
|
log.Printf("glossar suggest marshal error: %v", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
|
|
return
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("%s/rest/v1/glossar_suggestions", authClient.URL)
|
|
req2, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonBody))
|
|
if err != nil {
|
|
log.Printf("glossar suggest request error: %v", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
|
|
return
|
|
}
|
|
req2.Header.Set("Content-Type", "application/json")
|
|
req2.Header.Set("apikey", authClient.AnonKey)
|
|
if accessToken != "" {
|
|
req2.Header.Set("Authorization", "Bearer "+accessToken)
|
|
} else {
|
|
req2.Header.Set("Authorization", "Bearer "+authClient.AnonKey)
|
|
}
|
|
req2.Header.Set("Prefer", "return=minimal")
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
resp, err := client.Do(req2)
|
|
if err != nil {
|
|
log.Printf("glossar suggest supabase error: %v", err)
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 300 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
log.Printf("glossar suggest supabase status %d: %s", resp.StatusCode, string(body))
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusCreated, map[string]string{"ok": "true"})
|
|
}
|
|
|
|
func extractEmailFromJWT(token string) string {
|
|
parts := strings.Split(token, ".")
|
|
if len(parts) != 3 {
|
|
return ""
|
|
}
|
|
decoded, err := base64.RawURLEncoding.DecodeString(parts[1])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
var claims struct {
|
|
Email string `json:"email"`
|
|
}
|
|
if err := json.Unmarshal(decoded, &claims); err != nil {
|
|
return ""
|
|
}
|
|
return claims.Email
|
|
}
|