Skip to content

Commit

Permalink
Adds database and mutations (#2)
Browse files Browse the repository at this point in the history
* feat: Add university sports mutation and resolver with database integration

* refactor: Remove unused university sports mutation and resolvers; add caching for parking, charging, bus, train, and cl-events queries

* refactor: Replace create and update university sports mutations with a single upsert mutation

* feat: Add upsert and delete mutations for app announcements with GraphQL integration

* refactor: Update CI workflow to use Bun for package management and linting

* feat: Update app announcements and university sports mutations to use Unix timestamps for date fields

* adds jwt check

* feat: Enhance authentication handling by assigning default role for unauthenticated users

* feat: Update database connection to use Bun environment variables and enhance authorization checks

* feat: Integrate graphql-scalars for enhanced date handling in announcements and university sports

* feat: Update schema to use EmailAddress scalar and enhance documentation configuration

* feat: Refactor linting configuration and improve type safety in scraping and utility functions

* Adds Drizzle

* feat: Refactor database schema and migrations for app announcements and university sports

* feat: Update authentication handling to support multiple roles and adjust related migrations

* 🐛 fix deleteUniversitySport

* feat: Enhance JWT verification by fetching public key from JWKS endpoint

* 🩹 fix token deconstruction

* feat: Add created_at and updated_at fields to app_announcements and update related migrations

* feat: Deprecate announcements query and introduce appAnnouncements query with updated timestamps

* 🔧 update misc files

* 🐛 fix food API

* 📦 update bun.lockb

* fix: Update organizer formatting to handle 'e. V.' abbreviation

---------

Co-authored-by: Philipp Opheys <philipp@opheys.dev>
  • Loading branch information
Robert27 and BuildmodeOne authored Oct 7, 2024
1 parent 76de996 commit 86d1e11
Show file tree
Hide file tree
Showing 52 changed files with 2,821 additions and 441 deletions.
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.env
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
/data/*
.huksy
.github
.vscode
7 changes: 6 additions & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
DEEPL_API_KEY=""
MOODLE_USERNAME=""
MOODLE_PASSWORD=""
MOODLE_PASSWORD=""
DB_HOST=localhost
DB_PORT=5432
POSTGRES_DB=app
POSTGRES_USER=postgres
POSTGRES_PASSWORD=""
17 changes: 0 additions & 17 deletions .eslintrc.json

This file was deleted.

14 changes: 7 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
name: CI
on:
push:
branches: ['main', 'develop']
branches: [develop, main]
pull_request:
branches: [develop, main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
- uses: oven-sh/setup-bun@v2
with:
node-version: 20
bun-version: latest
- uses: actions/checkout@v4
- name: Install modules
run: npm install
run: bun install
- name: Run ESLint
run: npx eslint . --ext .js,.jsx,.ts,.tsx
run: bunx eslint .
- name: Run Prettier
run: npx prettier --check .
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
.DS_Store
src/data/announcements.json
src/data/cl-events.json
database/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# use the official Bun image
# see all versions at https://hub.docker.com/r/oven/bun/tags
FROM oven/bun:1 as base
FROM oven/bun:1 AS base
WORKDIR /usr/src/app

# install dependencies into temp directory
Expand Down
Binary file modified bun.lockb
Binary file not shown.
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:
app:
build:
Expand Down
6 changes: 3 additions & 3 deletions documentation/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
introspection:
schemaFile:
- ./src/schema.gql

url: http://localhost:4321/graphql
servers:
- url: https://api.neuland.app/graphql
description: Neuland API Server
Expand All @@ -19,6 +17,8 @@ info:
license:
name: AGPL-3.0
url: 'https://www.gnu.org/licenses/agpl-3.0.html'
extensions:
scalarGraphql: true
spectaql:
faviconFile: ./documentation/icon.svg
theme: spectaql
Expand Down
1,728 changes: 1,444 additions & 284 deletions documentation/generated/index.html

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pluginJs from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'

export default [
{ ignores: ['**/node_modules', '**/documentation'] },
{ files: ['**/*.{js,mjs,cjs,ts}'] },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parserOptions: {
tsconfigRootDir: './',
noUnusedLocals: true,
noUnusedParameters: true,
},
},
},
]
31 changes: 26 additions & 5 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getUserFromToken } from '@/utils/auth-utils'
import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'
import {
Expand All @@ -7,12 +8,15 @@ import {
import cors from 'cors'
import express from 'express'
import { readFileSync } from 'fs'
import type { JwtPayload } from 'jsonwebtoken'
import NodeCache from 'node-cache'
import path from 'path'

import { resolvers } from './src/resolvers'

const typeDefs = readFileSync('./src/schema.gql', { encoding: 'utf-8' })
const schema = readFileSync('./src/schema.gql', { encoding: 'utf-8' })
const typeDefs = schema
const port = process.env.PORT || 4000

const app = express()
app.use(
Expand All @@ -36,15 +40,32 @@ const apolloServer = new ApolloServer({
})
: ApolloServerPluginLandingPageLocalDefault(),
],
introspection: Bun.env.NODE_ENV !== 'production',
})

export const cache = new NodeCache({ stdTTL: 60 * 10 }) // 10 minutes default TTL

await apolloServer.start()

app.use('/', express.static(path.join(__dirname, 'documentation/generated')))
app.use(
'/graphql',
cors(),
express.json(),
expressMiddleware(apolloServer, {
context: async ({ req }): Promise<{ jwtPayload?: JwtPayload }> => {
const authHeader = req.headers.authorization
if (authHeader) {
return {
jwtPayload: await getUserFromToken(authHeader),
}
} else {
return {}
}
},
})
)

app.use('/graphql', cors(), express.json(), expressMiddleware(apolloServer))

app.listen(4000, () => {
console.log('🚀 Server ready at http://localhost:4000/graphql')
app.listen(port, () => {
console.log('🚀 Server ready at http://localhost:' + port + '/graphql')
})
30 changes: 21 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,58 @@
"start": "bun run index.ts",
"dev": "bun --hot run index.ts",
"prepare": "husky",
"docs": "bunx spectaql ./documentation/config.yml"
"docs": "concurrently --kill-others --success first \"cross-env PORT=4321 bun run start\" \"bunx spectaql ./documentation/config.yml\"",
"drizzle-kit": "drizzle-kit generate --schema ./src/db/schema/ --dialect postgresql --out ./src/db/migrations",
"migrate": "bun run ./src/db/migrate.ts"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"@apollo/server": "^4.11.0",
"@types/node": "^20.16.10",
"axios": "^1.7.7",
"cheerio": "^1.0.0",
"deepl": "^1.0.13",
"drizzle-orm": "^0.33.0",
"fetch-cookie": "^3.0.1",
"graphql": "^16.9.0",
"graphql-scalars": "^1.23.0",
"graphql-tag": "^2.12.6",
"jsonwebtoken": "^9.0.2",
"jwk-to-pem": "^2.0.6",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"node-cache": "^5.1.2",
"object-hash": "^3.0.0",
"pdf-parse": "https://github.com/neuland-ingolstadt/pdf-parse.git",
"postgres": "^3.4.4",
"sanitize-html": "^2.13.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.11.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/bun": "^1.1.10",
"@types/cors": "^2.8.17",
"@types/jsonwebtoken": "^9.0.7",
"@types/jwk-to-pem": "^2.0.3",
"@types/node": "^22.7.4",
"@types/object-hash": "^3.0.6",
"@types/pdf-parse": "^1.1.4",
"@types/sanitize-html": "^2.13.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint": "^8.57.1",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"concurrently": "^9.0.1",
"cross-env": "^7.0.3",
"drizzle-kit": "^0.24.2",
"eslint": "^9.11.1",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.6.0",
"globals": "^15.10.0",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"prettier": "^3.3.3",
"typescript": "^5.6.2"
"typescript": "^5.6.2",
"typescript-eslint": "^8.8.0"
},
"lint-staged": {
"**/*.{gql,graphql}": [
Expand Down
10 changes: 10 additions & 0 deletions src/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'

import schema from './schema'

export const CONNECTION_STRING = `postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.POSTGRES_DB}`

const queryClient = postgres(CONNECTION_STRING, { max: 1 })

export const db = drizzle(queryClient, { schema })
17 changes: 17 additions & 0 deletions src/db/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { drizzle } from 'drizzle-orm/postgres-js'
import { migrate } from 'drizzle-orm/postgres-js/migrator'
import postgres from 'postgres'

import { CONNECTION_STRING } from '.'

async function main() {
const client = postgres(CONNECTION_STRING, { max: 1 })
await migrate(drizzle(client), { migrationsFolder: './src/db/migrations' })

await client.end()
}

main().catch((err) => {
console.error(err)
process.exit(1)
})
41 changes: 41 additions & 0 deletions src/db/migrations/0000_round_scream.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
DO $$ BEGIN
CREATE TYPE "public"."campus" AS ENUM('Ingolstadt', 'Neuburg');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
CREATE TYPE "public"."weekday" AS ENUM('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "app_announcements" (
"id" serial PRIMARY KEY NOT NULL,
"title_de" text NOT NULL,
"title_en" text NOT NULL,
"description_de" text NOT NULL,
"description_en" text NOT NULL,
"start_date_time" timestamp with time zone NOT NULL,
"end_date_time" timestamp with time zone NOT NULL,
"priority" integer NOT NULL,
"url" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "university_sports" (
"id" serial PRIMARY KEY NOT NULL,
"title_de" text NOT NULL,
"description_de" text,
"title_en" text NOT NULL,
"description_en" text,
"campus" "campus" NOT NULL,
"location" text NOT NULL,
"weekday" "weekday" NOT NULL,
"start_time" time NOT NULL,
"end_time" time,
"requires_registration" boolean NOT NULL,
"invitation_link" text,
"e_mail" text,
"created_at" timestamp with time zone NOT NULL,
"updated_at" timestamp with time zone NOT NULL
);
2 changes: 2 additions & 0 deletions src/db/migrations/0001_free_midnight.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "app_announcements" ADD COLUMN "created_at" timestamp with time zone NOT NULL;--> statement-breakpoint
ALTER TABLE "app_announcements" ADD COLUMN "updated_at" timestamp with time zone NOT NULL;
Loading

0 comments on commit 86d1e11

Please sign in to comment.