From ac769e656b122ff569c3f1534701b71e00fed586 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Oct 2020 19:21:35 +0800 Subject: Split front and back end. --- BackEnd/Timeline/Entities/DataEntity.cs | 23 +++++++++ BackEnd/Timeline/Entities/DatabaseContext.cs | 34 +++++++++++++ BackEnd/Timeline/Entities/JwtTokenEntity.cs | 17 +++++++ BackEnd/Timeline/Entities/TimelineEntity.cs | 58 +++++++++++++++++++++++ BackEnd/Timeline/Entities/TimelineMemberEntity.cs | 24 ++++++++++ BackEnd/Timeline/Entities/TimelinePostEntity.cs | 43 +++++++++++++++++ BackEnd/Timeline/Entities/UserAvatarEntity.cs | 29 ++++++++++++ BackEnd/Timeline/Entities/UserEntity.cs | 56 ++++++++++++++++++++++ BackEnd/Timeline/Entities/UtcDateAnnotation.cs | 44 +++++++++++++++++ 9 files changed, 328 insertions(+) create mode 100644 BackEnd/Timeline/Entities/DataEntity.cs create mode 100644 BackEnd/Timeline/Entities/DatabaseContext.cs create mode 100644 BackEnd/Timeline/Entities/JwtTokenEntity.cs create mode 100644 BackEnd/Timeline/Entities/TimelineEntity.cs create mode 100644 BackEnd/Timeline/Entities/TimelineMemberEntity.cs create mode 100644 BackEnd/Timeline/Entities/TimelinePostEntity.cs create mode 100644 BackEnd/Timeline/Entities/UserAvatarEntity.cs create mode 100644 BackEnd/Timeline/Entities/UserEntity.cs create mode 100644 BackEnd/Timeline/Entities/UtcDateAnnotation.cs (limited to 'BackEnd/Timeline/Entities') diff --git a/BackEnd/Timeline/Entities/DataEntity.cs b/BackEnd/Timeline/Entities/DataEntity.cs new file mode 100644 index 00000000..b21e2dbf --- /dev/null +++ b/BackEnd/Timeline/Entities/DataEntity.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + [Table("data")] + public class DataEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("tag"), Required] + public string Tag { get; set; } = default!; + + [Column("data"), Required] +#pragma warning disable CA1819 // Properties should not return arrays + public byte[] Data { get; set; } = default!; +#pragma warning restore CA1819 // Properties should not return arrays + + [Column("ref"), Required] + public int Ref { get; set; } + } +} diff --git a/BackEnd/Timeline/Entities/DatabaseContext.cs b/BackEnd/Timeline/Entities/DatabaseContext.cs new file mode 100644 index 00000000..ecadd703 --- /dev/null +++ b/BackEnd/Timeline/Entities/DatabaseContext.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; + +namespace Timeline.Entities +{ + public class DatabaseContext : DbContext + { + public DatabaseContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().Property(e => e.Version).HasDefaultValue(0); + modelBuilder.Entity().HasIndex(e => e.Username).IsUnique(); + modelBuilder.Entity().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))"); + modelBuilder.Entity().Property(e => e.UsernameChangeTime).HasDefaultValueSql("datetime('now', 'utc')"); + modelBuilder.Entity().Property(e => e.CreateTime).HasDefaultValueSql("datetime('now', 'utc')"); + modelBuilder.Entity().Property(e => e.LastModified).HasDefaultValueSql("datetime('now', 'utc')"); + modelBuilder.Entity().HasIndex(e => e.Tag).IsUnique(); + modelBuilder.Entity().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))"); + + modelBuilder.ApplyUtcDateTimeConverter(); + } + + public DbSet Users { get; set; } = default!; + public DbSet UserAvatars { get; set; } = default!; + public DbSet Timelines { get; set; } = default!; + public DbSet TimelinePosts { get; set; } = default!; + public DbSet TimelineMembers { get; set; } = default!; + public DbSet JwtToken { get; set; } = default!; + public DbSet Data { get; set; } = default!; + } +} diff --git a/BackEnd/Timeline/Entities/JwtTokenEntity.cs b/BackEnd/Timeline/Entities/JwtTokenEntity.cs new file mode 100644 index 00000000..40cb230a --- /dev/null +++ b/BackEnd/Timeline/Entities/JwtTokenEntity.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + [Table("jwt_token")] + public class JwtTokenEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Required, Column("key")] +#pragma warning disable CA1819 // Properties should not return arrays + public byte[] Key { get; set; } = default!; +#pragma warning restore CA1819 // Properties should not return arrays + } +} diff --git a/BackEnd/Timeline/Entities/TimelineEntity.cs b/BackEnd/Timeline/Entities/TimelineEntity.cs new file mode 100644 index 00000000..3e592673 --- /dev/null +++ b/BackEnd/Timeline/Entities/TimelineEntity.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Timeline.Models; + +namespace Timeline.Entities +{ +#pragma warning disable CA2227 // Collection properties should be read only + // TODO: Create index for this table. + [Table("timelines")] + public class TimelineEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("unique_id"), Required] + public string UniqueId { get; set; } = default!; + + /// + /// If null, then this timeline is a personal timeline. + /// + [Column("name")] + public string? Name { get; set; } + + [Column("title")] + public string? Title { get; set; } + + [Column("name_last_modified")] + public DateTime NameLastModified { get; set; } + + [Column("description")] + public string? Description { get; set; } + + [Column("owner")] + public long OwnerId { get; set; } + + [ForeignKey(nameof(OwnerId))] + public UserEntity Owner { get; set; } = default!; + + [Column("visibility")] + public TimelineVisibility Visibility { get; set; } + + [Column("create_time")] + public DateTime CreateTime { get; set; } + + [Column("last_modified")] + public DateTime LastModified { get; set; } + + [Column("current_post_local_id")] + public long CurrentPostLocalId { get; set; } + + public List Members { get; set; } = default!; + + public List Posts { get; set; } = default!; + } +#pragma warning restore CA2227 // Collection properties should be read only +} diff --git a/BackEnd/Timeline/Entities/TimelineMemberEntity.cs b/BackEnd/Timeline/Entities/TimelineMemberEntity.cs new file mode 100644 index 00000000..e76f2099 --- /dev/null +++ b/BackEnd/Timeline/Entities/TimelineMemberEntity.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + [Table("timeline_members")] + public class TimelineMemberEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("user")] + public long UserId { get; set; } + + [ForeignKey(nameof(UserId))] + public UserEntity User { get; set; } = default!; + + [Column("timeline")] + public long TimelineId { get; set; } + + [ForeignKey(nameof(TimelineId))] + public TimelineEntity Timeline { get; set; } = default!; + } +} diff --git a/BackEnd/Timeline/Entities/TimelinePostEntity.cs b/BackEnd/Timeline/Entities/TimelinePostEntity.cs new file mode 100644 index 00000000..07367fba --- /dev/null +++ b/BackEnd/Timeline/Entities/TimelinePostEntity.cs @@ -0,0 +1,43 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + [Table("timeline_posts")] + public class TimelinePostEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("local_id")] + public long LocalId { get; set; } + + [Column("timeline")] + public long TimelineId { get; set; } + + [ForeignKey(nameof(TimelineId))] + public TimelineEntity Timeline { get; set; } = default!; + + [Column("author")] + public long? AuthorId { get; set; } + + [ForeignKey(nameof(AuthorId))] + public UserEntity? Author { get; set; } = default!; + + [Column("content_type"), Required] + public string ContentType { get; set; } = default!; + + [Column("content")] + public string? Content { get; set; } + + [Column("extra_content")] + public string? ExtraContent { get; set; } + + [Column("time")] + public DateTime Time { get; set; } + + [Column("last_updated")] + public DateTime LastUpdated { get; set; } + } +} diff --git a/BackEnd/Timeline/Entities/UserAvatarEntity.cs b/BackEnd/Timeline/Entities/UserAvatarEntity.cs new file mode 100644 index 00000000..3c2720f7 --- /dev/null +++ b/BackEnd/Timeline/Entities/UserAvatarEntity.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "This is data base entity.")] + [Table("user_avatars")] + public class UserAvatarEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("data_tag")] + public string? DataTag { get; set; } + + [Column("type")] + public string? Type { get; set; } + + [Column("last_modified"), Required] + public DateTime LastModified { get; set; } + + [Column("user"), Required] + public long UserId { get; set; } + + [ForeignKey(nameof(UserId))] + public UserEntity User { get; set; } = default!; + } +} diff --git a/BackEnd/Timeline/Entities/UserEntity.cs b/BackEnd/Timeline/Entities/UserEntity.cs new file mode 100644 index 00000000..0cfaa335 --- /dev/null +++ b/BackEnd/Timeline/Entities/UserEntity.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Timeline.Entities +{ + public static class UserRoles + { + public const string Admin = "admin"; + public const string User = "user"; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is an entity class.")] + [Table("users")] + public class UserEntity + { + [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + [Column("unique_id"), Required] + public string UniqueId { get; set; } = default!; + + [Column("username"), Required] + public string Username { get; set; } = default!; + + [Column("username_change_time")] + public DateTime UsernameChangeTime { get; set; } + + [Column("password"), Required] + public string Password { get; set; } = default!; + + [Column("roles"), Required] + public string Roles { get; set; } = default!; + + [Column("version"), Required] + public long Version { get; set; } + + [Column("nickname")] + public string? Nickname { get; set; } + + [Column("create_time")] + public DateTime CreateTime { get; set; } + + [Column("last_modified")] + public DateTime LastModified { get; set; } + + public UserAvatarEntity? Avatar { get; set; } + + public List Timelines { get; set; } = default!; + + public List TimelinePosts { get; set; } = default!; + + public List TimelinesJoined { get; set; } = default!; + } +} diff --git a/BackEnd/Timeline/Entities/UtcDateAnnotation.cs b/BackEnd/Timeline/Entities/UtcDateAnnotation.cs new file mode 100644 index 00000000..6600e701 --- /dev/null +++ b/BackEnd/Timeline/Entities/UtcDateAnnotation.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System; + +namespace Timeline.Entities +{ + // Copied from https://github.com/dotnet/efcore/issues/4711#issuecomment-589842988 + public static class UtcDateAnnotation + { + private const string IsUtcAnnotation = "IsUtc"; + private static readonly ValueConverter UtcConverter = + new ValueConverter(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc)); + + public static PropertyBuilder IsUtc(this PropertyBuilder builder, bool isUtc = true) => + builder.HasAnnotation(IsUtcAnnotation, isUtc); + + public static bool IsUtc(this IMutableProperty property) => + ((bool?)property.FindAnnotation(IsUtcAnnotation)?.Value) ?? true; + + /// + /// Make sure this is called after configuring all your entities. + /// + public static void ApplyUtcDateTimeConverter(this ModelBuilder builder) + { + foreach (var entityType in builder.Model.GetEntityTypes()) + { + foreach (var property in entityType.GetProperties()) + { + if (!property.IsUtc()) + { + continue; + } + + if (property.ClrType == typeof(DateTime)) + { + property.SetValueConverter(UtcConverter); + } + } + } + } + } +} -- cgit v1.2.3