Fixed issue with users not being updated

Re-worked setup.sh to use last 3 tags and the main branch (development latest)
This commit is contained in:
Muhammad Ibrahim
2025-10-05 19:12:51 +01:00
parent a3005bccb4
commit 581dc5884c
4 changed files with 172 additions and 58 deletions

View File

@@ -176,6 +176,8 @@ router.get(
id: true, id: true,
username: true, username: true,
email: true, email: true,
first_name: true,
last_name: true,
role: true, role: true,
is_active: true, is_active: true,
last_login: true, last_login: true,
@@ -314,6 +316,14 @@ router.put(
.isLength({ min: 3 }) .isLength({ min: 3 })
.withMessage("Username must be at least 3 characters"), .withMessage("Username must be at least 3 characters"),
body("email").optional().isEmail().withMessage("Valid email is required"), body("email").optional().isEmail().withMessage("Valid email is required"),
body("first_name")
.optional()
.isLength({ min: 1 })
.withMessage("First name must be at least 1 character"),
body("last_name")
.optional()
.isLength({ min: 1 })
.withMessage("Last name must be at least 1 character"),
body("role") body("role")
.optional() .optional()
.custom(async (value) => { .custom(async (value) => {
@@ -326,10 +336,10 @@ router.put(
} }
return true; return true;
}), }),
body("isActive") body("is_active")
.optional() .optional()
.isBoolean() .isBoolean()
.withMessage("isActive must be a boolean"), .withMessage("is_active must be a boolean"),
], ],
async (req, res) => { async (req, res) => {
try { try {
@@ -340,13 +350,16 @@ router.put(
return res.status(400).json({ errors: errors.array() }); return res.status(400).json({ errors: errors.array() });
} }
const { username, email, role, isActive } = req.body; const { username, email, first_name, last_name, role, is_active } =
req.body;
const updateData = {}; const updateData = {};
if (username) updateData.username = username; if (username) updateData.username = username;
if (email) updateData.email = email; if (email) updateData.email = email;
if (first_name !== undefined) updateData.first_name = first_name || null;
if (last_name !== undefined) updateData.last_name = last_name || null;
if (role) updateData.role = role; if (role) updateData.role = role;
if (typeof isActive === "boolean") updateData.is_active = isActive; if (typeof is_active === "boolean") updateData.is_active = is_active;
// Check if user exists // Check if user exists
const existingUser = await prisma.users.findUnique({ const existingUser = await prisma.users.findUnique({
@@ -381,7 +394,7 @@ router.put(
} }
// Prevent deactivating the last admin // Prevent deactivating the last admin
if (isActive === false && existingUser.role === "admin") { if (is_active === false && existingUser.role === "admin") {
const adminCount = await prisma.users.count({ const adminCount = await prisma.users.count({
where: { where: {
role: "admin", role: "admin",
@@ -404,6 +417,8 @@ router.put(
id: true, id: true,
username: true, username: true,
email: true, email: true,
first_name: true,
last_name: true,
role: true, role: true,
is_active: true, is_active: true,
last_login: true, last_login: true,

View File

@@ -92,7 +92,12 @@ const UsersTab = () => {
}; };
const handleEditUser = (user) => { const handleEditUser = (user) => {
// Reset editingUser first to force re-render with fresh data
setEditingUser(null);
// Use setTimeout to ensure the modal re-initializes with fresh data
setTimeout(() => {
setEditingUser(user); setEditingUser(user);
}, 0);
}; };
const handleResetPassword = (user) => { const handleResetPassword = (user) => {
@@ -314,7 +319,9 @@ const UsersTab = () => {
user={editingUser} user={editingUser}
isOpen={!!editingUser} isOpen={!!editingUser}
onClose={() => setEditingUser(null)} onClose={() => setEditingUser(null)}
onUserUpdated={() => updateUserMutation.mutate()} onUserUpdated={() => {
queryClient.invalidateQueries(["users"]);
}}
roles={roles} roles={roles}
/> />
)} )}
@@ -352,11 +359,29 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
}); });
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
// Reset form when modal is closed
useEffect(() => {
if (!isOpen) {
setFormData({
username: "",
email: "",
password: "",
first_name: "",
last_name: "",
role: "user",
});
setError("");
setSuccess(false);
}
}, [isOpen]);
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError(""); setError("");
setSuccess(false);
try { try {
// Only send role if roles are available from API // Only send role if roles are available from API
@@ -364,12 +389,19 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
username: formData.username, username: formData.username,
email: formData.email, email: formData.email,
password: formData.password, password: formData.password,
first_name: formData.first_name,
last_name: formData.last_name,
}; };
if (roles && Array.isArray(roles) && roles.length > 0) { if (roles && Array.isArray(roles) && roles.length > 0) {
payload.role = formData.role; payload.role = formData.role;
} }
await adminUsersAPI.create(payload); await adminUsersAPI.create(payload);
setSuccess(true);
onUserCreated(); onUserCreated();
// Auto-close after 1.5 seconds
setTimeout(() => {
onClose();
}, 1500);
} catch (err) { } catch (err) {
setError(err.response?.data?.error || "Failed to create user"); setError(err.response?.data?.error || "Failed to create user");
} finally { } finally {
@@ -517,6 +549,17 @@ const AddUserModal = ({ isOpen, onClose, onUserCreated, roles }) => {
</select> </select>
</div> </div>
{success && (
<div className="bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-md p-3">
<div className="flex items-center">
<CheckCircle className="h-4 w-4 text-green-600 dark:text-green-400 mr-2" />
<p className="text-sm text-green-700 dark:text-green-300">
User created successfully!
</p>
</div>
</div>
)}
{error && ( {error && (
<div className="bg-danger-50 dark:bg-danger-900 border border-danger-200 dark:border-danger-700 rounded-md p-3"> <div className="bg-danger-50 dark:bg-danger-900 border border-danger-200 dark:border-danger-700 rounded-md p-3">
<p className="text-sm text-danger-700 dark:text-danger-300"> <p className="text-sm text-danger-700 dark:text-danger-300">
@@ -566,15 +609,44 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
}); });
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
// Update formData when user prop changes or modal opens
useEffect(() => {
if (user && isOpen) {
setFormData({
username: user.username || "",
email: user.email || "",
first_name: user.first_name || "",
last_name: user.last_name || "",
role: user.role || "user",
is_active: user.is_active ?? true,
});
}
}, [user, isOpen]);
// Reset error and success when modal closes
useEffect(() => {
if (!isOpen) {
setError("");
setSuccess(false);
}
}, [isOpen]);
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError(""); setError("");
setSuccess(false);
try { try {
await adminUsersAPI.update(user.id, formData); await adminUsersAPI.update(user.id, formData);
setSuccess(true);
onUserUpdated(); onUserUpdated();
// Auto-close after 1.5 seconds
setTimeout(() => {
onClose();
}, 1500);
} catch (err) { } catch (err) {
setError(err.response?.data?.error || "Failed to update user"); setError(err.response?.data?.error || "Failed to update user");
} finally { } finally {
@@ -718,6 +790,17 @@ const EditUserModal = ({ user, isOpen, onClose, onUserUpdated, roles }) => {
</label> </label>
</div> </div>
{success && (
<div className="bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-md p-3">
<div className="flex items-center">
<CheckCircle className="h-4 w-4 text-green-600 dark:text-green-400 mr-2" />
<p className="text-sm text-green-700 dark:text-green-300">
User updated successfully!
</p>
</div>
</div>
)}
{error && ( {error && (
<div className="bg-danger-50 dark:bg-danger-900 border border-danger-200 dark:border-danger-700 rounded-md p-3"> <div className="bg-danger-50 dark:bg-danger-900 border border-danger-200 dark:border-danger-700 rounded-md p-3">
<p className="text-sm text-danger-700 dark:text-danger-300"> <p className="text-sm text-danger-700 dark:text-danger-300">

View File

@@ -18,7 +18,7 @@ import {
User, User,
} from "lucide-react"; } from "lucide-react";
import { useId, useState } from "react"; import { useEffect, useId, useState } from "react";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { useTheme } from "../contexts/ThemeContext"; import { useTheme } from "../contexts/ThemeContext";
@@ -45,6 +45,18 @@ const Profile = () => {
last_name: user?.last_name || "", last_name: user?.last_name || "",
}); });
// Update profileData when user data changes
useEffect(() => {
if (user) {
setProfileData({
username: user.username || "",
email: user.email || "",
first_name: user.first_name || "",
last_name: user.last_name || "",
});
}
}, [user]);
const [passwordData, setPasswordData] = useState({ const [passwordData, setPasswordData] = useState({
currentPassword: "", currentPassword: "",
newPassword: "", newPassword: "",

100
setup.sh
View File

@@ -254,7 +254,7 @@ check_prerequisites() {
} }
select_branch() { select_branch() {
print_info "Fetching available branches from GitHub repository..." print_info "Fetching available releases from GitHub repository..."
# Create temporary directory for git operations # Create temporary directory for git operations
TEMP_DIR="/tmp/patchmon_branches_$$" TEMP_DIR="/tmp/patchmon_branches_$$"
@@ -263,84 +263,88 @@ select_branch() {
# Try to clone the repository normally # Try to clone the repository normally
if git clone "$DEFAULT_GITHUB_REPO" . 2>/dev/null; then if git clone "$DEFAULT_GITHUB_REPO" . 2>/dev/null; then
# Get list of remote branches and trim whitespace # Get list of tags sorted by version (semantic versioning)
branches=$(git branch -r | grep -v HEAD | sed 's/origin\///' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//' | sort -u) # Using git tag with version sorting
tags=$(git tag -l --sort=-v:refname 2>/dev/null | head -3)
if [ -n "$branches" ]; then if [ -n "$tags" ]; then
print_info "Available branches with details:" print_info "Available releases and branches:"
echo "" echo ""
# Get branch information # Display last 3 release tags
branch_count=1 option_count=1
while IFS= read -r branch; do declare -A options_map
if [ -n "$branch" ]; then
# Get last commit date for this branch
last_commit=$(git log -1 --format="%ci" "origin/$branch" 2>/dev/null || echo "Unknown")
# Get release tag associated with this branch (if any) while IFS= read -r tag; do
release_tag=$(git describe --tags --exact-match "origin/$branch" 2>/dev/null || echo "") if [ -n "$tag" ]; then
# Get tag date and commit info
tag_date=$(git log -1 --format="%ci" "$tag" 2>/dev/null || echo "Unknown")
# Format the date # Format the date
if [ "$last_commit" != "Unknown" ]; then if [ "$tag_date" != "Unknown" ]; then
formatted_date=$(date -d "$last_commit" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$last_commit") formatted_date=$(date -d "$tag_date" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$tag_date")
else else
formatted_date="Unknown" formatted_date="Unknown"
fi fi
# Display branch info # Mark the first one as latest
printf "%2d. %-20s" "$branch_count" "$branch" if [ $option_count -eq 1 ]; then
printf " (Last commit: %s)" "$formatted_date" printf "%2d. %-20s (Latest Release - %s)\n" "$option_count" "$tag" "$formatted_date"
else
if [ -n "$release_tag" ]; then printf "%2d. %-20s (Release - %s)\n" "$option_count" "$tag" "$formatted_date"
printf " [Release: %s]" "$release_tag"
fi fi
echo "" # Store the tag for later selection
branch_count=$((branch_count + 1)) options_map[$option_count]="$tag"
option_count=$((option_count + 1))
fi fi
done <<< "$branches" done <<< "$tags"
# Add main branch as an option
main_commit=$(git log -1 --format="%ci" "origin/main" 2>/dev/null || echo "Unknown")
if [ "$main_commit" != "Unknown" ]; then
formatted_main_date=$(date -d "$main_commit" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$main_commit")
else
formatted_main_date="Unknown"
fi
printf "%2d. %-20s (Development Branch - %s)\n" "$option_count" "main" "$formatted_main_date"
options_map[$option_count]="main"
echo "" echo ""
# Determine default selection: prefer 'main' if present # Default to option 1 (latest release tag)
main_index=$(echo "$branches" | nl -w1 -s':' | awk -F':' '$2=="main"{print $1}' | head -1) default_option=1
if [ -z "$main_index" ]; then
main_index=1
fi
while true; do while true; do
read_input "Select branch number" BRANCH_NUMBER "$main_index" read_input "Select version/branch number" SELECTION_NUMBER "$default_option"
if [[ "$BRANCH_NUMBER" =~ ^[0-9]+$ ]]; then if [[ "$SELECTION_NUMBER" =~ ^[0-9]+$ ]]; then
selected_branch=$(echo "$branches" | sed -n "${BRANCH_NUMBER}p" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//') selected_option="${options_map[$SELECTION_NUMBER]}"
if [ -n "$selected_branch" ]; then if [ -n "$selected_option" ]; then
DEPLOYMENT_BRANCH="$selected_branch" DEPLOYMENT_BRANCH="$selected_option"
# Show additional info for selected branch # Show confirmation
last_commit=$(git log -1 --format="%ci" "origin/$selected_branch" 2>/dev/null || echo "Unknown") if [ "$selected_option" = "main" ]; then
release_tag=$(git describe --tags --exact-match "origin/$selected_branch" 2>/dev/null || echo "") print_status "Selected branch: main (latest development code)"
print_info "Last commit: $formatted_main_date"
if [ "$last_commit" != "Unknown" ]; then
formatted_date=$(date -d "$last_commit" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$last_commit")
else else
formatted_date="Unknown" print_status "Selected release: $selected_option"
tag_date=$(git log -1 --format="%ci" "$selected_option" 2>/dev/null || echo "Unknown")
if [ "$tag_date" != "Unknown" ]; then
formatted_date=$(date -d "$tag_date" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$tag_date")
print_info "Release date: $formatted_date"
fi fi
print_status "Selected branch: $DEPLOYMENT_BRANCH"
print_info "Last commit: $formatted_date"
if [ -n "$release_tag" ]; then
print_info "Release tag: $release_tag"
fi fi
break break
else else
print_error "Invalid branch number. Please try again." print_error "Invalid selection number. Please try again."
fi fi
else else
print_error "Please enter a valid number." print_error "Please enter a valid number."
fi fi
done done
else else
print_warning "No branches found, using default: main" print_warning "No release tags found, using default: main"
DEPLOYMENT_BRANCH="main" DEPLOYMENT_BRANCH="main"
fi fi
else else