197 lines
8.0 KiB
Python
197 lines
8.0 KiB
Python
"""
|
|
USAGE:
|
|
|
|
from app_settings_loader import ensure_settings_ini, get_setting
|
|
|
|
# Ensure settings.ini exists and is up to date
|
|
ensure_settings_ini()
|
|
|
|
# Get settings as needed
|
|
host = get_setting('FLASK', 'FLASK_HOST', fallback='0.0.0.0')
|
|
port = get_setting('FLASK', 'FLASK_PORT', fallback=5000, type_=int)
|
|
debug = get_setting('FLASK', 'DEBUG', fallback=False, type_=bool)
|
|
notes_dir = get_setting('FLASK', 'NOTES_DIR', fallback='notes')
|
|
|
|
# You can also use the preloaded variables if you want:
|
|
# FLASK_HOST, FLASK_PORT, SECRET_KEY, DEBUG, NOTES_DIR, MAX_CONTENT_LENGTH
|
|
"""
|
|
|
|
import configparser
|
|
from pathlib import Path
|
|
|
|
DEFAULT_CONFIG = {
|
|
'FLASK': {
|
|
'FLASK_HOST': '0.0.0.0',
|
|
'FLASK_PORT': '5000',
|
|
'SECRET_KEY': 'change-this-secret',
|
|
'DEBUG': 'False',
|
|
'Specify location where your notes .md files are at - the root folder': None,
|
|
'NOTES_DIR': 'notes',
|
|
'Maximum content length in MB': None,
|
|
'MAX_CONTENT_LENGTH': '16',
|
|
},
|
|
}
|
|
|
|
CONFIG_FILE = 'settings.ini'
|
|
|
|
def ensure_settings_ini():
|
|
"""
|
|
Ensure that settings.ini exists and contains all required settings.
|
|
- If the file does not exist, it is created with default values and comments.
|
|
- If the file exists but is missing any required setting, only the missing setting(s)
|
|
are added with default values (existing values and comments are not changed).
|
|
"""
|
|
# First check if file exists at all
|
|
if not Path(CONFIG_FILE).exists():
|
|
# Create new file with all default settings and comments
|
|
with open(CONFIG_FILE, 'w') as f:
|
|
for section, values in DEFAULT_CONFIG.items():
|
|
f.write(f'[{section}]\n')
|
|
|
|
# Get all items in order
|
|
items = list(values.items())
|
|
|
|
# Process each item in order
|
|
for i, (key, value) in enumerate(items):
|
|
if value is not None: # This is a setting
|
|
# Find the comment that belongs right before this setting
|
|
prev_comment = None
|
|
# Look backwards from current position to find the nearest comment
|
|
for prev_key, prev_val in reversed(items[:i]):
|
|
if prev_val is None: # It's a comment
|
|
# Check no other setting exists between comment and current setting
|
|
has_setting_between = any(
|
|
k for k, v in items[:i]
|
|
if v is not None and
|
|
prev_key.upper() < k.upper() < key.upper()
|
|
)
|
|
if not has_setting_between:
|
|
prev_comment = prev_key
|
|
break
|
|
|
|
# Write the comment if found
|
|
if prev_comment:
|
|
f.write(f'; {prev_comment}\n')
|
|
# Write the setting
|
|
f.write(f'{key} = {value}\n')
|
|
f.write('\n')
|
|
return
|
|
|
|
# If file exists, read it line by line to preserve comments and case
|
|
with open(CONFIG_FILE, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
# Parse existing settings while preserving case
|
|
config = configparser.ConfigParser()
|
|
config.read(CONFIG_FILE)
|
|
|
|
# Track what settings we've seen and what we need to add
|
|
current_section = None
|
|
settings_seen = {section: set() for section in DEFAULT_CONFIG}
|
|
missing_settings = {}
|
|
changed = False
|
|
|
|
# First pass: identify missing settings while preserving case
|
|
for section in DEFAULT_CONFIG:
|
|
missing_settings[section] = {}
|
|
if section not in config:
|
|
# New section needed
|
|
changed = True
|
|
continue
|
|
|
|
for key, value in DEFAULT_CONFIG[section].items():
|
|
if value is not None: # Skip comments
|
|
key_lower = key.lower()
|
|
# Check if setting exists (case-insensitive)
|
|
if not any(k.lower() == key_lower for k in config[section]):
|
|
missing_settings[section][key] = value
|
|
changed = True
|
|
else:
|
|
# Remember the actual case of existing keys
|
|
for existing_key in config[section]:
|
|
if existing_key.lower() == key_lower:
|
|
settings_seen[section].add(existing_key)
|
|
|
|
if not changed:
|
|
return
|
|
|
|
# Second pass: write the updated file
|
|
new_lines = []
|
|
current_section = None
|
|
|
|
for line in lines:
|
|
line = line.rstrip('\n')
|
|
|
|
# Handle section headers
|
|
if line.strip().startswith('[') and line.strip().endswith(']'):
|
|
if current_section and current_section in missing_settings and missing_settings[current_section]:
|
|
# Remove trailing empty lines before adding missing settings
|
|
while new_lines and not new_lines[-1].strip():
|
|
new_lines.pop()
|
|
# Add any missing settings before moving to next section
|
|
for key, value in missing_settings[current_section].items():
|
|
new_lines.append(f'{key} = {value}')
|
|
new_lines.append('') # Single newline after section
|
|
|
|
current_section = line.strip('[]').strip()
|
|
new_lines.append(line)
|
|
|
|
# If this is a new section, add all its settings
|
|
if current_section in DEFAULT_CONFIG and current_section not in config:
|
|
for key, value in DEFAULT_CONFIG[current_section].items():
|
|
if value is not None: # Only add settings, not comments
|
|
new_lines.append(f'{key} = {value}')
|
|
new_lines.append('') # Single newline after section
|
|
continue
|
|
|
|
# Skip empty lines at the end of sections
|
|
if line.strip() or not current_section:
|
|
new_lines.append(line)
|
|
|
|
# Handle missing settings in the last section
|
|
if current_section and current_section in missing_settings and missing_settings[current_section]:
|
|
# Remove trailing empty lines before adding missing settings
|
|
while new_lines and not new_lines[-1].strip():
|
|
new_lines.pop()
|
|
# Add missing settings
|
|
for key, value in missing_settings[current_section].items():
|
|
new_lines.append(f'{key} = {value}')
|
|
new_lines.append('') # Single newline after section
|
|
|
|
# Write the updated file
|
|
with open(CONFIG_FILE, 'w') as f:
|
|
f.write('\n'.join(new_lines) + '\n')
|
|
|
|
def get_setting(section, key, fallback=None, type_=str):
|
|
"""
|
|
Retrieve a setting from settings.ini.
|
|
Args:
|
|
section (str): The section in the ini file (e.g. 'FLASK').
|
|
key (str): The key to retrieve.
|
|
fallback: The value to return if the key is not found.
|
|
type_ (type): Optionally convert the value to int or bool. Default is str.
|
|
Returns:
|
|
The value from settings.ini, converted to the requested type if specified.
|
|
"""
|
|
config = configparser.ConfigParser()
|
|
config.read(CONFIG_FILE)
|
|
if config.has_option(section, key):
|
|
value = config.get(section, key)
|
|
if type_ == int:
|
|
try:
|
|
return int(value)
|
|
except Exception:
|
|
return fallback if fallback is not None else 0
|
|
if type_ == bool:
|
|
return value.lower() in ('true', '1', 'yes', 'on')
|
|
return value
|
|
return fallback
|
|
|
|
# Load settings from the configuration file settings.ini
|
|
FLASK_HOST = get_setting('FLASK', 'FLASK_HOST', fallback='0.0.0.0')
|
|
FLASK_PORT = get_setting('FLASK', 'FLASK_PORT', fallback=5000, type_=int)
|
|
SECRET_KEY = get_setting('FLASK', 'SECRET_KEY', fallback='change-this')
|
|
DEBUG = get_setting('FLASK', 'DEBUG', fallback='INFO')
|
|
NOTES_DIR = Path(get_setting('FLASK', 'NOTES_DIR', fallback='notes')).resolve()
|
|
MAX_CONTENT_LENGTH = get_setting('FLASK', 'MAX_CONTENT_LENGTH', fallback=16, type_=int)
|