Merge remote-tracking branch 'origin/nix'

This commit is contained in:
Julian 2023-04-29 12:39:58 -04:00
commit 3b11a40928
Signed by: NotNite
GPG key ID: BD91A5402CCEB08A
10 changed files with 2445 additions and 15 deletions

3
.gitignore vendored
View file

@ -38,3 +38,6 @@ database.db*
# graphql-codegen
src/__generated__
# NixOS
result

View file

@ -50,6 +50,7 @@ After cloning, create an `.env.local` with the following contents (in `key=value
- `LDAP_BIND_PASSWORD`: the password of the bind user
- `BASE_DOMAIN`: the domain gluestick is deployed on, with a protocol and trailing slash
- This domain will be used for OAuth redirects - if you are testing locally, set it to `http://localhost:3000/`
- `DATABASE_URL`: a Prisma-like path to your database
Example config:
@ -69,6 +70,7 @@ GITHUB_TOKEN=redacted
GITHUB_ORG=n2pm
BASE_DOMAIN=https://gluestick.n2.pm/
DATABASE_URL=file:./database.db
```
### Generating code
@ -93,7 +95,7 @@ export GRAPHQL_CODEDGEN_AUTH=...
Then, generate the GraphQL and database code:
```shell
npm run graphql-codegen
GRAPHQL_USE_INTROSPECTION=true npm run graphql-codegen
npm run prisma-generate
```
@ -108,7 +110,36 @@ npm run start
## Developing
You'll want to run these two commands at the same time:
### Generating GraphQL code
Because the LLDAP GraphQL API is locked behind authentication, and of a quirk with `graphl-codegen` configuration files, we need to set a temporary environment variable to generate GraphQL code. If not using introspection, you will need a running LLDAP server.
Run the `get-token.js` helper script and set the environment variable from its output:
```shell
node get-token.js
export GRAPHQL_CODEDGEN_AUTH=...
```
Then, generate the GraphQL code:
```shell
npm run graphql-codegen
```
If you want to use introspection, set `GRAPHQL_USE_INTROSPECTION=true` before generating the code. You won't need to set the auth environment variable in this case.
### Working with Prisma
gluestick uses [Prisma](https://www.prisma.io/) for accessing the database. If you will be modifying the database schema, you will need to work with it. Consider taking some time to familiarize yourself with the [Prisma CLI](https://www.prisma.io/docs/reference/api-reference/command-reference) first.
When first cloning, generate the Prisma client:
```shell
npm run prisma-generate
```
### Running the server
```shell
# Next.js hot reload
@ -119,5 +150,3 @@ npm run dev | pino-pretty
# Only required if working on GraphQL code
npm run graphql-codegen -- -w
```
If you're interacting with the database, take some time to familiarize yourself with the [Prisma CLI](https://www.prisma.io/docs/reference/api-reference/command-reference).

View file

@ -4,24 +4,32 @@ import { CodegenConfig } from "@graphql-codegen/cli";
import * as dotenv from "dotenv";
dotenv.config({ path: ".env.local" });
const useIntrospection = ["1", "true"].includes(
process.env.GRAPHQL_USE_INTROSPECTION?.toLowerCase() ?? ""
);
const config: CodegenConfig = {
schema: {
[`http://${process.env.LDAP_HOST}:17170/api/graphql`]: {
headers: {
// can't make the request automatically (await on top level)
Authorization: `Bearer ${process.env.GRAPHQL_CODEGEN_AUTH}`
}
}
},
schema: useIntrospection
? "introspection.json"
: {
[`http://${process.env.LDAP_HOST}:17170/api/graphql`]: {
headers: {
// can't make the request automatically (await on top level)
Authorization: `Bearer ${process.env.GRAPHQL_CODEGEN_AUTH}`
}
}
},
documents: ["src/**/*.ts", "src/**/*.tsx"],
generates: {
"./src/__generated__/": {
preset: "client",
plugins: [],
presetConfig: {
gqlTagName: "gql"
}
},
"introspection.json": {
plugins: ["introspection"]
}
},
ignoreNoDocuments: true

61
flake.lock Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1682656005,
"narHash": "sha256-fYplYo7so1O+rSQ2/aS+SbTPwLTeoUXk4ekKNtSl4P8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6806b63e824f84b0f0e60b6d660d4ae753de0477",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

178
flake.nix Normal file
View file

@ -0,0 +1,178 @@
{
description =
"NotNet's one stop shop for authentication and account onboarding";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
let
packages = flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages."${system}";
# I really cannot be assed to pick this apart
inputs = with pkgs; [ vips pkg-config python3 ];
# https://github.com/prisma/prisma/issues/3026#issuecomment-927258138
prismaHook = with pkgs; ''
export PRISMA_MIGRATION_ENGINE_BINARY="${prisma-engines}/bin/migration-engine"
export PRISMA_QUERY_ENGINE_BINARY="${prisma-engines}/bin/query-engine"
export PRISMA_QUERY_ENGINE_LIBRARY="${prisma-engines}/lib/libquery_engine.node"
export PRISMA_INTROSPECTION_ENGINE_BINARY="${prisma-engines}/bin/introspection-engine"
export PRISMA_FMT_BINARY="${prisma-engines}/bin/prisma-fmt"
'';
in rec {
packages.gluestick = pkgs.buildNpmPackage {
pname = "gluestick";
version = "0.1.0";
src = ./.;
npmDepsHash = "sha256-keOreamXKunlJzU2AKJo0J02ZxQrjLdoCIMCaiwEU4Y=";
nativeBuildInputs = inputs;
buildInputs = inputs;
preBuild = ''
${prismaHook}
# Use the introspection.json, because we can't connect to the API at build time
GRAPHQL_USE_INTROSPECTION=true npm run graphql-codegen
npm run prisma-generate
'';
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r .next/standalone $out/server
cp -r .next/static $out/server/.next/static
cp -r public $out/server/public
cp -r prisma $out/prisma
mkdir -p $out/bin
cat > $out/bin/gluestick <<EOF
#!${pkgs.stdenv.shell}
${prismaHook}
${pkgs.nodejs}/bin/node $out/server/server.js \$@
EOF
chmod +x $out/bin/gluestick
cat > $out/bin/prisma <<EOF
#!${pkgs.stdenv.shell}
${prismaHook}
${pkgs.nodePackages.prisma}/bin/prisma \$@
EOF
chmod +x $out/bin/prisma
runHook postInstall
'';
meta = with pkgs.lib; {
description =
"NotNet's one stop shop for authentication and account onboarding";
homepage = "https://git.n2.pm/NotNet/gluestick";
license = licenses.mit;
};
};
apps.gluestick = flake-utils.lib.mkApp {
name = "gluestick";
drv = packages.gluestick;
};
devShell = pkgs.mkShell {
inputsFrom = [ packages.gluestick ];
shellHook = ''
${prismaHook}
if [ -f .env.local ]; then
set -a
source .env.local
set +a
fi
'';
};
});
in packages // {
nixosModule = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.gluestick;
pkg = self.packages.${pkgs.system}.gluestick;
in {
options.services.gluestick = {
enable = mkEnableOption "gluestick";
user = mkOption {
type = types.str;
default = "gluestick";
};
group = mkOption {
type = types.str;
default = "gluestick";
};
port = mkOption {
type = types.int;
default = 3000;
};
envFile = mkOption {
type = types.path;
default = "/var/lib/gluestick/.env.local";
};
databaseFile = mkOption {
type = types.path;
default = "/var/lib/gluestick/database.db";
};
};
config = mkIf cfg.enable {
systemd.services.gluestick = {
description = "gluestick";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
preStart = ''
export DATABASE_URL="file:${cfg.databaseFile}"
${pkg}/bin/prisma migrate deploy --schema=${pkg}/prisma/schema.prisma
'';
script = ''
export PORT=${toString cfg.port}
export NODE_ENV=production
export DATABASE_URL="file:${cfg.databaseFile}"
${pkg}/bin/gluestick
'';
serviceConfig = {
User = cfg.user;
Group = cfg.group;
Restart = "always";
WorkingDirectory = "/var/lib/gluestick";
EnvironmentFile = cfg.envFile;
};
};
users = {
users = mkIf (cfg.user == "gluestick") {
gluestick = {
home = "/var/lib/gluestick";
createHome = true;
group = cfg.group;
isSystemUser = true;
};
};
groups = mkIf (cfg.group == "gluestick") { gluestick = { }; };
};
};
};
};
}

2123
introspection.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,8 @@
const nextConfig = {
experimental: {
appDir: true
}
},
output: "standalone"
};
module.exports = nextConfig;

26
package-lock.json generated
View file

@ -33,6 +33,7 @@
"devDependencies": {
"@graphql-codegen/cli": "^3.3.1",
"@graphql-codegen/client-preset": "^3.0.1",
"@graphql-codegen/introspection": "^3.0.1",
"@types/uuid": "^9.0.1",
"pino-pretty": "^10.0.0",
"prisma": "^4.13.0"
@ -1380,6 +1381,20 @@
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@graphql-codegen/introspection": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-3.0.1.tgz",
"integrity": "sha512-D6vJQTEL/np4EmeUHm5spLK59cr+AMXEoLRoTI+dagFzlHYDTfXZH6F7uhKaakxcj0SAQhIWKvGMggotUdEtyg==",
"dev": true,
"dependencies": {
"@graphql-codegen/plugin-helpers": "^4.1.0",
"@graphql-codegen/visitor-plugin-common": "^3.0.1",
"tslib": "~2.5.0"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@graphql-codegen/plugin-helpers": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz",
@ -9265,6 +9280,17 @@
"tslib": "~2.5.0"
}
},
"@graphql-codegen/introspection": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-3.0.1.tgz",
"integrity": "sha512-D6vJQTEL/np4EmeUHm5spLK59cr+AMXEoLRoTI+dagFzlHYDTfXZH6F7uhKaakxcj0SAQhIWKvGMggotUdEtyg==",
"dev": true,
"requires": {
"@graphql-codegen/plugin-helpers": "^4.1.0",
"@graphql-codegen/visitor-plugin-common": "^3.0.1",
"tslib": "~2.5.0"
}
},
"@graphql-codegen/plugin-helpers": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz",

View file

@ -37,6 +37,7 @@
"devDependencies": {
"@graphql-codegen/cli": "^3.3.1",
"@graphql-codegen/client-preset": "^3.0.1",
"@graphql-codegen/introspection": "^3.0.1",
"@types/uuid": "^9.0.1",
"pino-pretty": "^10.0.0",
"prisma": "^4.13.0"

View file

@ -4,7 +4,7 @@ generator client {
datasource db {
provider = "sqlite"
url = "file:./database.db"
url = env("DATABASE_URL")
}
model AuthTicket {