docs verzeichnis und .env.local in Gitea speichern
This commit is contained in:
@@ -6,8 +6,8 @@ DB_PASSWORD="root#$"
|
||||
DB_NAME=talas_v5
|
||||
DB_PORT=3306
|
||||
|
||||
# Public Settings (Client braucht IP/Domain)
|
||||
NEXT_PUBLIC_SERVER_URL="http://10.10.0.70" # oder evtl. später https://nodemap.firma.de
|
||||
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
||||
NEXT_PUBLIC_SERVER_URL="http://10.10.0.70"
|
||||
NEXT_PUBLIC_ENABLE_GEOCODER=true
|
||||
NEXT_PUBLIC_USE_MOCK_API=false
|
||||
NEXT_PUBLIC_DEBUG_LOG=true
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,8 +7,6 @@
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
@@ -29,5 +27,4 @@ pages-manifest.json
|
||||
nodeMap für 13 am 16.07.2024.zip
|
||||
Lastenheft.js
|
||||
# Dendron-Verzeichnis ignorieren
|
||||
docs/
|
||||
/draw.io/
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// /config/appVersion
|
||||
export const APP_VERSION = "1.1.64";
|
||||
export const APP_VERSION = "1.1.65";
|
||||
|
||||
@@ -7,7 +7,7 @@ describe("contextmenuTest", () => {
|
||||
cy.log("Viewport eingestellt auf 1920x1080");
|
||||
|
||||
// 2. Seite besuchen
|
||||
cy.visit("http://192.168.10.33:3000/?m=12&u=484");
|
||||
cy.visit("http://10.10.0.13:3000/?m=12&u=484");
|
||||
cy.wait(5000); // Wartezeit nach dem Laden
|
||||
cy.log("Seite geöffnet");
|
||||
|
||||
@@ -39,7 +39,7 @@ describe("contextmenuTest", () => {
|
||||
cy.log("Menüeintrag gefunden");
|
||||
|
||||
// 7. URL abfangen und testen, bevor der Tab geöffnet wird
|
||||
const targetUrl = "http://192.168.10.33/talas5/devices/cpl.aspx?ver=35&kue=24&id=50922";
|
||||
const targetUrl = "http://10.10.0.13/talas5/devices/cpl.aspx?ver=35&kue=24&id=50922";
|
||||
|
||||
// HTTP-Anfrage zur Überprüfung des Status
|
||||
cy.request(targetUrl).then((response) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ describe("GMA Markers Layer", () => {
|
||||
// Testbeschreibung: Dieser Test überprüft, ob der Tooltip selbst korrekt dargestellt wird und den erwarteten Inhalt anzeigt.
|
||||
|
||||
// Besuche die Map-Seite
|
||||
//cy.visit("http://192.168.10.33:3000/?m=12&u=484"); // Passe die URL an
|
||||
//cy.visit("http://10.10.0.13:3000/?m=12&u=484"); // Passe die URL an
|
||||
cy.visit("http://127.0.0.1:3000/?m=12&u=484");
|
||||
|
||||
cy.contains(".leaflet-tooltip", "Rastede")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
describe("TK-Komponenten", () => {
|
||||
before(() => {
|
||||
// Lade die Seite nur einmal vor allen Tests
|
||||
cy.visit("http://192.168.10.33:3000/?m=12&u=484");
|
||||
cy.visit("http://10.10.0.13:3000/?m=12&u=484");
|
||||
//cy.wait(5000); // Wartezeit, bis die Seite vollständig geladen ist, cypress macht automatisch , alsobrauchen wir im moment kein wait() wenn cy. schafft
|
||||
});
|
||||
|
||||
|
||||
12
docs/README.md
Normal file
12
docs/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Entwickler-Dokumentation
|
||||
|
||||
Willkommen in der technischen Dokumentation von NodeMap.
|
||||
|
||||
📂 Wichtige Themen:
|
||||
|
||||
- Webservices: `api/webservices.md`
|
||||
- Redux / Fetch-Logik: `redux/api/fromWebService.md`
|
||||
- Konfigurationsschema: `env/env.local.schema.md`
|
||||
- Deployment: `deployment.md`
|
||||
|
||||
Diese Doku ist für Entwickler, die an diesem Projekt mitarbeiten oder übernehmen.
|
||||
0
docs/architecture.md
Normal file
0
docs/architecture.md
Normal file
0
docs/deployment.md
Normal file
0
docs/deployment.md
Normal file
5
docs/env.local.schema.md
Normal file
5
docs/env.local.schema.md
Normal file
@@ -0,0 +1,5 @@
|
||||
### /docs/env.local.schema.md
|
||||
|
||||
- `NEXT_PUBLIC_API_HOST` → Webservice-DNS oder IP
|
||||
- `NEXT_PUBLIC_API_BASE_PATH` → z. B. `talas5`, per Deployment steuerbar
|
||||
- `DB_NAME` → hängt vom Kundenprojekt ab
|
||||
101
docs/frontend/redux/api/fromWebService.md
Normal file
101
docs/frontend/redux/api/fromWebService.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# 📡 Webservices – TALAS / GIS
|
||||
|
||||
## Basis-URL
|
||||
|
||||
```env
|
||||
NEXT_PUBLIC_API_BASE_URL=http://[DNS-Name-oder-IP]/[pfad]
|
||||
# Beispiel: http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Endpunkte
|
||||
|
||||
### 1. `GisStationsStatic`
|
||||
|
||||
- **Beschreibung:** Statische Geräteliste einer Karte
|
||||
- **Methode:** GET
|
||||
- **Parameter:** `idMap`
|
||||
- **Beispiel:**
|
||||
|
||||
```
|
||||
GET /GisStationsStatic?idMap=12
|
||||
http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisStationsStatic?idMap=12
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. `GisStationsStaticDistrict`
|
||||
|
||||
- **Beschreibung:** Statische Geräte inkl. Bereichszuordnung
|
||||
- **Methode:** GET
|
||||
- **Parameter:** `idMap`, `idUser`
|
||||
- **Beispiel:**
|
||||
```
|
||||
GET /GisStationsStaticDistrict?idMap=12&idUser=484
|
||||
http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisStationsStaticDistrict?idMap=12&idUser=484
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. `GisStationsStatusDistrict`
|
||||
|
||||
- **Beschreibung:** Aktueller Status von Geräten im Gebiet
|
||||
- **Methode:** GET
|
||||
- **Parameter:** `idMap`, `idUser`
|
||||
- **Beispiel:**
|
||||
```
|
||||
GET /GisStationsStatusDistrict?idMap=12&idUser=484
|
||||
http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisStationsStatusDistrict?idMap=12&idUser=484
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. `GisStationsMeasurements`
|
||||
|
||||
- **Beschreibung:** Live-Messwerte der Geräte
|
||||
- **Methode:** GET
|
||||
- **Parameter:** `idMap`, `idUser`
|
||||
- **Beispiel:**
|
||||
```
|
||||
GET /GisStationsMeasurements?idMap=12&idUser=484
|
||||
http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisStationsMeasurements?idMap=12&idUser=484
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. `GisSystemStatic`
|
||||
|
||||
- **Beschreibung:** Liste aller Systeme auf der Karte
|
||||
- **Methode:** GET
|
||||
- **Parameter:** `idMap`, `idUser`
|
||||
- **Beispiel:**
|
||||
```
|
||||
GET /GisSystemStatic?idMap=12&idUser=484
|
||||
http://10.10.0.13/talas5/ClientData/WebServiceMap.asmx/GisSystemStatic?idMap=12&idUser=484
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nutzung im Code
|
||||
|
||||
- Alle Webservice-Aufrufe basieren auf:
|
||||
```js
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
```
|
||||
- Fallback-IDs über `.env.local`:
|
||||
```env
|
||||
NEXT_PUBLIC_DEFAULT_ID_MAP=12
|
||||
NEXT_PUBLIC_DEFAULT_ID_USER=484
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hinweise
|
||||
|
||||
- ❌ Keine festen IPs/Domains in Code-Dateien verwenden
|
||||
- ✅ Nur `.env.local` als zentrale Konfiguration nutzen
|
||||
- ✅ Beispielaufrufe ausschließlich in dieser Datei dokumentieren
|
||||
- ✅ Webservice-Funktionen befinden sich in:
|
||||
`/redux/api/fromWebService/fetchGis*.js`
|
||||
@@ -4,14 +4,7 @@ import { createProxyMiddleware } from "http-proxy-middleware";
|
||||
//console.log("SERVER_URL:", SERVER_URL); // Debug-Ausgabe
|
||||
|
||||
export default createProxyMiddleware({
|
||||
//target: "http://192.168.10.58:3001",
|
||||
// Stationen bekommen
|
||||
//target: "http://10.10.0.13", // Ziel-URL des Proxys // API Aufruf zum mapGisStationsStaticDistrictUrl, mapGisStationsStatusDistrictUrl, mapGisStationsMeasurementsUrl, mapGisSystemStaticUrl und mapDataIconUrl
|
||||
target: `${process.env.NEXT_PUBLIC_SERVER_URL}`, //
|
||||
//target: urls.PROXY_TARGET,
|
||||
//target: "http://localhost:3000", // Ziel-URL des Proxys
|
||||
//target: "http://192.168.10.187:3000", // Ziel-URL des Proxys
|
||||
//target: "http://192.168.10.14",
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
"^/api": "/", // Optional: Entfernt /api aus dem Pfad, wenn das Backend dies nicht erfordert
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// /pages/api/gis-proxy.js
|
||||
export default async function handler(req, res) {
|
||||
// CORS-Header setzen
|
||||
res.setHeader("Access-Control-Allow-Credentials", true);
|
||||
@@ -12,7 +13,8 @@ export default async function handler(req, res) {
|
||||
}
|
||||
|
||||
// Ziel-URL direkt auf die Methode
|
||||
const targetUrl = "http://10.10.0.70/talas5/ClientData/WebServiceMap.asmx";
|
||||
const baseUrl = process.env.NEXT_PUBLIC_GIS_SERVER_URL;
|
||||
const targetUrl = `${baseUrl}/talas5/ClientData/WebServiceMap.asmx`;
|
||||
|
||||
// SOAP-Envelope für die Methode "GisStationsStaticDistrict"
|
||||
const soapEnvelope = `
|
||||
|
||||
@@ -28,8 +28,6 @@ export default async function handler(req, res) {
|
||||
return; // Beende die Funktion, wenn der Parameter fehlt
|
||||
}
|
||||
|
||||
console.log("idMap:", idMap); // Debugging
|
||||
|
||||
// Dynamische URL basierend auf der Anfrage
|
||||
const protocol = req.headers["x-forwarded-proto"] || "http"; // Protokoll ermitteln
|
||||
const host = req.headers.host.split(":")[0]; // Hostname ohne Port
|
||||
@@ -39,7 +37,7 @@ export default async function handler(req, res) {
|
||||
|
||||
// Ziel-URL für den Webservice
|
||||
const targetUrl = `${serverBaseUrl}/talas5/ClientData/WebServiceMap.asmx/GisStationsMeasurements?idMap=${idMap}`;
|
||||
console.log("Ziel-URL:", targetUrl); // Debugging
|
||||
console.log(`📦 Ziel-Request → idMap: ${idMap}, idUser: ${idUser}, URL: ${targetUrl}`);
|
||||
|
||||
// Daten vom Webservice abrufen
|
||||
const response = await fetch(targetUrl, {
|
||||
@@ -56,12 +54,13 @@ export default async function handler(req, res) {
|
||||
|
||||
// XML-Antwort als Text auslesen
|
||||
const xmlText = await response.text();
|
||||
console.log("XML-Antwort:", xmlText); // Debugging
|
||||
//console.log("XML-Antwort in pages/api/gisStationsMeasurements.js:", xmlText); // Debugging
|
||||
console.log("✅ Daten erhalten: /pages/api/gisStationsMeasurements.js:");
|
||||
|
||||
// XML direkt an den Client zurückgeben
|
||||
res.status(200).send(xmlText);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Abrufen der Daten:", error);
|
||||
console.error("Fehler beim Abrufen der Daten in pages/api/gisStationsMeasurements.js:", error);
|
||||
res.status(500).json({ error: "Fehler beim Abrufen der Daten" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,6 @@ export default async function handler(req, res) {
|
||||
return; // Beende die Funktion, wenn Parameter fehlen
|
||||
}
|
||||
|
||||
console.log("idMap:", idMap); // Debugging
|
||||
console.log("idUser:", idUser); // Debugging
|
||||
|
||||
// Dynamische URL basierend auf der Anfrage
|
||||
const protocol = req.headers["x-forwarded-proto"] || "http"; // Protokoll ermitteln
|
||||
const host = req.headers.host.split(":")[0]; // Hostname ohne Port
|
||||
@@ -40,7 +37,7 @@ export default async function handler(req, res) {
|
||||
|
||||
// Ziel-URL für den Webservice
|
||||
const targetUrl = `${serverBaseUrl}/talas5/ClientData/WebServiceMap.asmx/GisStationsStaticDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||
console.log("Ziel-URL:", targetUrl); // Debugging
|
||||
console.log(`📦 Ziel-Request → idMap: ${idMap}, idUser: ${idUser}, URL: ${targetUrl}`);
|
||||
|
||||
// Daten vom Webservice abrufen
|
||||
const response = await fetch(targetUrl, {
|
||||
@@ -57,12 +54,13 @@ export default async function handler(req, res) {
|
||||
|
||||
// XML-Antwort als Text auslesen
|
||||
const xmlText = await response.text();
|
||||
console.log("XML-Antwort:", xmlText); // Debugging
|
||||
//console.log("XML-Antwort in pages/api/gisStationsStaticDistrict.js :", xmlText); // Debugging
|
||||
console.log("✅ Daten erhalten: /pages/api/gisStationsStaticDistrict.js:");
|
||||
|
||||
// XML direkt an den Client zurückgeben
|
||||
res.status(200).send(xmlText);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Abrufen der Daten:", error);
|
||||
console.error("Fehler beim Abrufen der Daten in in pages/api/gisStationsStaticDistrict.js:", error);
|
||||
res.status(500).json({ error: "Fehler beim Abrufen der Daten" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ export default async function handler(req, res) {
|
||||
return; // Beende die Funktion, wenn Parameter fehlen
|
||||
}
|
||||
|
||||
console.log("idMap:", idMap); // Debugging
|
||||
console.log("idUser:", idUser); // Debugging
|
||||
|
||||
// Dynamische URL basierend auf der Anfrage
|
||||
const protocol = req.headers["x-forwarded-proto"] || "http"; // Protokoll ermitteln
|
||||
const host = req.headers.host.split(":")[0]; // Hostname ohne Port
|
||||
@@ -39,7 +36,7 @@ export default async function handler(req, res) {
|
||||
|
||||
// Ziel-URL für den Webservice
|
||||
const targetUrl = `${serverBaseUrl}/talas5/ClientData/WebServiceMap.asmx/GisStationsStatusDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||
console.log("Ziel-URL:", targetUrl); // Debugging
|
||||
console.log(`📦 Ziel-Request → idMap: ${idMap}, idUser: ${idUser}, URL: ${targetUrl}`);
|
||||
|
||||
// Daten vom Webservice abrufen
|
||||
const response = await fetch(targetUrl, {
|
||||
@@ -56,12 +53,13 @@ export default async function handler(req, res) {
|
||||
|
||||
// XML-Antwort als Text auslesen
|
||||
const xmlText = await response.text();
|
||||
console.log("XML-Antwort:", xmlText); // Debugging
|
||||
//console.log("XML-Antwort in pages/api/gisStationsStatusDistrict.js:", xmlText); // Debugging
|
||||
console.log("✅ Daten erhalten: /pages/api/gisStationsStatusDistrict.js:");
|
||||
|
||||
// XML direkt an den Client zurückgeben
|
||||
res.status(200).send(xmlText);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Abrufen der Daten:", error);
|
||||
console.error("Fehler beim Abrufen der Daten in pages/api/gisStationsStatusDistrict.js:", error);
|
||||
res.status(500).json({ error: "Fehler beim Abrufen der Daten" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ export default async function handler(req, res) {
|
||||
return; // Beende die Funktion, wenn Parameter fehlen
|
||||
}
|
||||
|
||||
console.log("idMap:", idMap); // Debugging
|
||||
console.log("idUser:", idUser); // Debugging
|
||||
|
||||
// Dynamische URL basierend auf der Anfrage
|
||||
const protocol = req.headers["x-forwarded-proto"] || "http"; // Protokoll ermitteln
|
||||
const host = req.headers.host.split(":")[0]; // Hostname ohne Port
|
||||
@@ -39,7 +36,7 @@ export default async function handler(req, res) {
|
||||
|
||||
// Ziel-URL für den Webservice
|
||||
const targetUrl = `${serverBaseUrl}/talas5/ClientData/WebServiceMap.asmx/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
||||
console.log("Ziel-URL:", targetUrl); // Debugging
|
||||
console.log(`📦 Ziel-Request → idMap: ${idMap}, idUser: ${idUser}, URL: ${targetUrl}`);
|
||||
|
||||
// Daten vom Webservice abrufen
|
||||
const response = await fetch(targetUrl, {
|
||||
@@ -56,12 +53,13 @@ export default async function handler(req, res) {
|
||||
|
||||
// XML-Antwort als Text auslesen
|
||||
const xmlText = await response.text();
|
||||
console.log("XML-Antwort:", xmlText); // Debugging
|
||||
//console.log("XML-Antwort in /pages/api/gisSystemStatic.js:", xmlText); // Debugging
|
||||
console.log("✅ Daten erhalten: /pages/api/gisSystemStatic.js:");
|
||||
|
||||
// XML direkt an den Client zurückgeben
|
||||
res.status(200).send(xmlText);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Abrufen der Daten:", error);
|
||||
console.error("Fehler beim Abrufen der Daten in in pages/api/gisSystemStatic.js:", error);
|
||||
res.status(500).json({ error: "Fehler beim Abrufen der Daten" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// /redux/api/fromWebService/fetchGisStationsMeasurements.js
|
||||
// http://192.168.10.33/talas5/ClientData/WebServiceMap.asmx/GisStationsMeasurements?idMap=12&idUser=484
|
||||
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// /redux/api/fromWebService/fetchGisStationsStatic.js
|
||||
// z.B. http://192.168.10.33/talas5/ClientData/WebServiceMap.asmx/GisStationsStatic?idMap=12
|
||||
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
|
||||
export const fetchGisStationsStatic = async () => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// /redux/api/fromWebService/fetchGisStationsStaticDistrict.js
|
||||
// http://192.168.10.33/talas5/ClientData/WebServiceMap.asmx/GisStationsStaticDistrict?idMap=12&idUser=484
|
||||
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// /redux/api/fromWebService/fetchGisStationsStatusDistrict.js
|
||||
// http://192.168.10.33/talas5/ClientData/WebServiceMap.asmx/GisStationsStatusDistrict?idMap=12&idUser=484
|
||||
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// /redux/api/fromWebService/fetchGisSystemStatic.js
|
||||
// http://192.168.10.33/talas5/ClientData/WebServiceMap.asmx/GisSystemStatic?idMap=12&idUser=484
|
||||
|
||||
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
|
||||
|
||||
|
||||
@@ -16,23 +16,34 @@ function getPool() {
|
||||
waitForConnections: true,
|
||||
queueLimit: 10, // Warteschlangenlimit für Verbindungen
|
||||
connectTimeout: 5000, // Timeout für Verbindungsversuche (5 Sekunden)
|
||||
acquireTimeout: 10000, // Timeout für Verbindungsanforderungen aus dem Pool (10 Sekunden)
|
||||
//acquireTimeout: 10000, // Timeout für Verbindungsanforderungen aus dem Pool (10 Sekunden)
|
||||
idleTimeout: 60000, // 1 Minute
|
||||
});
|
||||
|
||||
// Ereignisse für das Protokollieren der Verbindungsstatistiken
|
||||
let maxUsed = 0;
|
||||
|
||||
cachedPool.on("acquire", () => {
|
||||
connectionCount++;
|
||||
console.log(`[ACQUIRE] Active connections: ${connectionCount}`);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("\x1b[36m%s\x1b[0m", `➕ Connection acquired (${connectionCount} total)`);
|
||||
if (connectionCount > maxUsed) {
|
||||
maxUsed = connectionCount;
|
||||
console.log(`📈 Neue Höchstzahl aktiver gleichzeitiger Verbindungen: ${maxUsed}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cachedPool.on("release", () => {
|
||||
connectionCount--;
|
||||
console.log(`[RELEASE] Active connections: ${connectionCount}`);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log("\x1b[32m%s\x1b[0m", `➖ Connection released (${connectionCount} total)`);
|
||||
}
|
||||
});
|
||||
|
||||
cachedPool.on("enqueue", () => {
|
||||
console.log(`[ENQUEUE] Waiting for available connection slot.`);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.warn("\x1b[33m%s\x1b[0m", "⏳ Pool voll – Anfrage in Warteschlange");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user