Skip to content

Commit 5717b67

Browse files
committed
monographs: add slug field which regenerates on update
1 parent e489ce7 commit 5717b67

File tree

6 files changed

+104
-14
lines changed

6 files changed

+104
-14
lines changed

Notesnook.API/Controllers/MonographsController.cs

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,25 @@ You should have received a copy of the Affero GNU General Public License
1818
*/
1919

2020
using System;
21-
using System.Collections.Generic;
2221
using System.Linq;
2322
using System.Security.Claims;
2423
using System.Text.Json;
2524
using System.Threading.Tasks;
2625
using AngleSharp;
27-
using AngleSharp.Dom;
2826
using Microsoft.AspNetCore.Authorization;
2927
using Microsoft.AspNetCore.Http;
3028
using Microsoft.AspNetCore.Mvc;
3129
using Microsoft.Extensions.Logging;
3230
using MongoDB.Bson;
3331
using MongoDB.Driver;
34-
using Notesnook.API.Authorization;
32+
using NanoidDotNet;
33+
using Notesnook.API.Extensions;
3534
using Notesnook.API.Models;
3635
using Notesnook.API.Services;
3736
using Streetwriters.Common;
3837
using Streetwriters.Common.Helpers;
3938
using Streetwriters.Common.Interfaces;
4039
using Streetwriters.Common.Messages;
41-
using Streetwriters.Data.Interfaces;
4240
using Streetwriters.Data.Repositories;
4341

4442
namespace Notesnook.API.Controllers
@@ -95,6 +93,22 @@ private async Task<Monograph> FindMonographAsync(string itemId)
9593
return await result.FirstOrDefaultAsync();
9694
}
9795

96+
private async Task<Monograph> FindMonographBySlugAsync(string slug)
97+
{
98+
var result = await monographs.Collection.FindAsync(
99+
Builders<Monograph>.Filter.Eq("Slug", slug),
100+
new FindOptions<Monograph>
101+
{
102+
Limit = 1
103+
});
104+
return await result.FirstOrDefaultAsync();
105+
}
106+
107+
private static string GenerateSlug()
108+
{
109+
return Nanoid.Generate(size: 24);
110+
}
111+
98112
[HttpPost]
99113
public async Task<IActionResult> PublishAsync([FromQuery] string? deviceId, [FromBody] Monograph monograph)
100114
{
@@ -120,6 +134,7 @@ public async Task<IActionResult> PublishAsync([FromQuery] string? deviceId, [Fro
120134
}
121135
monograph.Deleted = false;
122136
monograph.ViewCount = 0;
137+
monograph.Slug = GenerateSlug();
123138
await monographs.Collection.ReplaceOneAsync(
124139
CreateMonographFilter(userId, monograph),
125140
monograph,
@@ -131,7 +146,8 @@ await monographs.Collection.ReplaceOneAsync(
131146
return Ok(new
132147
{
133148
id = monograph.ItemId,
134-
datePublished = monograph.DatePublished
149+
datePublished = monograph.DatePublished,
150+
publishUrl = monograph.ConstructPublishUrl()
135151
});
136152
}
137153
catch (Exception e)
@@ -164,6 +180,7 @@ public async Task<IActionResult> UpdateAsync([FromQuery] string? deviceId, [From
164180
monograph.Content = null;
165181

166182
monograph.DatePublished = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
183+
monograph.Slug = GenerateSlug();
167184
var result = await monographs.Collection.UpdateOneAsync(
168185
CreateMonographFilter(userId, monograph),
169186
Builders<Monograph>.Update
@@ -172,6 +189,7 @@ public async Task<IActionResult> UpdateAsync([FromQuery] string? deviceId, [From
172189
.Set(m => m.EncryptedContent, monograph.EncryptedContent)
173190
.Set(m => m.SelfDestruct, monograph.SelfDestruct)
174191
.Set(m => m.Title, monograph.Title)
192+
.Set(m => m.Slug, monograph.Slug)
175193
.Set(m => m.Password, monograph.Password)
176194
);
177195
if (!result.IsAcknowledged) return BadRequest();
@@ -181,7 +199,8 @@ public async Task<IActionResult> UpdateAsync([FromQuery] string? deviceId, [From
181199
return Ok(new
182200
{
183201
id = monograph.ItemId,
184-
datePublished = monograph.DatePublished
202+
datePublished = monograph.DatePublished,
203+
publishUrl = monograph.ConstructPublishUrl()
185204
});
186205
}
187206
catch (Exception e)
@@ -208,11 +227,25 @@ public async Task<IActionResult> GetUserMonographsAsync()
208227
return Ok(userMonographs.Select((m) => m.ItemId ?? m.Id));
209228
}
210229

211-
[HttpGet("{id}")]
230+
[HttpGet("{slugOrId}")]
212231
[AllowAnonymous]
213-
public async Task<IActionResult> GetMonographAsync([FromRoute] string id)
232+
public async Task<IActionResult> GetMonographAsync([FromRoute] string slugOrId)
214233
{
215-
var monograph = await FindMonographAsync(id);
234+
var monograph = await FindMonographBySlugAsync(slugOrId);
235+
236+
if (monograph == null)
237+
{
238+
monograph = await FindMonographAsync(slugOrId);
239+
if (!string.IsNullOrEmpty(monograph?.Slug))
240+
{
241+
return NotFound(new
242+
{
243+
error = "invalid_id",
244+
error_description = $"No such monograph found."
245+
});
246+
}
247+
}
248+
216249
if (monograph == null || monograph.Deleted)
217250
{
218251
return NotFound(new
@@ -317,6 +350,22 @@ await monographs.Collection.ReplaceOneAsync(
317350
return Ok();
318351
}
319352

353+
[HttpGet("{id}/publish-url")]
354+
public async Task<IActionResult> GetPublishUrlAsync([FromRoute] string id)
355+
{
356+
var userId = this.User.GetUserId();
357+
var monograph = await FindMonographAsync(id);
358+
if (monograph == null || monograph.Deleted || monograph.UserId != userId)
359+
{
360+
return NotFound();
361+
}
362+
363+
return Ok(new
364+
{
365+
publishUrl = monograph.ConstructPublishUrl()
366+
});
367+
}
368+
320369
private static async Task MarkMonographForSyncAsync(string userId, string monographId, string? deviceId, string? jti)
321370
{
322371
if (deviceId == null) return;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
3+
4+
Copyright (C) 2023 Streetwriters (Private) Limited
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the Affero GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
Affero GNU General Public License for more details.
15+
16+
You should have received a copy of the Affero GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
using Notesnook.API.Models;
21+
using Streetwriters.Common;
22+
23+
namespace Notesnook.API.Extensions
24+
{
25+
public static class MonographExtensions
26+
{
27+
public static string ConstructPublishUrl(this Monograph monograph)
28+
{
29+
var baseUrl = Constants.MONOGRAPH_PUBLIC_URL;
30+
return $"{baseUrl}/{monograph.Slug ?? monograph.ItemId}";
31+
}
32+
}
33+
}

Notesnook.API/Hubs/SyncV2Hub.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ You should have received a copy of the Affero GNU General Public License
3131
using Microsoft.AspNetCore.SignalR;
3232
using MongoDB.Driver;
3333
using Notesnook.API.Authorization;
34+
using Notesnook.API.Extensions;
3435
using Notesnook.API.Interfaces;
3536
using Notesnook.API.Models;
3637
using Notesnook.API.Services;
@@ -275,18 +276,20 @@ private async Task<SyncV2Metadata> HandleRequestFetch(string deviceId, bool incl
275276
Builders<Monograph>.Filter.In("_id", unsyncedMonographIds)
276277
)
277278
);
278-
var userMonographs = await Repositories.Monographs.Collection.Find(filter).Project((m) => new MonographMetadata
279+
var userMonographs = await Repositories.Monographs.Collection.Find(filter).ToListAsync();
280+
var userMonographMetadatas = userMonographs.Select((m) => new MonographMetadata
279281
{
280282
DatePublished = m.DatePublished,
281283
Deleted = m.Deleted,
282284
Password = m.Password,
283285
SelfDestruct = m.SelfDestruct,
284286
Title = m.Title,
287+
PublishUrl = m.ConstructPublishUrl(),
285288
ItemId = m.ItemId ?? m.Id.ToString(),
286289
ViewCount = m.ViewCount
287-
}).ToListAsync();
290+
}).ToList();
288291

289-
if (userMonographs.Count > 0 && !await Clients.Caller.SendMonographs(userMonographs).WaitAsync(TimeSpan.FromMinutes(10)))
292+
if (userMonographMetadatas.Count > 0 && !await Clients.Caller.SendMonographs(userMonographMetadatas).WaitAsync(TimeSpan.FromMinutes(10)))
290293
throw new HubException("Client rejected monographs.");
291294

292295
device.HasInitialMonographsSync = true;

Notesnook.API/Models/Monograph.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public Monograph()
5656
[JsonPropertyName("title")]
5757
public string? Title { get; set; }
5858

59+
[JsonPropertyName("slug")]
60+
public string? Slug { get; set; }
61+
5962
[JsonPropertyName("userId")]
6063
public string? UserId { get; set; }
6164

Notesnook.API/Models/MonographMetadata.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ You should have received a copy of the Affero GNU General Public License
1818
*/
1919

2020
using System.Text.Json.Serialization;
21-
using MongoDB.Bson;
22-
using MongoDB.Bson.Serialization.Attributes;
2321
using System.Runtime.Serialization;
2422

2523
namespace Notesnook.API.Models
@@ -37,6 +35,9 @@ public required string ItemId
3735
[JsonPropertyName("title")]
3836
public string? Title { get; set; }
3937

38+
[JsonPropertyName("publishUrl")]
39+
public string? PublishUrl { get; set; }
40+
4041
[JsonPropertyName("selfDestruct")]
4142
public bool SelfDestruct { get; set; }
4243

Streetwriters.Common/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class Constants
7979
public static string? SUBSCRIPTIONS_CERT_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_PATH");
8080
public static string? SUBSCRIPTIONS_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_KEY_PATH");
8181
public static string[] NOTESNOOK_CORS_ORIGINS => Environment.GetEnvironmentVariable("NOTESNOOK_CORS")?.Split(",") ?? new string[] { };
82+
public static string MONOGRAPH_PUBLIC_URL => Environment.GetEnvironmentVariable("MONOGRAPH_PUBLIC_URL") ?? "https://monogr.ph";
8283
}
8384
}
8485

0 commit comments

Comments
 (0)