transactions
This commit is contained in:
BIN
back/.program_data/app.db-shm
Normal file
BIN
back/.program_data/app.db-shm
Normal file
Binary file not shown.
BIN
back/.program_data/app.db-wal
Normal file
BIN
back/.program_data/app.db-wal
Normal file
Binary file not shown.
1
back/.program_data/imgs/systemkey.lock
Normal file
1
back/.program_data/imgs/systemkey.lock
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"Email":"@system","Key":"caeae1bc-3761-4b30-8627-d86af99b0a4f","Password":"M8I^7b,UF!)PIQ.A"}
|
8
back/DataModels/EfmigrationsLock.cs
Normal file
8
back/DataModels/EfmigrationsLock.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
public partial class EfmigrationsLock
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Timestamp { get; set; } = null!;
|
||||||
|
}
|
44
back/DataModels/Event.cs
Normal file
44
back/DataModels/Event.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Transactional.Abstractions;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Events")]
|
||||||
|
public partial class Event : ISoftDeletable, IEquatable<Event>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(50)]
|
||||||
|
public string Title { get; set; } = null!;
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Date { get; set; }
|
||||||
|
public string? Location { get; set; }
|
||||||
|
public string CreatedAt { get; set; } = null!;
|
||||||
|
public string UpdatedAt { get; set; } = null!;
|
||||||
|
public string? CreatedBy { get; set; }
|
||||||
|
public string? UpdatedBy { get; set; }
|
||||||
|
public int IsDeleted { get; set; }
|
||||||
|
public string? DeletedAt { get; set; }
|
||||||
|
public virtual ICollection<Gallery> Galleries { get; set; } = [];
|
||||||
|
public virtual ICollection<Photo> Photos { get; set; } = [];
|
||||||
|
public virtual ICollection<Tag> Tags { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
=> HashCode.Combine(Id, Title, Date, Location);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
=> obj is Event otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(Event? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id
|
||||||
|
|| (Title == other.Title && Date == other.Date && Location == other.Location)
|
||||||
|
|| GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,55 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Events")]
|
|
||||||
public class EventModel
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[Required, MaxLength(50)]
|
|
||||||
public string? Title { get; set; }
|
|
||||||
[MaxLength(500)]
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public DateTime? Date { get; set; }
|
|
||||||
public string? Location { get; set; }
|
|
||||||
[ForeignKey("TagId")]
|
|
||||||
public List<TagModel> RelatedTags { get; set; } = [];
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime UpdatedAt { get; set; }
|
|
||||||
public string? CreatedBy { get; set; }
|
|
||||||
public string? UpdatedBy { get; set; }
|
|
||||||
|
|
||||||
public EventModel() { }
|
|
||||||
|
|
||||||
public EventModel(string id)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EventModel(
|
|
||||||
string id,
|
|
||||||
string title,
|
|
||||||
string description,
|
|
||||||
DateTime date,
|
|
||||||
string location,
|
|
||||||
List<TagModel>? relatedTags = null,
|
|
||||||
DateTime? createdAt = null,
|
|
||||||
DateTime? updatedAt = null,
|
|
||||||
string? createdBy = null,
|
|
||||||
string? updatedBy = null)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Title = title;
|
|
||||||
Description = description;
|
|
||||||
Date = date;
|
|
||||||
Location = location;
|
|
||||||
RelatedTags = relatedTags ?? [];
|
|
||||||
CreatedAt = createdAt ?? DateTime.UtcNow;
|
|
||||||
UpdatedAt = updatedAt ?? DateTime.UtcNow;
|
|
||||||
CreatedBy = createdBy ?? "";
|
|
||||||
UpdatedBy = updatedBy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
44
back/DataModels/Gallery.cs
Normal file
44
back/DataModels/Gallery.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Transactional.Abstractions;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Galleries")]
|
||||||
|
public partial class Gallery: IEquatable<Gallery>, ISoftDeletable
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? Title { get; set; }
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? CreatedAt { get; set; }
|
||||||
|
public string? UpdatedAt { get; set; }
|
||||||
|
public string CreatedBy { get; set; } = null!;
|
||||||
|
public int? IsPublic { get; set; }
|
||||||
|
public int? IsArchived { get; set; }
|
||||||
|
public int? IsFavorite { get; set; }
|
||||||
|
public int IsDeleted { get; set; }
|
||||||
|
public string? DeletedAt { get; set; }
|
||||||
|
public string? EventId { get; set; }
|
||||||
|
public virtual User CreatedByNavigation { get; set; } = null!;
|
||||||
|
public virtual Event? Event { get; set; }
|
||||||
|
public virtual ICollection<Photo> Photos { get; set; } = [];
|
||||||
|
public virtual ICollection<Tag> Tags { get; set; } = [];
|
||||||
|
public virtual ICollection<User> Users { get; set; } = [];
|
||||||
|
|
||||||
|
public Gallery() { }
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Title);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is Gallery otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(Gallery? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,30 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Galleries")]
|
|
||||||
public class GalleryModel
|
|
||||||
{
|
|
||||||
public GalleryModel() { }
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[MaxLength(100)]
|
|
||||||
public string? Title { get; set; }
|
|
||||||
[MaxLength(500)]
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public DateTime? CreatedAt { get; set; }
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
public string? CreatedBy { get; set; }
|
|
||||||
public string? UpdatedBy { get; set; }
|
|
||||||
[ForeignKey("PhotoId")]
|
|
||||||
public List<PhotoModel> Photos { get; set; } = [];
|
|
||||||
public bool? IsPublic { get; set; } = true;
|
|
||||||
public bool? IsArchived { get; set; } = false;
|
|
||||||
public bool? IsFavorite { get; set; } = false;
|
|
||||||
public EventModel? Event { get; set; } = null;
|
|
||||||
public List<TagModel>? Tags { get; set; } = null;
|
|
||||||
public List<PersonModel>? PersonsInvolved { get; set; } = null;
|
|
||||||
public List<PersonModel>? UsersWhoCanSee { get; set; } = null;
|
|
||||||
}
|
|
40
back/DataModels/Permission.cs
Normal file
40
back/DataModels/Permission.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Permissions")]
|
||||||
|
public partial class Permission: IEquatable<Permission>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(100)]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public virtual ICollection<Role> Roles { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
=> obj is Permission other && Equals(other);
|
||||||
|
public bool Equals(Permission? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static permissions
|
||||||
|
public static readonly Permission ViewContentPermission = new() { Id = "1", Name = "VIEW_CONTENT", Description = "Permission to view content" };
|
||||||
|
public static readonly Permission LikeContentPermission = new() { Id = "2", Name = "LIKE_CONTENT", Description = "Permission to like content" };
|
||||||
|
public static readonly Permission EditContentPermission = new() { Id = "3", Name = "EDIT_CONTENT", Description = "Permission to edit content" };
|
||||||
|
public static readonly Permission DeleteContentPermission = new() { Id = "4", Name = "DELETE_CONTENT", Description = "Permission to delete content" };
|
||||||
|
public static readonly Permission CreateContentPermission = new() { Id = "5", Name = "CREATE_CONTENT", Description = "Permission to create new content" };
|
||||||
|
public static readonly Permission EditUserPermission = new() { Id = "6", Name = "EDIT_USER", Description = "Permission to edit user" };
|
||||||
|
public static readonly Permission DeleteUserPermission = new() { Id = "7", Name = "DELETE_USER", Description = "Permission to delete user" };
|
||||||
|
public static readonly Permission DisableUserPermission = new() { Id = "8", Name = "DISABLE_USER", Description = "Permission to disable user" };
|
||||||
|
public static readonly Permission CreateUserPermission = new() { Id = "9", Name = "CREATE_USER", Description = "Permission to create new user" };
|
||||||
|
public static readonly Permission EditWebConfigPermission = new() { Id = "10", Name = "EDIT_WEB_CONFIG", Description = "Permission to edit web configuration" };
|
||||||
|
}
|
@@ -1,36 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Permissions")]
|
|
||||||
public class PermissionModel
|
|
||||||
{
|
|
||||||
public PermissionModel() { }
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[Required, MaxLength(100)]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[MaxLength(255)]
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
public PermissionModel(string id, string name, string description)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
Description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static permissions
|
|
||||||
public static readonly PermissionModel ViewContentPermission = new("1", "VIEW_CONTENT", "Permission to view content");
|
|
||||||
public static readonly PermissionModel LikeContentPermission = new("2", "LIKE_CONTENT", "Permission to like content");
|
|
||||||
public static readonly PermissionModel EditContentPermission = new("3", "EDIT_CONTENT", "Permission to edit content");
|
|
||||||
public static readonly PermissionModel DeleteContentPermission = new("4", "DELETE_CONTENT", "Permission to delete content");
|
|
||||||
public static readonly PermissionModel CreateContentPermission = new("5", "CREATE_CONTENT", "Permission to create new content");
|
|
||||||
public static readonly PermissionModel EditUserPermission = new("6", "EDIT_USER", "Permission to edit user");
|
|
||||||
public static readonly PermissionModel DeleteUserPermission = new("7", "DELETE_USER", "Permission to delete user");
|
|
||||||
public static readonly PermissionModel DisableUserPermission = new("8", "DISABLE_USER", "Permission to disable user");
|
|
||||||
public static readonly PermissionModel CreateUserPermission = new("9", "CREATE_USER", "Permission to create new user");
|
|
||||||
public static readonly PermissionModel EditWebConfigPermission = new("10", "EDIT_WEB_CONFIG", "Permission to edit web configuration");
|
|
||||||
}
|
|
51
back/DataModels/Person.cs
Normal file
51
back/DataModels/Person.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Transactional.Abstractions;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Persons")]
|
||||||
|
public partial class Person: IEquatable<Person>, ISoftDeletable
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(100)]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
public string? ProfilePicture { get; set; }
|
||||||
|
public string? Avatar { get; set; }
|
||||||
|
public string? SocialMediaId { get; set; }
|
||||||
|
[MaxLength(250)]
|
||||||
|
public string? Bio { get; set; } // Optional field for a short biography or description
|
||||||
|
public string CreatedAt { get; set; } = null!;
|
||||||
|
public string? UpdatedAt { get; set; }
|
||||||
|
public int IsDeleted { get; set; }
|
||||||
|
public string? DeletedAt { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<Photo> Photos { get; set; } = [];
|
||||||
|
public virtual SocialMedia? SocialMedia { get; set; }
|
||||||
|
public virtual User? User { get; set; }
|
||||||
|
public virtual ICollection<Photo> PhotosNavigation { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
=> obj is Person other && Equals(other);
|
||||||
|
|
||||||
|
public bool Equals(Person? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public const string SystemPersonId = "00000000-0000-0000-0000-000000000001";
|
||||||
|
|
||||||
|
public static readonly Person SystemPerson = new()
|
||||||
|
{
|
||||||
|
Id = SystemPersonId,
|
||||||
|
Name = "System",
|
||||||
|
CreatedAt = DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss zz"),
|
||||||
|
User = User.SystemUser
|
||||||
|
};
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Persons")]
|
|
||||||
public class PersonModel
|
|
||||||
{
|
|
||||||
public PersonModel() { }
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[Required, MaxLength(100)]
|
|
||||||
public string? Name { get; set; }
|
|
||||||
public string? ProfilePicture { get; set; }
|
|
||||||
public string? Avatar { get; set; }
|
|
||||||
[ForeignKey("SocialMediaId")]
|
|
||||||
public SocialMedia? SocialMedia { get; set; }
|
|
||||||
[MaxLength(250)]
|
|
||||||
public string? Bio { get; set; } // Optional field for a short biography or description
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
public DateTime UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
public PersonModel(string id)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PersonModel(string id, string name, string? profilePicture = null, string? avatar = null, SocialMedia? socialMedia = null)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
ProfilePicture = profilePicture;
|
|
||||||
Avatar = avatar;
|
|
||||||
SocialMedia = socialMedia;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Table("SocialMedia")]
|
|
||||||
public class SocialMedia
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
|
||||||
public string? Facebook { get; set; }
|
|
||||||
public string? Instagram { get; set; }
|
|
||||||
public string? Twitter { get; set; }
|
|
||||||
public string? BlueSky { get; set; }
|
|
||||||
public string? Tiktok { get; set; }
|
|
||||||
public string? Linkedin { get; set; }
|
|
||||||
public string? Pinterest { get; set; }
|
|
||||||
public string? Discord { get; set; }
|
|
||||||
public string? Reddit { get; set; }
|
|
||||||
public string? Other { get; set; }
|
|
||||||
}
|
|
47
back/DataModels/Photo.cs
Normal file
47
back/DataModels/Photo.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Photos")]
|
||||||
|
public partial class Photo : IEquatable<Photo>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(100), MinLength(1)]
|
||||||
|
public string Title { get; set; } = null!;
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Extension { get; set; }
|
||||||
|
public string? LowResUrl { get; set; }
|
||||||
|
public string? MidResUrl { get; set; }
|
||||||
|
public string? HighResUrl { get; set; }
|
||||||
|
public string? CreatedAt { get; set; }
|
||||||
|
public string? UpdatedAt { get; set; }
|
||||||
|
public string CreatedBy { get; set; } = null!;
|
||||||
|
public string? UpdatedBy { get; set; }
|
||||||
|
public string? EventId { get; set; }
|
||||||
|
public string? RankingId { get; set; }
|
||||||
|
public int? IsFavorite { get; set; }
|
||||||
|
public int? IsPublic { get; set; }
|
||||||
|
public int? IsArchived { get; set; }
|
||||||
|
public virtual Person CreatedByNavigation { get; set; } = null!;
|
||||||
|
public virtual Event? Event { get; set; }
|
||||||
|
public virtual ICollection<Gallery> Galleries { get; set; } = [];
|
||||||
|
public virtual ICollection<Person> People { get; set; } = [];
|
||||||
|
public virtual ICollection<Tag> Tags { get; set; } = [];
|
||||||
|
public virtual ICollection<User> Users { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Title);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
=> obj is Photo otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(Photo? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,74 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Photos")]
|
|
||||||
public class PhotoModel
|
|
||||||
{
|
|
||||||
public PhotoModel() { }
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[Required, MaxLength(100), MinLength(1)]
|
|
||||||
public string Title { get; set; }
|
|
||||||
[MaxLength(500)]
|
|
||||||
public string Description { get; set; }
|
|
||||||
public string Extension { get; set; }
|
|
||||||
public string LowResUrl { get; set; }
|
|
||||||
public string MidResUrl { get; set; }
|
|
||||||
public string HighResUrl { get; set; }
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime UpdatedAt { get; set; }
|
|
||||||
public string CreatedBy { get; set; }
|
|
||||||
public string UpdatedBy { get; set; }
|
|
||||||
[ForeignKey("EventId")]
|
|
||||||
public EventModel? Event { get; set; } = null;
|
|
||||||
[ForeignKey("TagId")]
|
|
||||||
public List<TagModel> Tags { get; set; } = [];
|
|
||||||
[ForeignKey("RankingId")]
|
|
||||||
public RankingModel Ranking { get; set; } = new RankingModel(0);
|
|
||||||
public bool IsFavorite { get; set; } = false;
|
|
||||||
public bool IsPublic { get; set; } = true;
|
|
||||||
public bool IsArchived { get; set; } = false;
|
|
||||||
[ForeignKey("PersonId")]
|
|
||||||
public List<PersonModel>? Persons { get; set; }
|
|
||||||
|
|
||||||
public PhotoModel(
|
|
||||||
string id,
|
|
||||||
string title,
|
|
||||||
string description,
|
|
||||||
string lowResUrl,
|
|
||||||
string midResUrl,
|
|
||||||
string highResUrl,
|
|
||||||
DateTime createdAt,
|
|
||||||
DateTime updatedAt,
|
|
||||||
string createdBy,
|
|
||||||
string updatedBy,
|
|
||||||
EventModel? @event = null,
|
|
||||||
List<TagModel>? tags = null,
|
|
||||||
RankingModel? ranking = null,
|
|
||||||
bool isFavorite = false,
|
|
||||||
bool isPublic = true,
|
|
||||||
bool isArchived = false,
|
|
||||||
List<PersonModel>? persons = null)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Title = title;
|
|
||||||
Description = description;
|
|
||||||
LowResUrl = lowResUrl;
|
|
||||||
MidResUrl = midResUrl;
|
|
||||||
HighResUrl = highResUrl;
|
|
||||||
CreatedAt = createdAt;
|
|
||||||
UpdatedAt = updatedAt;
|
|
||||||
CreatedBy = createdBy;
|
|
||||||
UpdatedBy = updatedBy;
|
|
||||||
Event = @event ?? Event;
|
|
||||||
Tags = tags ?? Tags;
|
|
||||||
Ranking = ranking ?? Ranking;
|
|
||||||
IsFavorite = isFavorite;
|
|
||||||
IsPublic = isPublic;
|
|
||||||
IsArchived = isArchived;
|
|
||||||
Persons = persons ?? Persons;
|
|
||||||
}
|
|
||||||
}
|
|
119
back/DataModels/Ranking.cs
Normal file
119
back/DataModels/Ranking.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
public class RankingGroup
|
||||||
|
{
|
||||||
|
public const float ExtremlyBad = 1/5f;
|
||||||
|
public const float Bad = 2 / 5f;
|
||||||
|
public const float Normal = 3 / 5f;
|
||||||
|
public const float Good = 4 / 5f;
|
||||||
|
public const float ExtremlyGood = 5 / 5f;
|
||||||
|
|
||||||
|
public static string GetGroup(float score) => (float)Math.Ceiling(score) switch
|
||||||
|
{
|
||||||
|
<= ExtremlyBad => nameof(ExtremlyBad),
|
||||||
|
<= Bad => nameof(Bad),
|
||||||
|
<= Normal => nameof(Normal),
|
||||||
|
<= Good => nameof(Good),
|
||||||
|
_ => nameof(ExtremlyGood)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Table("Rankings")]
|
||||||
|
public partial class Ranking : IEquatable<Ranking>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
public int TotalVotes { get; set; }
|
||||||
|
public int UpVotes { get; set; }
|
||||||
|
public int DownVotes { get; set; }
|
||||||
|
|
||||||
|
public Ranking(int totalVotes, int upVotes = 0, int downVotes = 0)
|
||||||
|
{
|
||||||
|
TotalVotes = totalVotes;
|
||||||
|
UpVotes = upVotes;
|
||||||
|
DownVotes = downVotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ranking()
|
||||||
|
{
|
||||||
|
TotalVotes = 0;
|
||||||
|
UpVotes = 0;
|
||||||
|
DownVotes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DownVote()
|
||||||
|
{
|
||||||
|
DownVotes++;
|
||||||
|
TotalVotes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpVote()
|
||||||
|
{
|
||||||
|
UpVotes++;
|
||||||
|
TotalVotes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Score
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TotalVotes == 0) return 0;
|
||||||
|
return (float)(UpVotes - DownVotes) / TotalVotes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Group => RankingGroup.GetGroup(Score);
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, TotalVotes, UpVotes, DownVotes);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is Ranking otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(Ranking? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return
|
||||||
|
Id == other.Id
|
||||||
|
|| GetHashCode() == other.GetHashCode()
|
||||||
|
|| (TotalVotes == other.TotalVotes && UpVotes == other.UpVotes && DownVotes == other.DownVotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Ranking ranking1, Ranking ranking2)
|
||||||
|
{
|
||||||
|
if (ranking1 is null && ranking2 is null) return true;
|
||||||
|
if (ranking1 is null || ranking2 is null) return false;
|
||||||
|
return ranking1.Equals(ranking2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(Ranking ranking1, Ranking ranking2)
|
||||||
|
{
|
||||||
|
if (ranking1 is null && ranking2 is null) return false;
|
||||||
|
if (ranking1 is null || ranking2 is null) return true;
|
||||||
|
return !ranking1.Equals(ranking2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator < (Ranking ranking1, Ranking ranking2)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(ranking1, nameof(ranking1));
|
||||||
|
ArgumentNullException.ThrowIfNull(ranking2, nameof(ranking2));
|
||||||
|
return ranking1.Score < ranking2.Score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator > (Ranking ranking1, Ranking ranking2)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(ranking1, nameof(ranking1));
|
||||||
|
ArgumentNullException.ThrowIfNull(ranking2, nameof(ranking2));
|
||||||
|
if (ranking1 is null && ranking2 is null) return true;
|
||||||
|
if (ranking1 is null || ranking2 is null) return false;
|
||||||
|
return ranking1.Score > ranking2.Score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <= (Ranking ranking1, Ranking ranking2)
|
||||||
|
=> ranking1 == ranking2 || ranking1 < ranking2;
|
||||||
|
|
||||||
|
public static bool operator >= (Ranking ranking1, Ranking ranking2)
|
||||||
|
=> ranking1 == ranking2 || ranking1 > ranking2;
|
||||||
|
}
|
@@ -1,50 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Rankings")]
|
|
||||||
public class RankingModel
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
|
||||||
|
|
||||||
private int totalVotes;
|
|
||||||
private int upVotes;
|
|
||||||
private int downVotes;
|
|
||||||
|
|
||||||
public RankingModel(int totalVotes, int upVotes = 0, int downVotes = 0)
|
|
||||||
{
|
|
||||||
this.totalVotes = totalVotes;
|
|
||||||
this.upVotes = upVotes;
|
|
||||||
this.downVotes = downVotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RankingModel()
|
|
||||||
{
|
|
||||||
totalVotes = 0;
|
|
||||||
upVotes = 0;
|
|
||||||
downVotes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DownVote()
|
|
||||||
{
|
|
||||||
downVotes++;
|
|
||||||
totalVotes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpVote()
|
|
||||||
{
|
|
||||||
upVotes++;
|
|
||||||
totalVotes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Score
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (totalVotes == 0) return 0;
|
|
||||||
return (double)(upVotes - downVotes) / totalVotes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
94
back/DataModels/Role.cs
Normal file
94
back/DataModels/Role.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Roles")]
|
||||||
|
public partial class Role : IEquatable<Role>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(100)]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
[MaxLength(250)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? BaseRoleModelId { get; set; }
|
||||||
|
public virtual Role? BaseRoleModel { get; set; }
|
||||||
|
public virtual ICollection<Role> InverseBaseRoleModel { get; set; } = [];
|
||||||
|
public virtual ICollection<Permission> Permissions { get; set; } = new HashSet<Permission>();
|
||||||
|
public virtual ICollection<User> Users { get; set; } = [];
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsAdmin() => BaseRoleModel != null ? BaseRoleModel.IsAdmin() : Id == AdminRole.Id;
|
||||||
|
public bool IsContentManager() => BaseRoleModel != null ? BaseRoleModel.IsContentManager() : Id == ContentManagerRole.Id;
|
||||||
|
public bool IsUser() => BaseRoleModel != null ? BaseRoleModel.IsUser() : Id == UserRole.Id;
|
||||||
|
|
||||||
|
public bool HasPermission(Permission permission)
|
||||||
|
{
|
||||||
|
var baseRoleHasPermission = BaseRoleModel != null && BaseRoleModel.HasPermission(permission);
|
||||||
|
return baseRoleHasPermission || Permissions.Any(p => p.Id == permission.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Role() { }
|
||||||
|
|
||||||
|
public Role(string id, string name, string description, List<Permission>? permissions = null, Role? baseRoleModel = null)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Name = name;
|
||||||
|
Description = description;
|
||||||
|
Permissions = permissions ?? [];
|
||||||
|
if (baseRoleModel != null)
|
||||||
|
{
|
||||||
|
BaseRoleModel = baseRoleModel;
|
||||||
|
BaseRoleModelId = baseRoleModel.Id;
|
||||||
|
foreach (var permission in baseRoleModel.Permissions)
|
||||||
|
{
|
||||||
|
if (!Permissions.Any(p => p.Id == permission.Id))
|
||||||
|
{
|
||||||
|
Permissions.Add(permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is Role otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(Role? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly Role UserRole = new(
|
||||||
|
"1", "User", "Role for regular users",
|
||||||
|
[
|
||||||
|
Permission.ViewContentPermission,
|
||||||
|
Permission.LikeContentPermission
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly Role ContentManagerRole = new(
|
||||||
|
"2", "Content Manager", "Role for managing content",
|
||||||
|
[
|
||||||
|
Permission.CreateContentPermission,
|
||||||
|
Permission.EditContentPermission,
|
||||||
|
Permission.DeleteContentPermission
|
||||||
|
],
|
||||||
|
UserRole
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly Role AdminRole = new(
|
||||||
|
"3", "Admin", "Administrator role with full permissions",
|
||||||
|
[
|
||||||
|
Permission.CreateUserPermission,
|
||||||
|
Permission.DisableUserPermission,
|
||||||
|
Permission.EditUserPermission,
|
||||||
|
Permission.DeleteUserPermission,
|
||||||
|
Permission.EditWebConfigPermission
|
||||||
|
],
|
||||||
|
ContentManagerRole
|
||||||
|
);
|
||||||
|
}
|
@@ -1,74 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Tags("Roles")]
|
|
||||||
public class RoleModel
|
|
||||||
{
|
|
||||||
public RoleModel() { }
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[Required, MaxLength(100)]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[MaxLength(250)]
|
|
||||||
public string Description { get; set; }
|
|
||||||
[ForeignKey("PermissionId")]
|
|
||||||
public List<PermissionModel> Permissions { get; set; }
|
|
||||||
[ForeignKey("RoleId")]
|
|
||||||
public RoleModel? BaseRoleModel { get; set; }
|
|
||||||
|
|
||||||
public RoleModel(string id, string name, string description, List<PermissionModel>? permissions = null, RoleModel? baseRoleModel = null)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
Description = description;
|
|
||||||
Permissions = permissions ?? new List<PermissionModel>();
|
|
||||||
BaseRoleModel = baseRoleModel;
|
|
||||||
if (baseRoleModel != null)
|
|
||||||
{
|
|
||||||
Permissions.AddRange(baseRoleModel.Permissions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsAdmin => Id == AdminRole.Id;
|
|
||||||
public bool IsContentManager => Id == ContentManagerRole.Id;
|
|
||||||
public bool IsUser => Id == UserRole.Id;
|
|
||||||
|
|
||||||
public bool HasPermission(PermissionModel permission)
|
|
||||||
{
|
|
||||||
return Permissions.Exists(p => p.Id == permission.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly RoleModel UserRole = new(
|
|
||||||
"1", "User", "Role for regular users",
|
|
||||||
new List<PermissionModel> {
|
|
||||||
PermissionModel.ViewContentPermission,
|
|
||||||
PermissionModel.LikeContentPermission
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
public static readonly RoleModel ContentManagerRole = new(
|
|
||||||
"2", "Content Manager", "Role for managing content",
|
|
||||||
new List<PermissionModel> {
|
|
||||||
PermissionModel.CreateUserPermission,
|
|
||||||
PermissionModel.DisableUserPermission,
|
|
||||||
PermissionModel.CreateContentPermission,
|
|
||||||
PermissionModel.EditContentPermission,
|
|
||||||
PermissionModel.DeleteContentPermission
|
|
||||||
},
|
|
||||||
UserRole
|
|
||||||
);
|
|
||||||
|
|
||||||
public static readonly RoleModel AdminRole = new(
|
|
||||||
"3", "Admin", "Administrator role with full permissions",
|
|
||||||
new List<PermissionModel> {
|
|
||||||
PermissionModel.CreateUserPermission,
|
|
||||||
PermissionModel.EditUserPermission,
|
|
||||||
PermissionModel.DeleteUserPermission,
|
|
||||||
PermissionModel.EditWebConfigPermission
|
|
||||||
},
|
|
||||||
ContentManagerRole
|
|
||||||
);
|
|
||||||
}
|
|
33
back/DataModels/SocialMedia.cs
Normal file
33
back/DataModels/SocialMedia.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("SocialMedia")]
|
||||||
|
public partial class SocialMedia: IEquatable<SocialMedia>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
public string? Facebook { get; set; }
|
||||||
|
public string? Instagram { get; set; }
|
||||||
|
public string? Twitter { get; set; }
|
||||||
|
public string? BlueSky { get; set; }
|
||||||
|
public string? Tiktok { get; set; }
|
||||||
|
public string? Linkedin { get; set; }
|
||||||
|
public string? Pinterest { get; set; }
|
||||||
|
public string? Discord { get; set; }
|
||||||
|
public string? Reddit { get; set; }
|
||||||
|
public string? Other { get; set; }
|
||||||
|
public virtual ICollection<Person> People { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is SocialMedia otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(SocialMedia? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
15
back/DataModels/SystemKey.cs
Normal file
15
back/DataModels/SystemKey.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
public class SystemKey
|
||||||
|
{
|
||||||
|
public string Email { get; set; } = "@system";
|
||||||
|
public string Key { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
public required string Password { get; set; }
|
||||||
|
|
||||||
|
public bool IsValid(string email, string password, string key)
|
||||||
|
{
|
||||||
|
return Email.Equals(email, StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
|
Password.Equals(password, StringComparison.InvariantCulture) &&
|
||||||
|
Key.Equals(key, StringComparison.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
32
back/DataModels/Tag.cs
Normal file
32
back/DataModels/Tag.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Tags")]
|
||||||
|
public partial class Tag: IEquatable<Tag>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, MaxLength(25)]
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
[Required]
|
||||||
|
public string CreatedAt { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual ICollection<Event> Events { get; set; } = [];
|
||||||
|
|
||||||
|
public virtual ICollection<Gallery> Galleries { get; set; } = [];
|
||||||
|
|
||||||
|
public virtual ICollection<Photo> Photos { get; set; } = [];
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Name);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is Tag tag && Equals(tag);
|
||||||
|
|
||||||
|
public bool Equals(Tag? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return Id == other.Id || GetHashCode() == other.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,21 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Tags")]
|
|
||||||
public class TagModel
|
|
||||||
{
|
|
||||||
public TagModel() { }
|
|
||||||
public TagModel(string id, string? name = null)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Key]
|
|
||||||
public string Id { get; init; }
|
|
||||||
[Required, MaxLength(25)]
|
|
||||||
public string? Name { get; init; }
|
|
||||||
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
|
||||||
}
|
|
57
back/DataModels/User.cs
Normal file
57
back/DataModels/User.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace back.DataModels;
|
||||||
|
|
||||||
|
[Table("Users")]
|
||||||
|
public class User : IEquatable<User>
|
||||||
|
{
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
[Required, EmailAddress]
|
||||||
|
public string Email { get; set; } = null!;
|
||||||
|
[Required, MinLength(8)]
|
||||||
|
public string Password { get; set; } = null!;
|
||||||
|
[Required]
|
||||||
|
public string Salt { get; set; } = null!;
|
||||||
|
public string CreatedAt { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual Person IdNavigation { get; set; } = null!;
|
||||||
|
public virtual ICollection<Gallery> Galleries { get; set; } = [];
|
||||||
|
public virtual ICollection<Gallery> GalleriesNavigation { get; set; } = [];
|
||||||
|
public virtual ICollection<Photo> Photos { get; set; } = [];
|
||||||
|
public virtual ICollection<Role> Roles { get; set; } = [];
|
||||||
|
|
||||||
|
public User() { }
|
||||||
|
public User(string id, string email, string password, DateTimeOffset createdAt)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Email = email;
|
||||||
|
Password = password;
|
||||||
|
CreatedAt = createdAt.ToString("dd-MM-yyyy HH:mm:ss zz");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAdmin() => Roles.Any(r => r.IsAdmin());
|
||||||
|
public bool IsContentManager() => Roles.Any(r => r.IsContentManager());
|
||||||
|
public bool IsUser() => Roles.Any(r => r.IsUser());
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Id, Email);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is User otherEvent && Equals(otherEvent);
|
||||||
|
|
||||||
|
public bool Equals(User? other)
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return Id == other.Id && Email == other.Email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const string SystemUserId = "00000000-0000-0000-0000-000000000001";
|
||||||
|
|
||||||
|
public static readonly User SystemUser = new(
|
||||||
|
id: SystemUserId,
|
||||||
|
email: "@system",
|
||||||
|
password: "",
|
||||||
|
createdAt: DateTime.UtcNow
|
||||||
|
);
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace back.DataModels;
|
|
||||||
|
|
||||||
[Table("Users")]
|
|
||||||
public class UserModel : PersonModel
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public string Email { get; set; }
|
|
||||||
[Required, MinLength(8)]
|
|
||||||
public string Password { get; set; }
|
|
||||||
[Required]
|
|
||||||
public string Salt { get; set; }
|
|
||||||
[ForeignKey("RoleId")]
|
|
||||||
public List<RoleModel> Role { get; set; }
|
|
||||||
|
|
||||||
public UserModel(string id, string email, string password, string name, List<RoleModel> role, DateTime createdAt, DateTime updatedAt)
|
|
||||||
: base(id, name)
|
|
||||||
{
|
|
||||||
Email = email;
|
|
||||||
Password = password;
|
|
||||||
Role = role;
|
|
||||||
CreatedAt = createdAt;
|
|
||||||
UpdatedAt = updatedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserModel() { }
|
|
||||||
|
|
||||||
public bool IsAdmin => Role.Exists(r => r.IsAdmin);
|
|
||||||
public bool IsContentManager => Role.Exists(r => r.IsContentManager);
|
|
||||||
public bool IsUser => Role.Exists(r => r.IsUser);
|
|
||||||
|
|
||||||
public UserDto ToDto()
|
|
||||||
{
|
|
||||||
return new UserDto
|
|
||||||
{
|
|
||||||
Id = Id,
|
|
||||||
Name = Name,
|
|
||||||
Email = Email,
|
|
||||||
ProfilePicture = ProfilePicture,
|
|
||||||
Avatar = Avatar,
|
|
||||||
SocialMedia = SocialMedia,
|
|
||||||
Bio = Bio,
|
|
||||||
CreatedAt = CreatedAt,
|
|
||||||
UpdatedAt = UpdatedAt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UserDto : PersonModel
|
|
||||||
{
|
|
||||||
public required string Email { get; set; }
|
|
||||||
}
|
|
10
back/Options/MailServerOptions.cs
Normal file
10
back/Options/MailServerOptions.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace back.Options;
|
||||||
|
|
||||||
|
public sealed class MailServerOptions
|
||||||
|
{
|
||||||
|
public required string SmtpServer { get; set; }
|
||||||
|
public required int Puerto { get; set; }
|
||||||
|
public required string Usuario { get; set; }
|
||||||
|
public required string Password { get; set; }
|
||||||
|
public bool EnableSsl { get; set; }
|
||||||
|
}
|
@@ -9,7 +9,6 @@ public class Program
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.UseExtensions();
|
builder.Services.UseExtensions();
|
||||||
builder.Services.AddMemoryCache();
|
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
using back.context;
|
|
||||||
using back.Options;
|
using back.Options;
|
||||||
|
using back.persistance.data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -7,16 +7,9 @@ namespace back.ServicesExtensions;
|
|||||||
|
|
||||||
public static partial class ServicesExtensions
|
public static partial class ServicesExtensions
|
||||||
{
|
{
|
||||||
private static IServiceCollection AddDatabaseContexts(this IServiceCollection services)
|
private static IServiceCollection AddDatabaseContext(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services
|
services.AddContext<DataContext>();
|
||||||
.AddContext<EventContext>()
|
|
||||||
.AddContext<GalleryContext>()
|
|
||||||
.AddContext<PersonContext>()
|
|
||||||
.AddContext<PhotoContext>()
|
|
||||||
.AddContext<TagContext>()
|
|
||||||
.AddContext<UserContext>()
|
|
||||||
;
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -32,6 +25,25 @@ public static partial class ServicesExtensions
|
|||||||
{
|
{
|
||||||
options.UseDatabaseConfig(config);
|
options.UseDatabaseConfig(config);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
using var scope = services.BuildServiceProvider().CreateScope();
|
||||||
|
var context = scope.ServiceProvider
|
||||||
|
.GetRequiredService<T>();
|
||||||
|
var isDevelopment = scope.ServiceProvider
|
||||||
|
.GetRequiredService<IHostEnvironment>()
|
||||||
|
.IsDevelopment();
|
||||||
|
|
||||||
|
if (isDevelopment && !context.Database.HasPendingModelChanges())
|
||||||
|
{
|
||||||
|
context.Database.EnsureCreated();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Database.EnsureCreated();
|
||||||
|
context.Database.Migrate();
|
||||||
|
}
|
||||||
|
context.SaveChanges();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using back.Options;
|
using back.Options;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace back.ServicesExtensions;
|
namespace back.ServicesExtensions;
|
||||||
|
|
||||||
@@ -59,6 +60,14 @@ public static partial class DbContextOptionsBuilderExtensions
|
|||||||
switch (provider)
|
switch (provider)
|
||||||
{
|
{
|
||||||
case DatabaseProvider.Sqlite:
|
case DatabaseProvider.Sqlite:
|
||||||
|
var match = SQLiteRegex().Match(config.ConnectionString ?? string.Empty);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
string? folder = null;
|
||||||
|
string path = match.Groups[1].Value.Replace("\\", "/");
|
||||||
|
folder = path.Contains('/') ? path[..path.IndexOf('/')] : path;
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
}
|
||||||
options.UseSqlite(config.ConnectionString);
|
options.UseSqlite(config.ConnectionString);
|
||||||
break;
|
break;
|
||||||
case DatabaseProvider.InMemory:
|
case DatabaseProvider.InMemory:
|
||||||
@@ -77,4 +86,7 @@ public static partial class DbContextOptionsBuilderExtensions
|
|||||||
throw new InvalidOperationException($"Unsupported database provider: {config.Provider}");
|
throw new InvalidOperationException($"Unsupported database provider: {config.Provider}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"Data Source=([^;]+)")]
|
||||||
|
private static partial Regex SQLiteRegex();
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,11 @@ public static partial class ServicesExtensions
|
|||||||
services.Configure<Databases>(config.GetSection(nameof(Databases)));
|
services.Configure<Databases>(config.GetSection(nameof(Databases)));
|
||||||
services.Configure<DatabaseConfig>(DatabaseConfig.DataStorage, config.GetSection(DatabaseConfig.DataStorage));
|
services.Configure<DatabaseConfig>(DatabaseConfig.DataStorage, config.GetSection(DatabaseConfig.DataStorage));
|
||||||
services.Configure<DatabaseConfig>(DatabaseConfig.BlobStorage, config.GetSection(DatabaseConfig.BlobStorage));
|
services.Configure<DatabaseConfig>(DatabaseConfig.BlobStorage, config.GetSection(DatabaseConfig.BlobStorage));
|
||||||
|
services.Configure<MailServerOptions>(config.GetSection(nameof(MailServerOptions)));
|
||||||
|
|
||||||
services.PostConfigure<Databases>(databases =>
|
services.PostConfigure<Databases>(databases =>
|
||||||
{
|
{
|
||||||
if (databases.BaseDirectory != null && !Directory.Exists(databases.BaseDirectory))
|
if (!string.IsNullOrEmpty(databases.BaseDirectory) && !Directory.Exists(databases.BaseDirectory))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
using back.persistance.blob;
|
using back.persistance.data;
|
||||||
using back.services.Crypto;
|
using back.persistance.data.repositories;
|
||||||
using back.services.ImageResizer;
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using back.services.engine.SystemUser;
|
||||||
|
using DependencyInjector;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
using Transactional.Implementations.EntityFramework;
|
||||||
|
|
||||||
namespace back.ServicesExtensions;
|
namespace back.ServicesExtensions;
|
||||||
|
|
||||||
@@ -8,15 +12,20 @@ public static partial class ServicesExtensions
|
|||||||
{
|
{
|
||||||
public static IServiceCollection UseExtensions(this IServiceCollection services)
|
public static IServiceCollection UseExtensions(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var config = services.ConfigureOptions();
|
//var config =
|
||||||
|
services.ConfigureOptions();
|
||||||
|
|
||||||
services.AddDatabaseContexts();
|
services.AddMemoryCache();
|
||||||
|
|
||||||
// TODO: Move and configure for using S3, Azure Blob Storage, etc.
|
services.AddDatabaseContext();
|
||||||
services.AddSingleton<IBlobStorageService, FileSystemImageStorageService>();
|
services.AddServices();
|
||||||
|
|
||||||
|
services.AddScoped<ITransactionalService<DataContext>, EntityFrameworkTransactionalService<DataContext>>();
|
||||||
|
|
||||||
|
using var scope = services.BuildServiceProvider().CreateScope();
|
||||||
|
scope.ServiceProvider
|
||||||
|
.GetRequiredService<ISystemUserGenerator>().GenerateAsync().Wait();
|
||||||
|
|
||||||
services.AddSingleton<IImageResizer, ImageResizer>();
|
|
||||||
services.AddSingleton<ICryptoService, CryptoService>();
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,21 @@
|
|||||||
{
|
{
|
||||||
"Databases": {
|
"Databases": {
|
||||||
"BaseDirectory": "data",
|
"BaseDirectory": ".program_data",
|
||||||
"Data": {
|
"Data": {
|
||||||
"Provider": "sqlite",
|
"Provider": "sqlite",
|
||||||
"ConnectionString": "Data Source=data/app.db;Cache=Shared"
|
"ConnectionString": "Data Source=.program_data/app.db"
|
||||||
},
|
},
|
||||||
"Blob": {
|
"Blob": {
|
||||||
"Provider": "system",
|
"Provider": "system",
|
||||||
"baseUrl": "https://localhost:7273/api/photo/{id}/{res}",
|
"baseUrl": "https://localhost:7273/api/photo/{id}/{res}",
|
||||||
"SystemContainer": "imgs"
|
"SystemContainer": "imgs"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"MailServerOptions": {
|
||||||
|
"SmtpServer": "smtp.gmail.com",
|
||||||
|
"Puerto": 587,
|
||||||
|
"Usuario": "",
|
||||||
|
"Password": "",
|
||||||
|
"EnableSsl": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Identity" Version="1.14.2" />
|
<PackageReference Include="Azure.Identity" Version="1.14.2" />
|
||||||
|
<PackageReference Include="MailKit" Version="4.13.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
||||||
@@ -42,7 +43,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="persistance\data\" />
|
<ProjectReference Include="..\..\nuget\DependencyInjector\DependencyInjector.csproj" />
|
||||||
|
<ProjectReference Include="..\..\nuget\Transactional\Transactional.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.14.36401.2 d17.14
|
VisualStudioVersion = 17.14.36401.2
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "back", "back.csproj", "{392278F3-4B36-47F4-AD31-5FBFCC181AD4}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "back", "back.csproj", "{392278F3-4B36-47F4-AD31-5FBFCC181AD4}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transactional", "..\..\nuget\Transactional\Transactional.csproj", "{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjector", "..\..\nuget\DependencyInjector\DependencyInjector.csproj", "{DBDF84A4-235C-4F29-8626-5BD1DC255970}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -15,6 +19,14 @@ Global
|
|||||||
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{392278F3-4B36-47F4-AD31-5FBFCC181AD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@@ -1,52 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class EventContext : DbContext
|
|
||||||
{
|
|
||||||
private DbSet<EventModel> Events { get; set; }
|
|
||||||
public EventContext(DbContextOptions<EventContext> options) : base(options)
|
|
||||||
{
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<EventModel?> GetById(string id)
|
|
||||||
{
|
|
||||||
return await GetById(Guid.Parse(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<EventModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
return await Events.FindAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
return await Events.CountAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<EventModel>> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
|
|
||||||
return await Events
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(EventModel? photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Events.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
return await Events.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class GalleryContext : DbContext
|
|
||||||
{
|
|
||||||
public DbSet<GalleryModel> Galleries { get; set; }
|
|
||||||
public GalleryContext(DbContextOptions<GalleryContext> options) : base(options)
|
|
||||||
{
|
|
||||||
// Ensure database is created
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GalleryModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
return await Galleries.FindAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
return await Galleries.CountAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<GalleryModel>> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
|
|
||||||
return await Galleries
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(GalleryModel? photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Galleries.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
return await Galleries.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,53 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class PersonContext : DbContext
|
|
||||||
{
|
|
||||||
private DbSet<PersonModel> Persons { get; set; }
|
|
||||||
public PersonContext(DbContextOptions<PersonContext> options) : base(options)
|
|
||||||
{
|
|
||||||
// Ensure database is created
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PersonModel?> GetById(string id)
|
|
||||||
{
|
|
||||||
return await GetById(Guid.Parse(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PersonModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
return await Persons.FindAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
return await Persons.CountAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<PersonModel>> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
|
|
||||||
return await Persons
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(PersonModel? photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Persons.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
return await Persons.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,270 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using back.DTO;
|
|
||||||
using back.persistance.blob;
|
|
||||||
using back.services.ImageResizer;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class PhotoContext : DbContext
|
|
||||||
{
|
|
||||||
private DbSet<PhotoModel> Photos { get; set; }
|
|
||||||
private readonly IImageResizer _Resizer;
|
|
||||||
private readonly IBlobStorageService _BlobStorage;
|
|
||||||
|
|
||||||
private readonly TagContext _tagContext;
|
|
||||||
private readonly EventContext _eventContext;
|
|
||||||
private readonly PersonContext _personContext;
|
|
||||||
|
|
||||||
public PhotoContext(DbContextOptions<PhotoContext> options,
|
|
||||||
IImageResizer resizer,
|
|
||||||
IBlobStorageService blobStorage,
|
|
||||||
TagContext tags,
|
|
||||||
EventContext events,
|
|
||||||
PersonContext persons
|
|
||||||
) : base(options)
|
|
||||||
{
|
|
||||||
// Ensure database is created
|
|
||||||
Database.EnsureCreated();
|
|
||||||
_Resizer = resizer;
|
|
||||||
_BlobStorage = blobStorage;
|
|
||||||
_tagContext = tags;
|
|
||||||
_eventContext = events;
|
|
||||||
_personContext = persons;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateNew(PhotoFormModel? form)
|
|
||||||
{
|
|
||||||
if (form == null) { return; }
|
|
||||||
|
|
||||||
var photo = new PhotoModel(
|
|
||||||
Guid.NewGuid().ToString(),
|
|
||||||
form.Title,
|
|
||||||
form.Description ?? string.Empty,
|
|
||||||
string.Empty, // LowResUrl will be set later
|
|
||||||
string.Empty, // MidResUrl will be set later
|
|
||||||
string.Empty, // HighResUrl will be set later
|
|
||||||
DateTime.UtcNow,
|
|
||||||
DateTime.UtcNow,
|
|
||||||
form.UserId,
|
|
||||||
form.UserId
|
|
||||||
)
|
|
||||||
{
|
|
||||||
IsPublic = form.IsPublic
|
|
||||||
};
|
|
||||||
|
|
||||||
List<Task> tasks = [
|
|
||||||
SaveBlob(photo, form),
|
|
||||||
LinkTags(photo, form.Tags ?? [], form.UserId),
|
|
||||||
LinkEvent(photo, form.Evento ?? "", form.UserId),
|
|
||||||
LinkPersons(photo, form.People ?? [], form.UserId),
|
|
||||||
];
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
await Photos.AddAsync(photo);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkPersons(PhotoModel photo, string[] personas, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (photo == null || personas == null || personas.Length == 0) return;
|
|
||||||
foreach (var personId in personas)
|
|
||||||
{
|
|
||||||
var person = await _personContext.GetById(personId);
|
|
||||||
if (person != null)
|
|
||||||
{
|
|
||||||
await LinkPersons(photo, person, updatedBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkPersons(PhotoModel photo, PersonModel tag, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (tag == null) return;
|
|
||||||
// Ensure the tag exists
|
|
||||||
if (await _personContext.Exists(tag.Id))
|
|
||||||
{
|
|
||||||
photo.Persons ??= [];
|
|
||||||
photo.Persons.Add(tag);
|
|
||||||
photo.UpdatedAt = DateTime.UtcNow;
|
|
||||||
photo.UpdatedBy = updatedBy; // or use a more appropriate value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkTags(PhotoModel photo, string[] tags, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (photo == null || tags == null || tags.Length == 0) return;
|
|
||||||
foreach (var tagId in tags)
|
|
||||||
{
|
|
||||||
var tag = await _tagContext.GetById(tagId);
|
|
||||||
if (tag != null)
|
|
||||||
{
|
|
||||||
await LinkTag(photo, tag, updatedBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkTag(PhotoModel photo, TagModel tag, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (tag == null) return;
|
|
||||||
// Ensure the tag exists
|
|
||||||
if (await _tagContext.Exists(tag.Id))
|
|
||||||
{
|
|
||||||
photo.Tags.Add(tag);
|
|
||||||
photo.UpdatedAt = DateTime.UtcNow;
|
|
||||||
photo.UpdatedBy = updatedBy; // or use a more appropriate value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkEvent(PhotoModel photo, string eventId, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(eventId)) return;
|
|
||||||
var evento = await _eventContext.GetById(eventId);
|
|
||||||
if (evento != null)
|
|
||||||
{
|
|
||||||
await LinkEvent(photo, evento, updatedBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LinkEvent(PhotoModel photo, EventModel? evento, string updatedBy = "SYSTEM")
|
|
||||||
{
|
|
||||||
if (evento == null) return;
|
|
||||||
// Ensure the event exists
|
|
||||||
if (await _eventContext.Exists(evento.Id))
|
|
||||||
{
|
|
||||||
photo.Event = evento;
|
|
||||||
photo.UpdatedAt = DateTime.UtcNow;
|
|
||||||
photo.UpdatedBy = updatedBy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveBlob(PhotoModel photo, PhotoFormModel form)
|
|
||||||
{
|
|
||||||
if (form.Image != null && form.Image.Length > 0)
|
|
||||||
{
|
|
||||||
var lowRes = await _Resizer.ResizeImage(form.Image, 480);
|
|
||||||
var midRes = await _Resizer.ResizeImage(form.Image, 720);
|
|
||||||
// Upload images to blob storage
|
|
||||||
photo.Extension = form.Image.FileName.Split('.').Last();
|
|
||||||
photo.LowResUrl = $"low/{photo.Id}.webp";
|
|
||||||
photo.MidResUrl = $"mid/{photo.Id}.webp";
|
|
||||||
photo.HighResUrl = $"high/{photo.Id}.{photo.Extension}";
|
|
||||||
await _BlobStorage.SaveAsync(lowRes, photo.LowResUrl);
|
|
||||||
await _BlobStorage.SaveAsync(midRes, photo.MidResUrl);
|
|
||||||
await _BlobStorage.SaveAsync(form.Image.OpenReadStream(), photo.HighResUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PhotoModel?> GetById(string id)
|
|
||||||
{
|
|
||||||
return await GetById(Guid.Parse(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PhotoModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await Photos.FindAsync(id);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await Photos.CountAsync();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<PhotoModel>?> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await Photos
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(PhotoModel? photo)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Photos.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false; // Handle exceptions gracefully
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(id)) return false;
|
|
||||||
return await Photos.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false; // Handle exceptions gracefully
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Delete(PhotoModel photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return;
|
|
||||||
if (await Exists(photo))
|
|
||||||
{
|
|
||||||
// Delete the photo from blob storage
|
|
||||||
if (!string.IsNullOrEmpty(photo.LowResUrl))
|
|
||||||
await _BlobStorage.DeleteAsync(photo.LowResUrl);
|
|
||||||
if (!string.IsNullOrEmpty(photo.MidResUrl))
|
|
||||||
await _BlobStorage.DeleteAsync(photo.MidResUrl);
|
|
||||||
if (!string.IsNullOrEmpty(photo.HighResUrl))
|
|
||||||
await _BlobStorage.DeleteAsync(photo.HighResUrl);
|
|
||||||
Photos.Remove(photo);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Update(PhotoModel photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return;
|
|
||||||
if (await Exists(photo))
|
|
||||||
{
|
|
||||||
var evento = photo.Event;
|
|
||||||
photo.Event = null;
|
|
||||||
await LinkEvent(photo, evento, photo.UpdatedBy);
|
|
||||||
|
|
||||||
var tags = photo.Tags.Select(t => t.Id);
|
|
||||||
photo.Tags.Clear();
|
|
||||||
await LinkTags(photo, [.. tags], photo.UpdatedBy);
|
|
||||||
|
|
||||||
var persons = photo.Persons?.Select(t => t.Id) ?? [];
|
|
||||||
photo.Persons = null;
|
|
||||||
await LinkPersons(photo, [.. persons], photo.UpdatedBy);
|
|
||||||
|
|
||||||
Photos.Update(photo);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,52 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class TagContext : DbContext
|
|
||||||
{
|
|
||||||
private DbSet<TagModel> Tags { get; set; }
|
|
||||||
public TagContext(DbContextOptions<TagContext> options) : base(options)
|
|
||||||
{
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TagModel?> GetById(string id)
|
|
||||||
{
|
|
||||||
return await GetById(Guid.Parse(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TagModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
return await Tags.FindAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
return await Tags.CountAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<TagModel>> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
|
|
||||||
return await Tags
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(TagModel? photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Tags.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
return await Tags.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,142 +0,0 @@
|
|||||||
using back.DataModels;
|
|
||||||
using back.services.Crypto;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace back.context;
|
|
||||||
|
|
||||||
public class UserContext : DbContext
|
|
||||||
{
|
|
||||||
public record HttpErrorMap(HttpStatusCode Code, string Description);
|
|
||||||
|
|
||||||
public static class Errors
|
|
||||||
{
|
|
||||||
public static readonly HttpErrorMap Unauthorized =
|
|
||||||
new(HttpStatusCode.Unauthorized, "Invalid user data. Email or password are wrong.");
|
|
||||||
public static readonly HttpErrorMap BadRequest =
|
|
||||||
new(HttpStatusCode.BadRequest, "Missing user data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbSet<UserModel> Users { get; set; }
|
|
||||||
private readonly ICryptoService _cryptoService;
|
|
||||||
public UserContext(
|
|
||||||
DbContextOptions<UserContext> options,
|
|
||||||
ICryptoService cryptoService
|
|
||||||
) : base(options)
|
|
||||||
{
|
|
||||||
_cryptoService = cryptoService ?? throw new ArgumentNullException(nameof(cryptoService));
|
|
||||||
// Ensure database is created
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> Create(string clientId, UserModel user)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
|
||||||
|
|
||||||
if (await Exists(user))
|
|
||||||
{
|
|
||||||
return await GetById(Guid.Parse(user.Id)) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(user.Id))
|
|
||||||
{
|
|
||||||
user.Id = Guid.NewGuid().ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(user.Salt))
|
|
||||||
{
|
|
||||||
user.Salt = _cryptoService.Salt();
|
|
||||||
}
|
|
||||||
user.Password = _cryptoService.Decrypt(clientId, user.Password) ?? string.Empty;
|
|
||||||
user.Password = _cryptoService.Hash(user.Password + user.Salt + _cryptoService.Pepper()) ?? string.Empty;
|
|
||||||
|
|
||||||
user.CreatedAt = DateTime.UtcNow;
|
|
||||||
Users.Add(user);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> Update(UserModel user)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(user);
|
|
||||||
if (!await Exists(user))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var existingUser = await GetById(Guid.Parse(user.Id));
|
|
||||||
if (existingUser == null) return null;
|
|
||||||
existingUser.Name = user.Name;
|
|
||||||
existingUser.Email = user.Email;
|
|
||||||
existingUser.UpdatedAt = DateTime.UtcNow;
|
|
||||||
Users.Update(existingUser);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
return existingUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Delete(Guid id)
|
|
||||||
{
|
|
||||||
var user = await GetById(id);
|
|
||||||
if (user == null) return false;
|
|
||||||
Users.Remove(user);
|
|
||||||
await SaveChangesAsync();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> GetByEmail(string email)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(email)) return null;
|
|
||||||
return await Users.FirstOrDefaultAsync(u => u.Email == email);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetUserSaltByEmail(string email)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(email)) return string.Empty;
|
|
||||||
var user = await Users.FirstOrDefaultAsync(u => u.Email == email);
|
|
||||||
return user?.Salt ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> Login(string email, string password, string clientId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
|
||||||
|
|
||||||
var pass = _cryptoService.Decrypt(clientId, password) + await GetUserSaltByEmail(email) + _cryptoService.Pepper();
|
|
||||||
var hashedPassword = _cryptoService.Hash(pass);
|
|
||||||
var user = await Users
|
|
||||||
.FirstOrDefaultAsync(u => u.Email == email && u.Password == hashedPassword);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserModel?> GetById(Guid id)
|
|
||||||
{
|
|
||||||
return await Users.FindAsync(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetTotalItems()
|
|
||||||
{
|
|
||||||
return await Users.CountAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<UserModel>> GetPage(int page = 1, int pageSize = 20)
|
|
||||||
{
|
|
||||||
if (page < 1) page = 1;
|
|
||||||
if (pageSize < 1) pageSize = 20;
|
|
||||||
|
|
||||||
return await Users
|
|
||||||
.OrderByDescending(p => p.CreatedAt)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(UserModel? photo)
|
|
||||||
{
|
|
||||||
if (photo == null) return false;
|
|
||||||
if (string.IsNullOrEmpty(photo.Id)) return false;
|
|
||||||
return await Users.AnyAsync(p => p.Id == photo.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Exists(string id)
|
|
||||||
{
|
|
||||||
return await Users.AnyAsync(p => p.Id == id);
|
|
||||||
}
|
|
||||||
}
|
|
21
back/controllers/CryptoController.cs
Normal file
21
back/controllers/CryptoController.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using back.services.engine.Crypto;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace back.controllers;
|
||||||
|
|
||||||
|
[ApiController, Route("api/[controller]")]
|
||||||
|
public class CryptoController(ICryptoService cryptoService) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet("[action]")] public async Task<IActionResult> RSA([FromHeader(Name = "X-client-thumbprint")] string clientId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(clientId))
|
||||||
|
{
|
||||||
|
return BadRequest("Client ID is required.");
|
||||||
|
}
|
||||||
|
var key = cryptoService.GetPublicCertificate(clientId);
|
||||||
|
if (key == null)
|
||||||
|
return StatusCode((int)HttpStatusCode.InternalServerError, "Failed to generate RSA keys.");
|
||||||
|
return Ok(new { PublicKey = key });
|
||||||
|
}
|
||||||
|
}
|
@@ -1,60 +1,33 @@
|
|||||||
using back.context;
|
using back.DataModels;
|
||||||
using back.DataModels;
|
|
||||||
using back.DTO;
|
using back.DTO;
|
||||||
using back.persistance.blob;
|
using back.services.bussines.PhotoService;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace back.controllers;
|
namespace back.controllers;
|
||||||
|
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class PhotosController(PhotoContext photoContext, IBlobStorageService blobStorage) : ControllerBase
|
public class PhotosController(IPhotoService photoService) : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly PhotoContext _photoContext = photoContext;
|
private readonly IPhotoService _photoService = photoService;
|
||||||
|
|
||||||
// GET: api/<PhotoController>
|
// GET: api/<PhotoController>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<PhotoModel>>> Get([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
public async Task<ActionResult<IEnumerable<Photo>>> Get([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||||
{
|
{
|
||||||
var photos = await _photoContext.GetPage(page, pageSize);
|
(int totalItems, IEnumerable<Photo>? pageData) = await _photoService.GetPage(page, pageSize);
|
||||||
var totalItems = await _photoContext.GetTotalItems();
|
|
||||||
|
|
||||||
Response.Headers.Append("X-Total-Count", totalItems.ToString());
|
Response.Headers.Append("X-Total-Count", totalItems.ToString());
|
||||||
return Ok(photos);
|
return Ok(pageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/<PhotoController>/5
|
// GET api/<PhotoController>/5
|
||||||
[HttpGet("{res}/{id}")]
|
[HttpGet("{id}/{res}")]
|
||||||
public async Task<IActionResult> Get(string res, Guid id)
|
public async Task<IActionResult> Get(string id, string res)
|
||||||
{
|
{
|
||||||
var photo = await _photoContext.GetById(id);
|
(string? mediaType, byte[]? fileBytes) = await _photoService.GetBytes(id, res.ToLower());
|
||||||
if (photo == null)
|
if(fileBytes == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
return File(fileBytes, mediaType ?? "image/jpeg");
|
||||||
string? filePath = res.ToLower() switch
|
|
||||||
{
|
|
||||||
"high" => photo.HighResUrl,
|
|
||||||
"mid" => photo.MidResUrl,
|
|
||||||
"low" or _ => photo.LowResUrl
|
|
||||||
};
|
|
||||||
|
|
||||||
string? mediaType = res.ToLower() switch
|
|
||||||
{
|
|
||||||
"high" => $"image/{photo.Extension}",
|
|
||||||
"mid" or "low" or _ => "image/webp",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (filePath == null)
|
|
||||||
{
|
|
||||||
return NotFound();
|
|
||||||
}
|
|
||||||
var file = await blobStorage.GetBytesAsync(filePath);
|
|
||||||
if (file == null)
|
|
||||||
{
|
|
||||||
return NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return File(file, mediaType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<PhotoController>
|
// POST api/<PhotoController>
|
||||||
@@ -66,7 +39,7 @@ public class PhotosController(PhotoContext photoContext, IBlobStorageService blo
|
|||||||
if (form.Image == null || form.Image.Length == 0)
|
if (form.Image == null || form.Image.Length == 0)
|
||||||
return BadRequest("No image uploaded.");
|
return BadRequest("No image uploaded.");
|
||||||
|
|
||||||
await _photoContext.CreateNew(form);
|
await _photoService.Create(form);
|
||||||
|
|
||||||
return Created();
|
return Created();
|
||||||
}
|
}
|
||||||
@@ -78,21 +51,17 @@ public class PhotosController(PhotoContext photoContext, IBlobStorageService blo
|
|||||||
|
|
||||||
//// PUT api/<PhotoController>
|
//// PUT api/<PhotoController>
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
public async Task<IActionResult> Put([FromBody] PhotoModel photo)
|
public async Task<IActionResult> Put([FromBody] Photo photo)
|
||||||
{
|
{
|
||||||
await _photoContext.Update(photo);
|
await _photoService.Update(photo);
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE api/<PhotoController>/5
|
// DELETE api/<PhotoController>/5
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<IActionResult> Delete(Guid id)
|
public async Task<IActionResult> Delete(string id)
|
||||||
{
|
{
|
||||||
var photo = await _photoContext.GetById(id);
|
await _photoService.Delete(id);
|
||||||
if (photo == null)
|
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
await _photoContext.Delete(photo);
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
using back.context;
|
using back.DataModels;
|
||||||
using back.DataModels;
|
using back.services.bussines;
|
||||||
|
using back.services.bussines.UserService;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace back.controllers;
|
namespace back.controllers;
|
||||||
|
|
||||||
|
public record UserLoginFromModel(string Email, string Password, string? SystemKey);
|
||||||
|
public record ForgotPasswordFromModel(string Email);
|
||||||
|
public record RegisterFromModel(string Name, string Email, string Password);
|
||||||
|
|
||||||
[ApiController, Route("api/[controller]")]
|
[ApiController, Route("api/[controller]")]
|
||||||
public class UsersController(UserContext userContext) : ControllerBase
|
public class UsersController(IUserService user) : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly UserContext _userContext = userContext;
|
private readonly IUserService _user = user;
|
||||||
// GET: api/<UsersController>
|
// GET: api/<UsersController>
|
||||||
//[HttpGet]
|
//[HttpGet]
|
||||||
//public async Task<ActionResult<IEnumerable<UserModel>>> Get([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
//public async Task<ActionResult<IEnumerable<UserModel>>> Get([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||||
@@ -28,27 +32,59 @@ public class UsersController(UserContext userContext) : ControllerBase
|
|||||||
// return Ok(user);
|
// return Ok(user);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost("[action]")]
|
||||||
public async Task<IActionResult> Login(
|
public async Task<IActionResult> Login(
|
||||||
[FromHeader(Name = "X-client-thumbprint")] string clientId,
|
[FromHeader(Name = "X-client-thumbprint")] string clientId,
|
||||||
[FromBody] UserModel user
|
[FromBody] UserLoginFromModel user
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(clientId))
|
||||||
|
return BadRequest("Client ID cannot be null or empty");
|
||||||
|
|
||||||
if (user == null || string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password))
|
if (user == null || string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password))
|
||||||
return BadRequest(UserContext.Errors.BadRequest.Description);
|
return BadRequest(Errors.BadRequest.Description);
|
||||||
var existingUser = await _userContext.Login(user.Email, user.Password, clientId);
|
|
||||||
if (existingUser == null)
|
if (user.Email.Equals("@system", StringComparison.InvariantCultureIgnoreCase))
|
||||||
return Unauthorized(UserContext.Errors.Unauthorized.Description);
|
{
|
||||||
return Ok(existingUser.ToDto());
|
if (string.IsNullOrEmpty(user.SystemKey))
|
||||||
|
return Unauthorized(Errors.Unauthorized.Description);
|
||||||
|
var systemUser = await _user.ValidateSystemUser(user.Email, user.Password, user.SystemKey, clientId);
|
||||||
|
if (systemUser == null)
|
||||||
|
return Unauthorized(Errors.Unauthorized.Description);
|
||||||
|
return Ok(systemUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
//// POST api/<UsersController>
|
var existingUser = await _user.Login(user.Email, user.Password, clientId);
|
||||||
//[HttpPost]
|
if (existingUser == null)
|
||||||
//public async Task<IActionResult> Post([FromBody] UserModel user)
|
return Unauthorized(Errors.Unauthorized.Description);
|
||||||
//{
|
return Ok(existingUser);
|
||||||
// if (user == null)
|
}
|
||||||
// return BadRequest("User cannot be null");
|
|
||||||
// var createdUser = await _userContext.Create(user);
|
[HttpPost("forgot-password")]
|
||||||
// return CreatedAtAction(nameof(Get), new { id = createdUser.Id }, createdUser);
|
public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordFromModel user)
|
||||||
//}
|
{
|
||||||
|
if (string.IsNullOrEmpty(user.Email))
|
||||||
|
return BadRequest("Email cannot be null or empty");
|
||||||
|
await _user.SendResetPassword(user.Email);
|
||||||
|
return Ok("If the email exists, a reset password link has been sent.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/<UsersController>
|
||||||
|
[HttpPost("[action]")]
|
||||||
|
public async Task<IActionResult> Register(
|
||||||
|
[FromHeader(Name = "X-client-thumbprint")] string clientId,
|
||||||
|
[FromBody] RegisterFromModel user)
|
||||||
|
{
|
||||||
|
if (user == null)
|
||||||
|
return BadRequest("User cannot be null");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var createdUser = await _user.Create(clientId, new User() { Email = user.Email, Password = user.Password });
|
||||||
|
return Created();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,51 +10,51 @@ public class FileSystemImageStorageService(
|
|||||||
IMemoryCache memoryCache
|
IMemoryCache memoryCache
|
||||||
) : IBlobStorageService
|
) : IBlobStorageService
|
||||||
{
|
{
|
||||||
private readonly string RootPath = systemOptions.Value.BaseDirectory ?? "";
|
private readonly string RootPath = systemOptions.Value.BaseDirectory ?? "data";
|
||||||
private readonly DatabaseConfig config = options.Get(DatabaseConfig.BlobStorage);
|
private readonly DatabaseConfig config = options.Get(DatabaseConfig.BlobStorage);
|
||||||
private readonly IMemoryCache cache = memoryCache;
|
private readonly IMemoryCache cache = memoryCache;
|
||||||
|
|
||||||
private string GetFullPath(string fileName)
|
private string GetFullPath(string fileName)
|
||||||
{
|
{
|
||||||
// Ensure the directory exists
|
// Ensure the directory exists
|
||||||
var directory = Path.Join(RootPath, config.SystemContainer, fileName);
|
var path = Path.Join(RootPath, config.SystemContainer, fileName);
|
||||||
|
var directory = Path.GetDirectoryName(path);
|
||||||
if (directory != null && !Directory.Exists(directory))
|
if (directory != null && !Directory.Exists(directory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
}
|
}
|
||||||
return fileName;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(string fileName)
|
public async Task Delete(string fileName)
|
||||||
{
|
{
|
||||||
var path = GetFullPath(fileName);
|
|
||||||
if (cache.TryGetValue(path, out Stream cachedStream))
|
|
||||||
{
|
|
||||||
cachedStream.Dispose(); // Dispose the cached stream if it exists
|
|
||||||
cache.Remove(path); // Remove from cache
|
|
||||||
}
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
return; // No file to delete
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
var path = GetFullPath(fileName);
|
||||||
|
if (cache.TryGetValue(path, out Stream? cachedStream))
|
||||||
|
{
|
||||||
|
cachedStream?.Dispose(); // Dispose the cached stream if it exists
|
||||||
|
cache.Remove(path); // Remove from cache
|
||||||
|
}
|
||||||
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Error deleting file {fileName}: {ex.Message}", ex);
|
throw new InvalidOperationException($"Error deleting file {fileName}: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Stream?> GetStreamAsync(string fileName)
|
public async Task<Stream?> GetStream(string fileName)
|
||||||
{
|
{
|
||||||
var path = GetFullPath(fileName);
|
var path = GetFullPath(fileName);
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
if (cache.TryGetValue(path, out Stream? cachedStream))
|
if (cache.TryGetValue(path, out Stream? cachedStream))
|
||||||
{
|
{
|
||||||
return Task.FromResult<Stream?>(cachedStream);
|
return cachedStream;
|
||||||
}
|
}
|
||||||
// open the file stream for multiple reads and cache it for performance
|
// open the file stream for multiple reads and cache it for performance
|
||||||
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
@@ -63,14 +63,14 @@ public class FileSystemImageStorageService(
|
|||||||
.SetValue(fileStream)
|
.SetValue(fileStream)
|
||||||
.SetSlidingExpiration(TimeSpan.FromMinutes(30)); // Cache for 30 minutes
|
.SetSlidingExpiration(TimeSpan.FromMinutes(30)); // Cache for 30 minutes
|
||||||
|
|
||||||
return Task.FromResult<Stream?>(fileStream);
|
return fileStream;
|
||||||
}
|
}
|
||||||
return Task.FromResult<Stream?>(null);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]?> GetBytesAsync(string fileName)
|
public async Task<byte[]?> GetBytes(string fileName)
|
||||||
{
|
{
|
||||||
var stream = await GetStreamAsync(fileName);
|
var stream = await GetStream(fileName);
|
||||||
if (stream != null)
|
if (stream != null)
|
||||||
{
|
{
|
||||||
using var memoryStream = new MemoryStream();
|
using var memoryStream = new MemoryStream();
|
||||||
@@ -80,25 +80,25 @@ public class FileSystemImageStorageService(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Stream blobStream, string fileName)
|
public async Task Save(Stream blobStream, string fileName)
|
||||||
{
|
{
|
||||||
var path = GetFullPath(fileName);
|
var path = GetFullPath(fileName);
|
||||||
if (cache.TryGetValue(path, out Stream? _) || File.Exists(path))
|
if (cache.TryGetValue(path, out Stream? _) || File.Exists(path))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"File {fileName} already exists. Use Update for updating file info.");
|
throw new InvalidOperationException($"File {fileName} already exists. Use Update for updating file info.");
|
||||||
}
|
}
|
||||||
using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
|
using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
await blobStream.CopyToAsync(fileStream);
|
await blobStream.CopyToAsync(fileStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(Stream blobStream, string fileName)
|
public async Task Update(Stream blobStream, string fileName)
|
||||||
{
|
{
|
||||||
var path = GetFullPath(fileName);
|
var path = GetFullPath(fileName);
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
await DeleteAsync(fileName);
|
await Delete(fileName);
|
||||||
}
|
}
|
||||||
await SaveAsync(blobStream, fileName);
|
await Save(blobStream, fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
namespace back.persistance.blob;
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
public interface IBlobStorageService
|
namespace back.persistance.blob;
|
||||||
|
|
||||||
|
public interface IBlobStorageService : ISingleton
|
||||||
{
|
{
|
||||||
Task SaveAsync(Stream blobStream, string fileName);
|
Task Save(Stream blobStream, string fileName);
|
||||||
Task<Stream?> GetStreamAsync(string fileName);
|
Task<Stream?> GetStream(string fileName);
|
||||||
Task<byte[]?> GetBytesAsync(string fileName);
|
Task<byte[]?> GetBytes(string fileName);
|
||||||
Task DeleteAsync(string fileName);
|
Task Delete(string fileName);
|
||||||
Task UpdateAsync(Stream blobStream, string fileName);
|
Task Update(Stream blobStream, string fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
back/persistance/data/DataContext.cs
Normal file
65
back/persistance/data/DataContext.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.data.relations;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data;
|
||||||
|
|
||||||
|
public partial class DataContext : DbContext
|
||||||
|
{
|
||||||
|
public DataContext() { }
|
||||||
|
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
|
||||||
|
|
||||||
|
public virtual DbSet<EfmigrationsLock> EfmigrationsLocks { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Event> Events { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Gallery> Galleries { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Permission> Permissions { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Person> Persons { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Photo> Photos { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Ranking> Rankings { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Role> Roles { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<SocialMedia> SocialMedia { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Tag> Tags { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<User> Users { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<EfmigrationsLock>(entity =>
|
||||||
|
{
|
||||||
|
entity.ToTable("__EFMigrationsLock");
|
||||||
|
|
||||||
|
entity.Property(e => e.Id).ValueGeneratedNever();
|
||||||
|
});
|
||||||
|
|
||||||
|
typeof(IRelationEstablisher).Assembly.GetExportedTypes()
|
||||||
|
.Where(t => typeof(IRelationEstablisher).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(seederType =>
|
||||||
|
{
|
||||||
|
var relationEstablisher = (IRelationEstablisher?)Activator.CreateInstance(seederType);
|
||||||
|
relationEstablisher?.EstablishRelation(modelBuilder);
|
||||||
|
});
|
||||||
|
|
||||||
|
//typeof(ISeeder).Assembly.GetExportedTypes()
|
||||||
|
// .Where(t => typeof(ISeeder).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
|
||||||
|
// .ToList()
|
||||||
|
// .ForEach(seederType =>
|
||||||
|
// {
|
||||||
|
// var seeder = (ISeeder?)Activator.CreateInstance(seederType);
|
||||||
|
// seeder?.Seed(modelBuilder);
|
||||||
|
// });
|
||||||
|
|
||||||
|
OnModelCreatingPartial(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||||
|
}
|
752
back/persistance/data/migrations/20250824120656_InitialSetup.Designer.cs
generated
Normal file
752
back/persistance/data/migrations/20250824120656_InitialSetup.Designer.cs
generated
Normal file
@@ -0,0 +1,752 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using back.persistance.data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace back.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20250824120656_InitialSetup")]
|
||||||
|
partial class InitialSetup
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("EventTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("EventId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("EventTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryPhoto", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "PhotoId");
|
||||||
|
|
||||||
|
b.HasIndex("PhotoId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryPhotos", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryUserViewer", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryUserViewers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoPerson", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PersonId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "PersonId");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoPersons", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoUserBuyer", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoUserBuyers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RolePermission", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("RoleId", "PermissionId");
|
||||||
|
|
||||||
|
b.HasIndex("PermissionId");
|
||||||
|
|
||||||
|
b.ToTable("RolePermissions", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("UserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.EfmigrationsLock", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Timestamp")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("__EFMigrationsLock", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Event", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Gallery", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("IsArchived")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("IsFavorite")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsPublic")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Galleries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Permission", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Permissions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Avatar")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Bio")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProfilePicture")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SocialMediaId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SocialMediaId");
|
||||||
|
|
||||||
|
b.ToTable("Persons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Photo", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HighResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("IsArchived")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsFavorite")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsPublic")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
|
b.Property<string>("LowResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MidResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RankingId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Photos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Ranking", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("DownVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TotalVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UpVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Rankings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("BaseRoleModelId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BaseRoleModelId");
|
||||||
|
|
||||||
|
b.ToTable("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.SocialMedia", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("BlueSky")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Discord")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Facebook")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Instagram")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Linkedin")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Other")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Pinterest")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Reddit")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Tiktok")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Twitter")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SocialMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(25)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Name" }, "IX_Tags_Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Salt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("EventTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Event", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryPhoto", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryUserViewer", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoPerson", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PersonId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoUserBuyer", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RolePermission", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Permission", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PermissionId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Role", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserRole", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Role", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Gallery", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.User", "CreatedByNavigation")
|
||||||
|
.WithMany("Galleries")
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Event", "Event")
|
||||||
|
.WithMany("Galleries")
|
||||||
|
.HasForeignKey("EventId");
|
||||||
|
|
||||||
|
b.Navigation("CreatedByNavigation");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.SocialMedia", "SocialMedia")
|
||||||
|
.WithMany("People")
|
||||||
|
.HasForeignKey("SocialMediaId");
|
||||||
|
|
||||||
|
b.Navigation("SocialMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Photo", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", "CreatedByNavigation")
|
||||||
|
.WithMany("Photos")
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Event", "Event")
|
||||||
|
.WithMany("Photos")
|
||||||
|
.HasForeignKey("EventId");
|
||||||
|
|
||||||
|
b.Navigation("CreatedByNavigation");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Role", "BaseRoleModel")
|
||||||
|
.WithMany("InverseBaseRoleModel")
|
||||||
|
.HasForeignKey("BaseRoleModelId");
|
||||||
|
|
||||||
|
b.Navigation("BaseRoleModel");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", "IdNavigation")
|
||||||
|
.WithOne("User")
|
||||||
|
.HasForeignKey("back.DataModels.User", "Id")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("IdNavigation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Event", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Galleries");
|
||||||
|
|
||||||
|
b.Navigation("Photos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Photos");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InverseBaseRoleModel");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.SocialMedia", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("People");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Galleries");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
583
back/persistance/data/migrations/20250824120656_InitialSetup.cs
Normal file
583
back/persistance/data/migrations/20250824120656_InitialSetup.cs
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace back.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialSetup : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "__EFMigrationsLock",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Timestamp = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK___EFMigrationsLock", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Events",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||||
|
Date = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Location = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UpdatedBy = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
IsDeleted = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DeletedAt = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Events", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Permissions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Permissions", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Rankings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
TotalVotes = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
UpVotes = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DownVotes = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Rankings", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Roles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", maxLength: 250, nullable: true),
|
||||||
|
BaseRoleModelId = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Roles", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Roles_Roles_BaseRoleModelId",
|
||||||
|
column: x => x.BaseRoleModelId,
|
||||||
|
principalTable: "Roles",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SocialMedia",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Facebook = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Instagram = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Twitter = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
BlueSky = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Tiktok = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Linkedin = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Pinterest = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Discord = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Reddit = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Other = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SocialMedia", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Tags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 25, nullable: false),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Tags", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RolePermissions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
RoleId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
PermissionId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RolePermissions", x => new { x.RoleId, x.PermissionId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RolePermissions_Permissions_PermissionId",
|
||||||
|
column: x => x.PermissionId,
|
||||||
|
principalTable: "Permissions",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RolePermissions_Roles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "Roles",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Persons",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
ProfilePicture = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Avatar = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
SocialMediaId = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Bio = table.Column<string>(type: "TEXT", maxLength: 250, nullable: true),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
IsDeleted = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DeletedAt = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Persons", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Persons_SocialMedia_SocialMediaId",
|
||||||
|
column: x => x.SocialMediaId,
|
||||||
|
principalTable: "SocialMedia",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "EventTags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
EventId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
TagId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_EventTags", x => new { x.EventId, x.TagId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_EventTags_Events_EventId",
|
||||||
|
column: x => x.EventId,
|
||||||
|
principalTable: "Events",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_EventTags_Tags_TagId",
|
||||||
|
column: x => x.TagId,
|
||||||
|
principalTable: "Tags",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Photos",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||||
|
Extension = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
LowResUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
MidResUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
HighResUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UpdatedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedBy = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedBy = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
EventId = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
RankingId = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
IsFavorite = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 0),
|
||||||
|
IsPublic = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 1),
|
||||||
|
IsArchived = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 0)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Photos", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Photos_Events_EventId",
|
||||||
|
column: x => x.EventId,
|
||||||
|
principalTable: "Events",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Photos_Persons_CreatedBy",
|
||||||
|
column: x => x.CreatedBy,
|
||||||
|
principalTable: "Persons",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Email = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Password = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Salt = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Users_Persons_Id",
|
||||||
|
column: x => x.Id,
|
||||||
|
principalTable: "Persons",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PhotoPersons",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PhotoId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
PersonId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PhotoPersons", x => new { x.PhotoId, x.PersonId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoPersons_Persons_PersonId",
|
||||||
|
column: x => x.PersonId,
|
||||||
|
principalTable: "Persons",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoPersons_Photos_PhotoId",
|
||||||
|
column: x => x.PhotoId,
|
||||||
|
principalTable: "Photos",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PhotoTags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PhotoId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
TagId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PhotoTags", x => new { x.PhotoId, x.TagId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoTags_Photos_PhotoId",
|
||||||
|
column: x => x.PhotoId,
|
||||||
|
principalTable: "Photos",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoTags_Tags_TagId",
|
||||||
|
column: x => x.TagId,
|
||||||
|
principalTable: "Tags",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Galleries",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
|
||||||
|
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||||
|
CreatedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UpdatedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedBy = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
IsPublic = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 1),
|
||||||
|
IsArchived = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 0),
|
||||||
|
IsFavorite = table.Column<int>(type: "INTEGER", nullable: true, defaultValue: 0),
|
||||||
|
IsDeleted = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DeletedAt = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
EventId = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Galleries", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Galleries_Events_EventId",
|
||||||
|
column: x => x.EventId,
|
||||||
|
principalTable: "Events",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Galleries_Users_CreatedBy",
|
||||||
|
column: x => x.CreatedBy,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PhotoUserBuyers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PhotoId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PhotoUserBuyers", x => new { x.PhotoId, x.UserId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoUserBuyers_Photos_PhotoId",
|
||||||
|
column: x => x.PhotoId,
|
||||||
|
principalTable: "Photos",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_PhotoUserBuyers_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
RoleId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserRoles_Roles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "Roles",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserRoles_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GalleryPhotos",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
GalleryId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
PhotoId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GalleryPhotos", x => new { x.GalleryId, x.PhotoId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryPhotos_Galleries_GalleryId",
|
||||||
|
column: x => x.GalleryId,
|
||||||
|
principalTable: "Galleries",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryPhotos_Photos_PhotoId",
|
||||||
|
column: x => x.PhotoId,
|
||||||
|
principalTable: "Photos",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GalleryTags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
GalleryId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
TagId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GalleryTags", x => new { x.GalleryId, x.TagId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryTags_Galleries_GalleryId",
|
||||||
|
column: x => x.GalleryId,
|
||||||
|
principalTable: "Galleries",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryTags_Tags_TagId",
|
||||||
|
column: x => x.TagId,
|
||||||
|
principalTable: "Tags",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GalleryUserViewers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
GalleryId = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GalleryUserViewers", x => new { x.GalleryId, x.UserId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryUserViewers_Galleries_GalleryId",
|
||||||
|
column: x => x.GalleryId,
|
||||||
|
principalTable: "Galleries",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GalleryUserViewers_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EventTags_TagId",
|
||||||
|
table: "EventTags",
|
||||||
|
column: "TagId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Galleries_CreatedBy",
|
||||||
|
table: "Galleries",
|
||||||
|
column: "CreatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Galleries_EventId",
|
||||||
|
table: "Galleries",
|
||||||
|
column: "EventId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GalleryPhotos_PhotoId",
|
||||||
|
table: "GalleryPhotos",
|
||||||
|
column: "PhotoId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GalleryTags_TagId",
|
||||||
|
table: "GalleryTags",
|
||||||
|
column: "TagId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GalleryUserViewers_UserId",
|
||||||
|
table: "GalleryUserViewers",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Persons_SocialMediaId",
|
||||||
|
table: "Persons",
|
||||||
|
column: "SocialMediaId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PhotoPersons_PersonId",
|
||||||
|
table: "PhotoPersons",
|
||||||
|
column: "PersonId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Photos_CreatedBy",
|
||||||
|
table: "Photos",
|
||||||
|
column: "CreatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Photos_EventId",
|
||||||
|
table: "Photos",
|
||||||
|
column: "EventId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PhotoTags_TagId",
|
||||||
|
table: "PhotoTags",
|
||||||
|
column: "TagId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PhotoUserBuyers_UserId",
|
||||||
|
table: "PhotoUserBuyers",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RolePermissions_PermissionId",
|
||||||
|
table: "RolePermissions",
|
||||||
|
column: "PermissionId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Roles_BaseRoleModelId",
|
||||||
|
table: "Roles",
|
||||||
|
column: "BaseRoleModelId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Tags_Name",
|
||||||
|
table: "Tags",
|
||||||
|
column: "Name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserRoles_RoleId",
|
||||||
|
table: "UserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "__EFMigrationsLock");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "EventTags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GalleryPhotos");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GalleryTags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GalleryUserViewers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PhotoPersons");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PhotoTags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PhotoUserBuyers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Rankings");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RolePermissions");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Galleries");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Tags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Photos");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Permissions");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Roles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Events");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Persons");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SocialMedia");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
749
back/persistance/data/migrations/DataContextModelSnapshot.cs
Normal file
749
back/persistance/data/migrations/DataContextModelSnapshot.cs
Normal file
@@ -0,0 +1,749 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using back.persistance.data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace back.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
partial class DataContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("EventTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("EventId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("EventTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryPhoto", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "PhotoId");
|
||||||
|
|
||||||
|
b.HasIndex("PhotoId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryPhotos", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryUserViewer", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("GalleryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GalleryId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("GalleryUserViewers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoPerson", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PersonId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "PersonId");
|
||||||
|
|
||||||
|
b.HasIndex("PersonId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoPersons", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "TagId");
|
||||||
|
|
||||||
|
b.HasIndex("TagId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoTags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoUserBuyer", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("PhotoId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("PhotoId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("PhotoUserBuyers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RolePermission", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("RoleId", "PermissionId");
|
||||||
|
|
||||||
|
b.HasIndex("PermissionId");
|
||||||
|
|
||||||
|
b.ToTable("RolePermissions", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("UserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.EfmigrationsLock", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Timestamp")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("__EFMigrationsLock", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Event", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Location")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Gallery", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("IsArchived")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("IsFavorite")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsPublic")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Galleries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Permission", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Permissions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Avatar")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Bio")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DeletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("IsDeleted")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProfilePicture")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SocialMediaId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SocialMediaId");
|
||||||
|
|
||||||
|
b.ToTable("Persons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Photo", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EventId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HighResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("IsArchived")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsFavorite")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<int?>("IsPublic")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(1);
|
||||||
|
|
||||||
|
b.Property<string>("LowResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MidResUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RankingId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdatedBy")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.ToTable("Photos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Ranking", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("DownVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("TotalVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UpVotes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Rankings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("BaseRoleModelId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BaseRoleModelId");
|
||||||
|
|
||||||
|
b.ToTable("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.SocialMedia", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("BlueSky")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Discord")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Facebook")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Instagram")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Linkedin")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Other")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Pinterest")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Reddit")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Tiktok")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Twitter")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SocialMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(25)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Name" }, "IX_Tags_Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedAt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Salt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("EventTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Event", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryPhoto", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GalleryUserViewer", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Gallery", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoPerson", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PersonId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Tag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PhotoUserBuyer", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Photo", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RolePermission", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Permission", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PermissionId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Role", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("UserRole", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Role", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Gallery", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.User", "CreatedByNavigation")
|
||||||
|
.WithMany("Galleries")
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Event", "Event")
|
||||||
|
.WithMany("Galleries")
|
||||||
|
.HasForeignKey("EventId");
|
||||||
|
|
||||||
|
b.Navigation("CreatedByNavigation");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.SocialMedia", "SocialMedia")
|
||||||
|
.WithMany("People")
|
||||||
|
.HasForeignKey("SocialMediaId");
|
||||||
|
|
||||||
|
b.Navigation("SocialMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Photo", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", "CreatedByNavigation")
|
||||||
|
.WithMany("Photos")
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("back.DataModels.Event", "Event")
|
||||||
|
.WithMany("Photos")
|
||||||
|
.HasForeignKey("EventId");
|
||||||
|
|
||||||
|
b.Navigation("CreatedByNavigation");
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Role", "BaseRoleModel")
|
||||||
|
.WithMany("InverseBaseRoleModel")
|
||||||
|
.HasForeignKey("BaseRoleModelId");
|
||||||
|
|
||||||
|
b.Navigation("BaseRoleModel");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("back.DataModels.Person", "IdNavigation")
|
||||||
|
.WithOne("User")
|
||||||
|
.HasForeignKey("back.DataModels.User", "Id")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("IdNavigation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Event", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Galleries");
|
||||||
|
|
||||||
|
b.Navigation("Photos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Person", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Photos");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.Role", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("InverseBaseRoleModel");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.SocialMedia", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("People");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("back.DataModels.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Galleries");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
200
back/persistance/data/migrations/sqlite/tables.sql
Normal file
200
back/persistance/data/migrations/sqlite/tables.sql
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
-- Tabla de redes sociales (SocialMedia) y relación uno a uno con Person
|
||||||
|
CREATE TABLE IF NOT EXISTS SocialMedia (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Facebook TEXT,
|
||||||
|
Instagram TEXT,
|
||||||
|
Twitter TEXT,
|
||||||
|
BlueSky TEXT,
|
||||||
|
Tiktok TEXT,
|
||||||
|
Linkedin TEXT,
|
||||||
|
Pinterest TEXT,
|
||||||
|
Discord TEXT,
|
||||||
|
Reddit TEXT,
|
||||||
|
Other TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Person: cada persona tiene un grupo de redes sociales (uno a uno, fk opcional)
|
||||||
|
CREATE TABLE IF NOT EXISTS Persons (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Name TEXT NOT NULL,
|
||||||
|
ProfilePicture TEXT,
|
||||||
|
Avatar TEXT,
|
||||||
|
SocialMediaId TEXT,
|
||||||
|
Bio TEXT,
|
||||||
|
CreatedAt TEXT NOT NULL,
|
||||||
|
UpdatedAt TEXT,
|
||||||
|
FOREIGN KEY (SocialMediaId) REFERENCES SocialMedia(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- User: es una persona (herencia por clave primaria compartida)
|
||||||
|
CREATE TABLE IF NOT EXISTS Users (
|
||||||
|
Id TEXT PRIMARY KEY, -- MISMA clave y valor que Persons.Id
|
||||||
|
Email TEXT NOT NULL,
|
||||||
|
Password TEXT NOT NULL,
|
||||||
|
Salt TEXT NOT NULL,
|
||||||
|
FOREIGN KEY (Id) REFERENCES Persons(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Un usuario puede ver muchas galerías (muchos-a-muchos: Galleries <-> Users)
|
||||||
|
CREATE TABLE IF NOT EXISTS GalleryUserViewers (
|
||||||
|
GalleryId TEXT NOT NULL,
|
||||||
|
UserId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (GalleryId, UserId),
|
||||||
|
FOREIGN KEY (GalleryId) REFERENCES Galleries(Id),
|
||||||
|
FOREIGN KEY (UserId) REFERENCES Users(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Un usuario ha creado muchas galerías (uno a muchos)
|
||||||
|
-- Una galería solo puede ser creada por un usuario
|
||||||
|
CREATE TABLE IF NOT EXISTS Galleries (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Title TEXT,
|
||||||
|
Description TEXT,
|
||||||
|
CreatedAt TEXT,
|
||||||
|
UpdatedAt TEXT,
|
||||||
|
CreatedBy TEXT NOT NULL, -- FK a Users
|
||||||
|
IsPublic INTEGER DEFAULT 1,
|
||||||
|
IsArchived INTEGER DEFAULT 0,
|
||||||
|
IsFavorite INTEGER DEFAULT 0,
|
||||||
|
EventId TEXT, -- FK opcional a Events (una galería puede asociarse a un evento)
|
||||||
|
FOREIGN KEY (CreatedBy) REFERENCES Users(Id),
|
||||||
|
FOREIGN KEY (EventId) REFERENCES Events(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Galería-Photo: una galería contiene muchas imagenes, una imagen puede estar en muchas galerías (muchos-a-muchos)
|
||||||
|
CREATE TABLE IF NOT EXISTS GalleryPhotos (
|
||||||
|
GalleryId TEXT NOT NULL,
|
||||||
|
PhotoId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (GalleryId, PhotoId),
|
||||||
|
FOREIGN KEY (GalleryId) REFERENCES Galleries(Id),
|
||||||
|
FOREIGN KEY (PhotoId) REFERENCES Photos(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla de eventos
|
||||||
|
CREATE TABLE IF NOT EXISTS Events (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Title TEXT NOT NULL,
|
||||||
|
Description TEXT,
|
||||||
|
Date TEXT,
|
||||||
|
Location TEXT,
|
||||||
|
CreatedAt TEXT NOT NULL,
|
||||||
|
UpdatedAt TEXT NOT NULL,
|
||||||
|
CreatedBy TEXT,
|
||||||
|
UpdatedBy TEXT,
|
||||||
|
IsDeleted INTEGER NOT NULL DEFAULT 0,
|
||||||
|
DeletedAt TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla de fotos
|
||||||
|
CREATE TABLE IF NOT EXISTS Photos (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Title TEXT NOT NULL,
|
||||||
|
Description TEXT,
|
||||||
|
Extension TEXT,
|
||||||
|
LowResUrl TEXT,
|
||||||
|
MidResUrl TEXT,
|
||||||
|
HighResUrl TEXT,
|
||||||
|
CreatedAt TEXT,
|
||||||
|
UpdatedAt TEXT,
|
||||||
|
CreatedBy TEXT NOT NULL, -- Persona que subió la foto: FK a Persons
|
||||||
|
UpdatedBy TEXT,
|
||||||
|
EventId TEXT, -- Una photo solo puede tener un evento asociado (FK)
|
||||||
|
RankingId TEXT,
|
||||||
|
IsFavorite INTEGER DEFAULT 0,
|
||||||
|
IsPublic INTEGER DEFAULT 1,
|
||||||
|
IsArchived INTEGER DEFAULT 0,
|
||||||
|
FOREIGN KEY (CreatedBy) REFERENCES Persons(Id),
|
||||||
|
FOREIGN KEY (EventId) REFERENCES Events(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Una persona puede salir en muchas fotos, y una foto puede tener muchas personas (muchos-a-muchos)
|
||||||
|
CREATE TABLE IF NOT EXISTS PhotoPersons (
|
||||||
|
PhotoId TEXT NOT NULL,
|
||||||
|
PersonId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (PhotoId, PersonId),
|
||||||
|
FOREIGN KEY (PhotoId) REFERENCES Photos(Id),
|
||||||
|
FOREIGN KEY (PersonId) REFERENCES Persons(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Un usuario puede comprar muchas fotos para verlas, y una foto puede haber sido comprada por muchos usuarios
|
||||||
|
-- (solo necesario si IsPublic = 0)
|
||||||
|
CREATE TABLE IF NOT EXISTS PhotoUserBuyers (
|
||||||
|
PhotoId TEXT NOT NULL,
|
||||||
|
UserId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (PhotoId, UserId),
|
||||||
|
FOREIGN KEY (PhotoId) REFERENCES Photos(Id),
|
||||||
|
FOREIGN KEY (UserId) REFERENCES Users(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla de tags (únicos)
|
||||||
|
CREATE TABLE IF NOT EXISTS Tags (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Name TEXT NOT NULL UNIQUE,
|
||||||
|
CreatedAt TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Una foto puede tener muchos tags (muchos-a-muchos)
|
||||||
|
CREATE TABLE IF NOT EXISTS PhotoTags (
|
||||||
|
PhotoId TEXT NOT NULL,
|
||||||
|
TagId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (PhotoId, TagId),
|
||||||
|
FOREIGN KEY (PhotoId) REFERENCES Photos(Id),
|
||||||
|
FOREIGN KEY (TagId) REFERENCES Tags(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Un evento puede tener muchos tags (muchos-a-muchos)
|
||||||
|
CREATE TABLE IF NOT EXISTS EventTags (
|
||||||
|
EventId TEXT NOT NULL,
|
||||||
|
TagId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (EventId, TagId),
|
||||||
|
FOREIGN KEY (EventId) REFERENCES Events(Id),
|
||||||
|
FOREIGN KEY (TagId) REFERENCES Tags(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Una galería puede tener muchos tags (muchos-a-muchos)
|
||||||
|
CREATE TABLE IF NOT EXISTS GalleryTags (
|
||||||
|
GalleryId TEXT NOT NULL,
|
||||||
|
TagId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (GalleryId, TagId),
|
||||||
|
FOREIGN KEY (GalleryId) REFERENCES Galleries(Id),
|
||||||
|
FOREIGN KEY (TagId) REFERENCES Tags(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Rankings (por si corresponde)
|
||||||
|
CREATE TABLE IF NOT EXISTS Rankings (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
TotalVotes INTEGER NOT NULL,
|
||||||
|
UpVotes INTEGER NOT NULL,
|
||||||
|
DownVotes INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Permissions y Roles, tal y como en el mensaje anterior...
|
||||||
|
CREATE TABLE IF NOT EXISTS Permissions (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Name TEXT NOT NULL,
|
||||||
|
Description TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS Roles (
|
||||||
|
Id TEXT PRIMARY KEY,
|
||||||
|
Name TEXT NOT NULL,
|
||||||
|
Description TEXT,
|
||||||
|
BaseRoleModelId TEXT,
|
||||||
|
FOREIGN KEY (BaseRoleModelId) REFERENCES Roles(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS UserRoles (
|
||||||
|
UserId TEXT NOT NULL,
|
||||||
|
RoleId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (UserId, RoleId),
|
||||||
|
FOREIGN KEY (UserId) REFERENCES Users(Id),
|
||||||
|
FOREIGN KEY (RoleId) REFERENCES Roles(Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS RolePermissions (
|
||||||
|
RoleId TEXT NOT NULL,
|
||||||
|
PermissionId TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (RoleId, PermissionId),
|
||||||
|
FOREIGN KEY (RoleId) REFERENCES Roles(Id),
|
||||||
|
FOREIGN KEY (PermissionId) REFERENCES Permissions(Id)
|
||||||
|
);
|
28
back/persistance/data/relations/EventRelationEstablisher.cs
Normal file
28
back/persistance/data/relations/EventRelationEstablisher.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class EventRelationEstablisher: IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Event>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasMany(d => d.Tags).WithMany(p => p.Events)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"EventTag",
|
||||||
|
r => r.HasOne<Tag>().WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Event>().WithMany()
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("EventId", "TagId");
|
||||||
|
j.ToTable("EventTags");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,68 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class GalleryRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Gallery>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.IsArchived).HasDefaultValue(0);
|
||||||
|
entity.Property(e => e.IsFavorite).HasDefaultValue(0);
|
||||||
|
entity.Property(e => e.IsPublic).HasDefaultValue(1);
|
||||||
|
|
||||||
|
entity.HasOne(d => d.CreatedByNavigation).WithMany(p => p.Galleries)
|
||||||
|
.HasForeignKey(d => d.CreatedBy)
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull);
|
||||||
|
|
||||||
|
entity.HasOne(d => d.Event).WithMany(p => p.Galleries).HasForeignKey(d => d.EventId);
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Photos).WithMany(p => p.Galleries)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"GalleryPhoto",
|
||||||
|
r => r.HasOne<Photo>().WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Gallery>().WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("GalleryId", "PhotoId");
|
||||||
|
j.ToTable("GalleryPhotos");
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Tags).WithMany(p => p.Galleries)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"GalleryTag",
|
||||||
|
r => r.HasOne<Tag>().WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Gallery>().WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("GalleryId", "TagId");
|
||||||
|
j.ToTable("GalleryTags");
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Users).WithMany(p => p.GalleriesNavigation)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"GalleryUserViewer",
|
||||||
|
r => r.HasOne<User>().WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Gallery>().WithMany()
|
||||||
|
.HasForeignKey("GalleryId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("GalleryId", "UserId");
|
||||||
|
j.ToTable("GalleryUserViewers");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
8
back/persistance/data/relations/IRelationEstablisher.cs
Normal file
8
back/persistance/data/relations/IRelationEstablisher.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public interface IRelationEstablisher
|
||||||
|
{
|
||||||
|
void EstablishRelation(ModelBuilder modelBuilder);
|
||||||
|
}
|
68
back/persistance/data/relations/PersonRelationEstablisher.cs
Normal file
68
back/persistance/data/relations/PersonRelationEstablisher.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class PersonRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Photo>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.IsArchived).HasDefaultValue(0);
|
||||||
|
entity.Property(e => e.IsFavorite).HasDefaultValue(0);
|
||||||
|
entity.Property(e => e.IsPublic).HasDefaultValue(1);
|
||||||
|
|
||||||
|
entity.HasOne(d => d.CreatedByNavigation).WithMany(p => p.Photos)
|
||||||
|
.HasForeignKey(d => d.CreatedBy)
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull);
|
||||||
|
|
||||||
|
entity.HasOne(d => d.Event).WithMany(p => p.Photos).HasForeignKey(d => d.EventId);
|
||||||
|
|
||||||
|
entity.HasMany(d => d.People).WithMany(p => p.PhotosNavigation)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"PhotoPerson",
|
||||||
|
r => r.HasOne<Person>().WithMany()
|
||||||
|
.HasForeignKey("PersonId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Photo>().WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("PhotoId", "PersonId");
|
||||||
|
j.ToTable("PhotoPersons");
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Tags).WithMany(p => p.Photos)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"PhotoTag",
|
||||||
|
r => r.HasOne<Tag>().WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Photo>().WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("PhotoId", "TagId");
|
||||||
|
j.ToTable("PhotoTags");
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Users).WithMany(p => p.Photos)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"PhotoUserBuyer",
|
||||||
|
r => r.HasOne<User>().WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Photo>().WithMany()
|
||||||
|
.HasForeignKey("PhotoId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("PhotoId", "UserId");
|
||||||
|
j.ToTable("PhotoUserBuyers");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
307
back/persistance/data/relations/PhotoContext.cs
Normal file
307
back/persistance/data/relations/PhotoContext.cs
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
//using back.DataModels;
|
||||||
|
//using back.DTO;
|
||||||
|
//using back.persistance.blob;
|
||||||
|
//using back.services.ImageResizer;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
//using Microsoft.Extensions.Hosting.Internal;
|
||||||
|
|
||||||
|
//namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
//public class PhotoContext : DbContext
|
||||||
|
//{
|
||||||
|
|
||||||
|
// private readonly IImageResizer _Resizer;
|
||||||
|
// private readonly IBlobStorageService _BlobStorage;
|
||||||
|
|
||||||
|
// private readonly TagContext _tagContext;
|
||||||
|
// private readonly EventContext _eventContext;
|
||||||
|
// private readonly PersonRelationEstablisher _personContext;
|
||||||
|
|
||||||
|
// public PhotoContext(DbContextOptions<PhotoContext> options, IHostEnvironment hostingEnvironment,
|
||||||
|
// IImageResizer resizer,
|
||||||
|
// IBlobStorageService blobStorage,
|
||||||
|
// TagContext tags,
|
||||||
|
// EventContext events,
|
||||||
|
// PersonRelationEstablisher persons
|
||||||
|
// ) : base(options)
|
||||||
|
// {
|
||||||
|
// _Resizer = resizer;
|
||||||
|
// _BlobStorage = blobStorage;
|
||||||
|
// _tagContext = tags;
|
||||||
|
// _eventContext = events;
|
||||||
|
// _personContext = persons;
|
||||||
|
|
||||||
|
// if (hostingEnvironment.IsDevelopment())
|
||||||
|
// {
|
||||||
|
// Database.EnsureCreated();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Database.Migrate();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
// {
|
||||||
|
// // Photo -> Tags (muchos-a-muchos)
|
||||||
|
// modelBuilder.Entity<Photo>()
|
||||||
|
// .HasMany(p => p.Tags)
|
||||||
|
// .WithMany(t => t.Photos)
|
||||||
|
// .UsingEntity(j => j.ToTable("PhotoTags"));
|
||||||
|
|
||||||
|
// // Photo -> Persons (muchos-a-muchos)
|
||||||
|
// modelBuilder.Entity<Photo>()
|
||||||
|
// .HasMany(p => p.PersonsIn)
|
||||||
|
// .WithMany(per => per.Photos)
|
||||||
|
// .UsingEntity(j => j.ToTable("PhotoPersons"));
|
||||||
|
|
||||||
|
// // Photo -> Event (muchos-a-uno)
|
||||||
|
// modelBuilder.Entity<Photo>()
|
||||||
|
// .HasOne(p => p.Event)
|
||||||
|
// .WithMany() // Un evento puede tener múltiples fotos
|
||||||
|
// .HasForeignKey(p => p.EventId);
|
||||||
|
|
||||||
|
// // Photo -> Ranking (uno-a-uno)
|
||||||
|
// modelBuilder.Entity<Photo>()
|
||||||
|
// .HasOne(p => p.Ranking)
|
||||||
|
// .WithOne(r => r.Photo) // Un ranking está asociado a una sola foto
|
||||||
|
// .HasForeignKey<Photo>(p => p.RankingId);
|
||||||
|
|
||||||
|
// base.OnModelCreating(modelBuilder);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task CreateNew(PhotoFormModel? form)
|
||||||
|
// {
|
||||||
|
// if (form == null) { return; }
|
||||||
|
|
||||||
|
// var photo = new Photo(
|
||||||
|
// Guid.NewGuid().ToString(),
|
||||||
|
// form.Title,
|
||||||
|
// form.Description ?? string.Empty,
|
||||||
|
// string.Empty, // LowResUrl will be set later
|
||||||
|
// string.Empty, // MidResUrl will be set later
|
||||||
|
// string.Empty, // HighResUrl will be set later
|
||||||
|
// DateTime.UtcNow,
|
||||||
|
// DateTime.UtcNow,
|
||||||
|
// form.UserId,
|
||||||
|
// form.UserId
|
||||||
|
// )
|
||||||
|
// {
|
||||||
|
// IsPublic = form.IsPublic
|
||||||
|
// };
|
||||||
|
|
||||||
|
// List<Task> tasks = [
|
||||||
|
// SaveBlob(photo, form),
|
||||||
|
// LinkTags(photo, form.Tags ?? [], form.UserId),
|
||||||
|
// LinkEvent(photo, form.Evento ?? "", form.UserId),
|
||||||
|
// LinkPersons(photo, form.People ?? [], form.UserId),
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// await Task.WhenAll(tasks);
|
||||||
|
// await Photos.AddAsync(photo);
|
||||||
|
// await SaveChangesAsync();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkPersons(Photo photo, string[] personas, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (photo == null || personas == null || personas.Length == 0) return;
|
||||||
|
// foreach (var personId in personas)
|
||||||
|
// {
|
||||||
|
// var person = await _personContext.GetById(personId);
|
||||||
|
// if (person != null)
|
||||||
|
// {
|
||||||
|
// await LinkPersons(photo, person, updatedBy);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkPersons(Photo photo, Person tag, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (tag == null) return;
|
||||||
|
// // Ensure the tag exists
|
||||||
|
// if (await _personContext.Exists(tag.Id))
|
||||||
|
// {
|
||||||
|
// photo.PersonsIn ??= [];
|
||||||
|
// photo.PersonsIn.Add(tag);
|
||||||
|
// photo.UpdatedAt = DateTime.UtcNow;
|
||||||
|
// photo.UpdatedBy = updatedBy; // or use a more appropriate value
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkTags(Photo photo, string[] tags, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (photo == null || tags == null || tags.Length == 0) return;
|
||||||
|
// foreach (var tagId in tags)
|
||||||
|
// {
|
||||||
|
// var tag = await _tagContext.GetById(tagId);
|
||||||
|
// if (tag != null)
|
||||||
|
// {
|
||||||
|
// await LinkTag(photo, tag, updatedBy);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkTag(Photo photo, Tag tag, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (tag == null) return;
|
||||||
|
// // Ensure the tag exists
|
||||||
|
// if (await _tagContext.Exists(tag.Id))
|
||||||
|
// {
|
||||||
|
// photo.Tags.Add(tag);
|
||||||
|
// photo.UpdatedAt = DateTime.UtcNow;
|
||||||
|
// photo.UpdatedBy = updatedBy; // or use a more appropriate value
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkEvent(Photo photo, string eventId, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (string.IsNullOrEmpty(eventId)) return;
|
||||||
|
// var evento = await _eventContext.GetById(eventId);
|
||||||
|
// if (evento != null)
|
||||||
|
// {
|
||||||
|
// await LinkEvent(photo, evento, updatedBy);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task LinkEvent(Photo photo, Event? evento, string updatedBy = "SYSTEM")
|
||||||
|
// {
|
||||||
|
// if (evento == null) return;
|
||||||
|
// // Ensure the event exists
|
||||||
|
// if (await _eventContext.Exists(evento.Id))
|
||||||
|
// {
|
||||||
|
// photo.Event = evento;
|
||||||
|
// photo.UpdatedAt = DateTime.UtcNow;
|
||||||
|
// photo.UpdatedBy = updatedBy;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private async Task SaveBlob(Photo photo, PhotoFormModel form)
|
||||||
|
// {
|
||||||
|
// if (form.Image != null && form.Image.Length > 0)
|
||||||
|
// {
|
||||||
|
// var lowRes = await _Resizer.ResizeImage(form.Image, 480);
|
||||||
|
// var midRes = await _Resizer.ResizeImage(form.Image, 720);
|
||||||
|
// // Upload images to blob storage
|
||||||
|
// photo.Extension = form.Image.FileName.Split('.').Last();
|
||||||
|
// photo.LowResUrl = $"low/{photo.Id}.webp";
|
||||||
|
// photo.MidResUrl = $"mid/{photo.Id}.webp";
|
||||||
|
// photo.HighResUrl = $"high/{photo.Id}.{photo.Extension}";
|
||||||
|
// await _BlobStorage.SaveAsync(lowRes, photo.LowResUrl);
|
||||||
|
// await _BlobStorage.SaveAsync(midRes, photo.MidResUrl);
|
||||||
|
// await _BlobStorage.SaveAsync(form.Image.OpenReadStream(), photo.HighResUrl);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<Photo?> GetById(string id)
|
||||||
|
// {
|
||||||
|
// return await GetById(Guid.Parse(id));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<Photo?> GetById(Guid id)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// return await Photos.FindAsync(id);
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<int> GetTotalItems()
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// return await Photos.CountAsync();
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<IEnumerable<Photo>?> GetPage(int page = 1, int pageSize = 20)
|
||||||
|
// {
|
||||||
|
// if (page < 1) page = 1;
|
||||||
|
// if (pageSize < 1) pageSize = 20;
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// return await Photos
|
||||||
|
// .OrderByDescending(p => p.CreatedAt)
|
||||||
|
// .Skip((page - 1) * pageSize)
|
||||||
|
// .Take(pageSize)
|
||||||
|
// .ToListAsync();
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<bool> Exists(Photo? photo)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// if (photo == null) return false;
|
||||||
|
// if (string.IsNullOrEmpty(photo.Id)) return false;
|
||||||
|
// return await Photos.AnyAsync(p => p.Id == photo.Id);
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// return false; // Handle exceptions gracefully
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task<bool> Exists(string id)
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// if (string.IsNullOrEmpty(id)) return false;
|
||||||
|
// return await Photos.AnyAsync(p => p.Id == id);
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// return false; // Handle exceptions gracefully
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task Delete(Photo photo)
|
||||||
|
// {
|
||||||
|
// if (photo == null) return;
|
||||||
|
// if (await Exists(photo))
|
||||||
|
// {
|
||||||
|
// // Delete the photo from blob storage
|
||||||
|
// if (!string.IsNullOrEmpty(photo.LowResUrl))
|
||||||
|
// await _BlobStorage.DeleteAsync(photo.LowResUrl);
|
||||||
|
// if (!string.IsNullOrEmpty(photo.MidResUrl))
|
||||||
|
// await _BlobStorage.DeleteAsync(photo.MidResUrl);
|
||||||
|
// if (!string.IsNullOrEmpty(photo.HighResUrl))
|
||||||
|
// await _BlobStorage.DeleteAsync(photo.HighResUrl);
|
||||||
|
// Photos.Remove(photo);
|
||||||
|
// await SaveChangesAsync();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task Update(Photo photo)
|
||||||
|
// {
|
||||||
|
// if (photo == null) return;
|
||||||
|
// if (await Exists(photo))
|
||||||
|
// {
|
||||||
|
// var evento = photo.Event;
|
||||||
|
// photo.Event = null;
|
||||||
|
// await LinkEvent(photo, evento, photo.UpdatedBy);
|
||||||
|
|
||||||
|
// var tags = photo.Tags.Select(t => t.Id);
|
||||||
|
// photo.Tags.Clear();
|
||||||
|
// await LinkTags(photo, [.. tags], photo.UpdatedBy);
|
||||||
|
|
||||||
|
// var persons = photo.PersonsIn?.Select(t => t.Id) ?? [];
|
||||||
|
// photo.PersonsIn = null;
|
||||||
|
// await LinkPersons(photo, [.. persons], photo.UpdatedBy);
|
||||||
|
|
||||||
|
// Photos.Update(photo);
|
||||||
|
// await SaveChangesAsync();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
15
back/persistance/data/relations/PhotoRelationEstablisher.cs
Normal file
15
back/persistance/data/relations/PhotoRelationEstablisher.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class PhotoRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Person>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasOne(d => d.SocialMedia).WithMany(p => p.People).HasForeignKey(d => d.SocialMediaId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
25
back/persistance/data/relations/RoleContext.cs
Normal file
25
back/persistance/data/relations/RoleContext.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//using back.DataModels;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
|
||||||
|
//public class RoleContext : DbContext
|
||||||
|
//{
|
||||||
|
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
// {
|
||||||
|
// // Role -> Permissions (muchos-a-muchos)
|
||||||
|
// modelBuilder.Entity<Role>()
|
||||||
|
// .HasMany(r => r.Permissions)
|
||||||
|
// .WithMany(p => p.Roles)
|
||||||
|
// .UsingEntity(j => j.ToTable("RolePermissions"));
|
||||||
|
|
||||||
|
// // Role -> BaseRole (auto-referencial)
|
||||||
|
// modelBuilder.Entity<Role>()
|
||||||
|
// .HasOne(r => r.BaseRoleModel)
|
||||||
|
// .WithMany() // Un rol base puede ser heredado por múltiples roles
|
||||||
|
// .HasForeignKey(r => r.BaseRoleModelId);
|
||||||
|
|
||||||
|
// base.OnModelCreating(modelBuilder);
|
||||||
|
// }
|
||||||
|
//}
|
30
back/persistance/data/relations/RoleRelationEstablisher.cs
Normal file
30
back/persistance/data/relations/RoleRelationEstablisher.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class RoleRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Role>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasOne(d => d.BaseRoleModel).WithMany(p => p.InverseBaseRoleModel).HasForeignKey(d => d.BaseRoleModelId);
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Permissions).WithMany(p => p.Roles)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"RolePermission",
|
||||||
|
r => r.HasOne<Permission>().WithMany()
|
||||||
|
.HasForeignKey("PermissionId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<Role>().WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("RoleId", "PermissionId");
|
||||||
|
j.ToTable("RolePermissions");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
40
back/persistance/data/relations/SeedingDbContext.cs
Normal file
40
back/persistance/data/relations/SeedingDbContext.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//using back.DataModels;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
//public class SeedingDbContext : DbContext
|
||||||
|
//{
|
||||||
|
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
// {
|
||||||
|
// // 3. CONFIGURAR RELACIONES
|
||||||
|
// modelBuilder.Entity<Role>()
|
||||||
|
// .HasMany(r => r.Permissions)
|
||||||
|
// .WithMany(p => p.Roles)
|
||||||
|
// .UsingEntity<Dictionary<string, object>>(
|
||||||
|
// "RolePermissions",
|
||||||
|
// j => j.HasOne<Permission>().WithMany().HasForeignKey("PermissionsId"),
|
||||||
|
// j => j.HasOne<Role>().WithMany().HasForeignKey("RolesId"),
|
||||||
|
// j => j.HasData(
|
||||||
|
// // Usuario: VIEW_CONTENT y LIKE_CONTENT
|
||||||
|
// new { RolesId = "1", PermissionsId = "1" },
|
||||||
|
// new { RolesId = "1", PermissionsId = "2" },
|
||||||
|
|
||||||
|
// // Content Manager: permisos adicionales
|
||||||
|
// new { RolesId = "2", PermissionsId = "5" },
|
||||||
|
// new { RolesId = "2", PermissionsId = "3" },
|
||||||
|
// new { RolesId = "2", PermissionsId = "4" },
|
||||||
|
// new { RolesId = "2", PermissionsId = "9" },
|
||||||
|
// new { RolesId = "2", PermissionsId = "8" },
|
||||||
|
|
||||||
|
// // Admin: permisos adicionales
|
||||||
|
// new { RolesId = "3", PermissionsId = "6" },
|
||||||
|
// new { RolesId = "3", PermissionsId = "7" },
|
||||||
|
// new { RolesId = "3", PermissionsId = "10" }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Resto de configuraciones...
|
||||||
|
// base.OnModelCreating(modelBuilder);
|
||||||
|
// }
|
||||||
|
//}
|
15
back/persistance/data/relations/TagRelationEstablisher.cs
Normal file
15
back/persistance/data/relations/TagRelationEstablisher.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class TagRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Tag>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasIndex(e => e.Name, "IX_Tags_Name").IsUnique();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
back/persistance/data/relations/UserRelationEstablisher.cs
Normal file
32
back/persistance/data/relations/UserRelationEstablisher.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.relations;
|
||||||
|
|
||||||
|
public class UserRelationEstablisher : IRelationEstablisher
|
||||||
|
{
|
||||||
|
public void EstablishRelation(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<User>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasOne(d => d.IdNavigation).WithOne(p => p.User)
|
||||||
|
.HasForeignKey<User>(d => d.Id)
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull);
|
||||||
|
|
||||||
|
entity.HasMany(d => d.Roles).WithMany(p => p.Users)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"UserRole",
|
||||||
|
r => r.HasOne<Role>().WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
l => l.HasOne<User>().WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.ClientSetNull),
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
j.HasKey("UserId", "RoleId");
|
||||||
|
j.ToTable("UserRoles");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
|
public interface IPersonRepository : IRepository<Person, string>, IScoped
|
||||||
|
{
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
|
public interface IPhotoRepository : IRepository<Photo, string>, IScoped
|
||||||
|
{ }
|
@@ -0,0 +1,14 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
|
public interface IUserRepository : IRepository<User, string>, IScoped
|
||||||
|
{
|
||||||
|
Task<User?> GetByEmail(string email);
|
||||||
|
Task<string?> GetUserSaltByEmail(string email);
|
||||||
|
Task<User?> Login(string email, string password);
|
||||||
|
Task<bool> ExistsByEmail(string email);
|
||||||
|
//Task<bool> IsContentManager(string userId);
|
||||||
|
}
|
10
back/persistance/data/repositories/PersonRepository.cs
Normal file
10
back/persistance/data/repositories/PersonRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using Transactional.Implementations.EntityFramework;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
|
public class PersonRepository(DataContext context) : ReadWriteRepository<Person, string>(context), IPersonRepository
|
||||||
|
{
|
||||||
|
// Implement methods specific to Photo repository if needed
|
||||||
|
}
|
10
back/persistance/data/repositories/PhotoRepository.cs
Normal file
10
back/persistance/data/repositories/PhotoRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using Transactional.Implementations.EntityFramework;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
|
public class PhotoRepository(DataContext context) : ReadWriteRepository<Photo, string>(context), IPhotoRepository
|
||||||
|
{
|
||||||
|
// Implement methods specific to Photo repository if needed
|
||||||
|
}
|
70
back/persistance/data/repositories/UserRepository.cs
Normal file
70
back/persistance/data/repositories/UserRepository.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Transactional.Implementations.EntityFramework;
|
||||||
|
|
||||||
|
namespace back.persistance.data.repositories;
|
||||||
|
|
||||||
|
public class UserRepository(DataContext context) : ReadWriteRepository<User, string>(context), IUserRepository
|
||||||
|
{
|
||||||
|
public async Task<User?> GetByEmail(string email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email)) return null;
|
||||||
|
return await Entity.FirstOrDefaultAsync(u => u.Email == email);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> GetUserSaltByEmail(string email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email)) return string.Empty;
|
||||||
|
var user = await Entity.FirstOrDefaultAsync(u => u.Email == email);
|
||||||
|
return user?.Salt ?? string.Empty;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> Login(string email, string password)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await Entity.FirstOrDefaultAsync(u => u.Email == email && u.Password == password);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ExistsByEmail(string email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email)) return false;
|
||||||
|
return await Entity.AnyAsync(u => u.Email == email);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public async Task<bool> IsContentManager(string userId)
|
||||||
|
//{
|
||||||
|
// var user = await GetById(userId);
|
||||||
|
// if (user == null)
|
||||||
|
// return false;
|
||||||
|
// return user.Roles.Any(role => role.IsContentManager() || role.IsAdmin());
|
||||||
|
//}
|
||||||
|
}
|
8
back/persistance/data/seeders/ISeeder.cs
Normal file
8
back/persistance/data/seeders/ISeeder.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.seeders;
|
||||||
|
|
||||||
|
public interface ISeeder
|
||||||
|
{
|
||||||
|
void Seed(ModelBuilder modelBuilder);
|
||||||
|
}
|
23
back/persistance/data/seeders/PermissionSeeder.cs
Normal file
23
back/persistance/data/seeders/PermissionSeeder.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//using back.DataModels;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//namespace back.persistance.data.seeders;
|
||||||
|
|
||||||
|
//public class PermissionSeeder : ISeeder
|
||||||
|
//{
|
||||||
|
// public void Seed(ModelBuilder modelBuilder)
|
||||||
|
// {
|
||||||
|
// modelBuilder.Entity<Permission>().HasData(
|
||||||
|
// Permission.ViewContentPermission,
|
||||||
|
// Permission.LikeContentPermission,
|
||||||
|
// Permission.EditContentPermission,
|
||||||
|
// Permission.DeleteContentPermission,
|
||||||
|
// Permission.CreateContentPermission,
|
||||||
|
// Permission.EditUserPermission,
|
||||||
|
// Permission.DeleteUserPermission,
|
||||||
|
// Permission.DisableUserPermission,
|
||||||
|
// Permission.CreateUserPermission,
|
||||||
|
// Permission.EditWebConfigPermission
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//}
|
16
back/persistance/data/seeders/RoleSeeder.cs
Normal file
16
back/persistance/data/seeders/RoleSeeder.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//using back.DataModels;
|
||||||
|
//using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
//namespace back.persistance.data.seeders;
|
||||||
|
|
||||||
|
//public class RoleSeeder : ISeeder
|
||||||
|
//{
|
||||||
|
// public void Seed(ModelBuilder modelBuilder)
|
||||||
|
// {
|
||||||
|
// modelBuilder.Entity<Permission>().HasData(
|
||||||
|
// new Role { Id = "1", Name = "User", Description = "Role for regular users", BaseRoleModelId = null },
|
||||||
|
// new Role { Id = "2", Name = "Content Manager", Description = "Role for managing content", BaseRoleModelId = "1" },
|
||||||
|
// new Role { Id = "3", Name = "Admin", Description = "Administrator role with full permissions", BaseRoleModelId = "2" }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//}
|
14
back/persistance/data/seeders/SystemUserSeeder.cs
Normal file
14
back/persistance/data/seeders/SystemUserSeeder.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace back.persistance.data.seeders;
|
||||||
|
|
||||||
|
public class SystemUserSeeder : ISeeder
|
||||||
|
{
|
||||||
|
public void Seed(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<Permission>().HasData(
|
||||||
|
User.SystemUser
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +0,0 @@
|
|||||||
namespace back.services.ImageResizer;
|
|
||||||
|
|
||||||
public interface IImageResizer
|
|
||||||
{
|
|
||||||
Task<Stream> ResizeImage(IFormFile image, int v);
|
|
||||||
}
|
|
11
back/services/bussines/Errors.cs
Normal file
11
back/services/bussines/Errors.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace back.services.bussines;
|
||||||
|
|
||||||
|
public static class Errors
|
||||||
|
{
|
||||||
|
public static readonly HttpErrorMap Unauthorized =
|
||||||
|
new(HttpStatusCode.Unauthorized, "Invalid user data. Email or password are wrong.");
|
||||||
|
public static readonly HttpErrorMap BadRequest =
|
||||||
|
new(HttpStatusCode.BadRequest, "Missing user data.");
|
||||||
|
}
|
5
back/services/bussines/HttpErrorMap.cs
Normal file
5
back/services/bussines/HttpErrorMap.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace back.services.bussines;
|
||||||
|
|
||||||
|
public record HttpErrorMap(HttpStatusCode Code, string Description);
|
16
back/services/bussines/PhotoService/IPhotoService.cs
Normal file
16
back/services/bussines/PhotoService/IPhotoService.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.DTO;
|
||||||
|
using DependencyInjector.Abstractions.ClassTypes;
|
||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.bussines.PhotoService;
|
||||||
|
|
||||||
|
public interface IPhotoService: IScoped
|
||||||
|
{
|
||||||
|
Task<Photo?> Create(PhotoFormModel form);
|
||||||
|
Task Delete(string id, string userId = "00000000-0000-0000-0000-000000000001");
|
||||||
|
Task<Photo?> Get(string id, string userId = "00000000-0000-0000-0000-000000000001");
|
||||||
|
Task<(string? mediaType, byte[]? fileBytes)> GetBytes(string id, string res = "");
|
||||||
|
Task<(int totalItems, IEnumerable<Photo>? pageData)> GetPage(int page, int pageSize);
|
||||||
|
Task<Photo?> Update(Photo photo, string userId = "00000000-0000-0000-0000-000000000001");
|
||||||
|
}
|
82
back/services/bussines/PhotoService/PhotoService.cs
Normal file
82
back/services/bussines/PhotoService/PhotoService.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.DTO;
|
||||||
|
using back.persistance.blob;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
|
||||||
|
namespace back.services.bussines.PhotoService;
|
||||||
|
|
||||||
|
public class PhotoService(
|
||||||
|
IPhotoRepository photoRepository,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IBlobStorageService blobStorageService
|
||||||
|
) : IPhotoService
|
||||||
|
{
|
||||||
|
public async Task<Photo?> Create(PhotoFormModel form)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(form);
|
||||||
|
if (form.Image == null || form.Image.Length == 0)
|
||||||
|
throw new ArgumentException("No image uploaded.", nameof(form));
|
||||||
|
//if (string.IsNullOrEmpty(form.UserId) || await userRepository.IsContentManager(form.UserId))
|
||||||
|
// throw new ArgumentException("Invalid user ID or user is not a content manager.", nameof(form.UserId));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete(string id, string userId = User.SystemUserId)
|
||||||
|
{
|
||||||
|
//if (string.IsNullOrEmpty(userId) || await userRepository.IsContentManager(userId))
|
||||||
|
// throw new ArgumentException("Invalid user ID or user is not a content manager.", nameof(userId));
|
||||||
|
photoRepository.Delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Photo?> Get(string id, string userId = User.SystemUserId)
|
||||||
|
{
|
||||||
|
Photo? photo = await photoRepository.GetById(id);
|
||||||
|
return photo;
|
||||||
|
//return photo?.CanBeSeenBy(userId) ?? false
|
||||||
|
// ? photo
|
||||||
|
// : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(string? mediaType, byte[]? fileBytes)> GetBytes(string id, string res = "")
|
||||||
|
{
|
||||||
|
var photo = await photoRepository.GetById(id);
|
||||||
|
if (photo == null)
|
||||||
|
return (null, null);
|
||||||
|
|
||||||
|
string filePath = res.ToLower() switch
|
||||||
|
{
|
||||||
|
"high" => photo.HighResUrl,
|
||||||
|
"mid" => photo.MidResUrl,
|
||||||
|
"low" or _ => photo.LowResUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
string? mediaType = res.ToLower() switch
|
||||||
|
{
|
||||||
|
"high" => $"image/{photo.Extension}",
|
||||||
|
"mid" or "low" or _ => "image/webp",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
mediaType,
|
||||||
|
await blobStorageService.GetBytes(filePath) ?? throw new FileNotFoundException("File not found.", filePath)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(int totalItems, IEnumerable<Photo>? pageData)> GetPage(int page, int pageSize)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
totalItems: await photoRepository.GetTotalItems(),
|
||||||
|
pageData: photoRepository.GetPage(page, pageSize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Photo?> Update(Photo photo, string userId = "00000000-0000-0000-0000-000000000001")
|
||||||
|
{
|
||||||
|
//if (string.IsNullOrEmpty(userId) || await userRepository.IsContentManager(userId))
|
||||||
|
// throw new ArgumentException("Invalid user ID or user is not a content manager.", nameof(userId));
|
||||||
|
return await photoRepository.Update(photo);
|
||||||
|
}
|
||||||
|
}
|
14
back/services/bussines/UserService/IUserService.cs
Normal file
14
back/services/bussines/UserService/IUserService.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using DependencyInjector.Abstractions.ClassTypes;
|
||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.bussines.UserService;
|
||||||
|
|
||||||
|
public interface IUserService: IScoped
|
||||||
|
{
|
||||||
|
Task<User?> Create(string clientId, User user);
|
||||||
|
Task<User?> Login(string email, string password, string clientId);
|
||||||
|
Task SendResetPassword(string email);
|
||||||
|
Task<User?> Update(User user);
|
||||||
|
Task<User?> ValidateSystemUser(string email, string password, string systemKey, string clientId);
|
||||||
|
}
|
133
back/services/bussines/UserService/UserService.cs
Normal file
133
back/services/bussines/UserService/UserService.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.blob;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using back.services.engine.Crypto;
|
||||||
|
using back.services.engine.mailing;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace back.services.bussines.UserService;
|
||||||
|
|
||||||
|
public class UserService(
|
||||||
|
IUserRepository userRepository, ICryptoService cryptoService,
|
||||||
|
IEmailService emailService,
|
||||||
|
IBlobStorageService blobStorageService
|
||||||
|
) : IUserService
|
||||||
|
{
|
||||||
|
private readonly IUserRepository _repository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
|
||||||
|
private readonly ICryptoService _cryptoService = cryptoService;
|
||||||
|
private readonly IEmailService _emailService = emailService;
|
||||||
|
private readonly IBlobStorageService _blobStorageService = blobStorageService;
|
||||||
|
|
||||||
|
public async Task<User?> Create(string clientId, User user)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
|
|
||||||
|
if (user.Id != null && await _repository.Exists(user.Id))
|
||||||
|
{
|
||||||
|
return await _repository.GetById(user.Id);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (await _repository.Exists(user.Email))
|
||||||
|
{
|
||||||
|
return await _repository.GetByEmail(user.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(user.Salt))
|
||||||
|
{
|
||||||
|
user.Salt = _cryptoService.Salt();
|
||||||
|
}
|
||||||
|
user.Password = _cryptoService.Decrypt(clientId, user.Password) ?? string.Empty;
|
||||||
|
user.Password = _cryptoService.HashPassword(user.Password, user.Salt) ?? string.Empty;
|
||||||
|
|
||||||
|
user.CreatedAt = DateTimeOffset.UtcNow.ToString("dd-MM-yyyy HH:mm:ss zz");
|
||||||
|
|
||||||
|
//user.Roles.Add(Role.UserRole);
|
||||||
|
|
||||||
|
await _repository.Insert(user);
|
||||||
|
await _repository.SaveChanges();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> Update(User user)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
|
if (user.Id == null || !await _repository.Exists(user.Id))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var existingUser = await _repository.GetById(user.Id);
|
||||||
|
if (existingUser == null) return null;
|
||||||
|
existingUser.Email = user.Email;
|
||||||
|
await _repository.Update(existingUser);
|
||||||
|
await _repository.SaveChanges();
|
||||||
|
return existingUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> Login(string email, string password, string clientId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var decryptedPass = _cryptoService.Decrypt(clientId, password);
|
||||||
|
var salt = await _repository.GetUserSaltByEmail(email);
|
||||||
|
var hashedPassword = _cryptoService.HashPassword(decryptedPass, salt);
|
||||||
|
var user = await _repository.Login(email, hashedPassword ?? string.Empty);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendResetPassword(string email)
|
||||||
|
{
|
||||||
|
var exists = await _repository.ExistsByEmail(email);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _emailService.SendEmailAsync(
|
||||||
|
tos: email,
|
||||||
|
from: "admin@mmorales.photo",
|
||||||
|
subject: "Reset Password",
|
||||||
|
body: "If you received this email, it means that you have requested a password reset. Please follow the instructions in the email to reset your password."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> ValidateSystemUser(string email, string password, string systemKey, string clientId)
|
||||||
|
{
|
||||||
|
password = _cryptoService.Decrypt(clientId, password) ?? string.Empty;
|
||||||
|
systemKey = _cryptoService.Decrypt(clientId, systemKey) ?? string.Empty;
|
||||||
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(systemKey))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!email.Equals("@system", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemKeyBytes = await _blobStorageService.GetBytes("systemkey.lock");
|
||||||
|
var systemKeyString = Encoding.UTF8.GetString(systemKeyBytes ?? []);
|
||||||
|
var systemKeyObject = System.Text.Json.JsonSerializer.Deserialize<SystemKey>(systemKeyString);
|
||||||
|
if (systemKeyObject == null || !systemKeyObject.IsValid(email, password, systemKey))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!await _repository.ExistsByEmail(email))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var user = await _repository.GetByEmail(email);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await Login(user.Email!, user.Password!, clientId);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace back.services.Crypto;
|
namespace back.services.engine.Crypto;
|
||||||
|
|
||||||
public class CryptoService(IMemoryCache cache) : ICryptoService
|
public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public string? Encrypt(string plainText, string clientId)
|
public string? Encrypt(string clientId,string plainText)
|
||||||
{
|
{
|
||||||
// get keys from cache
|
// get keys from cache
|
||||||
if (!_cache.TryGetValue($"{clientId}_private", out string? privateCert) || string.IsNullOrEmpty(privateCert))
|
if (!_cache.TryGetValue($"{clientId}_private", out string? privateCert) || string.IsNullOrEmpty(privateCert))
|
||||||
@@ -41,7 +41,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
}
|
}
|
||||||
// import rsa keys and configure RSA for encryption
|
// import rsa keys and configure RSA for encryption
|
||||||
using var rsa = RSA.Create(2048);
|
using var rsa = RSA.Create(2048);
|
||||||
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicCert), out _);
|
rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicCert), out _);
|
||||||
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
||||||
// Encrypt the plain text using RSA
|
// Encrypt the plain text using RSA
|
||||||
string? encryptedText = null;
|
string? encryptedText = null;
|
||||||
@@ -59,7 +59,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
return encryptedText;
|
return encryptedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? Decrypt(string encryptedText, string clientId)
|
public string? Decrypt(string clientId, string encryptedText)
|
||||||
{
|
{
|
||||||
// get keys from cache
|
// get keys from cache
|
||||||
if (!_cache.TryGetValue($"{clientId}_private", out string? privateCert) || string.IsNullOrEmpty(privateCert))
|
if (!_cache.TryGetValue($"{clientId}_private", out string? privateCert) || string.IsNullOrEmpty(privateCert))
|
||||||
@@ -72,7 +72,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
}
|
}
|
||||||
// import rsa keys and configure RSA for decryption
|
// import rsa keys and configure RSA for decryption
|
||||||
using var rsa = RSA.Create(2048);
|
using var rsa = RSA.Create(2048);
|
||||||
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicCert), out _);
|
rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicCert), out _);
|
||||||
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
||||||
// Decrypt the encrypted text using RSA
|
// Decrypt the encrypted text using RSA
|
||||||
string? plainText = null;
|
string? plainText = null;
|
||||||
@@ -96,8 +96,9 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
{
|
{
|
||||||
return publicCert;
|
return publicCert;
|
||||||
}
|
}
|
||||||
(publicCert, _) = GenerateCertificate();
|
(publicCert, string privateCert) = GenerateCertificate();
|
||||||
_cache.Set($"{clientId}_public", publicCert, _CacheOptions);
|
_cache.Set($"{clientId}_public", publicCert, _CacheOptions);
|
||||||
|
_cache.Set($"{clientId}_private", privateCert, _CacheOptions);
|
||||||
return publicCert;
|
return publicCert;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +108,8 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
{
|
{
|
||||||
return privateCert;
|
return privateCert;
|
||||||
}
|
}
|
||||||
(_, privateCert) = GenerateCertificate();
|
(string publicCert, privateCert) = GenerateCertificate();
|
||||||
|
_cache.Set($"{clientId}_public", publicCert, _CacheOptions);
|
||||||
_cache.Set($"{clientId}_private", privateCert, _CacheOptions);
|
_cache.Set($"{clientId}_private", privateCert, _CacheOptions);
|
||||||
return privateCert;
|
return privateCert;
|
||||||
}
|
}
|
||||||
@@ -116,7 +118,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
{
|
{
|
||||||
// Generate a new RSA key pair for the client
|
// Generate a new RSA key pair for the client
|
||||||
using var rsa = RSA.Create(2048);
|
using var rsa = RSA.Create(2048);
|
||||||
var publicKey = rsa.ExportRSAPublicKey();
|
var publicKey = rsa.ExportSubjectPublicKeyInfo();
|
||||||
var privateKey = rsa.ExportRSAPrivateKey();
|
var privateKey = rsa.ExportRSAPrivateKey();
|
||||||
// Convert to Base64 strings for storage
|
// Convert to Base64 strings for storage
|
||||||
var publicCert = Convert.ToBase64String(publicKey);
|
var publicCert = Convert.ToBase64String(publicKey);
|
||||||
@@ -165,4 +167,9 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
|||||||
rng.GetBytes(saltBytes);
|
rng.GetBytes(saltBytes);
|
||||||
return Convert.ToBase64String(saltBytes);
|
return Convert.ToBase64String(saltBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string? HashPassword(string plainPassword, string plainSalt)
|
||||||
|
{
|
||||||
|
return Hash($"{plainPassword}{plainSalt}{Pepper()}");
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,10 +1,13 @@
|
|||||||
namespace back.services.Crypto;
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
public interface ICryptoService
|
namespace back.services.engine.Crypto;
|
||||||
|
|
||||||
|
public interface ICryptoService : ISingleton
|
||||||
{
|
{
|
||||||
string? Encrypt(string clientId, string plainText);
|
string? Encrypt(string clientId, string plainText);
|
||||||
string? Decrypt(string clientId, string encryptedText);
|
string? Decrypt(string clientId, string encryptedText);
|
||||||
string? Hash(string plainText);
|
string? Hash(string plainText);
|
||||||
|
string? HashPassword(string? plainPassword, string? plainSalt);
|
||||||
bool VerifyHash(string plainText, string hash);
|
bool VerifyHash(string plainText, string hash);
|
||||||
string Salt();
|
string Salt();
|
||||||
string Pepper();
|
string Pepper();
|
8
back/services/engine/ImageResizer/IImageResizer.cs
Normal file
8
back/services/engine/ImageResizer/IImageResizer.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.engine.ImageResizer;
|
||||||
|
|
||||||
|
public interface IImageResizer : ISingleton
|
||||||
|
{
|
||||||
|
Task<Stream> ResizeImage(IFormFile image, int v);
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
namespace back.services.ImageResizer;
|
namespace back.services.engine.ImageResizer;
|
||||||
|
|
||||||
public sealed class ImageResizer : IImageResizer
|
public sealed class ImageResizer : IImageResizer
|
||||||
{
|
{
|
@@ -0,0 +1,8 @@
|
|||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.engine.PasswordGenerator;
|
||||||
|
|
||||||
|
public interface IPasswordGenerator : ISingleton
|
||||||
|
{
|
||||||
|
string Generate(int length, bool includeNumbers = true, bool includeMayus = true, bool includeMinus = true, bool includeSpecials = true);
|
||||||
|
}
|
24
back/services/engine/PasswordGenerator/PasswordGenerator.cs
Normal file
24
back/services/engine/PasswordGenerator/PasswordGenerator.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace back.services.engine.PasswordGenerator;
|
||||||
|
|
||||||
|
public class PasswordGenerator : IPasswordGenerator
|
||||||
|
{
|
||||||
|
public string Generate(int length, bool includeNumbers = true, bool includeMayus = true, bool includeMinus = true, bool includeSpecials = true)
|
||||||
|
{
|
||||||
|
const string numbers = "0123456789";
|
||||||
|
const string mayus = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
const string minus = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
const string specials = "!@#$%^&*()_+[]{}|;:,.<>?";
|
||||||
|
var characters = minus;
|
||||||
|
if (includeNumbers) characters += numbers;
|
||||||
|
if (includeMayus) characters += mayus;
|
||||||
|
if (includeSpecials) characters += specials;
|
||||||
|
var random = new Random((int)DateTimeOffset.UtcNow.Ticks);
|
||||||
|
var password = new char[length];
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
password[i] = characters[random.Next(characters.Length)];
|
||||||
|
}
|
||||||
|
return new string(password);
|
||||||
|
}
|
||||||
|
}
|
8
back/services/engine/SystemUser/ISystemUserGenerator.cs
Normal file
8
back/services/engine/SystemUser/ISystemUserGenerator.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.engine.SystemUser;
|
||||||
|
|
||||||
|
public interface ISystemUserGenerator: IScoped
|
||||||
|
{
|
||||||
|
Task GenerateAsync();
|
||||||
|
}
|
47
back/services/engine/SystemUser/SystemUserGenerator.cs
Normal file
47
back/services/engine/SystemUser/SystemUserGenerator.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using back.DataModels;
|
||||||
|
using back.persistance.blob;
|
||||||
|
using back.persistance.data;
|
||||||
|
using back.persistance.data.repositories.Abstracts;
|
||||||
|
using back.services.engine.Crypto;
|
||||||
|
using back.services.engine.PasswordGenerator;
|
||||||
|
using Transactional.Abstractions.Interfaces;
|
||||||
|
|
||||||
|
namespace back.services.engine.SystemUser;
|
||||||
|
|
||||||
|
public class SystemUserGenerator(
|
||||||
|
ITransactionalService<DataContext> transactional,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IPersonRepository personRepository,
|
||||||
|
ICryptoService cryptoService,
|
||||||
|
IBlobStorageService blobStorageService,
|
||||||
|
IPasswordGenerator passwordGenerator) : ISystemUserGenerator
|
||||||
|
{
|
||||||
|
public async Task GenerateAsync()
|
||||||
|
{
|
||||||
|
var systemKey = new SystemKey() {
|
||||||
|
Password = passwordGenerator.Generate(16),
|
||||||
|
};
|
||||||
|
var systemKeyJson = System.Text.Json.JsonSerializer.Serialize(systemKey);
|
||||||
|
|
||||||
|
using Stream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(systemKeyJson));
|
||||||
|
|
||||||
|
await blobStorageService.Delete("systemkey.lock");
|
||||||
|
|
||||||
|
await blobStorageService.Save(
|
||||||
|
stream,
|
||||||
|
"systemkey.lock"
|
||||||
|
);
|
||||||
|
|
||||||
|
User.SystemUser.Password = systemKey.Password;
|
||||||
|
User.SystemUser.Salt = cryptoService.Salt();
|
||||||
|
User.SystemUser.Password = cryptoService.HashPassword(User.SystemUser.Password, User.SystemUser.Salt) ?? string.Empty;
|
||||||
|
|
||||||
|
if (!await userRepository.Exists(User.SystemUser.Id!))
|
||||||
|
{
|
||||||
|
await transactional.DoTransaction(async () => {
|
||||||
|
await personRepository.Insert(Person.SystemPerson);
|
||||||
|
await userRepository.Insert(User.SystemUser);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
back/services/engine/mailing/EmailService.cs
Normal file
66
back/services/engine/mailing/EmailService.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using back.Options;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Mail;
|
||||||
|
|
||||||
|
namespace back.services.engine.mailing;
|
||||||
|
|
||||||
|
public class EmailService(IOptions<MailServerOptions> options) : IEmailService
|
||||||
|
{
|
||||||
|
public async Task SendEmailAsync(List<string> tos, string from, string subject, string body, Dictionary<string, object>? attachments = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Parallel.ForEachAsync(tos, async (to, cancellationToken) => {
|
||||||
|
await SendEmailAsync(to, from, subject, body, attachments, cancellationToken);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log the exception or handle it as needed
|
||||||
|
Console.WriteLine($"Error sending email to multiple recipients: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task SendEmailAsync(string to, string from, string subject, string body, Dictionary<string, object>? attachments = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var message = new MailMessage();
|
||||||
|
message.From = new MailAddress(from);
|
||||||
|
message.To.Add(to);
|
||||||
|
message.Subject = subject;
|
||||||
|
message.Body = body;
|
||||||
|
message.IsBodyHtml = true;
|
||||||
|
message.Priority = MailPriority.Normal;
|
||||||
|
message.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
|
||||||
|
|
||||||
|
if (attachments != null)
|
||||||
|
{
|
||||||
|
foreach (var attachment in attachments)
|
||||||
|
{
|
||||||
|
if (attachment.Value is FileStream fileStream)
|
||||||
|
{
|
||||||
|
message.Attachments.Add(new Attachment(fileStream, attachment.Key));
|
||||||
|
}
|
||||||
|
if (attachment.Value is string filePath && File.Exists(filePath))
|
||||||
|
{
|
||||||
|
message.Attachments.Add(new Attachment(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var cliente = new SmtpClient(options.Value.SmtpServer, options.Value.Puerto);
|
||||||
|
cliente.UseDefaultCredentials = false;
|
||||||
|
cliente.Credentials = new NetworkCredential(options.Value.Usuario, options.Value.Password);
|
||||||
|
cliente.EnableSsl = options.Value.EnableSsl;
|
||||||
|
cliente.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||||
|
|
||||||
|
await cliente.SendMailAsync(message, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log the exception or handle it as needed
|
||||||
|
Console.WriteLine($"Error sending email: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
back/services/engine/mailing/IEmailService.cs
Normal file
9
back/services/engine/mailing/IEmailService.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using DependencyInjector.Lifetimes;
|
||||||
|
|
||||||
|
namespace back.services.engine.mailing;
|
||||||
|
|
||||||
|
public interface IEmailService : IScoped
|
||||||
|
{
|
||||||
|
Task SendEmailAsync(List<string> tos, string from, string subject, string body, Dictionary<string, object>? attachments = null, CancellationToken cancellationToken = default);
|
||||||
|
Task SendEmailAsync(string tos, string from, string subject, string body, Dictionary<string, object>? attachments = null, CancellationToken cancellationToken = default);
|
||||||
|
}
|
@@ -22,11 +22,7 @@ export class CryptoService {
|
|||||||
|
|
||||||
private async fetchPublicKey(): Promise<CryptoKey> {
|
private async fetchPublicKey(): Promise<CryptoKey> {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${this.cryptoUrl}`, {
|
const response = await axios.get(`${this.cryptoUrl}`);
|
||||||
headers: {
|
|
||||||
'X-Thumbprint': this.thumbprintService.getThumbprint(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new Error('Failed to fetch public key');
|
throw new Error('Failed to fetch public key');
|
||||||
@@ -39,7 +35,7 @@ export class CryptoService {
|
|||||||
binaryDer,
|
binaryDer,
|
||||||
{
|
{
|
||||||
name: 'RSA-OAEP',
|
name: 'RSA-OAEP',
|
||||||
hash: { name: 'SHA-256' },
|
hash: 'SHA-256',
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['encrypt']
|
['encrypt']
|
||||||
|
@@ -22,6 +22,28 @@ export class userService {
|
|||||||
return this.userSubject.asObservable();
|
return this.userSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async systemLogin(
|
||||||
|
email: string | null | undefined,
|
||||||
|
password: string | null | undefined,
|
||||||
|
systemKey: string | null | undefined
|
||||||
|
) {
|
||||||
|
if (email == null || password == null || systemKey == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const encryptedPassword = this.cryptoService.encryptData(password);
|
||||||
|
const encryptedSystemKey = this.cryptoService.encryptData(systemKey);
|
||||||
|
const response = await axios.post('/users/login', {
|
||||||
|
email,
|
||||||
|
password: await encryptedPassword,
|
||||||
|
systemKey: await encryptedSystemKey,
|
||||||
|
});
|
||||||
|
const { jwt, refresh, usermodel } = response.data;
|
||||||
|
localStorage.setItem('jwt', jwt);
|
||||||
|
localStorage.setItem('refresh', refresh);
|
||||||
|
this.setUser(usermodel);
|
||||||
|
return usermodel;
|
||||||
|
}
|
||||||
|
|
||||||
async login(
|
async login(
|
||||||
email: string | null | undefined,
|
email: string | null | undefined,
|
||||||
password: string | null | undefined
|
password: string | null | undefined
|
||||||
@@ -40,4 +62,25 @@ export class userService {
|
|||||||
this.setUser(usermodel);
|
this.setUser(usermodel);
|
||||||
return usermodel;
|
return usermodel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async register(
|
||||||
|
name: string | null | undefined,
|
||||||
|
email: string | null | undefined,
|
||||||
|
password: string | null | undefined
|
||||||
|
): Promise<userModel> {
|
||||||
|
if (email == null || password == null) {
|
||||||
|
throw new Error('Email and password must not be null');
|
||||||
|
}
|
||||||
|
const encrypted = this.cryptoService.encryptData(password);
|
||||||
|
const response = await axios.post('/users/register', {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
password: await encrypted,
|
||||||
|
});
|
||||||
|
const { jwt, refresh, usermodel } = response.data;
|
||||||
|
localStorage.setItem('jwt', jwt);
|
||||||
|
localStorage.setItem('refresh', refresh);
|
||||||
|
this.setUser(usermodel);
|
||||||
|
return usermodel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1 +1,29 @@
|
|||||||
<p>forgot-password-view works!</p>
|
<div class="forgot-password-form-container">
|
||||||
|
<h2>Recuperar contraseña</h2>
|
||||||
|
@if(showingForm()) {
|
||||||
|
<form class="forgot-password-form" [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="text-input effect-20" [class.has-content]="hasEmailContent()">
|
||||||
|
<input id="email" type="email" formControlName="email" />
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-buttons">
|
||||||
|
<svg-button
|
||||||
|
label="send-reset-link"
|
||||||
|
type="submit"
|
||||||
|
text="Enviar enlace de restablecimiento"
|
||||||
|
[disabled]="!form.valid"
|
||||||
|
></svg-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
} @else{
|
||||||
|
<div class="success-message">
|
||||||
|
<p>
|
||||||
|
Si existe un usuario con ese correo electrónico, se enviará un enlace para
|
||||||
|
iniciar sesión y restablecer la contraseña.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@@ -0,0 +1,18 @@
|
|||||||
|
.forgot-password-form-container {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
@@ -1,11 +1,37 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, inject, signal } from '@angular/core';
|
||||||
|
import { SvgButton } from '../../../utils/svg-button/svg-button';
|
||||||
|
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { emailValidator } from '../../../utils/validators/emailValidator';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'forgot-password-view',
|
selector: 'forgot-password-view',
|
||||||
imports: [],
|
imports: [SvgButton, ReactiveFormsModule],
|
||||||
templateUrl: './forgot-password-view.html',
|
templateUrl: './forgot-password-view.html',
|
||||||
styleUrl: './forgot-password-view.scss'
|
styleUrl: './forgot-password-view.scss',
|
||||||
})
|
})
|
||||||
export class ForgotPasswordView {
|
export class ForgotPasswordView {
|
||||||
|
private formBuilder = inject(FormBuilder);
|
||||||
|
form = this.formBuilder.group({
|
||||||
|
email: ['', [Validators.required, emailValidator]],
|
||||||
|
});
|
||||||
|
showingForm = signal(true);
|
||||||
|
|
||||||
|
get email() {
|
||||||
|
return this.form.get('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
hasEmailContent(): boolean {
|
||||||
|
const emailValue = this.email?.value;
|
||||||
|
return emailValue ? emailValue.trim().length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSubmit() {
|
||||||
|
if (this.form.valid) {
|
||||||
|
this.showingForm.set(false);
|
||||||
|
await axios.post('/users/forgot-password', {
|
||||||
|
email: this.email?.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,11 @@
|
|||||||
<h2>Acceder</h2>
|
<h2>Acceder</h2>
|
||||||
@if(submitError()) {
|
@if(submitError()) {
|
||||||
<div class="error-message">{{ submitError() }}</div>
|
<div class="error-message">{{ submitError() }}</div>
|
||||||
}
|
@if(showRegisterLink()) {
|
||||||
|
<div class="register-link">
|
||||||
|
<a href="/register">Pincha aquí para registrarte</a>
|
||||||
|
</div>
|
||||||
|
} }
|
||||||
<form class="login-form" [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
<form class="login-form" [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
||||||
<div
|
<div
|
||||||
id="login-view-email-input"
|
id="login-view-email-input"
|
||||||
@@ -53,7 +57,20 @@
|
|||||||
</span>
|
</span>
|
||||||
} }
|
} }
|
||||||
</div>
|
</div>
|
||||||
|
} @if(isLoginSystem()) {
|
||||||
|
<div
|
||||||
|
id="login-view-system-key-input"
|
||||||
|
class="text-input effect-20"
|
||||||
|
[class.has-content]="hasSystemKeyContent()"
|
||||||
|
>
|
||||||
|
<input id="system-key" type="password" formControlName="systemKey" />
|
||||||
|
<label for="system-key">System Key</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
<div class="login-buttons">
|
||||||
<svg-button
|
<svg-button
|
||||||
label="login"
|
label="login"
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -69,6 +86,7 @@
|
|||||||
[disabled]="!loginForm.valid"
|
[disabled]="!loginForm.valid"
|
||||||
(click)="onForgotPassword()"
|
(click)="onForgotPassword()"
|
||||||
></svg-button>
|
></svg-button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="provider-separator">
|
<div class="provider-separator">
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
@@ -108,6 +126,7 @@
|
|||||||
text="Más opciones de inicio de sesión"
|
text="Más opciones de inicio de sesión"
|
||||||
(click)="onMoreLoginOptions()"
|
(click)="onMoreLoginOptions()"
|
||||||
></svg-button>
|
></svg-button>
|
||||||
|
@if(!showRegisterLink()) {
|
||||||
<svg-button
|
<svg-button
|
||||||
label="register-link"
|
label="register-link"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -115,6 +134,7 @@
|
|||||||
ngClass="register-link-button"
|
ngClass="register-link-button"
|
||||||
(click)="onRegister()"
|
(click)="onRegister()"
|
||||||
></svg-button>
|
></svg-button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -7,14 +7,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
padding-top: 5rem;
|
padding-top: 2rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1.5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +46,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.provider-buttons {
|
.provider-buttons {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
@@ -6,7 +6,7 @@ import { Router } from '@angular/router';
|
|||||||
import { emailValidator } from '../../../utils/validators/emailValidator';
|
import { emailValidator } from '../../../utils/validators/emailValidator';
|
||||||
import { PasswordValidator } from '../../../utils/validators/passwordValidator';
|
import { PasswordValidator } from '../../../utils/validators/passwordValidator';
|
||||||
import { emailPasswordDistinctValidator } from '../../../utils/validators/distinctEmailPasswordValidator';
|
import { emailPasswordDistinctValidator } from '../../../utils/validators/distinctEmailPasswordValidator';
|
||||||
import { from, single } from 'rxjs';
|
import { from } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'login-view',
|
selector: 'login-view',
|
||||||
@@ -19,18 +19,20 @@ export class LoginView {
|
|||||||
private formBuilder = inject(FormBuilder);
|
private formBuilder = inject(FormBuilder);
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private timer: any;
|
private timer: any;
|
||||||
private cuentaAtras: number = 30;
|
cuentaAtras: number = 30;
|
||||||
private disableLogin = signal(false);
|
disableLogin = signal(false);
|
||||||
private entrando = signal(false);
|
entrando = signal(false);
|
||||||
private submitError = signal<string | null>(null);
|
submitError = signal<string | null>(null);
|
||||||
|
showRegisterLink = signal(false);
|
||||||
|
|
||||||
private loginForm = this.formBuilder.group(
|
loginForm = this.formBuilder.group(
|
||||||
{
|
{
|
||||||
email: ['', [Validators.required, emailValidator]],
|
email: ['', [Validators.required, emailValidator]],
|
||||||
password: [
|
password: [
|
||||||
'',
|
'',
|
||||||
[Validators.required, Validators.minLength(8), PasswordValidator],
|
[Validators.required, Validators.minLength(8), PasswordValidator],
|
||||||
],
|
],
|
||||||
|
systemKey: [''],
|
||||||
},
|
},
|
||||||
{ validators: emailPasswordDistinctValidator }
|
{ validators: emailPasswordDistinctValidator }
|
||||||
);
|
);
|
||||||
@@ -43,6 +45,10 @@ export class LoginView {
|
|||||||
return this.loginForm.get('password');
|
return this.loginForm.get('password');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get systemKey() {
|
||||||
|
return this.loginForm.get('systemKey');
|
||||||
|
}
|
||||||
|
|
||||||
// Métodos para verificar si los campos tienen contenido
|
// Métodos para verificar si los campos tienen contenido
|
||||||
hasEmailContent(): boolean {
|
hasEmailContent(): boolean {
|
||||||
const emailValue = this.email?.value;
|
const emailValue = this.email?.value;
|
||||||
@@ -72,6 +78,19 @@ export class LoginView {
|
|||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLoginSystem(): boolean {
|
||||||
|
const emailValue = this.email?.value;
|
||||||
|
if (emailValue && emailValue == '@system') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSystemKeyContent(): boolean {
|
||||||
|
const systemKeyValue = this.systemKey?.value;
|
||||||
|
return systemKeyValue ? systemKeyValue.trim().length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.submitError.set(null);
|
this.submitError.set(null);
|
||||||
this.limpiarTimer();
|
this.limpiarTimer();
|
||||||
@@ -101,10 +120,20 @@ export class LoginView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
if (this.loginForm.valid) {
|
|
||||||
this.entrando.set(true);
|
|
||||||
const email = this.loginForm.value.email;
|
const email = this.loginForm.value.email;
|
||||||
const password = this.loginForm.value.password;
|
const password = this.loginForm.value.password;
|
||||||
|
if (this.isLoginSystem()) {
|
||||||
|
const systemKey = this.systemKey?.value;
|
||||||
|
from(this.userService.systemLogin(email, password, systemKey)).subscribe({
|
||||||
|
next: (user) => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (this.loginForm.valid) {
|
||||||
|
this.entrando.set(true);
|
||||||
from(this.userService.login(email, password)).subscribe({
|
from(this.userService.login(email, password)).subscribe({
|
||||||
next: (user) => {
|
next: (user) => {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
@@ -131,9 +160,8 @@ export class LoginView {
|
|||||||
'No se ha podido conectar con el servidor. Vuelva a intentarlo más tarde. Reconectando...'
|
'No se ha podido conectar con el servidor. Vuelva a intentarlo más tarde. Reconectando...'
|
||||||
);
|
);
|
||||||
} else if (error.status === 404) {
|
} else if (error.status === 404) {
|
||||||
this.submitError.set(
|
this.submitError.set('El usuario nunca ha existido.');
|
||||||
'El usuario nunca ha existido. ¿Quieres registrarte?'
|
this.showRegisterLink.set(true);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -1 +1,83 @@
|
|||||||
<p>register-view works!</p>
|
<div class="register-view">
|
||||||
|
<form (ngSubmit)="onSubmit()" [formGroup]="form">
|
||||||
|
<div
|
||||||
|
id="login-view-email-input"
|
||||||
|
class="text-input effect-20"
|
||||||
|
[class.has-content]="hasNameContent()"
|
||||||
|
>
|
||||||
|
<input id="name" type="text" formControlName="name" />
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="login-view-email-input"
|
||||||
|
class="text-input effect-20"
|
||||||
|
[class.has-content]="hasEmailContent()"
|
||||||
|
>
|
||||||
|
<input id="email" type="email" formControlName="email" />
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if(emailHasErrors()) {
|
||||||
|
<div>
|
||||||
|
<span>* El email no es válido. </span>
|
||||||
|
@if(this.email?.hasError('email')) {
|
||||||
|
<span>El email no tiene un formato válido.</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div
|
||||||
|
id="login-view-password-input"
|
||||||
|
class="text-input effect-20"
|
||||||
|
[class.has-content]="hasPasswordContent()"
|
||||||
|
>
|
||||||
|
<input id="password" type="password" formControlName="password" />
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if(passwordHasErrors()) {
|
||||||
|
<div>
|
||||||
|
<span>* La contraseña no es válida. <br /></span>
|
||||||
|
@if(this.password?.hasError('minlength')) {
|
||||||
|
<span> Debe tener al menos 8 caracteres. </span>
|
||||||
|
} @else { @if(this.password?.hasError('passwordContainsEmailOrLeet')) {
|
||||||
|
<span> No puede contener trazas del email. </span>
|
||||||
|
} @if(this.password?.hasError('invalidPassword')) {
|
||||||
|
<span>
|
||||||
|
No tiene un formato válido. <br />
|
||||||
|
Tiene que contener al menos una letra mayúscula, una letra minúscula, un
|
||||||
|
número y un carácter especial.
|
||||||
|
</span>
|
||||||
|
} }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div
|
||||||
|
id="login-view-password-input"
|
||||||
|
class="text-input effect-20"
|
||||||
|
[class.has-content]="hasConfirmPasswordContent()"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="confirm-password"
|
||||||
|
type="password"
|
||||||
|
formControlName="confirmPassword"
|
||||||
|
/>
|
||||||
|
<label for="confirm-password">Confirm Password</label>
|
||||||
|
<span class="focus-border">
|
||||||
|
<i></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if(confirmPasswordHasErrors()) {
|
||||||
|
<div>
|
||||||
|
<span>* Las dos contraseñas tienen que coincidir.</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@@ -0,0 +1,67 @@
|
|||||||
|
.register-view {
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-input {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #007bff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
left: 0.5rem;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-content {
|
||||||
|
label {
|
||||||
|
top: -1rem;
|
||||||
|
left: 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-border {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: #007bff;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within .focus-border {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"] {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken(#007bff, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,11 +1,139 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { emailValidator } from '../../../utils/validators/emailValidator';
|
||||||
|
import { SigningMethods, userModel } from '../../../models/userModel';
|
||||||
|
import { emailPasswordDistinctValidator } from '../../../utils/validators/distinctEmailPasswordValidator';
|
||||||
|
import { from } from 'rxjs';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { userService } from '../../services/userService/userService';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'register-view',
|
selector: 'register-view',
|
||||||
imports: [],
|
imports: [ReactiveFormsModule],
|
||||||
templateUrl: './register-view.html',
|
templateUrl: './register-view.html',
|
||||||
styleUrl: './register-view.scss'
|
styleUrl: './register-view.scss',
|
||||||
})
|
})
|
||||||
export class RegisterView {
|
export class RegisterView {
|
||||||
|
private userService = inject(userService);
|
||||||
|
private formBuilder = inject(FormBuilder);
|
||||||
|
private router = inject(Router);
|
||||||
|
form = this.formBuilder.group(
|
||||||
|
{
|
||||||
|
name: ['', [Validators.required]],
|
||||||
|
email: ['', [Validators.required, emailValidator]],
|
||||||
|
password: ['', [Validators.required, Validators.minLength(6)]],
|
||||||
|
confirmPassword: [''],
|
||||||
|
// preferredSigninMethod: [SigningMethods.Password],
|
||||||
|
// profilePicture: [null],
|
||||||
|
// bio: ['', [Validators.maxLength(500)]],
|
||||||
|
// socialMedia: this.formBuilder.group({
|
||||||
|
// facebook: [''],
|
||||||
|
// twitter: [''],
|
||||||
|
// instagram: [''],
|
||||||
|
// }),
|
||||||
|
// termsAccepted: [false, [Validators.requiredTrue]],
|
||||||
|
},
|
||||||
|
{ validators: emailPasswordDistinctValidator }
|
||||||
|
);
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.form.get('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
get email() {
|
||||||
|
return this.form.get('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
get password() {
|
||||||
|
return this.form.get('password');
|
||||||
|
}
|
||||||
|
|
||||||
|
get confirmPassword() {
|
||||||
|
return this.form.get('confirmPassword');
|
||||||
|
}
|
||||||
|
|
||||||
|
get bio() {
|
||||||
|
return this.form.get('bio');
|
||||||
|
}
|
||||||
|
|
||||||
|
get socialMedia() {
|
||||||
|
return this.form.get('socialMedia');
|
||||||
|
}
|
||||||
|
|
||||||
|
get profilePicture() {
|
||||||
|
return this.form.get('profilePicture');
|
||||||
|
}
|
||||||
|
|
||||||
|
get preferredSigninMethod() {
|
||||||
|
return this.form.get('preferredSigninMethod');
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTermsAccepted() {
|
||||||
|
// return this.form.value.termsAccepted;
|
||||||
|
// }
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.form.valid) {
|
||||||
|
const email = this.form.value.email;
|
||||||
|
const password = this.form.value.password;
|
||||||
|
const name = this.form.value.name;
|
||||||
|
from(this.userService.register(name, email, password)).subscribe({
|
||||||
|
next: (user) => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Register error:', error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNameContent(): boolean {
|
||||||
|
const nameValue = this.name?.value;
|
||||||
|
return nameValue ? nameValue.trim().length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasEmailContent(): boolean {
|
||||||
|
const emailValue = this.email?.value;
|
||||||
|
return emailValue ? emailValue.trim().length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPasswordContent(): boolean {
|
||||||
|
const passwordValue = this.password?.value;
|
||||||
|
return passwordValue ? passwordValue.trim().length > 0 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasConfirmPasswordContent(): boolean {
|
||||||
|
const confirmPasswordValue = this.confirmPassword?.value;
|
||||||
|
return confirmPasswordValue
|
||||||
|
? confirmPasswordValue.trim().length > 0
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emailHasErrors(): boolean {
|
||||||
|
const emailControl = this.email;
|
||||||
|
return emailControl
|
||||||
|
? this.hasEmailContent() &&
|
||||||
|
emailControl.invalid &&
|
||||||
|
(emailControl.dirty || emailControl.touched)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordHasErrors(): boolean {
|
||||||
|
const passwordControl = this.password;
|
||||||
|
return passwordControl
|
||||||
|
? this.hasPasswordContent() &&
|
||||||
|
passwordControl.invalid &&
|
||||||
|
(passwordControl.dirty || passwordControl.touched)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmPasswordHasErrors(): boolean {
|
||||||
|
const confirmPasswordControl = this.confirmPassword;
|
||||||
|
return confirmPasswordControl
|
||||||
|
? this.hasConfirmPasswordContent() &&
|
||||||
|
confirmPasswordControl.invalid &&
|
||||||
|
(confirmPasswordControl.dirty || confirmPasswordControl.touched)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ type personModelType = {
|
|||||||
profilePicture?: string | null;
|
profilePicture?: string | null;
|
||||||
avatar?: string | null;
|
avatar?: string | null;
|
||||||
socialMedia?: socialMediaType | null;
|
socialMedia?: socialMediaType | null;
|
||||||
|
bio?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class personModel {
|
export class personModel {
|
||||||
@@ -26,18 +27,21 @@ export class personModel {
|
|||||||
profilePicture,
|
profilePicture,
|
||||||
avatar,
|
avatar,
|
||||||
socialMedia,
|
socialMedia,
|
||||||
|
bio,
|
||||||
}: personModelType) {
|
}: personModelType) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.profilePicture = profilePicture || null;
|
this.profilePicture = profilePicture || null;
|
||||||
this.avatar = avatar || null;
|
this.avatar = avatar || null;
|
||||||
this.socialMedia = socialMedia || null;
|
this.socialMedia = socialMedia || null;
|
||||||
|
this.bio = bio || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public id: string;
|
public id: string;
|
||||||
public name: string;
|
public name: string;
|
||||||
public profilePicture: string | null = null;
|
public profilePicture: string | null = null;
|
||||||
public avatar: string | null = null;
|
public avatar: string | null = null;
|
||||||
|
public bio: string | null = null;
|
||||||
public socialMedia: {
|
public socialMedia: {
|
||||||
facebook: string | null;
|
facebook: string | null;
|
||||||
instagram: string | null;
|
instagram: string | null;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { personModel } from './personModel';
|
import { personModel } from './personModel';
|
||||||
import { roleModel } from './roleModel';
|
import { roleModel } from './roleModel';
|
||||||
|
|
||||||
export enum SigningMethods {
|
enum SigningMethods {
|
||||||
Password = 'password',
|
Password = 'password',
|
||||||
MagicLink = 'magic-link',
|
MagicLink = 'magic-link',
|
||||||
Passkeys = 'passkeys',
|
Passkeys = 'passkeys',
|
||||||
@@ -11,7 +11,7 @@ export enum SigningMethods {
|
|||||||
Microsoft = 'microsoft',
|
Microsoft = 'microsoft',
|
||||||
}
|
}
|
||||||
|
|
||||||
export class userModel extends personModel {
|
class userModel extends personModel {
|
||||||
constructor(
|
constructor(
|
||||||
public override id: string,
|
public override id: string,
|
||||||
public email: string,
|
public email: string,
|
||||||
@@ -66,3 +66,5 @@ export class userModel extends personModel {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { SigningMethods, userModel };
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user