using Statum; using Invercargill; using Invercargill.DataStructures; namespace Example { /** * `POST /_statum/action/{guid}` — logs in: reads the submitted name, builds a * session `auth` slot (with a typed private `is_admin` flag) carrying a * `logout` action reference, and redirects home. */ public class LoginAction : StatumAction { public override async DirectiveBuilder handle() throws GLib.Error { var name = "guest"; if (request.form != null) { var submitted = request.form.get_field("name"); if (submitted != null && ((!)submitted).length > 0) { name = (!)submitted; } } var is_admin = name == "admin"; var auth_public = new PropertyDictionary(); auth_public.set_native("name", name); auth_public.set_native("role", is_admin ? "admin" : "member"); auth_public["logout"] = action_registry.author().to_element(); var auth_private = new PropertyDictionary(); auth_private.set_native("is_admin", is_admin); var slot = state_service.new_slot(Scope.SESSION, new State() { type_name = "auth", public_data = auth_public, private_data = auth_private }); return directives().set(slot).navigate("/"); } } /** * `POST /_statum/action/{guid}` — logs out: clears the held `auth` slot * (server cache + clear directive) and redirects home. */ public class LogoutAction : StatumAction { public override async DirectiveBuilder handle() throws GLib.Error { HeldSlot auth; if (request.held.try_get("auth", out auth)) { yield state_service.clear_slot(auth.key); return directives().clear(auth.key).navigate("/"); } return directives().navigate("/"); } } /** * `POST /_statum/action/{guid}` — bumps the counter by the typed private * `delta`, updating the held `counter` slot (sign + auto-push to subscribed * channels) and re-embedding the `bump` reference in the new public data. */ public class BumpCounterAction : TypedStatumAction { public override async DirectiveBuilder handle() throws GLib.Error { var delta = request_private != null ? ((!)request_private).delta : 1; HeldSlot held_counter; if (!request.held.try_get("counter", out held_counter)) { return directives().error("No counter slot held"); } var slot = state_service.get_slot(held_counter.key); if (slot == null || ((!)slot).current_state == null) { return directives().error("Counter slot not found"); } var current = ((!)slot).current_state; var value = GObjectMapping.get_int(current.public_data, "value") ?? 0; var new_public = new PropertyDictionary(); new_public.set_native("value", value + delta); var bump_private = new CounterPrivate(); bump_private.delta = delta; new_public["bump"] = action_registry .author_private(bump_private) .to_element(); var new_state = new State() { type_name = "counter", public_data = new_public, private_data = new PropertyDictionary() }; return directives().update(held_counter.key, new_state); } } }