acf2931fb4
Every event, profile and photo is now scoped to a signed-in account, so separate people can track separate puppies on one server. Server: - users + sessions tables; bcrypt passwords; random session tokens stored hashed and set as an HttpOnly cookie. Middleware gates /api/* behind a valid session. - register/login/logout/me endpoints. Registration requires a shared invite code (-invite-code / PUPPY_INVITE_CODE); empty disables it. - events, config and photos are keyed by user_id; the sync upsert guards against cross-user overwrites and reads are scoped, so accounts are isolated. Photos live under photos/<user_id>/ and are only served to their owner. - in-place schema migration adds user_id and reshapes config; legacy single-tenant data (including imported events.json) is parked ownerless and adopted by the first account to register. Client: - login/register gate in front of the app; the tracker only boots once the session check resolves. localStorage is namespaced per user. - 401s bounce back to login; an offline reload falls back to the last cached session so offline-first still works. Logout clears the session and reloads. Deployment: - module.nix gains inviteCodeFile (secret via EnvironmentFile) and secureCookies options. Verified end to end (curl + a headless-browser run of the auth flow): isolation between accounts, invite enforcement, first-user adoption, photo ownership, and session persistence across reload.
86 lines
2.8 KiB
Nix
86 lines
2.8 KiB
Nix
{
|
|
description = "Puppy Tracker — offline-first puppy tracking app with server-side sync.";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
};
|
|
|
|
outputs = { self, nixpkgs, flake-utils }:
|
|
let
|
|
mkStatic = pkgs:
|
|
pkgs.stdenvNoCC.mkDerivation {
|
|
pname = "puppy-tracker-static";
|
|
version = "0.2.0";
|
|
src = ./src;
|
|
dontConfigure = true;
|
|
dontBuild = true;
|
|
installPhase = ''
|
|
mkdir -p $out/share/puppy-tracker
|
|
cp -r ./* $out/share/puppy-tracker/
|
|
'';
|
|
};
|
|
|
|
mkServer = pkgs:
|
|
pkgs.buildGoModule {
|
|
pname = "puppy-tracker-server";
|
|
version = "0.2.0";
|
|
src = ./server;
|
|
vendorHash = "sha256-z9Kf7i4WfLAHmceRi8T42+uMitjxEzr0pmOn+STpsAU=";
|
|
# Pure-Go build for a tiny static binary.
|
|
env.CGO_ENABLED = "0";
|
|
ldflags = [ "-s" "-w" ];
|
|
# The binary built from `module puppy-tracker` is `puppy-tracker`,
|
|
# rename so the executable name reflects its role.
|
|
postInstall = ''
|
|
mv $out/bin/puppy-tracker $out/bin/puppy-tracker-server
|
|
'';
|
|
meta.mainProgram = "puppy-tracker-server";
|
|
};
|
|
in
|
|
{
|
|
nixosModules.default = import ./module.nix self;
|
|
nixosModules.puppy-tracker = self.nixosModules.default;
|
|
}
|
|
//
|
|
flake-utils.lib.eachDefaultSystem (system:
|
|
let
|
|
pkgs = import nixpkgs { inherit system; };
|
|
static = mkStatic pkgs;
|
|
server = mkServer pkgs;
|
|
in
|
|
{
|
|
packages.static = static;
|
|
packages.server = server;
|
|
# `default` bundles both so `nix build` produces a runnable directory.
|
|
packages.default = pkgs.symlinkJoin {
|
|
name = "puppy-tracker";
|
|
paths = [ server static ];
|
|
};
|
|
|
|
devShells.default = pkgs.mkShell {
|
|
packages = [ pkgs.go pkgs.python3 ];
|
|
shellHook = ''
|
|
echo "puppy-tracker dev shell"
|
|
echo " cd server && go run . -static ../src -data /tmp/puppy-events.json"
|
|
'';
|
|
};
|
|
|
|
# `nix run` → start the server, serving the bundled static files,
|
|
# storing data in $XDG_DATA_HOME/puppy-tracker/events.json.
|
|
apps.default = {
|
|
type = "app";
|
|
program = toString (pkgs.writeShellScript "puppy-tracker-run" ''
|
|
data_dir="''${XDG_DATA_HOME:-$HOME/.local/share}/puppy-tracker"
|
|
mkdir -p "$data_dir"
|
|
exec ${server}/bin/puppy-tracker-server \
|
|
-addr "''${PUPPY_ADDR:-:8080}" \
|
|
-static ${static}/share/puppy-tracker \
|
|
-data "$data_dir/puppy.db"
|
|
'');
|
|
meta.description = "Run puppy-tracker locally (data in $XDG_DATA_HOME/puppy-tracker)";
|
|
};
|
|
}
|
|
);
|
|
}
|