Skip to content

Commit

Permalink
Merge pull request #3 Nixification
Browse files Browse the repository at this point in the history
  • Loading branch information
fugidev authored Jan 12, 2024
2 parents 05aba76 + c3b9f0d commit 210cc01
Show file tree
Hide file tree
Showing 7 changed files with 346 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ yarn-error.log*

.idea
.vscode/

result
22 changes: 22 additions & 0 deletions backend/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{ lib
, buildGoModule
}:

buildGoModule rec {
pname = "exam-poll-backend";
version = "0.0.0";

src = ./.;

vendorHash = "sha256-1QmLYg+1GQwqexz1eFzsWt+H9t1CCN7fMialc07UWrg=";

postInstall = ''
mv $out/bin/exam-poll{,-backend}
'';

meta = {
license = lib.licenses.gpl3Plus;
maintainers = with lib.maintainers; [ fugi ];
mainProgram = pname;
};
}
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, flake-utils }:
(flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages = {
exam-poll-frontend = pkgs.callPackage ./frontend { };
exam-poll-backend = pkgs.callPackage ./backend { };
};

formatter = pkgs.nixpkgs-fmt;
})
) // {
overlays.default = (_: prev: {
inherit (self.packages.${prev.system})
exam-poll-frontend exam-poll-backend;
});

nixosModules.default = {
imports = [ ./module.nix ];

nixpkgs.overlays = [ self.overlays.default ];
};
};
}
72 changes: 72 additions & 0 deletions frontend/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{ lib
, fetchYarnDeps
, mkYarnPackage
, nodejs
, makeWrapper
, localApiBaseUrl ? null
, publicApiBaseUrl ? null
}:

mkYarnPackage rec {
pname = "exam-poll-frontend";
version = "0.1.0";
src = ./.;

yarnLock = ./yarn.lock;
packageJSON = ./package.json;

offlineCache = fetchYarnDeps {
inherit yarnLock;
hash = "sha256-qyo62/Apld/JslCshF6iSwegbfBl41zpYHabj5/0ARs=";
};

NODE_ENV = "production";
API_BASEURL = localApiBaseUrl;
NEXT_PUBLIC_API_BASEURL = publicApiBaseUrl;

nativeBuildInputs = [ makeWrapper ];

configurePhase = ''
runHook preConfigure
ln -s $node_modules node_modules
runHook postConfigure
'';

buildPhase = ''
runHook preBuild
export HOME=$(mktemp -d)
# pipe to cat to disable fancy progress indicators cluttering the log
yarn --offline run build | cat
runHook postBuild
'';

installPhase = ''
runHook preInstall
export OUT_LIBEXEC="$out/libexec/${pname}"
mkdir -p $out $OUT_LIBEXEC
# copy compiled files
cp -r .next/standalone/. $OUT_LIBEXEC
# copy static files too
cp -r .next/static $OUT_LIBEXEC/.next/static
cp -r public $OUT_LIBEXEC/public
# server wrapper
makeWrapper '${nodejs}/bin/node' "$out/bin/${pname}" \
--add-flags "$OUT_LIBEXEC/server.js"
runHook postInstall
'';

dontFixup = true;
doDist = false;

meta = {
license = lib.licenses.gpl3Plus;
maintainers = with lib.maintainers; [ fugi ];
mainProgram = pname;
};
}
1 change: 1 addition & 0 deletions frontend/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
module.exports = {
output: 'standalone',
reactStrictMode: true,
i18n: {
// https://nextjs.org/docs/advanced-features/i18n-routing
Expand Down
158 changes: 158 additions & 0 deletions module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.exam-poll;

mkService = service: lib.mkMerge [
{
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "exec";
Restart = "on-failure";
DynamicUser = true;
UMask = "0077";
WorkingDirectory = /tmp;
# Hardening
CapabilityBoundingSet = [ "" ];
DeviceAllow = [ "" ];
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
};
}
service
];
in
{
options.services.exam-poll = with lib; {
enable = mkEnableOption "Exam Poll";

configureNginx = mkOption {
type = types.bool;
default = true;
description = "Whether to configure nginx as reverse proxy.";
};

frontend = {
package = mkOption {
type = types.package;
default = pkgs.exam-poll-frontend;
description = "The package to use.";
};

port = mkOption {
type = types.port;
default = 3000;
description = "The port to listen on.";
};

hostName = mkOption {
type = types.str;
example = "exam-poll.example.com";
description = "The hostname the application will be served on.";
};
};

backend = {
package = mkOption {
type = types.package;
default = pkgs.exam-poll-backend;
description = "The package to use.";
};

port = mkOption {
type = types.port;
default = 8000;
description = "The port to listen on.";
};

hostName = mkOption {
type = types.str;
example = "exam-poll-api.example.com";
description = "The hostname the api will be served on.";
};
};

mongodb = {
uri = mkOption {
type = types.str;
example = "mongodb://localhost:27017";
description = "MongoDB connection string.";
};

database = mkOption {
type = types.str;
description = "The database name.";
};

collection = mkOption {
type = types.str;
description = "The collection name.";
};
};
};

config = lib.mkIf cfg.enable {
systemd.services = {
exam-poll-frontend = mkService {
description = "Exam Poll frontend";
environment = {
PORT = toString cfg.frontend.port;
};
serviceConfig = {
ExecStart = lib.getExe (cfg.frontend.package.override {
localApiBaseUrl = "http://localhost:${toString cfg.backend.port}";
publicApiBaseUrl = "https://${cfg.backend.hostName}";
});
};
};

exam-poll-backend = mkService {
description = "Exam Poll backend";
environment = {
EXAM_POLL_HTTP_LISTEN = "localhost:${toString cfg.backend.port}";
EXAM_POLL_CORS_LIST = "https://${cfg.frontend.hostName},localhost";
EXAM_POLL_MONGODB = cfg.mongodb.uri;
EXAM_POLL_DATABASE = cfg.mongodb.database;
EXAM_POLL_COLLECTION = cfg.mongodb.collection;
};
serviceConfig = {
ExecStart = lib.getExe pkgs.exam-poll-backend;
};
};
};

services.nginx = lib.mkIf cfg.configureNginx {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
${cfg.frontend.hostName} = {
forceSSL = lib.mkDefault true;
enableACME = lib.mkDefault true;
locations."/".proxyPass = "http://localhost:${toString cfg.frontend.port}";
};
${cfg.backend.hostName} = {
forceSSL = lib.mkDefault true;
enableACME = lib.mkDefault true;
locations."/".proxyPass = "http://localhost:${toString cfg.backend.port}";
};
};
};
};
}

0 comments on commit 210cc01

Please sign in to comment.