mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
better passwrod setting
This commit is contained in:
+42
-1
@@ -169,10 +169,22 @@ class SettingsUpdate(BaseModel):
|
||||
__root__: dict[str, object]
|
||||
|
||||
|
||||
class PasswordUpdate(BaseModel):
|
||||
current_password: str
|
||||
new_password: str
|
||||
|
||||
|
||||
@admin_router.patch("/api/settings", dependencies=[Depends(require_admin_api)])
|
||||
async def update_settings(request: Request, update: SettingsUpdate) -> dict:
|
||||
# Remove sensitive fields from general settings update
|
||||
settings_data = update.__root__.copy()
|
||||
sensitive_fields = ["admin_password", "upstream_api_key", "nsec"]
|
||||
for field in sensitive_fields:
|
||||
if field in settings_data:
|
||||
del settings_data[field]
|
||||
|
||||
async with create_session() as session:
|
||||
new_settings = await SettingsService.update(update.__root__, session)
|
||||
new_settings = await SettingsService.update(settings_data, session)
|
||||
data = new_settings.dict()
|
||||
if "upstream_api_key" in data:
|
||||
data["upstream_api_key"] = "[REDACTED]" if data["upstream_api_key"] else ""
|
||||
@@ -183,6 +195,35 @@ async def update_settings(request: Request, update: SettingsUpdate) -> dict:
|
||||
return data
|
||||
|
||||
|
||||
@admin_router.patch("/api/password", dependencies=[Depends(require_admin_api)])
|
||||
async def update_password(request: Request, password_update: PasswordUpdate) -> dict:
|
||||
# Verify current password
|
||||
try:
|
||||
current_settings = SettingsService.get()
|
||||
current_password = current_settings.admin_password
|
||||
except Exception:
|
||||
current_password = os.getenv("ADMIN_PASSWORD", "")
|
||||
|
||||
if not current_password:
|
||||
raise HTTPException(status_code=500, detail="Admin password not configured")
|
||||
|
||||
if password_update.current_password != current_password:
|
||||
raise HTTPException(status_code=401, detail="Current password is incorrect")
|
||||
|
||||
# Validate new password
|
||||
new_password = password_update.new_password.strip()
|
||||
if len(new_password) < 6:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="New password must be at least 6 characters"
|
||||
)
|
||||
|
||||
# Update password
|
||||
async with create_session() as session:
|
||||
await SettingsService.update({"admin_password": new_password}, session)
|
||||
|
||||
return {"ok": True, "message": "Password updated successfully"}
|
||||
|
||||
|
||||
class SetupRequest(BaseModel):
|
||||
password: str
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
@@ -24,7 +24,6 @@ interface SettingsData {
|
||||
description?: string;
|
||||
npub?: string;
|
||||
nsec?: string;
|
||||
admin_password?: string;
|
||||
upstream_api_key?: string;
|
||||
http_url?: string;
|
||||
onion_url?: string;
|
||||
@@ -32,6 +31,12 @@ interface SettingsData {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface PasswordData {
|
||||
current_password: string;
|
||||
new_password: string;
|
||||
confirm_password: string;
|
||||
}
|
||||
|
||||
export function AdminSettings() {
|
||||
const [settings, setSettings] = useState<SettingsData>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -39,6 +44,13 @@ export function AdminSettings() {
|
||||
const [error, setError] = useState<string>('');
|
||||
const [showSecrets, setShowSecrets] = useState(false);
|
||||
const [newMint, setNewMint] = useState('');
|
||||
const [passwordData, setPasswordData] = useState<PasswordData>({
|
||||
current_password: '',
|
||||
new_password: '',
|
||||
confirm_password: '',
|
||||
});
|
||||
const [passwordError, setPasswordError] = useState<string>('');
|
||||
const [passwordSaving, setPasswordSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
@@ -78,6 +90,45 @@ export function AdminSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordUpdate = async () => {
|
||||
try {
|
||||
setPasswordSaving(true);
|
||||
setPasswordError('');
|
||||
|
||||
if (passwordData.new_password !== passwordData.confirm_password) {
|
||||
setPasswordError('New passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordData.new_password.length < 6) {
|
||||
setPasswordError('New password must be at least 6 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiClient } = await import('@/lib/api/client');
|
||||
|
||||
await apiClient.patch('/admin/api/password', {
|
||||
current_password: passwordData.current_password,
|
||||
new_password: passwordData.new_password,
|
||||
});
|
||||
|
||||
setPasswordData({
|
||||
current_password: '',
|
||||
new_password: '',
|
||||
confirm_password: '',
|
||||
});
|
||||
|
||||
toast.success('Password updated successfully');
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : 'Failed to update password';
|
||||
setPasswordError(message);
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setPasswordSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (field: string, value: string | boolean) => {
|
||||
setSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -246,11 +297,6 @@ export function AdminSettings() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
{renderSecretField(
|
||||
'admin_password',
|
||||
'Admin Password',
|
||||
'Enter admin password'
|
||||
)}
|
||||
{renderSecretField(
|
||||
'upstream_api_key',
|
||||
'Upstream API Key',
|
||||
@@ -258,7 +304,6 @@ export function AdminSettings() {
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Cashu Mints */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -307,24 +352,104 @@ export function AdminSettings() {
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className='mt-6'>
|
||||
<CardFooter className='flex justify-between'>
|
||||
<Button
|
||||
variant='outline'
|
||||
onClick={loadSettings}
|
||||
disabled={loading || saving}
|
||||
>
|
||||
<RefreshCw className='mr-2 h-4 w-4' />
|
||||
Reload
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={loading || saving}>
|
||||
<Save className='mr-2 h-4 w-4' />
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card className='mt-6'>
|
||||
<CardFooter className='flex justify-between'>
|
||||
<Button
|
||||
variant='outline'
|
||||
onClick={loadSettings}
|
||||
disabled={loading || saving}
|
||||
>
|
||||
<RefreshCw className='mr-2 h-4 w-4' />
|
||||
Reload
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={loading || saving}>
|
||||
<Save className='mr-2 h-4 w-4' />
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
{/* Password Change */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Change Admin Password</CardTitle>
|
||||
<CardDescription>
|
||||
Update your admin password for enhanced security
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
{passwordError && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertDescription>{passwordError}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='current_password'>Current Password</Label>
|
||||
<Input
|
||||
id='current_password'
|
||||
type='password'
|
||||
value={passwordData.current_password}
|
||||
onChange={(e) =>
|
||||
setPasswordData((prev) => ({
|
||||
...prev,
|
||||
current_password: e.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder='Enter current password'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='new_password'>New Password</Label>
|
||||
<Input
|
||||
id='new_password'
|
||||
type='password'
|
||||
value={passwordData.new_password}
|
||||
onChange={(e) =>
|
||||
setPasswordData((prev) => ({
|
||||
...prev,
|
||||
new_password: e.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder='Enter new password (min 6 characters)'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='confirm_password'>Confirm New Password</Label>
|
||||
<Input
|
||||
id='confirm_password'
|
||||
type='password'
|
||||
value={passwordData.confirm_password}
|
||||
onChange={(e) =>
|
||||
setPasswordData((prev) => ({
|
||||
...prev,
|
||||
confirm_password: e.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder='Confirm new password'
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button
|
||||
onClick={handlePasswordUpdate}
|
||||
disabled={
|
||||
passwordSaving ||
|
||||
!passwordData.current_password ||
|
||||
!passwordData.new_password ||
|
||||
!passwordData.confirm_password
|
||||
}
|
||||
>
|
||||
<Save className='mr-2 h-4 w-4' />
|
||||
{passwordSaving ? 'Updating...' : 'Update Password'}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user