23 Commits

Author SHA1 Message Date
Abdullah Atta
b12eb39797 docker: use wget for healthcheck 2024-08-29 12:23:14 +05:00
Abdullah Atta
962b805054 api: remove s3 objects bigger than the maximum size 2024-08-29 12:20:32 +05:00
Abdullah Atta
f3216330a1 docker: remove even more unnecessary env vars 2024-08-29 12:19:37 +05:00
Abdullah Atta
63069ae573 sync: more stable deserializer for syncitem model 2024-08-11 10:18:55 +05:00
Abdullah Atta
cd06a31d1b docker: expose non-80 port inside containers 2024-08-09 11:20:07 +05:00
Abdullah Atta
21a9b4c203 docker: add service to validate enviroment variables before starting containers 2024-08-08 09:14:58 +05:00
Abdullah Atta
a1003ffdd5 docker: minor cleanup 2024-08-07 15:25:43 +05:00
Abdullah Atta
c66a084ed6 docker: explain why smtp configuration is required 2024-08-07 15:16:30 +05:00
Abdullah Atta
dfabfcbc23 common: simplify compatibility version 2024-08-07 15:06:58 +05:00
Abdullah Atta
e324b588a1 docker: fill out as many vars in .env as possible 2024-08-07 09:02:18 +05:00
Abdullah Atta
15b6947ff0 docker: remove support for 3rd-party S3 providers 2024-08-07 08:50:58 +05:00
Abdullah Atta
c441a1750c docker: add documentation for all variables in .env 2024-08-06 15:52:18 +05:00
Abdullah Atta
9f1f3e14d7 global: remove unncessary environment variables 2024-08-06 15:52:00 +05:00
Abdullah Atta
90118488cb docker: update .env 2024-08-02 11:37:47 +05:00
Abdullah Atta
e99f0f33d2 docker: fix attachments not uploading 2024-08-02 11:36:29 +05:00
Abdullah Atta
881354ab83 global: fix failing build 2024-08-01 12:20:37 +05:00
Abdullah Atta
5c1944d29f common: send more info in /version endpoint 2024-08-01 12:05:50 +05:00
Abdullah Atta
cbd0c01d28 identity: add support for disabling new signups 2024-08-01 10:32:51 +05:00
Abdullah Atta
ad590f6011 identity: auto enable 2fa by email on self hosted instance 2024-08-01 10:05:46 +05:00
Abdullah Atta
2f5bd75d4e identity: confirm email automatically on self hosted instances 2024-08-01 09:56:06 +05:00
Abdullah Atta
3c8c8ebc81 chore: update docker set up instructions 2024-07-31 13:28:59 +05:00
Abdullah Atta
2bbb50e9f6 docker: use container images from dockerhub in docker compose 2024-07-31 13:16:19 +05:00
Abdullah Atta
d0a1a2ea9f docker: enable reflection for json serializing 2024-07-31 12:58:38 +05:00
19 changed files with 217 additions and 193 deletions

100
.env
View File

@@ -1,51 +1,77 @@
# Required variables
NOTESNOOK_API_SECRET= # This should be a randomly generated secret
# Description: Name of your self hosted instance. Used in the client apps for identification purposes
# Required: yes
# Example: notesnook-instance-sg
INSTANCE_NAME=self-hosted-notesnook-instance
# SMTP settings required for delivering emails
# Description: This secret is used for generating, validating, and introspecting auth tokens. It must be a randomly generated token (preferably >32 characters).
# Required: yes
NOTESNOOK_API_SECRET=
# Description: Use this flag to disable creation of new accounts on your instance (i.e. in case it is exposed to the Internet).
# Required: yes
# Possible values: 0 for false; 1 for true
DISABLE_ACCOUNT_CREATION=0
### SMTP Configuration ###
# SMTP Configuration is required for sending emails for password reset, 2FA emails etc. You can get SMTP settings from your email provider.
# Description: Username for the SMTP connection (most time it is the email address of your account). Check your email provider's documentation to get the appropriate value.
# Required: yes
SMTP_USERNAME=
# Description: Password for the SMTP connection. Check your email provider's documentation to get the appropriate value.
# Required: yes
SMTP_PASSWORD=
# Description: Host on which the the SMTP connection is running. Check your email provider's documentation to get the appropriate value.
# Required: yes
# Example: smtp.gmail.com
SMTP_HOST=
# Description: Port on which the the SMTP connection is running. Check your email provider's documentation to get the appropriate value.
# Required: yes
# Example: 465
SMTP_PORT=
NOTESNOOK_SENDER_EMAIL= # optional
NOTESNOOK_SENDER_NAME= # optional
SMTP_REPLYTO_NAME= # optional
SMTP_REPLYTO_EMAIL= # optional
# Description: The FROM email address when sending out emails. Must be an email address under your control otherwise sending will fail. Most times it is the same email address as the SMTP_USERNAME.
# Required: no
# Example: support@notesnook.com
NOTESNOOK_SENDER_EMAIL=
# Description: The reply-to email is used whenever a user is replying to the email you sent. You can use this to set a different reply-to email address than the one you used to send the email.
# Required: no
# Example: support@notesnook.com
SMTP_REPLYTO_EMAIL=
# MessageBird or Twilio are used for 2FA via SMS
# You can setup either of them or none of them but keep in mind
# that 2FA via SMS will not work if you haven't set up at least
# one SMS provider.
MESSAGEBIRD_ACCESS_KEY=
# Description: Twilio account SID is required for sending SMS with 2FA codes. Learn more here: https://help.twilio.com/articles/14726256820123-What-is-a-Twilio-Account-SID-and-where-can-I-find-it-
# Required: no
TWILIO_ACCOUNT_SID=
# Description: Twilio account auth is required for sending SMS with 2FA codes. Learn more here: https://help.twilio.com/articles/223136027-Auth-Tokens-and-How-to-Change-Them
# Required: no
TWILIO_AUTH_TOKEN=
# Description: The unique string that we created to identify the Service resource.
# Required: no
# Example: VAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
TWILIO_SERVICE_SID=
# Server discovery settings
# The domain must be without protocol
# e.g. example.org NOT http://example.org
NOTESNOOK_SERVER_DOMAIN=
IDENTITY_SERVER_DOMAIN=
SSE_SERVER_DOMAIN=
# Description: This is the public domain for the Authentication server. It can also be the IP address if you don't own a domain name. The domain/IP must be accessible from wherever you are running the Notesnook clients. Used for generating email confirmation & password reset URLs.
# Required: yes
# Example: auth.streetwriters.co
IDENTITY_SERVER_DOMAIN=localhost:8264
# Add the origins on which you want to enable CORS.
# Leave it empty to allow all origins to access your server.
# Seperate each origin with a comma
# e.g. https://app.notesnook.com,http://localhost:3000
NOTESNOOK_CORS_ORIGINS= # optional
# Description: Add the origins for which you want to allow CORS. Leave it empty to allow all origins to access your server. If you want to allow multiple origins, seperate each origin with a comma.
# Required: no
# Example: https://app.notesnook.com,http://localhost:3000
NOTESNOOK_CORS_ORIGINS=
# url of the web app instance you want to use
# e.g. https://app.notesnook.com
# Note: no slashes at the end
NOTESNOOK_APP_HOST=
# Description: This is the URL for the web app, and is used by the backend for creating redirect URLs (e.g. after email confirmation etc).
# Note: the URL has no slashes at the end
# Required: yes
# Example: https://app.notesnook.com
NOTESNOOK_APP_HOST=https://app.notesnook.com
# Minio is used for S3 storage
MINIO_ROOT_USER= # aka. AccessKeyId (must be > 3 characters)
MINIO_ROOT_PASSWORD= # aka. AccessKey (must be > 8 characters)
# If you don't want to use Minio, you can use any other S3 compatible
# storage service.
S3_ACCESS_KEY=
S3_ACCESS_KEY_ID=
# Description: Custom username for the root Minio account. Minio is used for storing your attachments. This must be greater than 3 characters in length.
# Required: no
MINIO_ROOT_USER=
# Description: Custom password for the root Minio account. Minio is used for storing your attachments. This must be greater than 8 characters in length.
# Required: no
MINIO_ROOT_PASSWORD=
# Description: The URL must be accessible from wherever you are running the Notesnook clients. It'll be used by the Notesnook clients for uploading/downloading attachments.
# Required: no
# Example: https://attachments.notesnook.com
S3_SERVICE_URL=
S3_REGION=
S3_BUCKET_NAME=attachments # required

View File

@@ -1,7 +1,5 @@
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
ARG TARGETARCH
@@ -32,6 +30,7 @@ RUN dotnet publish -c Release -o /app/publish \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH
FROM --platform=$BUILDPLATFORM base AS final

View File

@@ -156,58 +156,50 @@ namespace Notesnook.API.Models
public override SyncItem Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var syncItem = new SyncItem();
var bsonReader = context.Reader;
bsonReader.ReadStartDocument();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var id = bsonReader.ReadObjectId();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var dateSynced = bsonReader.ReadInt64();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var userId = bsonReader.ReadString();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var iv = bsonReader.ReadString();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var cipher = bsonReader.ReadString();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var itemId = bsonReader.ReadString();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var length = bsonReader.ReadInt64();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var version = bsonReader.ReadDouble();
bsonReader.ReadBsonType();
bsonReader.SkipName();
var algorithm = bsonReader.ReadString();
bsonReader.ReadEndDocument();
return new SyncItem
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
{
Id = id,
DateSynced = dateSynced,
UserId = userId,
IV = iv,
Cipher = cipher,
ItemId = itemId,
Length = length,
Version = version,
Algorithm = algorithm
};
var fieldName = bsonReader.ReadName();
switch (fieldName)
{
case "DateSynced":
syncItem.DateSynced = bsonReader.ReadInt64();
break;
case "UserId":
syncItem.UserId = bsonReader.ReadString();
break;
case "IV":
syncItem.IV = bsonReader.ReadString();
break;
case "Cipher":
syncItem.Cipher = bsonReader.ReadString();
break;
case "ItemId":
syncItem.ItemId = bsonReader.ReadString();
break;
case "_id":
syncItem.Id = bsonReader.ReadObjectId();
break;
case "Length":
syncItem.Length = bsonReader.ReadInt64();
break;
case "Version":
syncItem.Version = bsonReader.ReadDouble();
break;
case "Algorithm":
syncItem.Algorithm = bsonReader.ReadString();
break;
default:
bsonReader.SkipValue();
break;
}
}
bsonReader.ReadEndDocument();
return syncItem;
}
}
}

View File

@@ -145,6 +145,12 @@ namespace Notesnook.API.Services
var request = new HttpRequestMessage(HttpMethod.Head, url);
var response = await httpClient.SendAsync(request);
const long MAX_SIZE = 513 * 1024 * 1024; // 512 MB
if (!Constants.IS_SELF_HOSTED && response.Content.Headers.ContentLength >= MAX_SIZE)
{
await this.DeleteObjectAsync(userId, name);
throw new Exception("File size exceeds the maximum allowed size.");
}
return response.Content.Headers.ContentLength ?? 0;
}

View File

@@ -233,7 +233,7 @@ namespace Notesnook.API
app.UseResponseCompression();
app.UseCors("notesnook");
app.UseVersion();
app.UseVersion(Servers.NotesnookAPI);
app.UseWamp(WampServers.NotesnookServer, (realm, server) =>
{

View File

@@ -8,7 +8,7 @@ This repo contains the full source code of the Notesnook Sync Server licensed un
Requirements:
1. [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)
1. [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
2. [git](https://git-scm.com/downloads)
The first step is to `clone` the repository:
@@ -55,19 +55,14 @@ dotnet run --project Streetwriters.Identity/Streetwriters.Identity.csproj
The sync server can easily be started using Docker.
The first step is to `clone` the repository:
```bash
git clone https://github.com/streetwriters/notesnook-sync-server.git
# change directory
cd notesnook-sync-server
wget https://raw.githubusercontent.com/streetwriters/notesnook-sync-server/master/docker-compose.yml
```
And then use Docker Compose to start the servers:
```bash
docker-compose up
docker compose up
```
This takes care of setting up everything including MongoDB, Minio etc.
@@ -81,7 +76,7 @@ This takes care of setting up everything including MongoDB, Minio etc.
- [x] Open source the SSE Messaging infrastructure
- [x] Fully Dockerize all services
- [x] Use self-hosted Minio for S3 storage
- [ ] Publish on DockerHub
- [x] Publish on DockerHub
- [ ] Write self hosting docs
- [ ] Add settings to change server URLs in Notesnook client apps

View File

@@ -34,7 +34,7 @@ namespace Streetwriters.Common
Id = "notesnook",
Name = "Notesnook",
SenderEmail = Constants.NOTESNOOK_SENDER_EMAIL,
SenderName = Constants.NOTESNOOK_SENDER_NAME,
SenderName = "Notesnook",
Type = ApplicationType.NOTESNOOK,
AppId = ApplicationType.NOTESNOOK,
AccountRecoveryRedirectURL = $"{Constants.NOTESNOOK_APP_HOST}/account/recovery",

View File

@@ -23,7 +23,10 @@ namespace Streetwriters.Common
{
public class Constants
{
public static int COMPATIBILITY_VERSION = 1;
public static bool IS_SELF_HOSTED => Environment.GetEnvironmentVariable("SELF_HOSTED") == "1";
public static bool DISABLE_ACCOUNT_CREATION => Environment.GetEnvironmentVariable("DISABLE_ACCOUNT_CREATION") == "1";
public static string INSTANCE_NAME => Environment.GetEnvironmentVariable("INSTANCE_NAME") ?? "default";
// S3 related
public static string S3_ACCESS_KEY => Environment.GetEnvironmentVariable("S3_ACCESS_KEY");
@@ -39,10 +42,8 @@ namespace Streetwriters.Common
public static string SMTP_PASSWORD => Environment.GetEnvironmentVariable("SMTP_PASSWORD");
public static string SMTP_HOST => Environment.GetEnvironmentVariable("SMTP_HOST");
public static string SMTP_PORT => Environment.GetEnvironmentVariable("SMTP_PORT");
public static string SMTP_REPLYTO_NAME => Environment.GetEnvironmentVariable("SMTP_REPLYTO_NAME");
public static string SMTP_REPLYTO_EMAIL => Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL");
public static string NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL");
public static string NOTESNOOK_SENDER_NAME => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_NAME");
public static string NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL") ?? Environment.GetEnvironmentVariable("SMTP_USERNAME");
public static string NOTESNOOK_APP_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_APP_HOST");
public static string NOTESNOOK_API_SECRET => Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET");
@@ -54,7 +55,6 @@ namespace Streetwriters.Common
// Server discovery
public static int NOTESNOOK_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_PORT") ?? "80");
public static string NOTESNOOK_SERVER_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_HOST");
public static string NOTESNOOK_SERVER_DOMAIN => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_DOMAIN");
public static string NOTESNOOK_CERT_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_PATH");
public static string NOTESNOOK_CERT_KEY_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_KEY_PATH");
@@ -66,7 +66,6 @@ namespace Streetwriters.Common
public static int SSE_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SSE_SERVER_PORT") ?? "80");
public static string SSE_SERVER_HOST => Environment.GetEnvironmentVariable("SSE_SERVER_HOST");
public static string SSE_SERVER_DOMAIN => Environment.GetEnvironmentVariable("SSE_SERVER_DOMAIN");
public static string SSE_CERT_PATH => Environment.GetEnvironmentVariable("SSE_CERT_PATH");
public static string SSE_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SSE_CERT_KEY_PATH");
@@ -75,7 +74,6 @@ namespace Streetwriters.Common
public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME");
public static int SUBSCRIPTIONS_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_PORT") ?? "80");
public static string SUBSCRIPTIONS_SERVER_HOST => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_HOST");
public static string SUBSCRIPTIONS_SERVER_DOMAIN => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_DOMAIN");
public static string SUBSCRIPTIONS_CERT_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_PATH");
public static string SUBSCRIPTIONS_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_KEY_PATH");
public static string[] NOTESNOOK_CORS_ORIGINS => Environment.GetEnvironmentVariable("NOTESNOOK_CORS")?.Split(",") ?? new string[] { };

View File

@@ -18,6 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@@ -30,13 +32,20 @@ namespace Streetwriters.Common.Extensions
{
public static class AppBuilderExtensions
{
public static IApplicationBuilder UseVersion(this IApplicationBuilder app)
public static IApplicationBuilder UseVersion(this IApplicationBuilder app, Server server)
{
app.Map("/version", (app) =>
{
app.Run(async context =>
{
await context.Response.WriteAsync(Version.AsString());
context.Response.ContentType = "application/json";
var data = new Dictionary<string, object>
{
{ "version", Constants.COMPATIBILITY_VERSION },
{ "id", server.Id },
{ "instance", Constants.INSTANCE_NAME }
};
await context.Response.WriteAsync(JsonSerializer.Serialize(data));
});
});
return app;

View File

@@ -34,7 +34,7 @@ namespace Streetwriters.Common
if (!string.IsNullOrEmpty(originCertPath) && !string.IsNullOrEmpty(originCertKeyPath))
this.SSLCertificate = X509Certificate2.CreateFromPemFile(originCertPath, originCertKeyPath);
}
public string Id { get; set; }
public int Port { get; set; }
public string Hostname { get; set; }
public string Domain { get; set; }
@@ -92,16 +92,16 @@ namespace Streetwriters.Common
#endif
public static Server NotesnookAPI { get; } = new(Constants.NOTESNOOK_CERT_PATH, Constants.NOTESNOOK_CERT_KEY_PATH)
{
Domain = Constants.NOTESNOOK_SERVER_DOMAIN,
Port = Constants.NOTESNOOK_SERVER_PORT,
Hostname = Constants.NOTESNOOK_SERVER_HOST,
Id = "notesnook-sync"
};
public static Server MessengerServer { get; } = new(Constants.SSE_CERT_PATH, Constants.SSE_CERT_KEY_PATH)
{
Domain = Constants.SSE_SERVER_DOMAIN,
Port = Constants.SSE_SERVER_PORT,
Hostname = Constants.SSE_SERVER_HOST,
Id = "sse"
};
public static Server IdentityServer { get; } = new(Constants.IDENTITY_CERT_PATH, Constants.IDENTITY_CERT_KEY_PATH)
@@ -109,13 +109,14 @@ namespace Streetwriters.Common
Domain = Constants.IDENTITY_SERVER_DOMAIN,
Port = Constants.IDENTITY_SERVER_PORT,
Hostname = Constants.IDENTITY_SERVER_HOST,
Id = "auth"
};
public static Server SubscriptionServer { get; } = new(Constants.SUBSCRIPTIONS_CERT_PATH, Constants.SUBSCRIPTIONS_CERT_KEY_PATH)
{
Domain = Constants.SUBSCRIPTIONS_SERVER_DOMAIN,
Port = Constants.SUBSCRIPTIONS_SERVER_PORT,
Hostname = Constants.SUBSCRIPTIONS_SERVER_HOST,
Id = "subscription"
};
}
}

View File

@@ -1,32 +0,0 @@
/*
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the Affero GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Affero GNU General Public License for more details.
You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Streetwriters.Common
{
public class Version
{
public const int MAJOR = 2;
public const int MINOR = 3;
public const int PATCH = 0;
public static string AsString()
{
return $"{Version.MAJOR}.{Version.MINOR}.{Version.PATCH}";
}
}
}

View File

@@ -26,6 +26,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Streetwriters.Common;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Models;
using Streetwriters.Identity.Enums;
using Streetwriters.Identity.Interfaces;
@@ -52,6 +53,8 @@ namespace Streetwriters.Identity.Controllers
[AllowAnonymous]
public async Task<IActionResult> Signup([FromForm] SignupForm form)
{
if (Constants.DISABLE_ACCOUNT_CREATION)
return BadRequest(new string[] { "Creating new accounts is not allowed." });
try
{
var client = Clients.FindClientById(form.ClientId);
@@ -68,7 +71,7 @@ namespace Streetwriters.Identity.Controllers
var result = await UserManager.CreateAsync(new User
{
Email = form.Email,
EmailConfirmed = false,
EmailConfirmed = Constants.IS_SELF_HOSTED,
UserName = form.Username ?? form.Email,
}, form.Password);
@@ -101,15 +104,18 @@ namespace Streetwriters.Identity.Controllers
if (result.Succeeded)
{
var user = await UserManager.FindByEmailAsync(form.Email);
await UserManager.AddToRoleAsync(user, client.Id);
if (Constants.IS_SELF_HOSTED)
{
await UserManager.AddClaimAsync(user, UserService.SubscriptionTypeToClaim(client.Id, Common.Enums.SubscriptionType.PREMIUM));
await UserManager.AddClaimAsync(user, new Claim("platform", PlatformFromUserAgent(base.HttpContext.Request.Headers.UserAgent)));
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.TokenLink(user.Id.ToString(), code, client.Id, TokenType.CONFRIM_EMAIL, Request.Scheme);
await EmailSender.SendConfirmationEmailAsync(user.Email, callbackUrl, client);
}
else
{
await UserManager.AddClaimAsync(user, new Claim("platform", PlatformFromUserAgent(base.HttpContext.Request.Headers.UserAgent)));
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.TokenLink(user.Id.ToString(), code, client.Id, TokenType.CONFRIM_EMAIL, Request.Scheme);
await EmailSender.SendConfirmationEmailAsync(user.Email, callbackUrl, client);
}
return Ok(new
{
userId = user.Id.ToString()

View File

@@ -1,7 +1,5 @@
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
ARG TARGETARCH
@@ -32,6 +30,7 @@ RUN dotnet publish -c Release -o /app/publish \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH
FROM --platform=$BUILDPLATFORM base AS final

View File

@@ -179,7 +179,7 @@ namespace Streetwriters.Identity.Services
{
if (int.TryParse(Constants.SMTP_PORT, out int port))
{
await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.StartTls);
await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.Auto);
}
else
{
@@ -196,8 +196,8 @@ namespace Streetwriters.Identity.Services
message.To.Add(new MailboxAddress("", email));
message.Subject = await Template.Parse(template.Subject).RenderAsync(template.Data);
if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_NAME) && !string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL))
message.ReplyTo.Add(new MailboxAddress(Constants.SMTP_REPLYTO_NAME, Constants.SMTP_REPLYTO_EMAIL));
if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL))
message.ReplyTo.Add(MailboxAddress.Parse(Constants.SMTP_REPLYTO_EMAIL));
message.Body = await GetEmailBodyAsync(template, client, sender);

View File

@@ -19,21 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using Streetwriters.Identity.Interfaces;
using Streetwriters.Common.Interfaces;
using MessageBird;
using MessageBird.Objects;
using Microsoft.Extensions.Options;
using Streetwriters.Identity.Models;
using Streetwriters.Common;
using Twilio.Rest.Verify.V2.Service;
using Twilio;
using System.Threading.Tasks;
using System;
namespace Streetwriters.Identity.Services
{
public class SMSSender : ISMSSender
{
private Client client;
public SMSSender()
{
if (!string.IsNullOrEmpty(Constants.TWILIO_ACCOUNT_SID) && !string.IsNullOrEmpty(Constants.TWILIO_AUTH_TOKEN))

View File

@@ -192,7 +192,7 @@ namespace Streetwriters.Identity
}
app.UseCors("notesnook");
app.UseVersion();
app.UseVersion(Servers.IdentityServer);
app.UseRouting();

View File

@@ -1,7 +1,5 @@
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
ARG TARGETARCH
@@ -32,6 +30,7 @@ RUN dotnet publish -c Release -o /app/publish \
/p:TrimMode=partial \
/p:PublishTrimmed=true \
/p:PublishSingleFile=true \
/p:JsonSerializerIsReflectionEnabledByDefault=true \
-a $TARGETARCH
FROM --platform=$BUILDPLATFORM base AS final

View File

@@ -103,7 +103,7 @@ namespace Streetwriters.Messenger
}
app.UseCors("notesnook");
app.UseVersion();
app.UseVersion(Servers.MessengerServer);
app.UseRouting();

View File

@@ -1,11 +1,9 @@
version: "3.4"
x-server-discovery: &server-discovery
NOTESNOOK_SERVER_PORT: 80
NOTESNOOK_SERVER_PORT: 5264
NOTESNOOK_SERVER_HOST: notesnook-server
IDENTITY_SERVER_PORT: 80
IDENTITY_SERVER_PORT: 8264
IDENTITY_SERVER_HOST: identity-server
SSE_SERVER_PORT: 80
SSE_SERVER_PORT: 7264
SSE_SERVER_HOST: sse-server
SELF_HOSTED: 1
@@ -13,12 +11,46 @@ x-env-files: &env-files
- .env
services:
validate:
image: vandot/alpine-bash
entrypoint: /bin/bash
env_file: *env-files
command:
- -c
- |
# List of required environment variables
required_vars=(
"INSTANCE_NAME"
"NOTESNOOK_API_SECRET"
"DISABLE_ACCOUNT_CREATION"
"SMTP_USERNAME"
"SMTP_PASSWORD"
"SMTP_HOST"
"SMTP_PORT"
"IDENTITY_SERVER_DOMAIN"
"NOTESNOOK_APP_HOST"
)
# Check each required environment variable
for var in "$${required_vars[@]}"; do
if [ -z "$${!var}" ]; then
echo "Error: Required environment variable $$var is not set."
exit 1
fi
done
echo "All required environment variables are set."
# Ensure the validate service runs first
restart: "no"
notesnook-db:
image: mongo:7.0.12
networks:
- notesnook
command: --replSet rs0 --bind_ip_all
depends_on:
validate:
condition: service_completed_successfully
# the notesnook sync server requires transactions which only work
# with a MongoDB replica set.
# This job just runs `rs.initiate()` on our mongodb instance
@@ -43,13 +75,15 @@ services:
image: minio/minio:RELEASE.2024-07-29T22-14-52Z
ports:
- 9000:9000
- 9090:9090
networks:
- notesnook
volumes:
- ${HOME}/.notesnook/s3:/data/s3
environment:
MINIO_BROWSER: "on"
depends_on:
validate:
condition: service_completed_successfully
env_file: *env-files
command: server /data/s3 --console-address :9090
@@ -61,7 +95,7 @@ services:
- notesnook-s3
networks:
- notesnook
entrypoint: /bin/sh
entrypoint: /bin/bash
env_file: *env-files
command:
- -c
@@ -69,21 +103,19 @@ services:
until mc alias set minio http://notesnook-s3:9000 ${MINIO_ROOT_USER:-minioadmin} ${MINIO_ROOT_PASSWORD:-minioadmin}; do
sleep 1;
done;
mc mb minio/$$S3_BUCKET_NAME -p
mc mb minio/attachments -p
identity-server:
build:
context: .
dockerfile: ./Streetwriters.Identity/Dockerfile
image: streetwriters/identity:latest
ports:
- "8264:80"
- 8264:8264
networks:
- notesnook
env_file: *env-files
depends_on:
- notesnook-db
healthcheck:
test: curl --fail http://localhost:8264/health || exit 1
test: wget --tries=1 -nv -q http://localhost:8264/health -O- || exit 1
interval: 40s
timeout: 30s
retries: 3
@@ -94,11 +126,9 @@ services:
MONGODB_DATABASE_NAME: identity
notesnook-server:
build:
context: .
dockerfile: ./Notesnook.API/Dockerfile
image: streetwriters/notesnook-sync:latest
ports:
- "5264:80"
- 5264:5264
networks:
- notesnook
env_file: *env-files
@@ -107,28 +137,27 @@ services:
- setup-s3
- identity-server
healthcheck:
test: curl --fail http://localhost:5264/health || exit 1
test: wget --tries=1 -nv -q http://localhost:5264/health -O- || exit 1
interval: 40s
timeout: 30s
retries: 3
start_period: 60s
environment:
<<: *server-discovery
MONGODB_CONNECTION_STRING: mongodb://notesnook-db:27017/notesnook?replSet=rs0
MONGODB_CONNECTION_STRING: mongodb://notesnook-db:27017/?replSet=rs0
MONGODB_DATABASE_NAME: notesnook
S3_INTERNAL_SERVICE_URL: "${S3_SERVICE_URL:-http://notesnook-s3:9000}"
S3_ACCESS_KEY_ID: "${S3_ACCESS_KEY_ID:-${MINIO_ROOT_USER:-minioadmin}}"
S3_ACCESS_KEY: "${S3_ACCESS_KEY:-${MINIO_ROOT_PASSWORD:-minioadmin}}"
S3_INTERNAL_BUCKET_NAME: "attachments"
S3_ACCESS_KEY_ID: "${MINIO_ROOT_USER:-minioadmin}"
S3_ACCESS_KEY: "${MINIO_ROOT_PASSWORD:-minioadmin}"
S3_SERVICE_URL: "${S3_SERVICE_URL:-http://localhost:9000}"
S3_REGION: "${S3_REGION:-us-east-1}"
S3_BUCKET_NAME: "${S3_BUCKET_NAME}"
S3_REGION: "us-east-1"
S3_BUCKET_NAME: "attachments"
sse-server:
build:
context: .
dockerfile: ./Streetwriters.Messenger/Dockerfile
image: streetwriters/sse:latest
ports:
- "7264:80"
- 7264:7264
env_file: *env-files
depends_on:
- identity-server
@@ -136,7 +165,7 @@ services:
networks:
- notesnook
healthcheck:
test: curl --fail http://localhost:7264/health || exit 1
test: wget --tries=1 -nv -q http://localhost:7264/health -O- || exit 1
interval: 40s
timeout: 30s
retries: 3
@@ -152,6 +181,9 @@ services:
- AUTOHEAL_INTERVAL=60
- AUTOHEAL_START_PERIOD=300
- AUTOHEAL_DEFAULT_STOP_TIMEOUT=10
depends_on:
validate:
condition: service_completed_successfully
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks: