aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2025-11-23 16:41:43 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2025-11-23 16:41:43 +0100
commit1e31b71afd1ead4644e99df6838a55481176e09a (patch)
treef1107a55f2b2f6ed838190ac259cd5b698bcc56f
parent2e8f59aaeb731dd726f8d49d120ae4cc8a7cf512 (diff)
add fonts and create-user endpoint
-rw-r--r--README.md2
-rw-r--r--src/routes/api/auth/create-user.zig29
-rw-r--r--src/routes/api/auth/root.zig1
-rw-r--r--src/routes/handler-info.zig10
-rw-r--r--src/server.zig20
-rw-r--r--static/fonts/noto-sans/default.ttfbin0 -> 2044548 bytes
-rw-r--r--static/fonts/noto-sans/italic.ttfbin0 -> 2300468 bytes
-rw-r--r--static/fonts/pacifico/regular.ttfbin0 -> 315408 bytes
-rw-r--r--static/index.css16
-rw-r--r--static/index.html3
-rw-r--r--static/index.js6
-rw-r--r--static/manifest.json2
-rw-r--r--static/pages/settings/index.js10
-rw-r--r--static/service-worker/index.js21
-rw-r--r--static/service-worker/worker.js18
15 files changed, 117 insertions, 21 deletions
diff --git a/README.md b/README.md
index db6b884..de30e9d 100644
--- a/README.md
+++ b/README.md
@@ -13,5 +13,5 @@
### Backend
- [x] Not all files are delivered.
-- [ ] **Removing file makes date ordering wrong**.
+- [X] **Removing file makes date ordering wrong**.
- [ ] Cache shuffle data in backend and drop them when next day starts.
diff --git a/src/routes/api/auth/create-user.zig b/src/routes/api/auth/create-user.zig
new file mode 100644
index 0000000..6bd9a82
--- /dev/null
+++ b/src/routes/api/auth/create-user.zig
@@ -0,0 +1,29 @@
+const std = @import("std");
+
+const memora = @import("memora");
+const Context = memora.Context;
+const Storage = memora.Storage;
+
+pub const access = .admins;
+
+pub const Body = struct {
+ name: []const u8,
+ full_name: []const u8,
+ birthday: []const u8,
+ password: []const u8,
+};
+
+pub fn post(ctx: *Context, body: Body) anyerror!void {
+ var user = try Storage.User.new(
+ ctx.storage,
+ body.name,
+ body.full_name,
+ body.birthday,
+ body.password,
+ false,
+ ctx.storage.allocator,
+ );
+ defer user.deinit();
+
+ try user.sync();
+}
diff --git a/src/routes/api/auth/root.zig b/src/routes/api/auth/root.zig
index 5f45891..fa93c92 100644
--- a/src/routes/api/auth/root.zig
+++ b/src/routes/api/auth/root.zig
@@ -2,3 +2,4 @@ const HandlerInfo = @import("../../handler-info.zig");
pub const login: HandlerInfo = .from_type(@import("login.zig"));
pub const @"first-login": HandlerInfo = .from_type(@import("first-login.zig"));
+pub const @"create-user": HandlerInfo = .from_type(@import("create-user.zig"));
diff --git a/src/routes/handler-info.zig b/src/routes/handler-info.zig
index 5183628..9e10bd5 100644
--- a/src/routes/handler-info.zig
+++ b/src/routes/handler-info.zig
@@ -122,6 +122,11 @@ pub fn handle(
.value = context.response.headers.content_type
});
+ try headers.append(allocator, .{
+ .name = "Service-Worker-Allowed",
+ .value = "/",
+ });
+
if (context.response.headers.fingerprint) |auth_token| {
var value = std.Io.Writer.Allocating.init(arena.allocator());
@@ -176,7 +181,10 @@ fn HandlerWrapper(T: type, name: []const u8) type {
ctx.allocator,
writer.written(),
.{},
- ) catch return error.BadRequest;
+ ) catch |err| {
+ log.warn("failed to parse JSON {}", .{err});
+ return error.BadRequest;
+ };
break :args .{ ctx, body };
} else {
@compileError("invalid amount of arguments for request function");
diff --git a/src/server.zig b/src/server.zig
index 184af92..5a9be74 100644
--- a/src/server.zig
+++ b/src/server.zig
@@ -52,16 +52,18 @@ fn handle_connection(
var writer = connection.stream.writer(&write_buf);
var http_server = std.http.Server.init(reader.interface(), &writer.interface);
- var request = http_server.receiveHead() catch return;
- log.info("{s} {s}", .{
- std.enums.tagName(std.http.Method, request.head.method) orelse "<unknown>",
- request.head.target,
- });
+ while (true) {
+ var request = http_server.receiveHead() catch return;
+ log.info("{s} {s}", .{
+ std.enums.tagName(std.http.Method, request.head.method) orelse "<unknown>",
+ request.head.target,
+ });
- const handler_info = routes.get(request.head.target);
- handler_info.handle(&request, &self.storage, allocator) catch |err| {
- std.log.err("{}", .{err});
- };
+ const handler_info = routes.get(request.head.target);
+ handler_info.handle(&request, &self.storage, allocator) catch |err| {
+ std.log.err("{}", .{err});
+ };
+ }
}
pub fn deinit(self: *Self) void {
diff --git a/static/fonts/noto-sans/default.ttf b/static/fonts/noto-sans/default.ttf
new file mode 100644
index 0000000..9530d84
--- /dev/null
+++ b/static/fonts/noto-sans/default.ttf
Binary files differ
diff --git a/static/fonts/noto-sans/italic.ttf b/static/fonts/noto-sans/italic.ttf
new file mode 100644
index 0000000..6245ba0
--- /dev/null
+++ b/static/fonts/noto-sans/italic.ttf
Binary files differ
diff --git a/static/fonts/pacifico/regular.ttf b/static/fonts/pacifico/regular.ttf
new file mode 100644
index 0000000..e7def95
--- /dev/null
+++ b/static/fonts/pacifico/regular.ttf
Binary files differ
diff --git a/static/index.css b/static/index.css
index 67c1407..d5d74cd 100644
--- a/static/index.css
+++ b/static/index.css
@@ -52,3 +52,19 @@ input {
width: 100%;
height: 100%;
}
+
+@font-face {
+ font-family: Pacifico;
+ src: url(/fonts/pacifico/regular.ttf);
+}
+
+@font-face {
+ font-family: "Noto Sans";
+ src: url(/fonts/noto-sans/default.ttf);
+}
+
+@font-face {
+ font-family: "Noto Sans";
+ font-style: italic
+ src: url(/fonts/noto-sans/italic.ttf);
+}
diff --git a/static/index.html b/static/index.html
index c69eb16..b3f609d 100644
--- a/static/index.html
+++ b/static/index.html
@@ -11,9 +11,6 @@
</script>
<title>Memora</title>
<script type="module" src="index.js"></script>
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&family=Pacifico&display=swap" rel="stylesheet">
<link rel="icon" href="icon.png" type="image/png">
</head>
<body>
diff --git a/static/index.js b/static/index.js
index d5d5c60..28c6228 100644
--- a/static/index.js
+++ b/static/index.js
@@ -1,8 +1,9 @@
import * as sfw from 'sfw';
-
import * as api from './api/index.js';
-
import { literal as m } from './month.js';
+import * as service_worker from './service-worker/index.js';
+
+//service_worker.register();
import LoginView from './pages/login/index.js';
import MainView from './pages/main/index.js';
@@ -60,6 +61,7 @@ const settings = SettingsView.new({
onlogout: () => {
login.show();
main.active_view = image_viewer;
+ settings.profile = null;
},
});
diff --git a/static/manifest.json b/static/manifest.json
index ae569c2..e91b5dc 100644
--- a/static/manifest.json
+++ b/static/manifest.json
@@ -12,6 +12,6 @@
"sizes": "192x192"
}
],
- "start_url": "https://debug.dom0.nathanreiner.xyz",
+ "start_url": ".",
"display": "standalone"
}
diff --git a/static/pages/settings/index.js b/static/pages/settings/index.js
index cc985a5..36c1d09 100644
--- a/static/pages/settings/index.js
+++ b/static/pages/settings/index.js
@@ -87,11 +87,13 @@ export default class SettingsView extends sfw.element.Container {
set profile(profile) {
this.#profile = profile;
- this.#profile_image.src = `/api/profile/image/load/${profile.name}`;
- this.#profile_image.onerror = () => this.#profile_image.removeAttribute('src');
+ if (this.#profile) {
+ this.#profile_image.src = `/api/profile/image/load/${profile.name}`;
+ this.#profile_image.onerror = () => this.#profile_image.removeAttribute('src');
- this.#name.value = profile.full_name;
- this.#birthday.value = profile.birthday;
+ this.#name.value = profile.full_name;
+ this.#birthday.value = profile.birthday;
+ }
}
get profile() {
diff --git a/static/service-worker/index.js b/static/service-worker/index.js
new file mode 100644
index 0000000..cff4b95
--- /dev/null
+++ b/static/service-worker/index.js
@@ -0,0 +1,21 @@
+export const register = async () => {
+ if (!'serviceWorker' in navigator) {
+ console.warn('service worker not supported by browser');
+ return;
+ }
+
+ try {
+ const registration = await navigator.serviceWorker.register("/service-worker/worker.js", {
+ scope: "/",
+ });
+ if (registration.installing) {
+ console.log("Service worker installing");
+ } else if (registration.waiting) {
+ console.log("Service worker installed");
+ } else if (registration.active) {
+ console.log("Service worker active");
+ }
+ } catch (error) {
+ console.error(`Registration failed with ${error}`);
+ }
+}
diff --git a/static/service-worker/worker.js b/static/service-worker/worker.js
new file mode 100644
index 0000000..ad5a374
--- /dev/null
+++ b/static/service-worker/worker.js
@@ -0,0 +1,18 @@
+const putInCache = async (request, response) => {
+ const cache = await caches.open("v1");
+ await cache.put(request, response);
+};
+
+const cacheFirst = async (request, event) => {
+ const responseFromCache = await caches.match(request);
+ if (responseFromCache) {
+ return responseFromCache;
+ }
+ const responseFromNetwork = await fetch(request);
+ event.waitUntil(putInCache(request, responseFromNetwork.clone()));
+ return responseFromNetwork;
+};
+
+self.addEventListener("fetch", (event) => {
+ event.respondWith(cacheFirst(event.request, event));
+});