Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 717e3ce15e | |||
| 007ae04bf3 | |||
| c0947c5cf1 | |||
| c1d2935ad6 | |||
| 9fde0b4b2a | |||
| da7b075cc4 | |||
| 347f258b8f | |||
| 2421f867ed | |||
| 921915e92f | |||
| a4b0866293 | |||
| e10d86da4b | |||
| f160f23e9c | |||
| ca272542de | |||
| 7df9b65389 | |||
| e4af76c9c1 | |||
| f5944bf090 |
+1
-2
@@ -8,5 +8,4 @@ INVENTAR_APP_MEM_SWAP_LIMIT=768m
|
|||||||
INVENTAR_WORKERS=4
|
INVENTAR_WORKERS=4
|
||||||
INVENTAR_THREADS=2
|
INVENTAR_THREADS=2
|
||||||
INVENTAR_WORKER_TIMEOUT=30
|
INVENTAR_WORKER_TIMEOUT=30
|
||||||
INVENTAR_WORKER_CONNECTIONS=100
|
INVENTAR_WORKER_CONNECTIONS=100
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
|
working_dir: /app/Web
|
||||||
|
command: ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4", "--threads", "2", "--timeout", "30", "--graceful-timeout", "20", "--worker-connections", "100", "--max-requests", "1000", "--max-requests-jitter", "100", "--log-level", "info", "--access-logfile", "-", "--error-logfile", "-"]
|
||||||
image: ghcr.io/aiirondev/legendary-octo-garbanzo:v0.7.42
|
image: ghcr.io/aiirondev/legendary-octo-garbanzo:v0.7.42
|
||||||
build: null
|
build: null
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
+17
-3
@@ -1736,7 +1736,6 @@ def is_valid_isbn13(isbn13):
|
|||||||
check_digit = (10 - (checksum % 10)) % 10
|
check_digit = (10 - (checksum % 10)) % 10
|
||||||
return check_digit == int(isbn13[12])
|
return check_digit == int(isbn13[12])
|
||||||
|
|
||||||
|
|
||||||
def normalize_and_validate_isbn(isbn_raw):
|
def normalize_and_validate_isbn(isbn_raw):
|
||||||
"""Normalize ISBN and return a valid canonical ISBN-13/10 or empty string."""
|
"""Normalize ISBN and return a valid canonical ISBN-13/10 or empty string."""
|
||||||
isbn = normalize_isbn(isbn_raw)
|
isbn = normalize_isbn(isbn_raw)
|
||||||
@@ -1996,7 +1995,7 @@ def _upload_student_cards_excel():
|
|||||||
synonyms = {
|
synonyms = {
|
||||||
'ausweis_id': ['ausweis_id', 'ausweisid', 'ausweis-id', 'karte', 'kartennummer', 'card_id', 'id'],
|
'ausweis_id': ['ausweis_id', 'ausweisid', 'ausweis-id', 'karte', 'kartennummer', 'card_id', 'id'],
|
||||||
'student_name': ['student_name', 'schuelername', 'schülername', 'schueler', 'schüler', 'name', 'vollname', 'vorname_nachname', 'nachname_vorname'],
|
'student_name': ['student_name', 'schuelername', 'schülername', 'schueler', 'schüler', 'name', 'vollname', 'vorname_nachname', 'nachname_vorname'],
|
||||||
'first_name': ['vorname', 'first_name', 'firstname'],
|
'first_name': ['vorname', 'first_name', 'firstname', 'rufname'],
|
||||||
'last_name': ['nachname', 'last_name', 'lastname'],
|
'last_name': ['nachname', 'last_name', 'lastname'],
|
||||||
'class_name': ['klasse', 'class', 'class_name', 'jahrgang', 'jahrgangsstufe', 'stufe', 'gruppe', 'asv_klasse'],
|
'class_name': ['klasse', 'class', 'class_name', 'jahrgang', 'jahrgangsstufe', 'stufe', 'gruppe', 'asv_klasse'],
|
||||||
'notes': ['notizen', 'notes', 'bemerkungen', 'bemerkung', 'hinweis', 'hinweise'],
|
'notes': ['notizen', 'notes', 'bemerkungen', 'bemerkung', 'hinweis', 'hinweise'],
|
||||||
@@ -4154,6 +4153,10 @@ def student_card_barcode_download():
|
|||||||
c.setFillColor(text_dark)
|
c.setFillColor(text_dark)
|
||||||
c.setFont("Helvetica-Bold", 9)
|
c.setFont("Helvetica-Bold", 9)
|
||||||
c.drawString(x_pos + 3*mm, y_pos - 28*mm, card['Klasse'])
|
c.drawString(x_pos + 3*mm, y_pos - 28*mm, card['Klasse'])
|
||||||
|
c.drawString(x_pos + 3*mm, y_pos - 31*mm, "-")
|
||||||
|
c.drawString(x_pos + 3*mm, y_pos - 34*mm, "-")
|
||||||
|
c.drawString(x_pos + 3*mm, y_pos - 37*mm, "-")
|
||||||
|
c.drawString(x_pos + 3*mm, y_pos - 40*mm, "-")
|
||||||
|
|
||||||
# Right barcode section with border highlight
|
# Right barcode section with border highlight
|
||||||
barcode_x_start = x_pos + info_width + 1*mm
|
barcode_x_start = x_pos + info_width + 1*mm
|
||||||
@@ -5112,6 +5115,7 @@ def upload_item():
|
|||||||
upload_mode = sanitize_form_value(request.form.get('upload_mode', 'item'))
|
upload_mode = sanitize_form_value(request.form.get('upload_mode', 'item'))
|
||||||
individual_codes_raw = sanitize_form_value(request.form.get('individual_codes', ''))
|
individual_codes_raw = sanitize_form_value(request.form.get('individual_codes', ''))
|
||||||
item_count_raw = sanitize_form_value(request.form.get('item_count', '1'))
|
item_count_raw = sanitize_form_value(request.form.get('item_count', '1'))
|
||||||
|
item_type_input = sanitize_form_value(request.form.get('item_type_input', ''))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item_count = int(item_count_raw) if item_count_raw else 1
|
item_count = int(item_count_raw) if item_count_raw else 1
|
||||||
@@ -5191,6 +5195,8 @@ def upload_item():
|
|||||||
item_isbn = isbn_raw
|
item_isbn = isbn_raw
|
||||||
if upload_mode == 'library':
|
if upload_mode == 'library':
|
||||||
item_type = 'book'
|
item_type = 'book'
|
||||||
|
if item_type_input != "":
|
||||||
|
item_type = item_type_input
|
||||||
|
|
||||||
if upload_mode == 'library':
|
if upload_mode == 'library':
|
||||||
if not cfg.MODULES.is_enabled('library'):
|
if not cfg.MODULES.is_enabled('library'):
|
||||||
@@ -10265,10 +10271,18 @@ def notifications_unread_status():
|
|||||||
@app.route('/admin/damaged_items')
|
@app.route('/admin/damaged_items')
|
||||||
def admin_damaged_items():
|
def admin_damaged_items():
|
||||||
"""Dedicated admin management window for damaged items."""
|
"""Dedicated admin management window for damaged items."""
|
||||||
if 'username' not in session or not us.check_admin(session['username']):
|
if 'username' not in session:
|
||||||
flash('Administratorrechte erforderlich.', 'error')
|
flash('Administratorrechte erforderlich.', 'error')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
'''
|
||||||
|
permissions = _get_current_user_permissions()
|
||||||
|
if not _action_access_allowed(permissions, 'can_manage_settings'):
|
||||||
|
flash('Sie haben keine Berechtigung, die Schulstammdaten zu ändern.', 'error')
|
||||||
|
if cfg.MODULES.is_enabled('library'):
|
||||||
|
return redirect(url_for('library_admin'))
|
||||||
|
'''
|
||||||
|
|
||||||
client = None
|
client = None
|
||||||
try:
|
try:
|
||||||
client = MongoClient(MONGODB_HOST, MONGODB_PORT)
|
client = MongoClient(MONGODB_HOST, MONGODB_PORT)
|
||||||
|
|||||||
@@ -284,7 +284,6 @@ def _match_inventory(path):
|
|||||||
if path == '/' or path.startswith('/home'): return True
|
if path == '/' or path.startswith('/home'): return True
|
||||||
return path.startswith(('/scanner', '/inventory', '/upload_admin', '/manage_filters', '/manage_locations', '/admin_borrowings', '/admin_damaged_items', '/admin/borrowings', '/admin/damaged_items'))
|
return path.startswith(('/scanner', '/inventory', '/upload_admin', '/manage_filters', '/manage_locations', '/admin_borrowings', '/admin_damaged_items', '/admin/borrowings', '/admin/damaged_items'))
|
||||||
|
|
||||||
|
|
||||||
def _match_terminplan(path):
|
def _match_terminplan(path):
|
||||||
if not path:
|
if not path:
|
||||||
return False
|
return False
|
||||||
@@ -292,7 +291,7 @@ def _match_terminplan(path):
|
|||||||
|
|
||||||
def _match_library(path):
|
def _match_library(path):
|
||||||
if not path: return False
|
if not path: return False
|
||||||
return path.startswith(('/library', '/library_', '/student_cards'))
|
return path.startswith(('/library', '/library_', '/student_cards', '/admin_damaged_items', '/admin_borrowings', '/admin/library'))
|
||||||
|
|
||||||
def _match_student_cards(path):
|
def _match_student_cards(path):
|
||||||
if not path: return False
|
if not path: return False
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ def check_password_strength(password):
|
|||||||
|
|
||||||
def hashing(password):
|
def hashing(password):
|
||||||
"""
|
"""
|
||||||
Hash a password using SHA-512.
|
Hash a password using scrypt.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
password (str): Password to hash
|
password (str): Password to hash
|
||||||
@@ -440,7 +440,8 @@ def hashing(password):
|
|||||||
Returns:
|
Returns:
|
||||||
str: Hexadecimal digest of the hashed password
|
str: Hexadecimal digest of the hashed password
|
||||||
"""
|
"""
|
||||||
return hashlib.sha512(password.encode()).hexdigest()
|
hashed = hashlib.scrypt(password.encode(), salt=b'some_salt', n=16384, r=8, p=1)
|
||||||
|
return hashed.hex()
|
||||||
|
|
||||||
|
|
||||||
def check_nm_pwd(username, password):
|
def check_nm_pwd(username, password):
|
||||||
|
|||||||
@@ -788,7 +788,19 @@
|
|||||||
{% if show_library_features %}
|
{% if show_library_features %}
|
||||||
<!-- Library Mode: Single Customizable Filter -->
|
<!-- Library Mode: Single Customizable Filter -->
|
||||||
<div class="filter-inputs">
|
<div class="filter-inputs">
|
||||||
<h3>Kategorie/Typ (customisierbar):</h3>
|
<h3>Medientyp</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<select name="item_type_input" id="item_type_input">
|
||||||
|
<option value="" disabled selected>Medientyp auswählen...</option>
|
||||||
|
<option value="Buch">Buch</option>
|
||||||
|
<option value="Schulbuch">Schulbuch</option>
|
||||||
|
<option value="CD">CD</option>
|
||||||
|
<option value="DVD">DVD</option>
|
||||||
|
<option value="Sonstiges">Sonstiges</option>
|
||||||
|
</select>
|
||||||
|
<small style="display:block; color:#666;">Wählen Sie einen Medientyp aus zur Klassifizierung.</small>
|
||||||
|
</div>
|
||||||
|
<h3>Kategorie/Typ:</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" name="library_category" id="library_category" placeholder="z.B. Belletristik, Sachbücher, Nachschlagewerke, etc.">
|
<input type="text" name="library_category" id="library_category" placeholder="z.B. Belletristik, Sachbücher, Nachschlagewerke, etc.">
|
||||||
<small style="display:block; color:#666;">Geben Sie hier eine beliebige Kategorie ein zur freien Klassifizierung.</small>
|
<small style="display:block; color:#666;">Geben Sie hier eine beliebige Kategorie ein zur freien Klassifizierung.</small>
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
start_period: 30s
|
||||||
environment:
|
environment:
|
||||||
MONGO_INITDB_DATABASE: inventar_default
|
MONGO_INITDB_DATABASE: inventar_default
|
||||||
|
|
||||||
|
|||||||
+133
-68
@@ -103,53 +103,6 @@ PY
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
register_tenant_port() {
|
|
||||||
local tenant_id="$1"
|
|
||||||
local port="$2"
|
|
||||||
|
|
||||||
if python3 - <<'PY' "$CONFIG_FILE" "$tenant_id" "$port"
|
|
||||||
import json, sys, os
|
|
||||||
path, tenant_id, port_str = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
print(f"Error: config file not found: {path}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
with open(path, 'r', encoding='utf-8') as f:
|
|
||||||
cfg = json.load(f)
|
|
||||||
tenants = cfg.get('tenants')
|
|
||||||
if tenants is None or not isinstance(tenants, dict):
|
|
||||||
tenants = {}
|
|
||||||
aliases = {tenant_id}
|
|
||||||
normalized = tenant_id.lower()
|
|
||||||
if normalized.startswith('schule'):
|
|
||||||
aliases.add('school' + normalized[len('schule'):])
|
|
||||||
elif normalized.startswith('school'):
|
|
||||||
aliases.add('schule' + normalized[len('school'):])
|
|
||||||
for tid, conf in tenants.items():
|
|
||||||
if isinstance(conf, dict) and str(conf.get('port')) == port_str and tid not in aliases:
|
|
||||||
print(f"Error: port {port_str} is already mapped to tenant {tid}", file=sys.stderr)
|
|
||||||
sys.exit(2)
|
|
||||||
existing = {}
|
|
||||||
for alias in aliases:
|
|
||||||
alias_cfg = tenants.get(alias)
|
|
||||||
if isinstance(alias_cfg, dict):
|
|
||||||
existing = alias_cfg
|
|
||||||
break
|
|
||||||
existing['port'] = int(port_str)
|
|
||||||
for alias in aliases:
|
|
||||||
tenants[alias] = dict(existing)
|
|
||||||
cfg['tenants'] = tenants
|
|
||||||
with open(path, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(cfg, f, indent=4, ensure_ascii=False)
|
|
||||||
print(f"Registered tenant port {port_str} for {tenant_id}")
|
|
||||||
PY
|
|
||||||
then
|
|
||||||
echo "Tenant $tenant_id port $port registered in config.json"
|
|
||||||
else
|
|
||||||
echo "Failed to register tenant port $port for $tenant_id"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
write_trial_tenant_config() {
|
write_trial_tenant_config() {
|
||||||
local tenant_id="$1"
|
local tenant_id="$1"
|
||||||
local port="$2"
|
local port="$2"
|
||||||
@@ -258,7 +211,7 @@ sanitized = "".join(c for c in tenant_id if c.isalnum() or c == "_")
|
|||||||
db_name = f"inventar_{sanitized}"
|
db_name = f"inventar_{sanitized}"
|
||||||
client = MongoClient(settings.MONGODB_HOST, int(settings.MONGODB_PORT))
|
client = MongoClient(settings.MONGODB_HOST, int(settings.MONGODB_PORT))
|
||||||
db = client[db_name]
|
db = client[db_name]
|
||||||
hashed_pw = hashlib.sha512("admin123".encode()).hexdigest()
|
hashed_pw = hashlib.scrypt('admin123'.encode(), salt=b'some_salt', n=16384, r=8, p=1).hex()
|
||||||
|
|
||||||
action_permissions = {
|
action_permissions = {
|
||||||
"can_borrow": True,
|
"can_borrow": True,
|
||||||
@@ -524,6 +477,8 @@ remove_runtime_port() {
|
|||||||
local port_list=()
|
local port_list=()
|
||||||
local new_ports=()
|
local new_ports=()
|
||||||
local first_port=""
|
local first_port=""
|
||||||
|
local ports_csv=""
|
||||||
|
|
||||||
|
|
||||||
if [ ! -f "$env_file" ]; then
|
if [ ! -f "$env_file" ]; then
|
||||||
return 0
|
return 0
|
||||||
@@ -551,7 +506,8 @@ remove_runtime_port() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
first_port="${new_ports[0]}"
|
first_port="${new_ports[0]}"
|
||||||
local ports_csv="$(IFS=,; echo "${new_ports[*]}")"
|
# Das 'local' hier entfernen, da wir es oben deklariert haben:
|
||||||
|
ports_csv="$(IFS=,; echo "${new_ports[*]}")"
|
||||||
|
|
||||||
if grep -q '^INVENTAR_HTTP_PORTS=' "$env_file" 2>/dev/null; then
|
if grep -q '^INVENTAR_HTTP_PORTS=' "$env_file" 2>/dev/null; then
|
||||||
sed -i "s|^INVENTAR_HTTP_PORTS=.*|INVENTAR_HTTP_PORTS=$ports_csv|" "$env_file"
|
sed -i "s|^INVENTAR_HTTP_PORTS=.*|INVENTAR_HTTP_PORTS=$ports_csv|" "$env_file"
|
||||||
@@ -566,6 +522,102 @@ remove_runtime_port() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_tenant_port() {
|
||||||
|
local tenant_id="$1"
|
||||||
|
local port="$2"
|
||||||
|
|
||||||
|
if python3 - <<'PY' "$CONFIG_FILE" "$tenant_id" "$port"
|
||||||
|
import json, sys, os
|
||||||
|
path, tenant_id, port_str = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
print(f"Error: config file not found: {path}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
cfg = json.load(f)
|
||||||
|
tenants = cfg.get('tenants')
|
||||||
|
if tenants is None or not isinstance(tenants, dict):
|
||||||
|
tenants = {}
|
||||||
|
aliases = {tenant_id}
|
||||||
|
normalized = tenant_id.lower()
|
||||||
|
if normalized.startswith('schule'):
|
||||||
|
aliases.add('school' + normalized[len('schule'):])
|
||||||
|
elif normalized.startswith('school'):
|
||||||
|
aliases.add('schule' + normalized[len('school'):])
|
||||||
|
for tid, conf in tenants.items():
|
||||||
|
if isinstance(conf, dict) and str(conf.get('port')) == port_str and tid not in aliases:
|
||||||
|
print(f"Error: port {port_str} is already mapped to tenant {tid}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
existing = {}
|
||||||
|
for alias in aliases:
|
||||||
|
alias_cfg = tenants.get(alias)
|
||||||
|
if isinstance(alias_cfg, dict):
|
||||||
|
existing = alias_cfg
|
||||||
|
break
|
||||||
|
existing['port'] = int(port_str)
|
||||||
|
for alias in aliases:
|
||||||
|
tenants[alias] = dict(existing)
|
||||||
|
cfg['tenants'] = tenants
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(cfg, f, indent=4, ensure_ascii=False)
|
||||||
|
print(f"Registered tenant port {port_str} for {tenant_id}")
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
echo "Tenant $tenant_id port $port registered in config.json"
|
||||||
|
else
|
||||||
|
echo "Failed to register tenant port $port for $tenant_id"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
local script_name
|
||||||
|
script_name="$(basename "$0")"
|
||||||
|
|
||||||
|
echo "=== MULTI-TENANT INVENTAR MANAGER ==="
|
||||||
|
echo ""
|
||||||
|
echo "NUTZUNG:"
|
||||||
|
echo " ./$script_name <befehl> [tenant_id] [optionen]"
|
||||||
|
echo ""
|
||||||
|
echo "VERFÜGBARE BEFEHLE:"
|
||||||
|
echo " add <tenant_id> [port]"
|
||||||
|
echo " Legt einen neuen Tenant an, registriert den Port und initialisiert"
|
||||||
|
echo " die MongoDB-Datenbank mit einem Standard-Admin (admin / admin123)."
|
||||||
|
echo ""
|
||||||
|
echo " trial <tenant_id> [port] [tage]"
|
||||||
|
echo " Erstellt einen temporären Test-Tenant (Standardlaufzeit: 7 Tage)."
|
||||||
|
echo " Dieser läuft nach der festgelegten Zeit ab und wird automatisch gelöscht."
|
||||||
|
echo ""
|
||||||
|
echo " remove [-y|--yes] <tenant_id>"
|
||||||
|
echo " Löscht einen Tenant komplett (Konfiguration, Ports und Datenbank)."
|
||||||
|
echo " Nutze -y oder --yes, um die Bestätigungsabfrage zu überspringen."
|
||||||
|
echo ""
|
||||||
|
echo " restart-tenant <tenant_id>"
|
||||||
|
echo " Startet einen spezifischen Tenant neu, indem alle aktiven Sessions"
|
||||||
|
echo " und der Cache in der MongoDB geleert werden (Sitzungs-Reset)."
|
||||||
|
echo ""
|
||||||
|
echo " restart-all"
|
||||||
|
echo " Führt einen Zero-Downtime Rolling-Restart für alle App-Container durch."
|
||||||
|
echo ""
|
||||||
|
echo " list"
|
||||||
|
echo " Listet alle registrierten Tenants (aus der config.json) sowie alle"
|
||||||
|
echo " aktiven Tenant-Datenbanken (aus der MongoDB) übersichtlich auf."
|
||||||
|
echo ""
|
||||||
|
echo " module <tenant_id> <modulname>=<on|off> [...]"
|
||||||
|
echo " Aktiviert oder deaktiviert bestimmte Features/Module für einen Tenant."
|
||||||
|
echo " Es können mehrere Module gleichzeitig konfiguriert werden."
|
||||||
|
echo ""
|
||||||
|
echo "GLOBALE OPTIONEN:"
|
||||||
|
echo " -h, --help"
|
||||||
|
echo " Zeigt dieses Hilfemenü an."
|
||||||
|
echo ""
|
||||||
|
echo "BEISPIELE:"
|
||||||
|
echo " ./$script_name add schule_muenchen 8081"
|
||||||
|
echo " ./$script_name trial test_user 8082 14"
|
||||||
|
echo " ./$script_name module schule_muenchen inventory=on mail=off"
|
||||||
|
echo " ./$script_name remove --yes test_user"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
if [ -z "${1:-}" ]; then
|
if [ -z "${1:-}" ]; then
|
||||||
show_help
|
show_help
|
||||||
fi
|
fi
|
||||||
@@ -609,7 +661,7 @@ sanitized = ''.join(c for c in tenant_id if c.isalnum() or c == '_')
|
|||||||
db_name = f'inventar_{sanitized}'
|
db_name = f'inventar_{sanitized}'
|
||||||
client = MongoClient(settings.MONGODB_HOST, int(settings.MONGODB_PORT))
|
client = MongoClient(settings.MONGODB_HOST, int(settings.MONGODB_PORT))
|
||||||
db = client[db_name]
|
db = client[db_name]
|
||||||
hashed_pw = hashlib.sha512('admin123'.encode()).hexdigest()
|
hashed_pw = hashlib.scrypt('admin123'.encode(), salt=b'some_salt', n=16384, r=8, p=1).hex()
|
||||||
if db.users.count_documents({'Username': 'admin'}) == 0:
|
if db.users.count_documents({'Username': 'admin'}) == 0:
|
||||||
db.users.insert_one({
|
db.users.insert_one({
|
||||||
'Username': 'admin',
|
'Username': 'admin',
|
||||||
@@ -690,7 +742,7 @@ print(f'Tenant {sys.argv[1]} database initialized. Default admin: admin / admin1
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
remove)
|
remove)
|
||||||
FORCE_REMOVE=false
|
FORCE_REMOVE=true
|
||||||
if [ "${2:-}" = "--yes" ] || [ "${2:-}" = "-y" ]; then
|
if [ "${2:-}" = "--yes" ] || [ "${2:-}" = "-y" ]; then
|
||||||
FORCE_REMOVE=true
|
FORCE_REMOVE=true
|
||||||
TENANT_ID="${3:-}"
|
TENANT_ID="${3:-}"
|
||||||
@@ -713,33 +765,45 @@ print(f'Tenant {sys.argv[1]} database initialized. Default admin: admin / admin1
|
|||||||
echo "Removing tenant '$TENANT_ID'..."
|
echo "Removing tenant '$TENANT_ID'..."
|
||||||
APP_CONTAINER=$(docker ps -qf "name=app" | head -n 1)
|
APP_CONTAINER=$(docker ps -qf "name=app" | head -n 1)
|
||||||
port_to_remove=""
|
port_to_remove=""
|
||||||
|
|
||||||
|
if port_to_remove="$(remove_tenant_port "$TENANT_ID" 2>/dev/null)"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "Warning: tenant '$TENANT_ID' was not found in config.json."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Dann die Datenbank via Container hart über PyMongo löschen
|
||||||
if [ -n "$APP_CONTAINER" ]; then
|
if [ -n "$APP_CONTAINER" ]; then
|
||||||
port_to_remove="$(docker exec "$APP_CONTAINER" python3 - "$TENANT_ID" <<'PY'
|
docker exec "$APP_CONTAINER" python3 - "$TENANT_ID" <<'PY'
|
||||||
import sys
|
import sys, re
|
||||||
sys.path.insert(0, '/app')
|
sys.path.insert(0, '/app')
|
||||||
sys.path.insert(0, '/app/Web')
|
sys.path.insert(0, '/app/Web')
|
||||||
from tenant import delete_tenant, get_tenant_config
|
from Web.modules.database import settings
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
tenant_id = sys.argv[1]
|
tenant_id = sys.argv[1].lower()
|
||||||
tenant_cfg = get_tenant_config(tenant_id)
|
# Den genauen Datenbanknamen rekonstruieren (wie beim 'add' Befehl)
|
||||||
port = tenant_cfg.get('port')
|
sanitized = "".join(c for c in tenant_id if c.isalnum() or c == "_")
|
||||||
|
db_name = f"inventar_{sanitized}"
|
||||||
|
|
||||||
if not delete_tenant(tenant_id):
|
try:
|
||||||
print(f'Error: failed to delete tenant {tenant_id}', file=sys.stderr)
|
# Direkte Verbindung zur Datenbank herstellen und komplett löschen
|
||||||
sys.exit(1)
|
client = MongoClient(settings.MONGODB_HOST, int(settings.MONGODB_PORT))
|
||||||
|
client.drop_database(db_name)
|
||||||
|
print(f"MongoDB database '{db_name}' dropped successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not drop database '{db_name}': {e}", file=sys.stderr)
|
||||||
|
|
||||||
if port is not None:
|
# Fallback: Die interne Funktion trotzdem aufrufen, falls sie noch Ordner/Dateien bereinigt
|
||||||
print(port)
|
try:
|
||||||
|
from tenant import delete_tenant
|
||||||
|
delete_tenant(sys.argv[1])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
PY
|
PY
|
||||||
)"
|
|
||||||
echo "Tenant '$TENANT_ID' database and config removed."
|
echo "Tenant '$TENANT_ID' database and config removed."
|
||||||
else
|
else
|
||||||
echo "Warning: Application container not running. Tenant database may still exist in MongoDB."
|
echo "Warning: Application container not running. Tenant database may still exist in MongoDB."
|
||||||
if port_to_remove="$(remove_tenant_port "$TENANT_ID" 2>/dev/null)"; then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
echo "Warning: tenant '$TENANT_ID' was not configured in config.json or could not be removed."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$port_to_remove" ]; then
|
if [ -n "$port_to_remove" ]; then
|
||||||
@@ -749,6 +813,7 @@ PY
|
|||||||
if [ -n "$(docker ps -qf 'name=app' | head -n 1)" ]; then
|
if [ -n "$(docker ps -qf 'name=app' | head -n 1)" ]; then
|
||||||
restart_app_container
|
restart_app_container
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$port_to_remove" ]; then
|
if [ -n "$port_to_remove" ]; then
|
||||||
echo "Removed tenant '$TENANT_ID' and cleaned runtime port $port_to_remove."
|
echo "Removed tenant '$TENANT_ID' and cleaned runtime port $port_to_remove."
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user