working nix builds
can compile both the react application statically so it can be served by nginx and then builds a docker container with nginx for easy deployment to something like fly.io I mean since it's static I could probably host it for free on a website like netlify or something, but I still wanted a docker container for now. Notably I did have to move the generated javascript parser into the mast-react-vite folder so that `buildNpmPackage` was scoped to the mast-react-vite folder. I'm not sure how to get it working in a truly monorepo style build working for it yet, but I'm not exactly inclined to make it super shiny right now either.
This commit is contained in:
parent
f24e56159a
commit
0ca396148b
114
flake.nix
114
flake.nix
|
@ -9,20 +9,108 @@
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
in
|
username = "admin";
|
||||||
{
|
passwordHash =
|
||||||
devShells.default = pkgs.mkShell {
|
"$apr1$bhZ6EAAr$E8cv5p/2RUBrxLZD.9Jpi."; # Replace with your hash
|
||||||
buildInputs = with pkgs; [
|
|
||||||
go
|
|
||||||
pigeon
|
|
||||||
nodejs
|
|
||||||
sqlite
|
|
||||||
];
|
|
||||||
|
|
||||||
shellHook = ''
|
nginxConfig = pkgs.writeText "nginx.conf" ''
|
||||||
echo "System: ${system}"
|
worker_processes auto;
|
||||||
|
daemon off;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include ${pkgs.nginx}/conf/mime.types;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
auth_basic "Restricted Content";
|
||||||
|
auth_basic_user_file /etc/nginx/htpasswd;
|
||||||
|
|
||||||
|
root /app;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
htpasswd = pkgs.writeText "htpasswd" ''
|
||||||
|
${username}:${passwordHash}
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
packages = rec {
|
||||||
|
react = pkgs.buildNpmPackage {
|
||||||
|
pname = "mast-react";
|
||||||
|
version = "0.0.1";
|
||||||
|
src = ./mast-react-vite;
|
||||||
|
|
||||||
|
npmDepsHash = "sha256-oqq55MEzXGEiF9UW7rZFyQrkyTPrUEkh2enZmbbZ7Ks=";
|
||||||
|
buildInputs = with pkgs; [ nodejs typescript ];
|
||||||
|
nativeBuildInputs = with pkgs; [ nodejs ];
|
||||||
|
|
||||||
|
# npmWorkspace = ./mast-react-vite;
|
||||||
|
npmBuildScript = "build";
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r dist $out/
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
react-server = pkgs.dockerTools.buildImage {
|
||||||
);
|
name = "react-server";
|
||||||
|
tag = "latest";
|
||||||
|
|
||||||
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
name = "image-root";
|
||||||
|
paths = [
|
||||||
|
pkgs.nginx
|
||||||
|
react
|
||||||
|
(pkgs.writeScriptBin "start-server" ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
mkdir -p /etc/nginx
|
||||||
|
cp ${htpasswd} /etc/nginx/htpasswd
|
||||||
|
|
||||||
|
# Create app directory and copy built files
|
||||||
|
mkdir -p /app
|
||||||
|
cp -r ${react}/* /app/
|
||||||
|
|
||||||
|
# Start nginx
|
||||||
|
${pkgs.nginx}/bin/nginx -c ${nginxConfig}
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
pathsToLink = [ "/bin" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
Cmd = [ "/bin/start-server" ];
|
||||||
|
ExposedPorts = { "8080/tcp" = { }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [ go pigeon nodejs sqlite git-bug openssl ];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
alias start-dev='npm run dev'
|
||||||
|
alias build-dev='npm run build'
|
||||||
|
alias clean-dev='rm -rf dist node_modules'
|
||||||
|
alias ws-server='go run ./server/main.go'
|
||||||
|
alias gen-pass='openssl passwd -apr1 '
|
||||||
|
|
||||||
|
echo "System: ${system}"
|
||||||
|
echo "Available commands:"
|
||||||
|
echo " start-dev - Start Vite dev server"
|
||||||
|
echo " build-dev - Build the application"
|
||||||
|
echo " clean-dev - Clean build artifacts"
|
||||||
|
echo " ws-server - Run the websocket server"
|
||||||
|
echo " gen-pass - Generate a basic_auth password"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
223
mast-react-vite/package-lock.json
generated
223
mast-react-vite/package-lock.json
generated
|
@ -8,10 +8,12 @@
|
||||||
"name": "mast-react",
|
"name": "mast-react",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@tanstack/react-table": "^8.20.5",
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@vlcn.io/react": "^3.1.0",
|
"@vlcn.io/react": "^3.1.0",
|
||||||
"@vlcn.io/rx-tbl": "^0.15.0",
|
"@vlcn.io/rx-tbl": "^0.15.0",
|
||||||
|
"@vlcn.io/ws-browserdb": "^0.2.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.454.0",
|
"lucide-react": "^0.454.0",
|
||||||
|
@ -692,6 +694,68 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-checkbox": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
|
"@radix-ui/react-use-previous": "1.1.0",
|
||||||
|
"@radix-ui/react-use-size": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-icons": {
|
"node_modules/@radix-ui/react-icons": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
||||||
|
@ -700,6 +764,144 @@
|
||||||
"react": "^16.x || ^17.x || ^18.x"
|
"react": "^16.x || ^17.x || ^18.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-previous": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-size": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.24.2",
|
"version": "4.24.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz",
|
||||||
|
@ -1203,13 +1405,13 @@
|
||||||
"version": "15.7.13",
|
"version": "15.7.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
|
||||||
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
|
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.3.12",
|
"version": "18.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
|
||||||
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
|
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
|
@ -1219,7 +1421,7 @@
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
|
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
|
@ -1503,6 +1705,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/@vlcn.io/wa-sqlite/-/wa-sqlite-0.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vlcn.io/wa-sqlite/-/wa-sqlite-0.22.0.tgz",
|
||||||
"integrity": "sha512-OujKro0mAqP7/efUeCGB6zBiyMoSCFVe7jQKPF0n47U9ZhOaIW3kQUVCwF+CmzvzQfN1Vl4PrFQRNNxlSwTCNQ=="
|
"integrity": "sha512-OujKro0mAqP7/efUeCGB6zBiyMoSCFVe7jQKPF0n47U9ZhOaIW3kQUVCwF+CmzvzQfN1Vl4PrFQRNNxlSwTCNQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@vlcn.io/ws-browserdb": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vlcn.io/ws-browserdb/-/ws-browserdb-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-TKPygQomMPurQ8mwAwEefN5BROw30FagL21S3FzvUunyCybOByysAEoV/OrJ7xNCvYXN9yjg0qyf7iZZfeOsYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/throttle-debounce": "^5.0.0",
|
||||||
|
"@vlcn.io/crsqlite-wasm": "0.16.0",
|
||||||
|
"@vlcn.io/rx-tbl": "0.15.0",
|
||||||
|
"@vlcn.io/ws-client": "0.2.0",
|
||||||
|
"@vlcn.io/ws-common": "0.2.0",
|
||||||
|
"@vlcn.io/xplat-api": "0.15.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vlcn.io/ws-client": {
|
"node_modules/@vlcn.io/ws-client": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vlcn.io/ws-client/-/ws-client-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vlcn.io/ws-client/-/ws-client-0.2.0.tgz",
|
||||||
|
@ -1909,7 +2124,7 @@
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
|
|
|
@ -5,15 +5,17 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@tanstack/react-table": "^8.20.5",
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@vlcn.io/react": "^3.1.0",
|
"@vlcn.io/react": "^3.1.0",
|
||||||
"@vlcn.io/rx-tbl": "^0.15.0",
|
"@vlcn.io/rx-tbl": "^0.15.0",
|
||||||
|
"@vlcn.io/ws-browserdb": "^0.2.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.454.0",
|
"lucide-react": "^0.454.0",
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useCachedState, useQuery } from "@vlcn.io/react";
|
import { useQuery } from "@vlcn.io/react";
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { DataTable } from "@/components/ui/data-table"
|
import { DataTable } from "@/components/ui/data-table"
|
||||||
import * as addParser from '../../parser/add_parser.js';
|
import * as commandParser from '@/lib/command_parser.js';
|
||||||
import * as commandParser from '../../parser/command_parser.js';
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
|
|
||||||
function Marshall(parsed) {
|
interface MarshalledCommand {
|
||||||
|
description: string;
|
||||||
|
tags: string;
|
||||||
|
project: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Marshall(parsed): MarshalledCommand {
|
||||||
let project = "";
|
let project = "";
|
||||||
const tags = [];
|
const tags = [];
|
||||||
for (const attr of parsed.attributes) {
|
for (const attr of parsed.attributes) {
|
||||||
|
@ -171,6 +173,10 @@ function App({ ctx }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// React to key presses for selection
|
// React to key presses for selection
|
||||||
|
// TODO:
|
||||||
|
// We should use the react way
|
||||||
|
// Where we update based on changes to newText
|
||||||
|
// Rather than changes to the input value
|
||||||
else if (e.target.value.trim() !== "") {
|
else if (e.target.value.trim() !== "") {
|
||||||
try {
|
try {
|
||||||
const parsed = commandParser.parse(e.target.value);
|
const parsed = commandParser.parse(e.target.value);
|
||||||
|
|
78
mast-react-vite/src/Root.tsx
Normal file
78
mast-react-vite/src/Root.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import App from "./App.tsx";
|
||||||
|
import schemaContent from "./schemas/main2.sql?raw";
|
||||||
|
import { DBProvider } from "@vlcn.io/react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random room name to sync with or pulls one from local storage.
|
||||||
|
*/
|
||||||
|
function getRoom(hash: HashBag): string {
|
||||||
|
return hash.room || localStorage.getItem("room") || newRoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hashChanged() {
|
||||||
|
const hash = parseHash();
|
||||||
|
const room = getRoom(hash);
|
||||||
|
if (room != hash.room) {
|
||||||
|
hash.room = room;
|
||||||
|
window.location.hash = writeHash(hash);
|
||||||
|
}
|
||||||
|
localStorage.setItem("room", room);
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
const room = hashChanged();
|
||||||
|
|
||||||
|
export default function Root() {
|
||||||
|
const [theRoom, setTheRoom] = useState(room);
|
||||||
|
useEffect(() => {
|
||||||
|
const cb = () => {
|
||||||
|
const room = hashChanged();
|
||||||
|
if (room != theRoom) {
|
||||||
|
setTheRoom(room);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEventListener("hashchange", cb);
|
||||||
|
return () => {
|
||||||
|
removeEventListener("hashchange", cb);
|
||||||
|
};
|
||||||
|
}, []); // ignore -- theRoom is managed by the effect
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DBProvider
|
||||||
|
dbname={theRoom}
|
||||||
|
schema={{
|
||||||
|
name: "main2.sql",
|
||||||
|
content: schemaContent,
|
||||||
|
}}
|
||||||
|
Render={() => <App dbname={theRoom} />}
|
||||||
|
></DBProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type HashBag = { [key: string]: string };
|
||||||
|
function parseHash(): HashBag {
|
||||||
|
const hash = window.location.hash;
|
||||||
|
const ret: { [key: string]: string } = {};
|
||||||
|
if (hash.length > 1) {
|
||||||
|
const substr = hash.substring(1);
|
||||||
|
const parts = substr.split(",");
|
||||||
|
for (const part of parts) {
|
||||||
|
const [key, value] = part.split("=");
|
||||||
|
ret[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeHash(hash: HashBag) {
|
||||||
|
const parts = [];
|
||||||
|
for (const key in hash) {
|
||||||
|
parts.push(`${key}=${hash[key]}`);
|
||||||
|
}
|
||||||
|
return parts.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
function newRoom() {
|
||||||
|
return crypto.randomUUID().replaceAll("-", "");
|
||||||
|
}
|
1304
mast-react-vite/src/lib/command_parser.js
Normal file
1304
mast-react-vite/src/lib/command_parser.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,15 +2,18 @@ import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
|
import Root from "./Root.tsx";
|
||||||
|
import { start } from "@vlcn.io/ws-client/worker.js";
|
||||||
|
import { createDbProvider } from "@vlcn.io/ws-browserdb";
|
||||||
|
|
||||||
import initWasm from '@vlcn.io/crsqlite-wasm';
|
import initWasm from '@vlcn.io/crsqlite-wasm';
|
||||||
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
||||||
import tblrx from '@vlcn.io/rx-tbl';
|
import tblrx from '@vlcn.io/rx-tbl';
|
||||||
|
|
||||||
const sqlite = await initWasm(() => wasmUrl);
|
async function initializeDatabase() {
|
||||||
|
const sqlite = await initWasm(() => wasmUrl);
|
||||||
const db = await sqlite.open("todo.db");
|
const db = await sqlite.open("todo.db");
|
||||||
await db.exec(`
|
await db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS todos (
|
CREATE TABLE IF NOT EXISTS todos (
|
||||||
id BLOB PRIMARY KEY NOT NULL,
|
id BLOB PRIMARY KEY NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
|
@ -23,10 +26,65 @@ await db.exec(`
|
||||||
completed INTEGER NOT NULL DEFAULT 0
|
completed INTEGER NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
SELECT crsql_as_crr('todos');
|
SELECT crsql_as_crr('todos');
|
||||||
`);
|
`);
|
||||||
|
db.onUpdate(async () => {
|
||||||
|
const changes = await db.execA(`SELECT * FROM crsql_changes WHERE db_version > 0 AND site_id = crsql_site_id()`);
|
||||||
|
|
||||||
const rx = tblrx(db);
|
sendChangesToServer(changes);
|
||||||
const ctx = { db, rx };
|
});
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
dbProvider: createDbProvider(),
|
||||||
|
transportProvider: (dbName) => new WebSocket(`ws://localhost:8080/sync/${dbName}`),
|
||||||
|
};
|
||||||
|
|
||||||
|
start(config);
|
||||||
|
const ws = new WebSocket('ws://localhost:8080/sync');
|
||||||
|
|
||||||
|
ws.onmessage = async function(event) {
|
||||||
|
const { type, data } = JSON.parse(event.data);
|
||||||
|
if (type === 'changes') {
|
||||||
|
await applyChanges(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendChangesToServer(changes) {
|
||||||
|
const formattedChanges = changes.map(change => ({
|
||||||
|
TableName: change[0],
|
||||||
|
PK: Array.isArray(change[1]) ? btoa(String.fromCharCode.apply(null, change[1])) : btoa(change[1]),
|
||||||
|
ColumnName: change[2],
|
||||||
|
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : change[3],
|
||||||
|
ColVersion: Number(change[4]),
|
||||||
|
DBVersion: Number(change[5]),
|
||||||
|
SiteID: Array.isArray(change[6]) ? btoa(new TextDecoder('utf8').decode(change[6])) : btoa(change[6]),
|
||||||
|
CL: Number(change[7]),
|
||||||
|
Seq: Number(change[8])
|
||||||
|
}));
|
||||||
|
console.log(formattedChanges)
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
type: "changes",
|
||||||
|
data: formattedChanges
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.send(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyChanges(changes) {
|
||||||
|
const db = initializeDatabase();
|
||||||
|
const rx = tblrx(db);
|
||||||
|
const ctx = { db, rx };
|
||||||
|
await db.tx(async (tx) => {
|
||||||
|
for (const change of changes) {
|
||||||
|
await tx.exec(
|
||||||
|
`INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
change
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("root")!.classList.add("dark");
|
document.getElementById("root")!.classList.add("dark");
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"noEmitOnError": false,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
|
|
Loading…
Reference in a new issue