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);
|
||||
|
||||
builder.Services.UseExtensions();
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using back.context;
|
||||
using back.Options;
|
||||
using back.persistance.data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -7,16 +7,9 @@ namespace back.ServicesExtensions;
|
||||
|
||||
public static partial class ServicesExtensions
|
||||
{
|
||||
private static IServiceCollection AddDatabaseContexts(this IServiceCollection services)
|
||||
private static IServiceCollection AddDatabaseContext(this IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddContext<EventContext>()
|
||||
.AddContext<GalleryContext>()
|
||||
.AddContext<PersonContext>()
|
||||
.AddContext<PhotoContext>()
|
||||
.AddContext<TagContext>()
|
||||
.AddContext<UserContext>()
|
||||
;
|
||||
services.AddContext<DataContext>();
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -32,6 +25,25 @@ public static partial class ServicesExtensions
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using back.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace back.ServicesExtensions;
|
||||
|
||||
@@ -59,6 +60,14 @@ public static partial class DbContextOptionsBuilderExtensions
|
||||
switch (provider)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
case DatabaseProvider.InMemory:
|
||||
@@ -77,4 +86,7 @@ public static partial class DbContextOptionsBuilderExtensions
|
||||
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<DatabaseConfig>(DatabaseConfig.DataStorage, config.GetSection(DatabaseConfig.DataStorage));
|
||||
services.Configure<DatabaseConfig>(DatabaseConfig.BlobStorage, config.GetSection(DatabaseConfig.BlobStorage));
|
||||
services.Configure<MailServerOptions>(config.GetSection(nameof(MailServerOptions)));
|
||||
|
||||
services.PostConfigure<Databases>(databases =>
|
||||
{
|
||||
if (databases.BaseDirectory != null && !Directory.Exists(databases.BaseDirectory))
|
||||
if (!string.IsNullOrEmpty(databases.BaseDirectory) && !Directory.Exists(databases.BaseDirectory))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -1,6 +1,10 @@
|
||||
using back.persistance.blob;
|
||||
using back.services.Crypto;
|
||||
using back.services.ImageResizer;
|
||||
using back.persistance.data;
|
||||
using back.persistance.data.repositories;
|
||||
using back.persistance.data.repositories.Abstracts;
|
||||
using back.services.engine.SystemUser;
|
||||
using DependencyInjector;
|
||||
using Transactional.Abstractions.Interfaces;
|
||||
using Transactional.Implementations.EntityFramework;
|
||||
|
||||
namespace back.ServicesExtensions;
|
||||
|
||||
@@ -8,15 +12,20 @@ public static partial class ServicesExtensions
|
||||
{
|
||||
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.AddSingleton<IBlobStorageService, FileSystemImageStorageService>();
|
||||
services.AddDatabaseContext();
|
||||
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;
|
||||
}
|
||||
|
@@ -1,14 +1,21 @@
|
||||
{
|
||||
"Databases": {
|
||||
"BaseDirectory": "data",
|
||||
"BaseDirectory": ".program_data",
|
||||
"Data": {
|
||||
"Provider": "sqlite",
|
||||
"ConnectionString": "Data Source=data/app.db;Cache=Shared"
|
||||
"ConnectionString": "Data Source=.program_data/app.db"
|
||||
},
|
||||
"Blob": {
|
||||
"Provider": "system",
|
||||
"baseUrl": "https://localhost:7273/api/photo/{id}/{res}",
|
||||
"SystemContainer": "imgs"
|
||||
}
|
||||
},
|
||||
"MailServerOptions": {
|
||||
"SmtpServer": "smtp.gmail.com",
|
||||
"Puerto": 587,
|
||||
"Usuario": "",
|
||||
"Password": "",
|
||||
"EnableSsl": true
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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.Identity.EntityFrameworkCore" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
||||
@@ -42,7 +43,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="persistance\data\" />
|
||||
<ProjectReference Include="..\..\nuget\DependencyInjector\DependencyInjector.csproj" />
|
||||
<ProjectReference Include="..\..\nuget\Transactional\Transactional.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -1,10 +1,14 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36401.2 d17.14
|
||||
VisualStudioVersion = 17.14.36401.2
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "back", "back.csproj", "{392278F3-4B36-47F4-AD31-5FBFCC181AD4}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
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.persistance.blob;
|
||||
using back.services.bussines.PhotoService;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace back.controllers;
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[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>
|
||||
[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);
|
||||
var totalItems = await _photoContext.GetTotalItems();
|
||||
|
||||
(int totalItems, IEnumerable<Photo>? pageData) = await _photoService.GetPage(page, pageSize);
|
||||
Response.Headers.Append("X-Total-Count", totalItems.ToString());
|
||||
return Ok(photos);
|
||||
return Ok(pageData);
|
||||
}
|
||||
|
||||
// GET api/<PhotoController>/5
|
||||
[HttpGet("{res}/{id}")]
|
||||
public async Task<IActionResult> Get(string res, Guid id)
|
||||
[HttpGet("{id}/{res}")]
|
||||
public async Task<IActionResult> Get(string id, string res)
|
||||
{
|
||||
var photo = await _photoContext.GetById(id);
|
||||
if (photo == null)
|
||||
(string? mediaType, byte[]? fileBytes) = await _photoService.GetBytes(id, res.ToLower());
|
||||
if(fileBytes == null)
|
||||
return NotFound();
|
||||
|
||||
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);
|
||||
return File(fileBytes, mediaType ?? "image/jpeg");
|
||||
}
|
||||
|
||||
// POST api/<PhotoController>
|
||||
@@ -66,7 +39,7 @@ public class PhotosController(PhotoContext photoContext, IBlobStorageService blo
|
||||
if (form.Image == null || form.Image.Length == 0)
|
||||
return BadRequest("No image uploaded.");
|
||||
|
||||
await _photoContext.CreateNew(form);
|
||||
await _photoService.Create(form);
|
||||
|
||||
return Created();
|
||||
}
|
||||
@@ -78,21 +51,17 @@ public class PhotosController(PhotoContext photoContext, IBlobStorageService blo
|
||||
|
||||
//// PUT api/<PhotoController>
|
||||
[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();
|
||||
}
|
||||
|
||||
// DELETE api/<PhotoController>/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
var photo = await _photoContext.GetById(id);
|
||||
if (photo == null)
|
||||
return NotFound();
|
||||
|
||||
await _photoContext.Delete(photo);
|
||||
await _photoService.Delete(id);
|
||||
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 System.Net;
|
||||
|
||||
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]")]
|
||||
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>
|
||||
//[HttpGet]
|
||||
//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);
|
||||
//}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("[action]")]
|
||||
public async Task<IActionResult> Login(
|
||||
[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))
|
||||
return BadRequest(UserContext.Errors.BadRequest.Description);
|
||||
var existingUser = await _userContext.Login(user.Email, user.Password, clientId);
|
||||
return BadRequest(Errors.BadRequest.Description);
|
||||
|
||||
if (user.Email.Equals("@system", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
var existingUser = await _user.Login(user.Email, user.Password, clientId);
|
||||
if (existingUser == null)
|
||||
return Unauthorized(UserContext.Errors.Unauthorized.Description);
|
||||
return Ok(existingUser.ToDto());
|
||||
return Unauthorized(Errors.Unauthorized.Description);
|
||||
return Ok(existingUser);
|
||||
}
|
||||
|
||||
//// POST api/<UsersController>
|
||||
//[HttpPost]
|
||||
//public async Task<IActionResult> Post([FromBody] UserModel user)
|
||||
//{
|
||||
// if (user == null)
|
||||
// return BadRequest("User cannot be null");
|
||||
// var createdUser = await _userContext.Create(user);
|
||||
// return CreatedAtAction(nameof(Get), new { id = createdUser.Id }, createdUser);
|
||||
//}
|
||||
[HttpPost("forgot-password")]
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,41 +5,41 @@ using Microsoft.Extensions.Options;
|
||||
namespace back.persistance.blob;
|
||||
|
||||
public class FileSystemImageStorageService(
|
||||
IOptions<Databases> systemOptions,
|
||||
IOptions<Databases> systemOptions,
|
||||
IOptionsMonitor<DatabaseConfig> options,
|
||||
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 IMemoryCache cache = memoryCache;
|
||||
|
||||
private string GetFullPath(string fileName)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
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
|
||||
{
|
||||
File.Delete(path);
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -47,14 +47,14 @@ public class FileSystemImageStorageService(
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Stream?> GetStreamAsync(string fileName)
|
||||
public async Task<Stream?> GetStream(string fileName)
|
||||
{
|
||||
var path = GetFullPath(fileName);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
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
|
||||
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
@@ -63,14 +63,14 @@ public class FileSystemImageStorageService(
|
||||
.SetValue(fileStream)
|
||||
.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)
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
@@ -80,25 +80,25 @@ public class FileSystemImageStorageService(
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Stream blobStream, string fileName)
|
||||
public async Task Save(Stream blobStream, string fileName)
|
||||
{
|
||||
var path = GetFullPath(fileName);
|
||||
if (cache.TryGetValue(path, out Stream? _) || File.Exists(path))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Stream blobStream, string fileName)
|
||||
public async Task Update(Stream blobStream, string fileName)
|
||||
{
|
||||
var path = GetFullPath(fileName);
|
||||
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<Stream?> GetStreamAsync(string fileName);
|
||||
Task<byte[]?> GetBytesAsync(string fileName);
|
||||
Task DeleteAsync(string fileName);
|
||||
Task UpdateAsync(Stream blobStream, string fileName);
|
||||
Task Save(Stream blobStream, string fileName);
|
||||
Task<Stream?> GetStream(string fileName);
|
||||
Task<byte[]?> GetBytes(string fileName);
|
||||
Task Delete(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 System.Security.Cryptography;
|
||||
|
||||
namespace back.services.Crypto;
|
||||
namespace back.services.engine.Crypto;
|
||||
|
||||
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
|
||||
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
|
||||
using var rsa = RSA.Create(2048);
|
||||
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicCert), out _);
|
||||
rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicCert), out _);
|
||||
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
||||
// Encrypt the plain text using RSA
|
||||
string? encryptedText = null;
|
||||
@@ -59,7 +59,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||
return encryptedText;
|
||||
}
|
||||
|
||||
public string? Decrypt(string encryptedText, string clientId)
|
||||
public string? Decrypt(string clientId, string encryptedText)
|
||||
{
|
||||
// get keys from cache
|
||||
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
|
||||
using var rsa = RSA.Create(2048);
|
||||
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicCert), out _);
|
||||
rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicCert), out _);
|
||||
rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateCert), out _);
|
||||
// Decrypt the encrypted text using RSA
|
||||
string? plainText = null;
|
||||
@@ -96,8 +96,9 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||
{
|
||||
return publicCert;
|
||||
}
|
||||
(publicCert, _) = GenerateCertificate();
|
||||
(publicCert, string privateCert) = GenerateCertificate();
|
||||
_cache.Set($"{clientId}_public", publicCert, _CacheOptions);
|
||||
_cache.Set($"{clientId}_private", privateCert, _CacheOptions);
|
||||
return publicCert;
|
||||
}
|
||||
|
||||
@@ -107,7 +108,8 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||
{
|
||||
return privateCert;
|
||||
}
|
||||
(_, privateCert) = GenerateCertificate();
|
||||
(string publicCert, privateCert) = GenerateCertificate();
|
||||
_cache.Set($"{clientId}_public", publicCert, _CacheOptions);
|
||||
_cache.Set($"{clientId}_private", privateCert, _CacheOptions);
|
||||
return privateCert;
|
||||
}
|
||||
@@ -116,7 +118,7 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||
{
|
||||
// Generate a new RSA key pair for the client
|
||||
using var rsa = RSA.Create(2048);
|
||||
var publicKey = rsa.ExportRSAPublicKey();
|
||||
var publicKey = rsa.ExportSubjectPublicKeyInfo();
|
||||
var privateKey = rsa.ExportRSAPrivateKey();
|
||||
// Convert to Base64 strings for storage
|
||||
var publicCert = Convert.ToBase64String(publicKey);
|
||||
@@ -165,4 +167,9 @@ public class CryptoService(IMemoryCache cache) : ICryptoService
|
||||
rng.GetBytes(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? Decrypt(string clientId, string encryptedText);
|
||||
string? Hash(string plainText);
|
||||
string? HashPassword(string? plainPassword, string? plainSalt);
|
||||
bool VerifyHash(string plainText, string hash);
|
||||
string Salt();
|
||||
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.Processing;
|
||||
|
||||
namespace back.services.ImageResizer;
|
||||
namespace back.services.engine.ImageResizer;
|
||||
|
||||
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> {
|
||||
try {
|
||||
const response = await axios.get(`${this.cryptoUrl}`, {
|
||||
headers: {
|
||||
'X-Thumbprint': this.thumbprintService.getThumbprint(),
|
||||
},
|
||||
});
|
||||
const response = await axios.get(`${this.cryptoUrl}`);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error('Failed to fetch public key');
|
||||
@@ -39,7 +35,7 @@ export class CryptoService {
|
||||
binaryDer,
|
||||
{
|
||||
name: 'RSA-OAEP',
|
||||
hash: { name: 'SHA-256' },
|
||||
hash: 'SHA-256',
|
||||
},
|
||||
false,
|
||||
['encrypt']
|
||||
|
@@ -22,6 +22,28 @@ export class userService {
|
||||
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(
|
||||
email: string | null | undefined,
|
||||
password: string | null | undefined
|
||||
@@ -40,4 +62,25 @@ export class userService {
|
||||
this.setUser(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({
|
||||
selector: 'forgot-password-view',
|
||||
imports: [],
|
||||
imports: [SvgButton, ReactiveFormsModule],
|
||||
templateUrl: './forgot-password-view.html',
|
||||
styleUrl: './forgot-password-view.scss'
|
||||
styleUrl: './forgot-password-view.scss',
|
||||
})
|
||||
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>
|
||||
@if(submitError()) {
|
||||
<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()">
|
||||
<div
|
||||
id="login-view-email-input"
|
||||
@@ -53,22 +57,36 @@
|
||||
</span>
|
||||
} }
|
||||
</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>
|
||||
}
|
||||
<svg-button
|
||||
label="login"
|
||||
type="submit"
|
||||
text="Entrar"
|
||||
ngClass="login-button"
|
||||
[disabled]="!loginForm.valid || disableLogin()"
|
||||
></svg-button>
|
||||
<svg-button
|
||||
label="forgot-password"
|
||||
type="button"
|
||||
text="¿Contraseña olvidada?"
|
||||
ngClass="forgot-password-button"
|
||||
[disabled]="!loginForm.valid"
|
||||
(click)="onForgotPassword()"
|
||||
></svg-button>
|
||||
<div class="login-buttons">
|
||||
<svg-button
|
||||
label="login"
|
||||
type="submit"
|
||||
text="Entrar"
|
||||
ngClass="login-button"
|
||||
[disabled]="!loginForm.valid || disableLogin()"
|
||||
></svg-button>
|
||||
<svg-button
|
||||
label="forgot-password"
|
||||
type="button"
|
||||
text="¿Contraseña olvidada?"
|
||||
ngClass="forgot-password-button"
|
||||
[disabled]="!loginForm.valid"
|
||||
(click)="onForgotPassword()"
|
||||
></svg-button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="provider-separator">
|
||||
<span class="line"></span>
|
||||
@@ -108,6 +126,7 @@
|
||||
text="Más opciones de inicio de sesión"
|
||||
(click)="onMoreLoginOptions()"
|
||||
></svg-button>
|
||||
@if(!showRegisterLink()) {
|
||||
<svg-button
|
||||
label="register-link"
|
||||
type="button"
|
||||
@@ -115,6 +134,7 @@
|
||||
ngClass="register-link-button"
|
||||
(click)="onRegister()"
|
||||
></svg-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@@ -7,14 +7,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20%;
|
||||
padding-top: 5rem;
|
||||
padding-top: 2rem;
|
||||
margin: 0 auto;
|
||||
gap: 1.5rem;
|
||||
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.login-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.provider-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
@@ -6,7 +6,7 @@ import { Router } from '@angular/router';
|
||||
import { emailValidator } from '../../../utils/validators/emailValidator';
|
||||
import { PasswordValidator } from '../../../utils/validators/passwordValidator';
|
||||
import { emailPasswordDistinctValidator } from '../../../utils/validators/distinctEmailPasswordValidator';
|
||||
import { from, single } from 'rxjs';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'login-view',
|
||||
@@ -19,18 +19,20 @@ export class LoginView {
|
||||
private formBuilder = inject(FormBuilder);
|
||||
private router = inject(Router);
|
||||
private timer: any;
|
||||
private cuentaAtras: number = 30;
|
||||
private disableLogin = signal(false);
|
||||
private entrando = signal(false);
|
||||
private submitError = signal<string | null>(null);
|
||||
cuentaAtras: number = 30;
|
||||
disableLogin = signal(false);
|
||||
entrando = signal(false);
|
||||
submitError = signal<string | null>(null);
|
||||
showRegisterLink = signal(false);
|
||||
|
||||
private loginForm = this.formBuilder.group(
|
||||
loginForm = this.formBuilder.group(
|
||||
{
|
||||
email: ['', [Validators.required, emailValidator]],
|
||||
password: [
|
||||
'',
|
||||
[Validators.required, Validators.minLength(8), PasswordValidator],
|
||||
],
|
||||
systemKey: [''],
|
||||
},
|
||||
{ validators: emailPasswordDistinctValidator }
|
||||
);
|
||||
@@ -43,6 +45,10 @@ export class LoginView {
|
||||
return this.loginForm.get('password');
|
||||
}
|
||||
|
||||
get systemKey() {
|
||||
return this.loginForm.get('systemKey');
|
||||
}
|
||||
|
||||
// Métodos para verificar si los campos tienen contenido
|
||||
hasEmailContent(): boolean {
|
||||
const emailValue = this.email?.value;
|
||||
@@ -72,6 +78,19 @@ export class LoginView {
|
||||
: 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() {
|
||||
this.submitError.set(null);
|
||||
this.limpiarTimer();
|
||||
@@ -101,10 +120,20 @@ export class LoginView {
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.loginForm.valid) {
|
||||
const email = this.loginForm.value.email;
|
||||
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);
|
||||
const email = this.loginForm.value.email;
|
||||
const password = this.loginForm.value.password;
|
||||
from(this.userService.login(email, password)).subscribe({
|
||||
next: (user) => {
|
||||
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...'
|
||||
);
|
||||
} else if (error.status === 404) {
|
||||
this.submitError.set(
|
||||
'El usuario nunca ha existido. ¿Quieres registrarte?'
|
||||
);
|
||||
this.submitError.set('El usuario nunca ha existido.');
|
||||
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({
|
||||
selector: 'register-view',
|
||||
imports: [],
|
||||
imports: [ReactiveFormsModule],
|
||||
templateUrl: './register-view.html',
|
||||
styleUrl: './register-view.scss'
|
||||
styleUrl: './register-view.scss',
|
||||
})
|
||||
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;
|
||||
avatar?: string | null;
|
||||
socialMedia?: socialMediaType | null;
|
||||
bio?: string | null;
|
||||
};
|
||||
|
||||
export class personModel {
|
||||
@@ -26,18 +27,21 @@ export class personModel {
|
||||
profilePicture,
|
||||
avatar,
|
||||
socialMedia,
|
||||
bio,
|
||||
}: personModelType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.profilePicture = profilePicture || null;
|
||||
this.avatar = avatar || null;
|
||||
this.socialMedia = socialMedia || null;
|
||||
this.bio = bio || null;
|
||||
}
|
||||
|
||||
public id: string;
|
||||
public name: string;
|
||||
public profilePicture: string | null = null;
|
||||
public avatar: string | null = null;
|
||||
public bio: string | null = null;
|
||||
public socialMedia: {
|
||||
facebook: string | null;
|
||||
instagram: string | null;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { personModel } from './personModel';
|
||||
import { roleModel } from './roleModel';
|
||||
|
||||
export enum SigningMethods {
|
||||
enum SigningMethods {
|
||||
Password = 'password',
|
||||
MagicLink = 'magic-link',
|
||||
Passkeys = 'passkeys',
|
||||
@@ -11,7 +11,7 @@ export enum SigningMethods {
|
||||
Microsoft = 'microsoft',
|
||||
}
|
||||
|
||||
export class userModel extends personModel {
|
||||
class userModel extends personModel {
|
||||
constructor(
|
||||
public override id: string,
|
||||
public email: string,
|
||||
@@ -66,3 +66,5 @@ export class userModel extends personModel {
|
||||
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