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