using PrivaPub.ClientModels.Resources; using System.ComponentModel.DataAnnotations; namespace PrivaPub.ClientModels.Admin { // The server's own settings as the administrator's page shows them (GET /clientapi/admin/settings): what the instance // API, NodeInfo, the instance actor, the web pages and the emails say of the server, its rules, its relays, its media // limits next to the configured ones, and whether its weekly activity is published. Registrations are read-only: they // stay in the configuration (Registrations:Mode). public class ViewServerSettings { public string Title { get; set; } public string ShortDescription { get; set; } public string Description { get; set; }//Markdown public DateTime? DescriptionUpdatedAt { get; set; } public string ContactEmail { get; set; } public string ContactAccountId { get; set; } public ViewContactAccount ContactAccount { get; set; }//null when none is set, or it is gone public string ThumbnailUrl { get; set; }//what the instance API shows (the default picture when none was uploaded) public bool ThumbnailIsDefault { get; set; } = true; public string PrivacyPolicy { get; set; }//Markdown public DateTime? PrivacyPolicyUpdatedAt { get; set; } public string TermsOfService { get; set; }//Markdown; null: none public DateTime? TermsEffectiveAt { get; set; } public List Rules { get; set; } = [];//deleted ones too (IsDeleted), as old reports name them public List Relays { get; set; } = []; public ViewMediaLimits Media { get; set; } = new(); public string RegistrationMode { get; set; }//"Invitations" or "Open", from the configuration public bool PublishActivity { get; set; } = false; public DateTime? UpdatedAt { get; set; }//null: never saved, every value is the default public string UpdatedBy { get; set; }//the administrator's login, or "cli" } public class ViewContactAccount { public string Id { get; set; } public string UserName { get; set; } public string Name { get; set; } public string Handle { get; set; }//user@host } public class ViewServerRule { public string Id { get; set; } public string Text { get; set; } public string Hint { get; set; } public int Position { get; set; } = 0; public bool IsDeleted { get; set; } = false; public DateTime? DeletedAt { get; set; } } // the administrator's overrides (null: the configured value holds), what the configuration says, and the hard caps public class ViewMediaLimits { public long? QuotaBytesPerRoot { get; set; } public long? MaxImageBytes { get; set; } public long? MaxVideoBytes { get; set; } public int? MaxSeconds { get; set; } public long ConfiguredQuotaBytesPerRoot { get; set; } = 0;//0: no quota public long ConfiguredMaxImageBytes { get; set; } = 0; public long ConfiguredMaxVideoBytes { get; set; } = 0; public int ConfiguredMaxSeconds { get; set; } = 0; public long EffectiveQuotaBytesPerRoot { get; set; } = 0; public long EffectiveMaxImageBytes { get; set; } = 0; public long EffectiveMaxVideoBytes { get; set; } = 0; public int EffectiveMaxSeconds { get; set; } = 0; public long HardMaxImageBytes { get; set; } = ServerMediaLimitsForm.HardMaxImageBytes; public long HardMaxVideoBytes { get; set; } = ServerMediaLimitsForm.HardMaxVideoBytes; public int HardMaxSeconds { get; set; } = ServerMediaLimitsForm.HardMaxSeconds; } // POST /clientapi/admin/settings: everything the page edits at once (relays and the thumbnail have their own routes). // A rule keeps its id when the form names it; one without an id is new; one left out is deleted (kept, marked). public class ServerSettingsForm { public const int MaxRules = 50; public const int MaxText = 20000; [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(64, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource)), Display(Name = "Title", ResourceType = typeof(FieldsNameResource))] public string Title { get; set; } [StringLength(500, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string ShortDescription { get; set; } [StringLength(MaxText, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource)), Display(Name = "Description", ResourceType = typeof(FieldsNameResource))] public string Description { get; set; } [EmailAddress(ErrorMessageResourceName = "InvalidEmail", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(254, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource)), Display(Name = "Email", ResourceType = typeof(FieldsNameResource))] public string ContactEmail { get; set; } [StringLength(64, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string ContactAccountId { get; set; }//a local persona's account id (its Mastodon id), or none [StringLength(MaxText, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string PrivacyPolicy { get; set; } [StringLength(MaxText, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string TermsOfService { get; set; }//empty: none public DateTime? TermsEffectiveAt { get; set; }//a date; today when terms are given without one [MaxLength(MaxRules, ErrorMessageResourceName = "MaxLengthArray", ErrorMessageResourceType = typeof(ErrorsResource))] public List Rules { get; set; } = []; public ServerMediaLimitsForm Media { get; set; } = new(); public bool PublishActivity { get; set; } = false; } public class ServerRuleForm { [StringLength(64, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string Id { get; set; }//an existing rule's id, or none for a new rule [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(500, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string Text { get; set; } [StringLength(1000, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string Hint { get; set; } } // null keeps the configured value public class ServerMediaLimitsForm { public const long HardMaxImageBytes = 40L * 1024 * 1024; public const long HardMaxVideoBytes = 99L * 1024 * 1024; public const int HardMaxSeconds = 4 * 3600; [Range(0L, long.MaxValue, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(ErrorsResource))] public long? QuotaBytesPerRoot { get; set; }//0: no quota [Range(1L, HardMaxImageBytes, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(ErrorsResource))] public long? MaxImageBytes { get; set; } [Range(1L, HardMaxVideoBytes, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(ErrorsResource))] public long? MaxVideoBytes { get; set; } [Range(1, HardMaxSeconds, ErrorMessageResourceName = "Range", ErrorMessageResourceType = typeof(ErrorsResource))] public int? MaxSeconds { get; set; } } // a relay the instance actor subscribes to: named by the configuration (Federation:Relays, removed only there) or by // the settings (added and removed from the page) public class ViewRelay { public string Address { get; set; } public string Source { get; set; }//"configuration" or "settings" public string State { get; set; }//"pending", "accepted", "rejected", or "none" before its first request public string ActorURI { get; set; } public DateTime? RequestedAt { get; set; } public DateTime? AnsweredAt { get; set; } } public class RelayForm { [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), StringLength(2048, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))] public string Address { get; set; }//the relay's actor (https://relay.example/actor) or its inbox } }