2026-10-17 04:02:47 +02:00
|
|
|
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<ViewServerRule> Rules { get; set; } = [];//deleted ones too (IsDeleted), as old reports name them
|
|
|
|
|
public List<ViewRelay> Relays { get; set; } = [];
|
2026-10-18 08:38:11 +02:00
|
|
|
public List<ViewStaffPick> StaffPicks { get; set; } = [];//the accounts suggested first to every persona (R8), their own routes
|
2026-10-17 04:02:47 +02:00
|
|
|
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<ServerRuleForm> 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; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-18 08:38:11 +02:00
|
|
|
// an account the administrators suggest to every persona here, first (R8: Mastodon's staff picks, `featured` among a
|
|
|
|
|
// suggestion's sources): a persona of this server or an account elsewhere, by its Mastodon account id. Added and removed
|
|
|
|
|
// on their own routes (/clientapi/admin/staff-picks), never through the settings form, so an older page saving the form
|
|
|
|
|
// never drops them.
|
|
|
|
|
public class ViewStaffPick
|
|
|
|
|
{
|
|
|
|
|
public string AccountId { get; set; }
|
|
|
|
|
public bool IsLocal { get; set; } = false;
|
|
|
|
|
public string Handle { get; set; }//user@host
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
public bool IsShown { get; set; } = true;//false: deleted, suspended or silenced since, so suggested to nobody
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class StaffPickForm
|
|
|
|
|
{
|
|
|
|
|
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)),
|
|
|
|
|
StringLength(64, ErrorMessageResourceName = "StringLength", ErrorMessageResourceType = typeof(ErrorsResource))]
|
|
|
|
|
public string AccountId { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-17 04:02:47 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|