5 Commits

Author SHA1 Message Date
Abdullah Atta
03f230dbca cors: add special handling for youtube embeds to bypass referer policy restrictions 2026-01-15 15:07:34 +05:00
Abdullah Atta
0a8c707387 docker: do not publish trimmed images 2026-01-01 10:36:02 +05:00
Abdullah Atta
8908b8c6ed sync: minor refactors 2026-01-01 10:35:35 +05:00
Abdullah Atta
da8df8973c sync: reduce batch size for fetching chunks 2025-12-30 12:52:56 +05:00
Abdullah Atta
7e50311c92 sync: reduce device ids chunk size 2025-12-30 12:52:36 +05:00
8 changed files with 95 additions and 15 deletions

View File

@@ -27,8 +27,6 @@ FROM build AS publish
RUN dotnet publish -c Release -o /app/publish \
#--runtime alpine-x64 \
--self-contained true \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH

View File

@@ -247,7 +247,7 @@ namespace Notesnook.API.Hubs
var chunks = PrepareChunks(
userId,
ids,
size: 1000,
size: 100,
resetSync: device.IsSyncReset,
maxBytes: 7 * 1024 * 1024
);

View File

@@ -91,11 +91,7 @@ namespace Notesnook.API.Repositories
public void DeleteByUserId(string userId)
{
var filter = Builders<SyncItem>.Filter.Eq("UserId", userId);
var writes = new List<WriteModel<SyncItem>>
{
new DeleteManyModel<SyncItem>(filter)
};
dbContext.AddCommand((handle, ct) => Collection.BulkWriteAsync(handle, writes, options: null, ct));
dbContext.AddCommand((handle, ct) => Collection.DeleteManyAsync(handle, filter, null, ct));
}
public void Upsert(SyncItem item, string userId, long dateSynced)

View File

@@ -73,7 +73,7 @@ namespace Notesnook.API.Services
return result;
}
const int MaxIdsPerChunk = 400_000;
const int MaxIdsPerChunk = 25_000;
public async Task AppendIdsAsync(string userId, string deviceId, string key, IEnumerable<ItemKey> ids)
{
var filter = DeviceIdsChunkFilter(userId, deviceId, key) & Builders<DeviceIdsChunk>.Filter.Where(x => x.Ids.Length < MaxIdsPerChunk);
@@ -81,7 +81,7 @@ namespace Notesnook.API.Services
if (chunk != null)
{
var update = Builders<DeviceIdsChunk>.Update.PushEach(x => x.Ids, ids.Select(i => i.ToString()));
var update = Builders<DeviceIdsChunk>.Update.AddToSetEach(x => x.Ids, ids.Select(i => i.ToString()));
await repositories.DeviceIdsChunks.Collection.UpdateOneAsync(
Builders<DeviceIdsChunk>.Filter.Eq(x => x.Id, chunk.Id),
update

View File

@@ -182,6 +182,7 @@ namespace Notesnook.API.Services
public async Task DeleteUserAsync(string userId)
{
logger.LogInformation("Deleting user {UserId}", userId);
var cc = new CancellationTokenSource();
Repositories.Notes.DeleteByUserId(userId);

View File

@@ -27,8 +27,6 @@ FROM build AS publish
RUN dotnet publish -c Release -o /app/publish \
#--runtime alpine-x64 \
--self-contained true \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH

View File

@@ -27,8 +27,6 @@ FROM build AS publish
RUN dotnet publish -c Release -o /app/publish \
#--runtime alpine-x64 \
--self-contained true \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH

View File

@@ -210,7 +210,19 @@ const server = Bun.serve({
});
}
// Proxy the request
// Check if it's a YouTube URL and redirect instead of proxying
if (isYouTubeEmbed(targetUrl)) {
// YouTube URL detected, redirect to youtube-nocookie.com
logRequest(req.method, targetUrl, 200);
return new Response(serveYouTubeEmbed(targetUrl), {
status: 200,
headers: {
"Content-Type": "text/html; charset=utf-8",
},
});
}
// Proxy the request for non-YouTube URLs
const response = await proxyRequest(targetUrl);
logRequest(req.method, targetUrl, response.status);
return response;
@@ -229,3 +241,80 @@ console.log(
);
console.log(`📋 Health check: http://${server.hostname}:${server.port}/health`);
console.log(`🌍 Environment: ${Bun.env.NODE_ENV || "development"}`);
/**
* This is required to bypass YouTube's Referrer Policy restrictions when
* embedding videos on the mobile app. It basically "proxies" the Referrer and
* allows any YouTube video to be embedded anywhere without restrictions.
*/
function serveYouTubeEmbed(url: string) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="referrer" content="strict-origin-when-cross-origin">
<meta name="robots" content="noindex,nofollow">
<title>YouTube Video Embed</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing:border-box
}
body, html {
overflow: hidden;
background:#000
}
iframe {
border: 0;
width: 100vw;
height: 100vh;
display: block
}
</style>
</head>
<body>
<iframe src="${transformYouTubeUrl(
url
)}" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture;web-share" allowfullscreen referrerpolicy="strict-origin-when-cross-origin" title="Video player"></iframe>
</body>
</html>`;
}
// Check if URL is a YouTube embed (including youtube-nocookie.com)
function isYouTubeEmbed(urlString: string) {
const url = new URL(urlString);
return (
(url.hostname === "www.youtube.com" ||
url.hostname === "youtube.com" ||
url.hostname === "m.youtube.com" ||
url.hostname === "www.youtube-nocookie.com" ||
url.hostname === "youtube-nocookie.com") &&
url.pathname.startsWith("/embed/")
);
}
// Transform YouTube URLs to use youtube-nocookie.com for enhanced privacy
function transformYouTubeUrl(urlString: string): string {
try {
const url = new URL(urlString);
// Check if it's a YouTube domain
if (
url.hostname === "www.youtube.com" ||
url.hostname === "youtube.com" ||
url.hostname === "m.youtube.com"
) {
// Replace with youtube-nocookie.com
url.hostname = "www.youtube-nocookie.com";
return url.toString();
}
return urlString;
} catch {
return urlString;
}
}