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