Skip to content

Commit

Permalink
Merge branch 'main' into 0-last-active-downstream
Browse files Browse the repository at this point in the history
  • Loading branch information
JJ-8 authored May 5, 2023
2 parents 78f239c + b6e3ec5 commit 9b1f009
Show file tree
Hide file tree
Showing 33 changed files with 3,523 additions and 3,676 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/build-and-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: "Build and publish Docker images to GHCR"

on:
workflow_call:
push:
branches:
- "main"

jobs:
build_and_publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: PrepareReg Names
run: |
echo IMAGE_REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
echo IMAGE_TAG=$(echo ${{ github.ref }} | tr '[:upper:]' '[:lower:]' | awk '{split($0,a,"/"); print a[3]}') >> $GITHUB_ENV
- name: Build and push
uses: docker/build-push-action@v4
with:
context: ./front
push: true
tags: ghcr.io/${{env.IMAGE_REPOSITORY}}/front:latest
- name: Build and push
uses: docker/build-push-action@v4
with:
context: ./api
push: true
tags: ghcr.io/${{env.IMAGE_REPOSITORY}}/api:latest
- name: Build and push
uses: docker/build-push-action@v4
with:
context: ./db
push: true
tags: ghcr.io/${{env.IMAGE_REPOSITORY}}/db:latest
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,31 @@ CTFNote is a collaborative tool aiming to help CTF teams to organise their work.

Before starting, make sure to fill in the information in the `.env` file.

Then you can start it with `docker-compose`. The default
### Pre-build images

Building CTFNote requires at least 3 GB of RAM. If you want to host CTFNote
on a server with less than 3 GB of RAM, you can use the pre-build images
from the Github Container Registry.

Make sure to [authenticate Docker to GHCR](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic).

Download `docker-compose.yml` and `docker-compose.prebuild.yml` for example through cloning the repository and run:

```shell
$ docker compose -f docker-compose.prebuild.yml up -d
```

### Self-build images

You can build and start CTFNote with `docker compose`. The default
configuration makes it super easy to start a new instance!

```shell
$ docker-compose up -d
$ docker compose up -d
```

### Accessing the instance

The instance will spawn a web server on port `127.0.0.1:8080`. The first account created will
have administrative privileges.

Expand Down
5 changes: 5 additions & 0 deletions api/migrations/41-ical-link.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE ctfnote.settings
ADD COLUMN "ical_password" TEXT DEFAULT encode(gen_random_bytes(16), 'hex');

GRANT SELECT ("ical_password") ON ctfnote.settings TO user_guest;

1 change: 1 addition & 0 deletions api/migrations/41-illegal-dates.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE ctfnote.ctf ADD CONSTRAINT no_illegal_end_time CHECK (end_time >= start_time);
42 changes: 19 additions & 23 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,29 @@
]
},
"dependencies": {
"@graphile-contrib/pg-many-to-many": "^1.0.0",
"@graphile-contrib/pg-many-to-many": "^1.0.2",
"@graphile-contrib/pg-simplify-inflector": "^6.1.0",
"@graphile/pg-pubsub": "^4.11.0",
"axios": "^0.21.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"graphile-utils": "^4.11.2",
"graphql": "^15.6.1",
"graphql-upload": "^12.0.0",
"postgraphile": "^4.12.8",
"postgraphile-plugin-connection-filter": "^2.2.2",
"@graphile/pg-pubsub": "^4.13.0",
"axios": "^1.3.4",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"graphile-utils": "^4.13.0",
"graphql": "^16.6.0",
"graphql-upload-ts": "^2.0.5",
"ical-generator": "^3.2.1",
"postgraphile": "^4.13.0",
"postgraphile-plugin-connection-filter": "^2.3.0",
"postgres-migrations": "^5.3.0"
},
"devDependencies": {
"@types/express": "^4.17.11",
"@types/graphql-upload": "^8.0.7",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^7.24.0",
"lint-staged": "^11.2.3",
"nodemon": "^2.0.7",
"prettier": "^2.2.1",
"@types/express": "^4.17.17",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"eslint": "^8.36.0",
"lint-staged": "^13.2.0",
"nodemon": "^2.0.22",
"prettier": "^2.8.7",
"ts-node": "^10.9.1",
"tslint": "^6.1.3",
"typescript": "^4.7.4"
},
"resolutions": {
"graphql-upload/busboy/dicer": "0.3.1"
"typescript": "^5.0.2"
}
}
11 changes: 9 additions & 2 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import simplifyPlugin from "@graphile-contrib/pg-simplify-inflector";
import PgPubsub from "@graphile/pg-pubsub";
import crypto from "crypto";
import express from "express";
import { graphqlUploadExpress } from "graphql-upload";
import { graphqlUploadExpress } from "graphql-upload-ts";
import {
makePluginHook,
postgraphile,
Expand All @@ -14,6 +14,8 @@ import createTasKPlugin from "./plugins/createTask";
import importCtfPlugin from "./plugins/importCtf";
import uploadLogoPlugin from "./plugins/uploadLogo";
import uploadScalar from "./plugins/uploadScalar";
import { Pool } from "pg";
import { icalRoute } from "./routes/ical";
import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter";

function getDbUrl(role: "user" | "admin") {
Expand Down Expand Up @@ -75,6 +77,10 @@ function createOptions() {
}

function createApp(postgraphileOptions: PostGraphileOptions) {
const pool = new Pool({
connectionString: getDbUrl("user"),
});

const app = express();
app.use(graphqlUploadExpress());
app.use(
Expand All @@ -85,7 +91,8 @@ function createApp(postgraphileOptions: PostGraphileOptions) {
},
})
);
app.use(postgraphile(getDbUrl("user"), "ctfnote", postgraphileOptions));
app.use(postgraphile(pool, "ctfnote", postgraphileOptions));
app.use("/calendar.ics", icalRoute(pool));
return app;
}

Expand Down
9 changes: 3 additions & 6 deletions api/src/plugins/uploadLogo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import crypto from "crypto";
import fs from "fs";
import { ReadStream } from "fs-capacitor";
import { gql, makeExtendSchemaPlugin } from "graphile-utils";
import { FileUpload } from "graphql-upload";
import { FileUpload } from "graphql-upload-ts";
import path from "path";
import { Client } from "pg";

Expand Down Expand Up @@ -30,7 +29,7 @@ interface Context {
}

function saveLocal(
stream: ReadStream,
stream: fs.ReadStream,
filename: string,
folder = ""
): Promise<string> {
Expand All @@ -45,9 +44,7 @@ function saveLocal(
return new Promise((resolve, reject) =>
stream
.on("error", (error) => {
if (stream.truncated)
// Delete the truncated file
fs.unlinkSync(fsPath);
fs.unlinkSync(fsPath);
reject(error);
})
.on("end", () => resolve(filePath))
Expand Down
2 changes: 1 addition & 1 deletion api/src/plugins/uploadScalar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SchemaBuilder } from "graphile-build";
import { Upload } from "graphql-upload";
import { Upload } from "graphql-upload-ts";

export default function (builder: SchemaBuilder): void {
builder.hook("build", (_, build) => {
Expand Down
66 changes: 66 additions & 0 deletions api/src/routes/ical.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { ICalCalendar } from "ical-generator";
import { Request, Response, Handler } from "express";
import { Pool } from "pg";

type CtfRow = {
id: number;
title: string;
start_time: string;
end_time: string;
ctf_url: string;
description: string;
};

type IcalPasswordRow = {
ical_password: string;
};

export function icalRoute(pool: Pool): Handler {
async function checkIcalPassword(
userPass: string | undefined
): Promise<boolean> {
const r = await pool.query<IcalPasswordRow>(
"SELECT ical_password FROM ctfnote.settings"
);
const db_password = r.rows[0].ical_password;
// If the password is null or empty allow any user
if (!db_password) return true;
return db_password === userPass;
}

async function getCtfs(): Promise<CtfRow[]> {
const r = await pool.query<CtfRow>(
"SELECT id, title, start_time, end_time, ctf_url, description FROM ctfnote.ctf"
);

return r.rows;
}

return async function (req: Request, res: Response): Promise<void> {
const { key } = req.query;

if (
!(typeof key == "string" || key == undefined) ||
!(await checkIcalPassword(key))
) {
res.status(403);
res.send("Forbidden\n");
return;
}

const cal = new ICalCalendar();
const ctfs = await getCtfs();

for (const ctf of ctfs) {
cal.createEvent({
start: ctf.start_time,
end: ctf.end_time,
description: ctf.description,
summary: ctf.title,
url: ctf.ctf_url,
});
}

cal.serve(res);
};
}
Loading

0 comments on commit 9b1f009

Please sign in to comment.