Initial commit: Vue 3 webapp NixOS module
This commit is contained in:
commit
b30b37d5ce
3 changed files with 253 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
result
|
||||||
10
flake.nix
Normal file
10
flake.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
description = "Vue 3 webapp NixOS module";
|
||||||
|
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }: {
|
||||||
|
nixosModules.webapp = import ./webapp.nix;
|
||||||
|
nixosModules.default = self.nixosModules.webapp;
|
||||||
|
};
|
||||||
|
}
|
||||||
242
webapp.nix
Normal file
242
webapp.nix
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
# NixOS module for building and serving the Vue 3 webapp
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.webapp;
|
||||||
|
|
||||||
|
# Build the Vue 3 webapp using buildNpmPackage
|
||||||
|
webapp = pkgs.buildNpmPackage {
|
||||||
|
pname = "aio-webapp";
|
||||||
|
version = "1.0.0";
|
||||||
|
|
||||||
|
# Fetch source from git repository
|
||||||
|
src = builtins.fetchGit {
|
||||||
|
url = cfg.gitUrl;
|
||||||
|
ref = cfg.gitRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
# SHA256 hash of npm dependencies
|
||||||
|
# Run `nix-prefetch-npm-deps package-lock.json` to get this hash
|
||||||
|
# Or use lib.fakeHash initially and let the build tell you the correct hash
|
||||||
|
npmDepsHash = cfg.npmDepsHash;
|
||||||
|
|
||||||
|
# Node.js version (use LTS)
|
||||||
|
nodejs = pkgs.nodejs_20;
|
||||||
|
|
||||||
|
# Include devDependencies (vue-tsc, vite, etc. are needed for build)
|
||||||
|
npmFlags = [ "--include=dev" ];
|
||||||
|
makeCacheWritable = true;
|
||||||
|
|
||||||
|
# Skip Electron binary download (we're building a web app, not desktop)
|
||||||
|
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||||||
|
|
||||||
|
# Environment variables for Vite build (VITE_* are embedded at build time)
|
||||||
|
# App Configuration
|
||||||
|
VITE_APP_NAME = cfg.appName;
|
||||||
|
|
||||||
|
# Nostr Configuration
|
||||||
|
VITE_NOSTR_RELAYS = builtins.toJSON cfg.nostrRelays;
|
||||||
|
VITE_ADMIN_PUBKEYS = builtins.toJSON cfg.adminPubkeys;
|
||||||
|
|
||||||
|
# API Configuration
|
||||||
|
VITE_LNBITS_BASE_URL = cfg.lnbitsBaseUrl;
|
||||||
|
VITE_API_KEY = cfg.apiKey;
|
||||||
|
VITE_LNBITS_DEBUG = if cfg.lnbitsDebug then "true" else "false";
|
||||||
|
VITE_WEBSOCKET_ENABLED = if cfg.websocketEnabled then "true" else "false";
|
||||||
|
|
||||||
|
# Lightning Address Domain
|
||||||
|
VITE_LIGHTNING_DOMAIN = cfg.lightningDomain;
|
||||||
|
|
||||||
|
# Push Notifications
|
||||||
|
VITE_VAPID_PUBLIC_KEY = cfg.vapidPublicKey;
|
||||||
|
VITE_PUSH_NOTIFICATIONS_ENABLED = if cfg.pushNotificationsEnabled then "true" else "false";
|
||||||
|
|
||||||
|
# Image Upload Configuration (pict-rs)
|
||||||
|
VITE_PICTRS_BASE_URL = cfg.pictrsBaseUrl;
|
||||||
|
|
||||||
|
# Market Configuration
|
||||||
|
VITE_MARKET_NADDR = cfg.marketNaddr;
|
||||||
|
|
||||||
|
# Additional env vars
|
||||||
|
NODE_ENV = "production";
|
||||||
|
|
||||||
|
# Explicitly add node_modules/.bin to PATH for vue-tsc, vite, etc.
|
||||||
|
buildPhase = ''
|
||||||
|
export PATH="$PWD/node_modules/.bin:$PATH"
|
||||||
|
npm run build
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Install the built static files (Vite outputs to ./dist)
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/share/webapp
|
||||||
|
cp -r dist/* $out/share/webapp/
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Don't run npm test
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "AIO Community Hub - Vue 3 webapp";
|
||||||
|
license = licenses.mit;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.services.webapp = {
|
||||||
|
enable = lib.mkEnableOption "AIO webapp";
|
||||||
|
|
||||||
|
gitUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://git.atitlan.io/aiolabs/webapp";
|
||||||
|
description = "Git repository URL for the webapp source";
|
||||||
|
};
|
||||||
|
|
||||||
|
gitRef = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "main";
|
||||||
|
example = "demo";
|
||||||
|
description = "Git branch or tag to build from";
|
||||||
|
};
|
||||||
|
|
||||||
|
domain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "app.example.com";
|
||||||
|
description = "Domain name for the webapp";
|
||||||
|
};
|
||||||
|
|
||||||
|
npmDepsHash = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = lib.fakeHash;
|
||||||
|
description = ''
|
||||||
|
SHA256 hash of npm dependencies.
|
||||||
|
Run `nix-prefetch-npm-deps package-lock.json` on the webapp source
|
||||||
|
to get the correct hash.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# App Configuration
|
||||||
|
appName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "MyApp";
|
||||||
|
description = "Application name displayed in the UI";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Nostr Configuration
|
||||||
|
nostrRelays = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ "wss://relay.damus.io" "wss://relay.snort.social" ];
|
||||||
|
description = "List of Nostr relay URLs";
|
||||||
|
};
|
||||||
|
|
||||||
|
adminPubkeys = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [];
|
||||||
|
description = "List of admin Nostr public keys (hex format)";
|
||||||
|
};
|
||||||
|
|
||||||
|
# API Configuration
|
||||||
|
lnbitsBaseUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
example = "https://lnbits.example.com";
|
||||||
|
description = "LNBits API base URL";
|
||||||
|
};
|
||||||
|
|
||||||
|
apiKey = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "LNBits invoice/read API key";
|
||||||
|
};
|
||||||
|
|
||||||
|
lnbitsDebug = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable LNBits debug mode";
|
||||||
|
};
|
||||||
|
|
||||||
|
websocketEnabled = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable WebSocket for real-time updates";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Lightning Address Domain
|
||||||
|
lightningDomain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Domain used for Lightning Addresses.
|
||||||
|
Example: mydomain.com will show addresses as username@mydomain.com
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Push Notifications
|
||||||
|
vapidPublicKey = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "VAPID public key for push notifications";
|
||||||
|
};
|
||||||
|
|
||||||
|
pushNotificationsEnabled = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable push notifications";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Image Upload Configuration (pict-rs)
|
||||||
|
pictrsBaseUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "Pict-rs image service base URL";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Market Configuration
|
||||||
|
marketNaddr = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "Nostr address (naddr) for the market configuration";
|
||||||
|
};
|
||||||
|
|
||||||
|
enableSSL = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable SSL/TLS with Let's Encrypt";
|
||||||
|
};
|
||||||
|
|
||||||
|
acmeEmail = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = "Email for Let's Encrypt certificate notifications";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# Nginx virtualHost for webapp (global nginx settings are in config/nginx.nix)
|
||||||
|
services.nginx.virtualHosts.${cfg.domain} = {
|
||||||
|
# SSL configuration
|
||||||
|
forceSSL = cfg.enableSSL;
|
||||||
|
enableACME = cfg.enableSSL;
|
||||||
|
|
||||||
|
# Serve the built webapp
|
||||||
|
root = "${webapp}/share/webapp";
|
||||||
|
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
# Try files, fallback to index.html for SPA routing
|
||||||
|
tryFiles = "$uri $uri/ /index.html";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Cache static assets aggressively
|
||||||
|
"~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# ACME and firewall are configured globally in config/nginx.nix
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue