Updated handy-httpd dependency, added Clear Notes and Print this list buttons, improved button spacing.
This commit is contained in:
parent
6eabfe4c6e
commit
ec5b0b0719
|
@ -7,7 +7,7 @@
|
||||||
"botan": "~>1.13.5",
|
"botan": "~>1.13.5",
|
||||||
"d-properties": "~>1.0.5",
|
"d-properties": "~>1.0.5",
|
||||||
"d2sqlite3": "~>1.0.0",
|
"d2sqlite3": "~>1.0.0",
|
||||||
"handy-httpd": "~>7.10.4",
|
"handy-httpd": "~>7.13.0",
|
||||||
"jwt": "~>0.4.0",
|
"jwt": "~>0.4.0",
|
||||||
"resusage": "~>0.3.2",
|
"resusage": "~>0.3.2",
|
||||||
"slf4d": "~>2.4.3"
|
"slf4d": "~>2.4.3"
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
{
|
{
|
||||||
"fileVersion": 1,
|
"fileVersion": 1,
|
||||||
"versions": {
|
"versions": {
|
||||||
"botan": "1.13.5",
|
"botan": "1.13.6",
|
||||||
"botan-math": "1.0.4",
|
"botan-math": "1.0.4",
|
||||||
"d-properties": "1.0.5",
|
"d-properties": "1.0.5",
|
||||||
"d2sqlite3": "1.0.0",
|
"d2sqlite3": "1.0.0",
|
||||||
"handy-httpd": "7.10.4",
|
"handy-httpd": "7.13.0",
|
||||||
"httparsed": "1.2.1",
|
"httparsed": "1.2.1",
|
||||||
"jwt": "0.4.0",
|
"jwt": "0.4.0",
|
||||||
"memutils": "1.0.9",
|
"memutils": "1.0.9",
|
||||||
|
"path-matcher": "1.1.3",
|
||||||
"resusage": "0.3.2",
|
"resusage": "0.3.2",
|
||||||
"slf4d": "2.4.3",
|
"slf4d": "2.4.3",
|
||||||
"streams": "3.5.0"
|
"streams": "3.5.0"
|
||||||
|
|
|
@ -16,7 +16,7 @@ void main() {
|
||||||
* Returns: The HTTP server to use.
|
* Returns: The HTTP server to use.
|
||||||
*/
|
*/
|
||||||
private HttpServer initServer() {
|
private HttpServer initServer() {
|
||||||
import handy_httpd.handlers.path_delegating_handler;
|
import handy_httpd.handlers.path_handler;
|
||||||
import handy_httpd.handlers.filtered_handler;
|
import handy_httpd.handlers.filtered_handler;
|
||||||
import d_properties;
|
import d_properties;
|
||||||
import endpoints.auth;
|
import endpoints.auth;
|
||||||
|
@ -59,7 +59,7 @@ private HttpServer initServer() {
|
||||||
|
|
||||||
immutable string API_PATH = "/api";
|
immutable string API_PATH = "/api";
|
||||||
|
|
||||||
PathDelegatingHandler mainHandler = new PathDelegatingHandler();
|
PathHandler mainHandler = new PathHandler();
|
||||||
mainHandler.addMapping(Method.GET, API_PATH ~ "/status", &handleStatus);
|
mainHandler.addMapping(Method.GET, API_PATH ~ "/status", &handleStatus);
|
||||||
mainHandler.addMapping(Method.POST, API_PATH ~ "/register", &createNewUser);
|
mainHandler.addMapping(Method.POST, API_PATH ~ "/register", &createNewUser);
|
||||||
mainHandler.addMapping(Method.POST, API_PATH ~ "/login", &handleLogin);
|
mainHandler.addMapping(Method.POST, API_PATH ~ "/login", &handleLogin);
|
||||||
|
@ -74,22 +74,23 @@ private HttpServer initServer() {
|
||||||
mainHandler.addMapping(Method.OPTIONS, API_PATH ~ "/**", optionsHandler);
|
mainHandler.addMapping(Method.OPTIONS, API_PATH ~ "/**", optionsHandler);
|
||||||
|
|
||||||
// Separate handler for authenticated paths, protected by a TokenFilter.
|
// Separate handler for authenticated paths, protected by a TokenFilter.
|
||||||
PathDelegatingHandler authHandler = new PathDelegatingHandler();
|
PathHandler authHandler = new PathHandler();
|
||||||
authHandler.addMapping(Method.GET, API_PATH ~ "/me", &getMyUser);
|
authHandler.addMapping(Method.GET, API_PATH ~ "/me", &getMyUser);
|
||||||
authHandler.addMapping(Method.DELETE, API_PATH ~ "/me", &deleteMyUser);
|
authHandler.addMapping(Method.DELETE, API_PATH ~ "/me", &deleteMyUser);
|
||||||
authHandler.addMapping(Method.GET, API_PATH ~ "/renew-token", &renewToken);
|
authHandler.addMapping(Method.GET, API_PATH ~ "/renew-token", &renewToken);
|
||||||
|
|
||||||
authHandler.addMapping(Method.GET, API_PATH ~ "/lists", &getNoteLists);
|
authHandler.addMapping(Method.GET, API_PATH ~ "/lists", &getNoteLists);
|
||||||
authHandler.addMapping(Method.POST, API_PATH ~ "/lists", &createNoteList);
|
authHandler.addMapping(Method.POST, API_PATH ~ "/lists", &createNoteList);
|
||||||
authHandler.addMapping(Method.GET, API_PATH ~ "/lists/{id}", &getNoteList);
|
authHandler.addMapping(Method.GET, API_PATH ~ "/lists/:id:ulong", &getNoteList);
|
||||||
authHandler.addMapping(Method.DELETE, API_PATH ~ "/lists/{id}", &deleteNoteList);
|
authHandler.addMapping(Method.DELETE, API_PATH ~ "/lists/:id:ulong", &deleteNoteList);
|
||||||
authHandler.addMapping(Method.POST, API_PATH ~ "/lists/{listId}/notes", &createNote);
|
authHandler.addMapping(Method.POST, API_PATH ~ "/lists/:listId:ulong/notes", &createNote);
|
||||||
authHandler.addMapping(Method.DELETE, API_PATH ~ "/lists/{listId}/notes/{noteId}", &deleteNote);
|
authHandler.addMapping(Method.DELETE, API_PATH ~ "/lists/:listId:ulong/notes/:noteId:ulong", &deleteNote);
|
||||||
|
authHandler.addMapping(Method.DELETE, API_PATH ~ "/lists/:listId:ulong/notes", &deleteAllNotes);
|
||||||
HttpRequestFilter tokenFilter = new TokenFilter(loadTokenSecret());
|
HttpRequestFilter tokenFilter = new TokenFilter(loadTokenSecret());
|
||||||
HttpRequestFilter adminFilter = new AdminFilter();
|
HttpRequestFilter adminFilter = new AdminFilter();
|
||||||
|
|
||||||
// Separate handler for admin paths, protected by an AdminFilter.
|
// Separate handler for admin paths, protected by an AdminFilter.
|
||||||
PathDelegatingHandler adminHandler = new PathDelegatingHandler();
|
PathHandler adminHandler = new PathHandler();
|
||||||
adminHandler.addMapping(Method.GET, API_PATH ~ "/admin/users", &getAllUsers);
|
adminHandler.addMapping(Method.GET, API_PATH ~ "/admin/users", &getAllUsers);
|
||||||
mainHandler.addMapping(API_PATH ~ "/admin/**", new FilteredRequestHandler(adminHandler, [tokenFilter, adminFilter]));
|
mainHandler.addMapping(API_PATH ~ "/admin/**", new FilteredRequestHandler(adminHandler, [tokenFilter, adminFilter]));
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,13 @@ class SqliteNoteListDataSource : NoteListDataSource {
|
||||||
db.commit();
|
db.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteAllNotes(string username, ulong id) {
|
||||||
|
Database db = getDb(username);
|
||||||
|
db.begin();
|
||||||
|
db.execute("DELETE FROM note WHERE note_list_id = ?", id);
|
||||||
|
db.commit();
|
||||||
|
}
|
||||||
|
|
||||||
NoteList updateNoteList(string username, ulong id, NoteList newData) {
|
NoteList updateNoteList(string username, ulong id, NoteList newData) {
|
||||||
Database db = getDb(username);
|
Database db = getDb(username);
|
||||||
ResultRange result = db.execute("SELECT * FROM note_list WHERE id = ?", id);
|
ResultRange result = db.execute("SELECT * FROM note_list WHERE id = ?", id);
|
||||||
|
|
|
@ -9,6 +9,7 @@ interface NoteListDataSource {
|
||||||
Nullable!NoteList getList(string username, ulong id);
|
Nullable!NoteList getList(string username, ulong id);
|
||||||
NoteList createNoteList(string username, string name, string description = null);
|
NoteList createNoteList(string username, string name, string description = null);
|
||||||
void deleteNoteList(string username, ulong id);
|
void deleteNoteList(string username, ulong id);
|
||||||
|
void deleteAllNotes(string username, ulong id);
|
||||||
NoteList updateNoteList(string username, ulong id, NoteList newData);
|
NoteList updateNoteList(string username, ulong id, NoteList newData);
|
||||||
ulong countLists(string username);
|
ulong countLists(string username);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,12 @@ void deleteNote(ref HttpRequestContext ctx) {
|
||||||
noteDataSource.deleteNote(auth.user.username, noteId);
|
noteDataSource.deleteNote(auth.user.username, noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteAllNotes(ref HttpRequestContext ctx) {
|
||||||
|
AuthContext auth = getAuthContextOrThrow(ctx);
|
||||||
|
ulong listId = ctx.request.getPathParamAs!ulong("listId");
|
||||||
|
noteListDataSource.deleteAllNotes(auth.user.username, listId);
|
||||||
|
}
|
||||||
|
|
||||||
private JSONValue serializeList(NoteList list) {
|
private JSONValue serializeList(NoteList list) {
|
||||||
JSONValue listObj = JSONValue(string[string].init);
|
JSONValue listObj = JSONValue(string[string].init);
|
||||||
listObj.object["id"] = JSONValue(list.id);
|
listObj.object["id"] = JSONValue(list.id);
|
||||||
|
|
|
@ -77,3 +77,10 @@ export async function deleteNote(token: string, listId: number, id: number): Pro
|
||||||
headers: {"Authorization": "Bearer " + token}
|
headers: {"Authorization": "Bearer " + token}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteAllNotes(token: string, listId: number): Promise<void> {
|
||||||
|
await fetch(API_URL + "/lists/" + listId + "/notes", {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {"Authorization": "Bearer " + token}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import type {NoteList} from "@/api/lists";
|
import type {NoteList} from "@/api/lists";
|
||||||
import {nextTick, onMounted, ref, type Ref} from "vue";
|
import {nextTick, onMounted, ref, type Ref} from "vue";
|
||||||
import {useAuthStore} from "@/stores/auth";
|
import {useAuthStore} from "@/stores/auth";
|
||||||
import {createNote, deleteNote, deleteNoteList, getNoteList} from "@/api/lists";
|
import {createNote, deleteAllNotes, deleteNote, deleteNoteList, getNoteList} from "@/api/lists";
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import PageContainer from "@/components/PageContainer.vue";
|
import PageContainer from "@/components/PageContainer.vue";
|
||||||
import LogOutButton from "@/components/LogOutButton.vue";
|
import LogOutButton from "@/components/LogOutButton.vue";
|
||||||
|
@ -64,6 +64,22 @@ function toggleCreatingNewNote() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function clearList() {
|
||||||
|
if (!list.value) return
|
||||||
|
const dialog = document.getElementById("list-clear-notes-dialog") as HTMLDialogElement
|
||||||
|
dialog.showModal()
|
||||||
|
const confirmButton = document.getElementById("clear-notes-confirm-button") as HTMLButtonElement
|
||||||
|
confirmButton.onclick = async () => {
|
||||||
|
dialog.close()
|
||||||
|
await deleteAllNotes(authStore.token, list.value.id)
|
||||||
|
list.value.notes = []
|
||||||
|
}
|
||||||
|
const cancelButton = document.getElementById("clear-notes-cancel-button") as HTMLButtonElement
|
||||||
|
cancelButton.onclick = async () => {
|
||||||
|
dialog.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function createNoteAndRefresh() {
|
async function createNoteAndRefresh() {
|
||||||
if (!list.value) return
|
if (!list.value) return
|
||||||
await createNote(authStore.token, list.value.id, newNoteText.value)
|
await createNote(authStore.token, list.value.id, newNoteText.value)
|
||||||
|
@ -71,6 +87,32 @@ async function createNoteAndRefresh() {
|
||||||
newNoteText.value = ""
|
newNoteText.value = ""
|
||||||
list.value = await getNoteList(authStore.token, list.value.id)
|
list.value = await getNoteList(authStore.token, list.value.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printList() {
|
||||||
|
if (!list.value) return
|
||||||
|
const l: NoteList = list.value
|
||||||
|
const header = `<h1>${l.name}</h1>`
|
||||||
|
const description = `<p>${l.description}</p>`
|
||||||
|
let checkboxList = `<div>`
|
||||||
|
for (let i = 0; i < l.notes.length; i++) {
|
||||||
|
const note = l.notes[i]
|
||||||
|
checkboxList += `<input type="checkbox" id="note-${i}"><label for="note-${i}">${note.content}</label><br/>`
|
||||||
|
}
|
||||||
|
checkboxList += `</div>`
|
||||||
|
const w = window.open()
|
||||||
|
const html = `
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang='en'>
|
||||||
|
<body>
|
||||||
|
${header}
|
||||||
|
${description}
|
||||||
|
${checkboxList}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
w.document.write(html)
|
||||||
|
w.window.print()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -80,9 +122,8 @@ async function createNoteAndRefresh() {
|
||||||
<p><em v-text="list.description"></em></p>
|
<p><em v-text="list.description"></em></p>
|
||||||
<div class="buttons-list">
|
<div class="buttons-list">
|
||||||
<button @click="toggleCreatingNewNote()">Add Note</button>
|
<button @click="toggleCreatingNewNote()">Add Note</button>
|
||||||
<button @click="deleteList(list.id)">
|
<button @click="clearList()" v-if="list.notes.length > 0">Clear Notes</button>
|
||||||
Delete this List
|
<button @click="deleteList(list.id)">Delete this List</button>
|
||||||
</button>
|
|
||||||
<button @click="router.push('/lists')">All Lists</button>
|
<button @click="router.push('/lists')">All Lists</button>
|
||||||
<LogOutButton/>
|
<LogOutButton/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,6 +154,10 @@ async function createNoteAndRefresh() {
|
||||||
<em>There are no notes in this list.</em> <button @click="toggleCreatingNewNote()">Add one!</button>
|
<em>There are no notes in this list.</em> <button @click="toggleCreatingNewNote()">Add one!</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div class="buttons-list">
|
||||||
|
<button @click="printList()" v-if="list.notes.length > 0">Print this list</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<dialog id="list-delete-dialog">
|
<dialog id="list-delete-dialog">
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<p>
|
<p>
|
||||||
|
@ -124,6 +169,18 @@ async function createNoteAndRefresh() {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="list-clear-notes-dialog">
|
||||||
|
<form method="dialog">
|
||||||
|
<p>
|
||||||
|
Are you sure you want to clear <strong>all</strong> notes from this list?
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button id="clear-notes-cancel-button" value="cancel" formmethod="dialog">Cancel</button>
|
||||||
|
<button id="clear-notes-confirm-button" value="default">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -134,6 +191,8 @@ h1 {
|
||||||
|
|
||||||
.buttons-list button {
|
.buttons-list button {
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
font-size: medium;
|
font-size: medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue