forked from NotNet/gluestick
Extremely hacky Nix support
This commit is contained in:
parent
457c33e4a1
commit
26f5051a8e
11 changed files with 2446 additions and 15 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -38,3 +38,6 @@ database.db*
|
|||
|
||||
# graphql-codegen
|
||||
src/__generated__
|
||||
|
||||
# NixOS
|
||||
result
|
||||
|
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
src/graphql.ts
|
37
README.md
37
README.md
|
@ -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).
|
||||
|
|
26
codegen.ts
26
codegen.ts
|
@ -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
61
flake.lock
Normal 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
178
flake.nix
Normal 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
2123
introspection.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,8 @@
|
|||
const nextConfig = {
|
||||
experimental: {
|
||||
appDir: true
|
||||
}
|
||||
},
|
||||
output: "standalone"
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
|
26
package-lock.json
generated
26
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -4,7 +4,7 @@ generator client {
|
|||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:./database.db"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model AuthTicket {
|
||||
|
|
Loading…
Reference in a new issue