Compare commits
No commits in common. "master" and "dev/multiple-notes" have entirely different histories.
master
...
dev/multip
@ -1,12 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "8.0.0",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -229,9 +229,6 @@ _pkginfo.txt
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# exclude notes
|
||||
notes/*
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
@ -8,9 +8,12 @@ namespace BinaryDad.Notes.Controllers
|
||||
{
|
||||
private readonly INoteService noteService;
|
||||
|
||||
public ApiController(INoteService noteService) => this.noteService = noteService;
|
||||
public ApiController(INoteService noteService)
|
||||
{
|
||||
this.noteService = noteService;
|
||||
}
|
||||
|
||||
[Route("note/{noteName}")]
|
||||
public string Note(string noteName) => noteService.GetNote(noteName);
|
||||
public string Note(string noteName) => noteService.GetText(noteName);
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,6 @@ namespace BinaryDad.Notes.Controllers
|
||||
{
|
||||
public class LoginController : Controller
|
||||
{
|
||||
private readonly IConfiguration configuration;
|
||||
|
||||
public LoginController(IConfiguration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
[Route("login")]
|
||||
public IActionResult Login()
|
||||
{
|
||||
@ -28,7 +21,7 @@ namespace BinaryDad.Notes.Controllers
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var appPassphrase = configuration["APP_PASSPHRASE"];
|
||||
var appPassphrase = Environment.GetEnvironmentVariable("APP_PASSPHRASE");
|
||||
|
||||
if (passphrase == appPassphrase)
|
||||
{
|
||||
|
@ -10,7 +10,10 @@ public class NoteController : Controller
|
||||
{
|
||||
private readonly INoteService noteService;
|
||||
|
||||
public NoteController(INoteService noteService) => this.noteService = noteService;
|
||||
public NoteController(INoteService noteService)
|
||||
{
|
||||
this.noteService = noteService;
|
||||
}
|
||||
|
||||
[Route("{noteName=default}")]
|
||||
public IActionResult Index(string noteName)
|
||||
@ -18,7 +21,7 @@ public class NoteController : Controller
|
||||
var model = new ContentModel
|
||||
{
|
||||
CurrentNote = noteName,
|
||||
Text = noteService.GetNote(noteName),
|
||||
Text = noteService.GetText(noteName),
|
||||
NoteNames = noteService.GetNoteNames()
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
|
||||
RUN dotnet publish "BinaryDad.Notes.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
|
||||
WORKDIR /app
|
||||
RUN mkdir notes
|
||||
COPY --from=build /app/publish .
|
||||
|
||||
EXPOSE 8080
|
||||
EXPOSE 80
|
||||
|
||||
ENTRYPOINT ["dotnet", "BinaryDad.Notes.dll"]
|
37
NoteHub.cs
37
NoteHub.cs
@ -1,19 +1,15 @@
|
||||
using BinaryDad.Notes.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace BinaryDad.Notes
|
||||
{
|
||||
[Authorize]
|
||||
public class NoteHub : Hub
|
||||
{
|
||||
private readonly INoteService noteService;
|
||||
private readonly ILogger<NoteHub> logger;
|
||||
|
||||
public NoteHub(INoteService noteService, ILogger<NoteHub> logger)
|
||||
public NoteHub(INoteService noteService)
|
||||
{
|
||||
this.noteService = noteService;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override Task OnConnectedAsync()
|
||||
@ -25,29 +21,20 @@ namespace BinaryDad.Notes
|
||||
return base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
public async Task SaveNote(string noteName, string content)
|
||||
public async Task SaveNote(string content, string? noteName)
|
||||
{
|
||||
try
|
||||
{
|
||||
noteService.SaveNote(noteName, content);
|
||||
noteService.SaveText(content, noteName);
|
||||
|
||||
// find all other connections except for the current one
|
||||
var clientConnections = NoteContext.ClientNotes
|
||||
.Where(c => c.Value == noteName && c.Key != Context.ConnectionId)
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
// find all other connections except for the current one
|
||||
var clientConnections = NoteContext.ClientNotes
|
||||
.Where(c => c.Value == noteName && c.Key != Context.ConnectionId)
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
// update note for all other clients
|
||||
await Clients
|
||||
.Clients(clientConnections)
|
||||
.SendAsync("updateNote", content);
|
||||
|
||||
logger.LogInformation($"Note \"{noteName}\" saved! Updated {clientConnections.Count} other client(s).");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"Unable to save note \"{noteName}\" => {ex}");
|
||||
}
|
||||
// update note for all other clients
|
||||
await Clients
|
||||
.Clients(clientConnections)
|
||||
.SendAsync("updateNote", content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Configuration.AddEnvironmentVariables();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddSignalR();
|
||||
@ -16,7 +14,6 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
|
||||
o.LoginPath = "/login";
|
||||
o.Cookie.Name = "NotesUser";
|
||||
o.Cookie.MaxAge = TimeSpan.FromDays(3);
|
||||
o.SlidingExpiration = true;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
@ -14,7 +14,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<SiteUrlToLaunchAfterPublish />
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ProjectGuid>bf137709-fcd2-4bb0-ade0-8fc71a244485</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
|
@ -16,7 +16,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
public string GetNote(string noteName)
|
||||
public string GetText(string noteName)
|
||||
{
|
||||
CheckFile(noteName);
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void SaveNote(string noteName, string content)
|
||||
public void SaveText(string content, string noteName)
|
||||
{
|
||||
File.WriteAllText(GetFilePath(noteName), content);
|
||||
}
|
||||
@ -51,7 +51,7 @@
|
||||
{
|
||||
Directory.CreateDirectory(folderPath);
|
||||
|
||||
SaveNote(noteName, "Hi! Feel free to start typing. Everything will be saved soon after you are done typing.");
|
||||
SaveText("Hi! Feel free to start typing. Everything will be saved soon after you are done typing.", noteName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
public interface INoteService
|
||||
{
|
||||
ICollection<string> GetNoteNames();
|
||||
string GetNote(string noteName);
|
||||
void SaveNote(string noteName, string content);
|
||||
string GetText(string noteName);
|
||||
void SaveText(string content, string noteName);
|
||||
void DeleteNote(string noteName);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<form method="post">
|
||||
<input type="password" name="passphrase" placeholder="Passphrase" />
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
|
||||
@section scripts {
|
||||
|
@ -19,7 +19,4 @@
|
||||
<script>
|
||||
var noteName = '@Model.CurrentNote';
|
||||
</script>
|
||||
|
||||
<script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
}
|
||||
|
@ -8,13 +8,15 @@
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
</head>
|
||||
|
||||
<body class="dark">
|
||||
<body>
|
||||
@RenderBody()
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
|
||||
@RenderSection("scripts", false)
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -28,19 +28,19 @@ div.note-names {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
div.note-names a {
|
||||
color: #aaa;
|
||||
padding-left: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.note-names a {
|
||||
color: #666;
|
||||
padding-left: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.note-names a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
div.note-names a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div .note-names a:not(:last-of-type) {
|
||||
border-right: 1px solid #666;
|
||||
}
|
||||
div .note-names a:not(:last-of-type) {
|
||||
border-right: 1px solid #666;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
@ -57,9 +57,7 @@ textarea {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
body.dark,
|
||||
body.dark input,
|
||||
body.dark textarea {
|
||||
body.dark, body.dark input, body.dark textarea {
|
||||
background-color: #222;
|
||||
color: #ddd;
|
||||
}
|
||||
@ -76,37 +74,24 @@ body.dark textarea {
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
bottom: 0;
|
||||
}
|
||||
.toast.show {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.toast#saved-indicator {
|
||||
background-color: green;
|
||||
}
|
||||
.toast#saved-indicator {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.toast#update-indicator {
|
||||
background-color: orangered;
|
||||
}
|
||||
.toast#update-indicator {
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
form input[type=password],
|
||||
button {
|
||||
form input[type=password] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin: 20px auto;
|
||||
font-size: 20px;
|
||||
padding: 8px;
|
||||
border: 1px solid #333;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
form input[type=password] {
|
||||
border: 1px solid #999;
|
||||
border-radius: 4px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
form button {
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
background-color: #009E60;
|
||||
}
|
@ -9,7 +9,7 @@ function start() {
|
||||
console.log('Started websocket listener');
|
||||
}).catch(function (err) {
|
||||
console.error(err.toString());
|
||||
location.reload();
|
||||
return alert('Connection error. Reload page.');
|
||||
});
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ function saveContent($textarea) {
|
||||
|
||||
var content = $textarea.val();
|
||||
|
||||
connection.invoke('SaveNote', noteName, content).then(function () {
|
||||
connection.invoke('SaveNote', content, noteName).then(function () {
|
||||
showToast('#saved-indicator');
|
||||
}).catch(function (err) {
|
||||
console.error(err.toString());
|
||||
@ -63,6 +63,11 @@ $(function () {
|
||||
});
|
||||
});
|
||||
|
||||
// set dark mode
|
||||
if (window.location.hash == '#dark') {
|
||||
$('body').addClass('dark');
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
|
||||
const ignoredKeyCodes = [17, 18, 20, 27, 37, 38, 39, 40, 91];
|
||||
|
Loading…
x
Reference in New Issue
Block a user