Merge pull request #198 from the-luap/fix/github-issues-194-197

fix: resolve issues #194, #195, #196, #197
This commit is contained in:
Paul Nothaft
2026-02-22 22:37:11 +01:00
committed by GitHub
6 changed files with 48 additions and 31 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ nano .env
# Start with Docker Compose
docker compose up -d
# Access at http://localhost:3005
# Access at http://localhost:3000
```
Note on Docker file permissions (PUID/PGID)
+2 -2
View File
@@ -14,8 +14,8 @@ exports.up = async function(knex) {
// Create default admin user if none exists
const adminExists = await knex('admin_users').first();
if (!adminExists) {
// Generate a secure random password
const generatedPassword = generateReadablePassword();
// Use ADMIN_PASSWORD from environment if set, otherwise generate a random one
const generatedPassword = process.env.ADMIN_PASSWORD || generateReadablePassword();
const passwordHash = await bcrypt.hash(generatedPassword, 12); // Increased rounds for better security
// Get admin credentials from environment or use defaults
+29 -18
View File
@@ -44,28 +44,39 @@ async function createAdmin() {
.orWhere('username', username)
.first();
if (existingUser) {
console.error(`Error: User with email "${email}" or username "${username}" already exists`);
process.exit(1);
}
// Hash password
const passwordHash = await bcrypt.hash(password, 10);
// Create admin user
await db('admin_users').insert({
username,
email,
password_hash: passwordHash,
is_active: true,
created_at: new Date(),
updated_at: new Date()
});
if (existingUser) {
// Update existing user's password
await db('admin_users')
.where('id', existingUser.id)
.update({
password_hash: passwordHash,
updated_at: new Date()
});
console.log(`✅ Admin user created successfully!`);
console.log(` Email: ${email}`);
console.log(` Username: ${username}`);
console.log(` Login URL: ${process.env.ADMIN_URL || 'http://localhost:3000'}/admin/login`);
console.log(`✅ Admin user updated successfully!`);
console.log(` Email: ${existingUser.email}`);
console.log(` Username: ${existingUser.username}`);
console.log(` Password has been reset to the provided value`);
console.log(` Login URL: ${process.env.ADMIN_URL || 'http://localhost:3000'}/admin/login`);
} else {
// Create new admin user
await db('admin_users').insert({
username,
email,
password_hash: passwordHash,
is_active: true,
created_at: new Date(),
updated_at: new Date()
});
console.log(`✅ Admin user created successfully!`);
console.log(` Email: ${email}`);
console.log(` Username: ${username}`);
console.log(` Login URL: ${process.env.ADMIN_URL || 'http://localhost:3000'}/admin/login`);
}
process.exit(0);
} catch (error) {
@@ -75,7 +75,20 @@ export const PhotoExportMenu: React.FC<PhotoExportMenuProps> = ({
if (selectedPhotoIds.length > 0) {
options.photo_ids = selectedPhotoIds;
} else if (filters) {
options.filter = filters;
// Convert camelCase filter keys to snake_case for backend
options.filter = {
min_rating: filters.minRating,
max_rating: filters.maxRating,
has_likes: filters.hasLikes,
min_likes: filters.minLikes,
has_favorites: filters.hasFavorites,
min_favorites: filters.minFavorites,
has_comments: filters.hasComments,
category_id: filters.categoryId,
logic: filters.logic,
sort: filters.sort,
order: filters.order,
};
}
exportMutation.mutate(options);
@@ -256,11 +256,7 @@ export function useSettingsState() {
mutationFn: async () => {
const settingsData: Record<string, unknown> = {};
Object.entries(generalSettings).forEach(([key, value]) => {
if (key === 'date_format' && typeof value === 'object' && value.format) {
settingsData[`general_${key}`] = value.format;
} else {
settingsData[`general_${key}`] = value;
}
settingsData[`general_${key}`] = value;
});
return settingsService.updateSettings(settingsData);
},
+1 -4
View File
@@ -200,7 +200,7 @@ export const AdminLoginPage: React.FC = () => {
</div>
</div>
{/* Remember Me & Forgot Password */}
{/* Remember Me */}
<div className="flex items-center justify-between">
<label className="flex items-center">
<input
@@ -209,9 +209,6 @@ export const AdminLoginPage: React.FC = () => {
/>
<span className="ml-2 text-sm text-neutral-700">{t('adminLogin.rememberMe')}</span>
</label>
<a href="#" className="text-sm text-primary-600 hover:text-primary-700">
{t('adminLogin.forgotPassword')}
</a>
</div>
{/* reCAPTCHA */}