diff --git a/DysonNetwork.Shared/Models/PublicationSite.cs b/DysonNetwork.Shared/Models/PublicationSite.cs
index 8445b9a..98a42f3 100644
--- a/DysonNetwork.Shared/Models/PublicationSite.cs
+++ b/DysonNetwork.Shared/Models/PublicationSite.cs
@@ -1,9 +1,25 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
+using Google.Api;
namespace DysonNetwork.Shared.Models;
+public enum PublicationSiteMode
+{
+ ///
+ /// The fully managed mode which allow other server to handle all the requests
+ /// Including the rendering, data fetching etc. /// Very easy to use and efficient.
+ ///
+ FullyManaged,
+
+ ///
+ /// The self-managed mode allows user to upload their own site assets and use the Solar Network Pages
+ /// as a static site hosting platform.
+ ///
+ SelfManaged
+}
+
public class SnPublicationSite : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
@@ -11,34 +27,36 @@ public class SnPublicationSite : ModelBase
[MaxLength(4096)] public string Name { get; set; } = null!;
[MaxLength(8192)] public string? Description { get; set; }
+ public PublicationSiteMode Mode { get; set; } = PublicationSiteMode.FullyManaged;
public List Pages { get; set; } = [];
-
+
public Guid PublisherId { get; set; }
[NotMapped] public SnPublisher Publisher { get; set; } = null!;
public Guid AccountId { get; set; }
- // Preloaded via the remote services
[NotMapped] public SnAccount? Account { get; set; }
}
-public abstract class PublicationPagePresets
+public enum PublicationPageType
{
- // Will told the Isolated Island to render according to prebuilt pages by us
- public const string Landing = "landing"; // Some kind of the mixed version of the profile and the posts
- public const string Profile = "profile";
- public const string Posts = "posts";
-
- // Will told the Isolated Island to render according to the blocks to use custom stuff
- public const string Custom = "custom";
+ ///
+ /// The HTML page type allows user adding a custom HTML page in the fully managed mode.
+ ///
+ HtmlPage,
+ ///
+ /// The redirect mode allows user to create a shortcut for their own link.
+ /// such as example.solian.page/rickroll -- DyZ 301 -> youtube.com/...
+ ///
+ Redirect
}
public class SnPublicationPage : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
- [MaxLength(8192)] public string Preset { get; set; } = PublicationPagePresets.Landing;
+ public PublicationPageType Type { get; set; } = PublicationPageType.HtmlPage;
[MaxLength(8192)] public string Path { get; set; } = "/";
[Column(TypeName = "jsonb")] public Dictionary Config { get; set; } = new();
-
+
public Guid SiteId { get; set; }
[JsonIgnore] public SnPublicationSite Site { get; set; } = null!;
}
\ No newline at end of file
diff --git a/DysonNetwork.Zone/.gitignore b/DysonNetwork.Zone/.gitignore
index 542867e..1d8c562 100644
--- a/DysonNetwork.Zone/.gitignore
+++ b/DysonNetwork.Zone/.gitignore
@@ -33,3 +33,5 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Finder (MacOS) folder config
.DS_Store
+
+SiteData
diff --git a/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.Designer.cs b/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.Designer.cs
new file mode 100644
index 0000000..43c5b1c
--- /dev/null
+++ b/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.Designer.cs
@@ -0,0 +1,150 @@
+//
+using System;
+using System.Collections.Generic;
+using DysonNetwork.Zone;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NodaTime;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace DysonNetwork.Zone.Migrations
+{
+ [DbContext(typeof(AppDatabase))]
+ [Migration("20251120134237_AddSiteMode")]
+ partial class AddSiteMode
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("DysonNetwork.Shared.Models.SnPublicationPage", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property>("Config")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("config");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(8192)
+ .HasColumnType("character varying(8192)")
+ .HasColumnName("path");
+
+ b.Property("SiteId")
+ .HasColumnType("uuid")
+ .HasColumnName("site_id");
+
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasColumnName("type");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("updated_at");
+
+ b.HasKey("Id")
+ .HasName("pk_publication_pages");
+
+ b.HasIndex("SiteId")
+ .HasDatabaseName("ix_publication_pages_site_id");
+
+ b.ToTable("publication_pages", (string)null);
+ });
+
+ modelBuilder.Entity("DysonNetwork.Shared.Models.SnPublicationSite", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("AccountId")
+ .HasColumnType("uuid")
+ .HasColumnName("account_id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property("Description")
+ .HasMaxLength(8192)
+ .HasColumnType("character varying(8192)")
+ .HasColumnName("description");
+
+ b.Property("Mode")
+ .HasColumnType("integer")
+ .HasColumnName("mode");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("name");
+
+ b.Property("PublisherId")
+ .HasColumnType("uuid")
+ .HasColumnName("publisher_id");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasMaxLength(4096)
+ .HasColumnType("character varying(4096)")
+ .HasColumnName("slug");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("updated_at");
+
+ b.HasKey("Id")
+ .HasName("pk_publication_sites");
+
+ b.ToTable("publication_sites", (string)null);
+ });
+
+ modelBuilder.Entity("DysonNetwork.Shared.Models.SnPublicationPage", b =>
+ {
+ b.HasOne("DysonNetwork.Shared.Models.SnPublicationSite", "Site")
+ .WithMany("Pages")
+ .HasForeignKey("SiteId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_publication_pages_publication_sites_site_id");
+
+ b.Navigation("Site");
+ });
+
+ modelBuilder.Entity("DysonNetwork.Shared.Models.SnPublicationSite", b =>
+ {
+ b.Navigation("Pages");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.cs b/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.cs
new file mode 100644
index 0000000..3c4c111
--- /dev/null
+++ b/DysonNetwork.Zone/Migrations/20251120134237_AddSiteMode.cs
@@ -0,0 +1,52 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace DysonNetwork.Zone.Migrations
+{
+ ///
+ public partial class AddSiteMode : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "preset",
+ table: "publication_pages");
+
+ migrationBuilder.AddColumn(
+ name: "mode",
+ table: "publication_sites",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.AddColumn(
+ name: "type",
+ table: "publication_pages",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "mode",
+ table: "publication_sites");
+
+ migrationBuilder.DropColumn(
+ name: "type",
+ table: "publication_pages");
+
+ migrationBuilder.AddColumn(
+ name: "preset",
+ table: "publication_pages",
+ type: "character varying(8192)",
+ maxLength: 8192,
+ nullable: false,
+ defaultValue: "");
+ }
+ }
+}
diff --git a/DysonNetwork.Zone/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Zone/Migrations/AppDatabaseModelSnapshot.cs
index 1f448c4..1ce3f91 100644
--- a/DysonNetwork.Zone/Migrations/AppDatabaseModelSnapshot.cs
+++ b/DysonNetwork.Zone/Migrations/AppDatabaseModelSnapshot.cs
@@ -50,16 +50,14 @@ namespace DysonNetwork.Zone.Migrations
.HasColumnType("character varying(8192)")
.HasColumnName("path");
- b.Property("Preset")
- .IsRequired()
- .HasMaxLength(8192)
- .HasColumnType("character varying(8192)")
- .HasColumnName("preset");
-
b.Property("SiteId")
.HasColumnType("uuid")
.HasColumnName("site_id");
+ b.Property("Type")
+ .HasColumnType("integer")
+ .HasColumnName("type");
+
b.Property("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at");
@@ -97,6 +95,10 @@ namespace DysonNetwork.Zone.Migrations
.HasColumnType("character varying(8192)")
.HasColumnName("description");
+ b.Property("Mode")
+ .HasColumnType("integer")
+ .HasColumnName("mode");
+
b.Property("Name")
.IsRequired()
.HasMaxLength(4096)
diff --git a/DysonNetwork.Zone/Publication/PublicationSiteController.cs b/DysonNetwork.Zone/Publication/PublicationSiteController.cs
index 1f59b37..5d430b2 100644
--- a/DysonNetwork.Zone/Publication/PublicationSiteController.cs
+++ b/DysonNetwork.Zone/Publication/PublicationSiteController.cs
@@ -4,7 +4,6 @@ using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Models = DysonNetwork.Shared.Models;
-using PublicationPagePresets = DysonNetwork.Shared.Models.PublicationPagePresets;
namespace DysonNetwork.Zone.Publication;
@@ -15,10 +14,10 @@ public class PublicationSiteController(
RemotePublisherService publisherService
) : ControllerBase
{
- [HttpGet("site/{slug}")]
- public async Task> GetSite(string slug)
+ [HttpGet("{pubName}/{slug}")]
+ public async Task> GetSite(string pubName, string slug)
{
- var site = await publicationService.GetSiteBySlug(slug);
+ var site = await publicationService.GetSiteBySlug(slug, pubName);
if (site == null)
return NotFound();
return Ok(site);
@@ -71,6 +70,7 @@ public class PublicationSiteController(
var site = new SnPublicationSite
{
+ Mode = request.Mode,
Slug = request.Slug,
Name = request.Name,
Description = request.Description,
@@ -106,12 +106,10 @@ public class PublicationSiteController(
if (publisher == null) return NotFound();
var site = await publicationService.GetSiteById(id);
- if (site == null)
- return NotFound();
-
- if (site.PublisherId != publisher.Id)
+ if (site == null || site.PublisherId != publisher.Id)
return NotFound();
+ site.Mode = request.Mode;
site.Slug = request.Slug;
site.Name = request.Name;
site.Description = request.Description ?? site.Description;
@@ -204,7 +202,7 @@ public class PublicationSiteController(
var page = new SnPublicationPage
{
- Preset = request.Preset ?? PublicationPagePresets.Landing,
+ Type = request.Type,
Path = request.Path ?? "/",
Config = request.Config ?? new Dictionary(),
SiteId = site.Id
@@ -239,7 +237,7 @@ public class PublicationSiteController(
var accountId = Guid.Parse(currentUser.Id);
- if (request.Preset != null) page.Preset = request.Preset;
+ page.Type = request.Type;
if (request.Path != null) page.Path = request.Path;
if (request.Config != null) page.Config = request.Config;
@@ -278,6 +276,7 @@ public class PublicationSiteController(
public class PublicationSiteRequest
{
+ public PublicationSiteMode Mode { get; set; }
[MaxLength(4096)] public string Slug { get; set; } = null!;
[MaxLength(4096)] public string Name { get; set; } = null!;
[MaxLength(8192)] public string? Description { get; set; }
@@ -285,7 +284,7 @@ public class PublicationSiteController(
public class PublicationPageRequest
{
- [MaxLength(8192)] public string? Preset { get; set; }
+ public PublicationPageType Type { get; set; }
[MaxLength(8192)] public string? Path { get; set; }
public Dictionary? Config { get; set; }
}
diff --git a/DysonNetwork.Zone/Publication/PublicationSiteService.cs b/DysonNetwork.Zone/Publication/PublicationSiteService.cs
index 3231fb5..b172e0d 100644
--- a/DysonNetwork.Zone/Publication/PublicationSiteService.cs
+++ b/DysonNetwork.Zone/Publication/PublicationSiteService.cs
@@ -1,5 +1,6 @@
using System.Text.RegularExpressions;
using DysonNetwork.Shared.Auth;
+using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Registry;
using Microsoft.EntityFrameworkCore;
@@ -9,7 +10,8 @@ namespace DysonNetwork.Zone.Publication;
public class PublicationSiteService(
AppDatabase db,
RemotePublisherService publisherService,
- RemoteAccountService remoteAccounts
+ RemoteAccountService remoteAccounts,
+ RemotePublisherService remotePublishers
)
{
public async Task GetSiteById(Guid id)
@@ -19,9 +21,18 @@ public class PublicationSiteService(
.FirstOrDefaultAsync(s => s.Id == id);
}
- public async Task GetSiteBySlug(string slug)
+ public async Task GetSiteBySlug(string slug, string? pubName = null)
{
+ Guid? pubId = null;
+ if (pubName != null)
+ {
+ var pub = await remotePublishers.GetPublisherByName(pubName);
+ if (pub == null) throw new InvalidOperationException("Publisher not found.");
+ pubId = pub.Id;
+ }
+
return await db.PublicationSites
+ .If(pubId.HasValue, q => q.Where(s => s.PublisherId == pubId.Value))
.Include(s => s.Pages)
.FirstOrDefaultAsync(s => s.Slug == slug);
}
@@ -39,7 +50,7 @@ public class PublicationSiteService(
var perk = (await remoteAccounts.GetAccount(accountId)).PerkSubscription;
var perkLevel = perk is not null ? PerkSubscriptionPrivilege.GetPrivilegeFromIdentifier(perk.Identifier) : 0;
- var maxSite = (perkLevel) switch
+ var maxSite = perkLevel switch
{
1 => 2,
2 => 3,
diff --git a/DysonNetwork.Zone/appsettings.json b/DysonNetwork.Zone/appsettings.json
index e9d6c80..f669dd6 100644
--- a/DysonNetwork.Zone/appsettings.json
+++ b/DysonNetwork.Zone/appsettings.json
@@ -13,5 +13,8 @@
"KnownProxies": ["127.0.0.1", "::1"],
"Swagger": {
"PublicBasePath": "/zone"
+ },
+ "Sites": {
+ "BasePath": "/SiteData"
}
}