Add NextSnap PWA with photo gallery viewer and continuous capture
Offline-first photo capture app for Nextcloud with: - Camera capture with continuous mode (auto-reopens after each photo) - File browser with fullscreen image gallery, swipe navigation, and rename - Upload queue with background sync engine - Admin panel for Nextcloud user management - Service worker for offline-first caching (v13) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
72
app/routes/auth.py
Normal file
72
app/routes/auth.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from flask import Blueprint, request, jsonify, session
|
||||
from app.services.nextcloud import NextcloudClient
|
||||
from config import Config
|
||||
import base64
|
||||
|
||||
bp = Blueprint('auth', __name__, url_prefix='/api/auth')
|
||||
|
||||
def _encrypt_password(password: str) -> str:
|
||||
"""Simple base64 encoding for password storage in session."""
|
||||
return base64.b64encode(password.encode()).decode()
|
||||
|
||||
def _decrypt_password(encrypted: str) -> str:
|
||||
"""Decode base64 encoded password from session."""
|
||||
return base64.b64decode(encrypted.encode()).decode()
|
||||
|
||||
@bp.route('/login', methods=['POST'])
|
||||
def login():
|
||||
"""Authenticate user against Nextcloud."""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or 'username' not in data or 'password' not in data:
|
||||
return jsonify({'error': 'Username and password required'}), 400
|
||||
|
||||
username = data['username'].strip()
|
||||
password = data['password']
|
||||
|
||||
if not username or not password:
|
||||
return jsonify({'error': 'Username and password cannot be empty'}), 400
|
||||
|
||||
try:
|
||||
# Validate credentials by attempting to connect to Nextcloud
|
||||
nc_client = NextcloudClient(Config.NEXTCLOUD_URL, username, password)
|
||||
|
||||
if not nc_client.verify_credentials():
|
||||
return jsonify({'error': 'Invalid username or password'}), 401
|
||||
|
||||
# Check if user is admin
|
||||
is_admin = nc_client.check_admin()
|
||||
|
||||
# Store credentials in session
|
||||
session['username'] = username
|
||||
session['password'] = _encrypt_password(password)
|
||||
session['is_admin'] = is_admin
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'username': username,
|
||||
'is_admin': is_admin
|
||||
}), 200
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'Authentication failed: {str(e)}'}), 500
|
||||
|
||||
@bp.route('/logout', methods=['POST'])
|
||||
def logout():
|
||||
"""Clear user session."""
|
||||
session.clear()
|
||||
return jsonify({'success': True}), 200
|
||||
|
||||
@bp.route('/status', methods=['GET'])
|
||||
def status():
|
||||
"""Check current authentication status."""
|
||||
if 'username' in session:
|
||||
return jsonify({
|
||||
'authenticated': True,
|
||||
'username': session['username'],
|
||||
'is_admin': session.get('is_admin', False)
|
||||
}), 200
|
||||
else:
|
||||
return jsonify({
|
||||
'authenticated': False
|
||||
}), 200
|
||||
Reference in New Issue
Block a user