Compare commits
2 Commits
cce2866b52
...
c34c46cbc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c34c46cbc2 | ||
|
|
32a473fe29 |
3
frontend/src/app.d.ts
vendored
3
frontend/src/app.d.ts
vendored
@@ -33,7 +33,7 @@ interface BankAccount {
|
|||||||
interface Licence {
|
interface Licence {
|
||||||
id: number | -1;
|
id: number | -1;
|
||||||
status: number | -1;
|
status: number | -1;
|
||||||
licence_number: string | '';
|
number: string | '';
|
||||||
issued_date: string | '';
|
issued_date: string | '';
|
||||||
expiration_date: string | '';
|
expiration_date: string | '';
|
||||||
country: string | '';
|
country: string | '';
|
||||||
@@ -49,6 +49,7 @@ interface User {
|
|||||||
email: string | '';
|
email: string | '';
|
||||||
first_name: string | '';
|
first_name: string | '';
|
||||||
last_name: string | '';
|
last_name: string | '';
|
||||||
|
password: string | '';
|
||||||
phone: string | '';
|
phone: string | '';
|
||||||
notes: string | '';
|
notes: string | '';
|
||||||
address: string | '';
|
address: string | '';
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
email: '',
|
email: '',
|
||||||
first_name: '',
|
first_name: '',
|
||||||
last_name: '',
|
last_name: '',
|
||||||
|
password: '',
|
||||||
phone: '',
|
phone: '',
|
||||||
address: '',
|
address: '',
|
||||||
zip_code: '',
|
zip_code: '',
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
licence: {
|
licence: {
|
||||||
id: 0,
|
id: 0,
|
||||||
status: 1,
|
status: 1,
|
||||||
licence_number: '',
|
number: '',
|
||||||
issued_date: '',
|
issued_date: '',
|
||||||
expiration_date: '',
|
expiration_date: '',
|
||||||
country: '',
|
country: '',
|
||||||
@@ -83,14 +84,6 @@
|
|||||||
// $: isNewUser = user === null;
|
// $: isNewUser = user === null;
|
||||||
$: isLoading = user === undefined;
|
$: isLoading = user === undefined;
|
||||||
|
|
||||||
$: {
|
|
||||||
console.log('incomingUser:', user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add debug logging for user
|
|
||||||
$: {
|
|
||||||
console.log('processed user:', user);
|
|
||||||
}
|
|
||||||
/** @type {App.Locals['licence_categories']} */
|
/** @type {App.Locals['licence_categories']} */
|
||||||
export let licence_categories;
|
export let licence_categories;
|
||||||
|
|
||||||
@@ -345,7 +338,7 @@
|
|||||||
name="user[licence][number]"
|
name="user[licence][number]"
|
||||||
type="text"
|
type="text"
|
||||||
label={$t('licence_number')}
|
label={$t('licence_number')}
|
||||||
bind:value={localUser.licence.licence_number}
|
bind:value={localUser.licence.number}
|
||||||
placeholder={$t('placeholder.licence_number')}
|
placeholder={$t('placeholder.licence_number')}
|
||||||
toUpperCase={true}
|
toUpperCase={true}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,17 +3,20 @@ import { toRFC3339 } from './helpers';
|
|||||||
/**
|
/**
|
||||||
* Converts FormData to a nested object structure
|
* Converts FormData to a nested object structure
|
||||||
* @param {FormData} formData - The FormData object to convert
|
* @param {FormData} formData - The FormData object to convert
|
||||||
* @returns {{ user: Partial<App.Locals['user']> }} Nested object representation of the form data
|
* @returns {{ user: Partial<App.Locals['user']>,password2: string }} Nested object representation of the form data
|
||||||
*/
|
*/
|
||||||
export function formDataToObject(formData) {
|
export function formDataToObject(formData) {
|
||||||
/** @type { Partial<App.Locals['user']> } */
|
/** @type { Partial<App.Locals['user']> } */
|
||||||
const object = {};
|
const object = {};
|
||||||
|
let password2 = '';
|
||||||
|
|
||||||
console.log('Form data entries:');
|
console.log('Form data entries:');
|
||||||
for (const [key, value] of formData.entries()) {
|
for (const [key, value] of formData.entries()) {
|
||||||
console.log('Key:', key, 'Value:', value);
|
console.log('Key:', key, 'Value:', value);
|
||||||
|
if (key == 'password2') {
|
||||||
|
password2 = String(value);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
for (const [key, value] of formData.entries()) {
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const keys = key.match(/\[([^\]]+)\]/g)?.map((k) => k.slice(1, -1)) || [key];
|
const keys = key.match(/\[([^\]]+)\]/g)?.map((k) => k.slice(1, -1)) || [key];
|
||||||
console.log('Processed keys:', keys);
|
console.log('Processed keys:', keys);
|
||||||
@@ -51,17 +54,17 @@ export function formDataToObject(formData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { user: object };
|
return { user: object, password2: password2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the raw form data into the expected user data structure
|
* Processes the raw form data into the expected user data structure
|
||||||
* @param {{ user: Partial<App.Locals['user']> } } rawData - The raw form data object
|
* @param {{ user: Partial<App.Locals['user']>, password2: string} } rawData - The raw form data object
|
||||||
* @returns {{ user: Partial<App.Locals['user']> }} Processed user data
|
* @returns {{ user: Partial<App.Locals['user']> }} Processed user data
|
||||||
*/
|
*/
|
||||||
export function processFormData(rawData) {
|
export function processFormData(rawData) {
|
||||||
/** @type {{ user: Partial<App.Locals['user']> }} */
|
/** @type {{ user: Partial<App.Locals['user']> }} */
|
||||||
const processedData = {
|
let processedData = {
|
||||||
user: {
|
user: {
|
||||||
id: Number(rawData.user.id) || 0,
|
id: Number(rawData.user.id) || 0,
|
||||||
status: Number(rawData.user.status),
|
status: Number(rawData.user.status),
|
||||||
@@ -93,7 +96,7 @@ export function processFormData(rawData) {
|
|||||||
licence: {
|
licence: {
|
||||||
id: Number(rawData.user.licence?.id) || 0,
|
id: Number(rawData.user.licence?.id) || 0,
|
||||||
status: Number(rawData.user.licence?.status),
|
status: Number(rawData.user.licence?.status),
|
||||||
licence_number: String(rawData.user.licence?.licence_number || ''),
|
number: String(rawData.user.licence?.number || ''),
|
||||||
issued_date: toRFC3339(rawData.user.licence?.issued_date),
|
issued_date: toRFC3339(rawData.user.licence?.issued_date),
|
||||||
expiration_date: toRFC3339(rawData.user.licence?.expiration_date),
|
expiration_date: toRFC3339(rawData.user.licence?.expiration_date),
|
||||||
country: String(rawData.user.licence?.country || ''),
|
country: String(rawData.user.licence?.country || ''),
|
||||||
@@ -112,6 +115,15 @@ export function processFormData(rawData) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
rawData.user.password &&
|
||||||
|
rawData.password2 &&
|
||||||
|
rawData.user.password === rawData.password2 &&
|
||||||
|
rawData.user.password.trim() !== ''
|
||||||
|
) {
|
||||||
|
processedData.user.password = rawData.user.password;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove undefined or null properties
|
// Remove undefined or null properties
|
||||||
const cleanUpdateData = JSON.parse(JSON.stringify(processedData), (key, value) =>
|
const cleanUpdateData = JSON.parse(JSON.stringify(processedData), (key, value) =>
|
||||||
value !== null && value !== '' ? value : undefined
|
value !== null && value !== '' ? value : undefined
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const actions = {
|
|||||||
console.log('Is creating: ', isCreating);
|
console.log('Is creating: ', isCreating);
|
||||||
// console.dir(formData);
|
// console.dir(formData);
|
||||||
console.dir(processedData.user.membership);
|
console.dir(processedData.user.membership);
|
||||||
const apiURL = `${BASE_API_URI}/backend/users/update/`;
|
const apiURL = `${BASE_API_URI}/backend/users/upsert/`;
|
||||||
|
|
||||||
/** @type {RequestInit} */
|
/** @type {RequestInit} */
|
||||||
const requestUpdateOptions = {
|
const requestUpdateOptions = {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const actions = {
|
|||||||
console.dir(processedData.user.membership);
|
console.dir(processedData.user.membership);
|
||||||
const isCreating = !processedData.user.id || processedData.user.id === 0;
|
const isCreating = !processedData.user.id || processedData.user.id === 0;
|
||||||
console.log('Is creating: ', isCreating);
|
console.log('Is creating: ', isCreating);
|
||||||
const apiURL = `${BASE_API_URI}/backend/users/update`;
|
const apiURL = `${BASE_API_URI}/backend/users/upsert`;
|
||||||
|
|
||||||
/** @type {RequestInit} */
|
/** @type {RequestInit} */
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
@@ -59,6 +59,6 @@ export const actions = {
|
|||||||
console.log('Server success response:', response);
|
console.log('Server success response:', response);
|
||||||
locals.user = response;
|
locals.user = response;
|
||||||
userDatesFromRFC3339(locals.user);
|
userDatesFromRFC3339(locals.user);
|
||||||
throw redirect(303, `/auth/about/${response.id}`);
|
throw redirect(303, `/auth/admin/users`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -223,6 +223,7 @@
|
|||||||
{subscriptions}
|
{subscriptions}
|
||||||
{licence_categories}
|
{licence_categories}
|
||||||
on:cancel={close}
|
on:cancel={close}
|
||||||
|
on:close={close}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -95,6 +95,36 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
|
|||||||
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser.Safe()})
|
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser.Safe()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uc *UserController) DeleteUser(c *gin.Context) {
|
||||||
|
|
||||||
|
requestUser, err := uc.extractUserFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, "general", "server.validation.no_auth_tokenw")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type deleteData = struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
LastName string `json:"lastname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var deletedUser deleteData
|
||||||
|
if err := c.ShouldBindJSON(&deletedUser); err != nil {
|
||||||
|
utils.HandleValidationError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.HasPrivilige(requestUser, constants.Priviliges.Update) && deletedUser.ID != requestUser.ID {
|
||||||
|
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to delete user", http.StatusForbidden, "user", "server.error.unauthorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := uc.Service.DeleteUser(deletedUser.LastName, deletedUser.ID); err != nil {
|
||||||
|
utils.RespondWithError(c, err, "Error during user deletion", http.StatusInternalServerError, "user", "server.error.internal_server_error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (uc *UserController) extractUserFromContext(c *gin.Context) (*models.User, error) {
|
func (uc *UserController) extractUserFromContext(c *gin.Context) (*models.User, error) {
|
||||||
|
|
||||||
tokenString, err := c.Cookie("jwt")
|
tokenString, err := c.Cookie("jwt")
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ type BankAccount struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
MandateDateSigned time.Time `gorm:"not null" json:"mandate_date_signed"`
|
MandateDateSigned time.Time `gorm:"not null" json:"mandate_date_signed"`
|
||||||
Bank string `json:"bank_name" binding:"omitempty,alphanumunicode,safe_content"`
|
Bank string `json:"bank_name" binding:"safe_content"`
|
||||||
AccountHolderName string `json:"account_holder_name" binding:"omitempty,alphaunicode,safe_content"`
|
AccountHolderName string `json:"account_holder_name" binding:"safe_content"`
|
||||||
IBAN string `json:"iban"`
|
IBAN string `json:"iban"`
|
||||||
BIC string `json:"bic"`
|
BIC string `json:"bic"`
|
||||||
MandateReference string `gorm:"not null" json:"mandate_reference"`
|
MandateReference string `gorm:"not null" json:"mandate_reference"`
|
||||||
|
|||||||
@@ -21,10 +21,15 @@ type UserRepositoryInterface interface {
|
|||||||
SetVerificationToken(verification *models.Verification) (uint, error)
|
SetVerificationToken(verification *models.Verification) (uint, error)
|
||||||
IsVerified(userID *uint) (bool, error)
|
IsVerified(userID *uint) (bool, error)
|
||||||
GetVerificationOfToken(token *string) (*models.Verification, error)
|
GetVerificationOfToken(token *string) (*models.Verification, error)
|
||||||
|
DeleteUser(id uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRepository struct{}
|
type UserRepository struct{}
|
||||||
|
|
||||||
|
func (ur *UserRepository) DeleteUser(id uint) error {
|
||||||
|
return database.DB.Delete(&models.User{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
|
||||||
func PasswordExists(userID *uint) (bool, error) {
|
func PasswordExists(userID *uint) (bool, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
result := database.DB.Select("password").First(&user, userID)
|
result := database.DB.Select("password").First(&user, userID)
|
||||||
@@ -60,7 +65,7 @@ func (ur *UserRepository) UpdateUser(user *models.User) (*models.User, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Update the user's main fields
|
// Update the user's main fields
|
||||||
result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Updates(user)
|
result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Omit("Password").Updates(user)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return result.Error
|
return result.Error
|
||||||
}
|
}
|
||||||
@@ -68,6 +73,14 @@ func (ur *UserRepository) UpdateUser(user *models.User) (*models.User, error) {
|
|||||||
return errors.ErrNoRowsAffected
|
return errors.ErrNoRowsAffected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.Password != "" {
|
||||||
|
if err := tx.Model(&models.User{}).
|
||||||
|
Where("id = ?", user.ID).
|
||||||
|
Update("Password", user.Password).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the Membership if provided
|
// Update the Membership if provided
|
||||||
if user.Membership.ID != 0 {
|
if user.Membership.ID != 0 {
|
||||||
if err := tx.Model(&existingUser.Membership).Updates(user.Membership).Error; err != nil {
|
if err := tx.Model(&existingUser.Membership).Updates(user.Membership).Error; err != nil {
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ func RegisterRoutes(router *gin.Engine, userController *controllers.UserControll
|
|||||||
{
|
{
|
||||||
userRouter.GET("/current", userController.CurrentUserHandler)
|
userRouter.GET("/current", userController.CurrentUserHandler)
|
||||||
userRouter.POST("/logout", userController.LogoutHandler)
|
userRouter.POST("/logout", userController.LogoutHandler)
|
||||||
userRouter.PATCH("/update", userController.UpdateHandler)
|
userRouter.PATCH("/upsert", userController.UpdateHandler)
|
||||||
userRouter.POST("/update", userController.RegisterUser)
|
userRouter.POST("/upsert", userController.RegisterUser)
|
||||||
userRouter.GET("/all", userController.GetAllUsers)
|
userRouter.GET("/all", userController.GetAllUsers)
|
||||||
|
userRouter.DELETE("/delete", userController.DeleteUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
membershipRouter := router.Group("/backend/membership")
|
membershipRouter := router.Group("/backend/membership")
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type UserServiceInterface interface {
|
|||||||
GetUsers(where map[string]interface{}) (*[]models.User, error)
|
GetUsers(where map[string]interface{}) (*[]models.User, error)
|
||||||
VerifyUser(token *string) (*models.User, error)
|
VerifyUser(token *string) (*models.User, error)
|
||||||
UpdateUser(user *models.User) (*models.User, error)
|
UpdateUser(user *models.User) (*models.User, error)
|
||||||
|
DeleteUser(lastname string, id uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserService struct {
|
type UserService struct {
|
||||||
@@ -31,6 +32,22 @@ type UserService struct {
|
|||||||
Licences repositories.LicenceInterface
|
Licences repositories.LicenceInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *UserService) DeleteUser(lastname string, id uint) error {
|
||||||
|
if id == 0 || lastname == "" {
|
||||||
|
return errors.ErrNoData
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := service.GetUserByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
return errors.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.Repo.DeleteUser(id)
|
||||||
|
}
|
||||||
|
|
||||||
func (service *UserService) UpdateUser(user *models.User) (*models.User, error) {
|
func (service *UserService) UpdateUser(user *models.User) (*models.User, error) {
|
||||||
|
|
||||||
if user.ID == 0 {
|
if user.ID == 0 {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
@@ -10,7 +9,8 @@ import (
|
|||||||
|
|
||||||
func validateDriverslicence(sl validator.StructLevel) {
|
func validateDriverslicence(sl validator.StructLevel) {
|
||||||
dl := sl.Current().Interface().(models.User).Licence
|
dl := sl.Current().Interface().(models.User).Licence
|
||||||
if !validateLicence(dl.Number) {
|
// if !vValidateLicence(dl.Number) {
|
||||||
|
if dl.Number == "" {
|
||||||
sl.ReportError(dl.Number, "licence_number", "", "invalid", "")
|
sl.ReportError(dl.Number, "licence_number", "", "invalid", "")
|
||||||
}
|
}
|
||||||
if dl.IssuedDate.After(time.Now()) {
|
if dl.IssuedDate.After(time.Now()) {
|
||||||
@@ -21,32 +21,33 @@ func validateDriverslicence(sl validator.StructLevel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateLicence(fieldValue string) bool {
|
// seems like not every country has to have an licence id and it seems that germany changed their id generation type..
|
||||||
if len(fieldValue) != 11 {
|
// func validateLicence(fieldValue string) bool {
|
||||||
return false
|
// if len(fieldValue) != 11 {
|
||||||
}
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
id, tenthChar := string(fieldValue[:9]), string(fieldValue[9])
|
// id, tenthChar := string(fieldValue[:9]), string(fieldValue[9])
|
||||||
|
|
||||||
if tenthChar == "X" {
|
// if tenthChar == "X" {
|
||||||
tenthChar = "10"
|
// tenthChar = "10"
|
||||||
}
|
// }
|
||||||
tenthValue, _ := strconv.ParseInt(tenthChar, 10, 8)
|
// tenthValue, _ := strconv.ParseInt(tenthChar, 10, 8)
|
||||||
|
|
||||||
// for readability
|
// // for readability
|
||||||
weights := []int{9, 8, 7, 6, 5, 4, 3, 2, 1}
|
// weights := []int{9, 8, 7, 6, 5, 4, 3, 2, 1}
|
||||||
sum := 0
|
// sum := 0
|
||||||
|
|
||||||
for i := 0; i < 9; i++ {
|
// for i := 0; i < 9; i++ {
|
||||||
char := string(id[i])
|
// char := string(id[i])
|
||||||
value, _ := strconv.ParseInt(char, 36, 64)
|
// value, _ := strconv.ParseInt(char, 36, 64)
|
||||||
sum += int(value) * weights[i]
|
// sum += int(value) * weights[i]
|
||||||
}
|
// }
|
||||||
|
|
||||||
calcCheckDigit := sum % 11
|
// calcCheckDigit := sum % 11
|
||||||
if calcCheckDigit != int(tenthValue) {
|
// if calcCheckDigit != int(tenthValue) {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true
|
// return true
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ func validateUser(sl validator.StructLevel) {
|
|||||||
sl.ReportError(user.DateOfBirth, "DateOfBirth", "dateofbirth", "age", "")
|
sl.ReportError(user.DateOfBirth, "DateOfBirth", "dateofbirth", "age", "")
|
||||||
}
|
}
|
||||||
// validate subscriptionModel
|
// validate subscriptionModel
|
||||||
logger.Error.Printf("User SubscriptionModel.Name: %#v", user.Membership.SubscriptionModel.Name)
|
|
||||||
if user.Membership.SubscriptionModel.Name == "" {
|
if user.Membership.SubscriptionModel.Name == "" {
|
||||||
sl.ReportError(user.Membership.SubscriptionModel.Name, "SubscriptionModel.Name", "name", "required", "")
|
sl.ReportError(user.Membership.SubscriptionModel.Name, "SubscriptionModel.Name", "name", "required", "")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user