Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .commitlintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
extends: ["@solvro/config/commitlint"],
};
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "npm"
directories:
- "/"
- "/frontend"
- "/backend"
schedule:
interval: "daily"
allow:
- dependency-name: "@solvro/config"
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI Backend
name: Backend - CI

on:
push:
Expand All @@ -18,39 +18,34 @@ jobs:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install deps
run: npm ci

- name: Set up AdonisJS environment
run: |
cp .env.development .env
node ace generate:key

- name: Lint
run: npm run lint
if: always()

- name: Format
run: npm run format:check
if: always()

- name: Type check
run: npm run typecheck
if: always()

- name: Run Test Build
run: npm run build
env:
NODE_ENV: production
PORT: 3333
HOST: 0.0.0.0
LOG_LEVEL: debug
APP_KEY: really-hard-secret-key-to-guess
SESSION_DRIVER: cookie
DB_HOST: localhost
DB_PORT: 5432
DB_USER: postgres
DB_DATABASE: postgres
DB_PASSWORD: postgres
USOS_CONSUMER_KEY: test
USOS_CONSUMER_SECRET: test
if: always()
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: Frontend - CI

on:
push:
Expand All @@ -18,10 +18,10 @@ jobs:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
Expand Down
1 change: 1 addition & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx commitlint --edit "$1"
1 change: 0 additions & 1 deletion .lintstagedrc.json

This file was deleted.

5 changes: 2 additions & 3 deletions backend/ace.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
| and then imports the "bin/console.ts" file.
|
*/

/**
* Register hook to process TypeScript files using ts-node
*/
import 'ts-node-maintained/register/esm'
import "ts-node-maintained/register/esm";

/**
* Import ace console entrypoint
*/
await import('./bin/console.js')
await import("./bin/console.js");
42 changes: 21 additions & 21 deletions backend/adonisrc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfig } from '@adonisjs/core/app'
import { defineConfig } from "@adonisjs/core/app";

export default defineConfig({
/*
Expand All @@ -11,9 +11,9 @@ export default defineConfig({
|
*/
commands: [
() => import('@adonisjs/core/commands'),
() => import('@adonisjs/lucid/commands'),
() => import('adonisjs-scheduler/commands'),
() => import("@adonisjs/core/commands"),
() => import("@adonisjs/lucid/commands"),
() => import("adonisjs-scheduler/commands"),
],

/*
Expand All @@ -26,20 +26,20 @@ export default defineConfig({
|
*/
providers: [
() => import('@adonisjs/core/providers/app_provider'),
() => import('@adonisjs/core/providers/hash_provider'),
() => import("@adonisjs/core/providers/app_provider"),
() => import("@adonisjs/core/providers/hash_provider"),
{
file: () => import('@adonisjs/core/providers/repl_provider'),
environment: ['repl', 'test'],
file: () => import("@adonisjs/core/providers/repl_provider"),
environment: ["repl", "test"],
},
() => import('@adonisjs/core/providers/vinejs_provider'),
() => import('@adonisjs/cors/cors_provider'),
() => import('@adonisjs/lucid/database_provider'),
() => import('@adonisjs/session/session_provider'),
() => import('@adonisjs/auth/auth_provider'),
() => import("@adonisjs/core/providers/vinejs_provider"),
() => import("@adonisjs/cors/cors_provider"),
() => import("@adonisjs/lucid/database_provider"),
() => import("@adonisjs/session/session_provider"),
() => import("@adonisjs/auth/auth_provider"),
{
file: () => import('adonisjs-scheduler/scheduler_provider'),
environment: ['console'],
file: () => import("adonisjs-scheduler/scheduler_provider"),
environment: ["console"],
},
],

Expand All @@ -51,7 +51,7 @@ export default defineConfig({
| List of modules to import before starting the application.
|
*/
preloads: [() => import('#start/routes'), () => import('#start/kernel')],
preloads: [() => import("#start/routes"), () => import("#start/kernel")],

/*
|--------------------------------------------------------------------------
Expand All @@ -65,16 +65,16 @@ export default defineConfig({
tests: {
suites: [
{
files: ['tests/unit/**/*.spec(.ts|.js)'],
name: 'unit',
files: ["tests/unit/**/*.spec(.ts|.js)"],
name: "unit",
timeout: 2000,
},
{
files: ['tests/functional/**/*.spec(.ts|.js)'],
name: 'functional',
files: ["tests/functional/**/*.spec(.ts|.js)"],
name: "functional",
timeout: 30000,
},
],
forceExit: false,
},
})
});
54 changes: 37 additions & 17 deletions backend/app/controllers/auth_controller.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,65 @@
import { HttpContext } from '@adonisjs/core/http'
import { createClient } from '../usos/usos_client.js'
import User from '#models/user'
import assert from "node:assert";

import { HttpContext } from "@adonisjs/core/http";

import User from "#models/user";

import { createClient } from "../usos/usos_client.js";

export default class AuthController {
async store({ request, response, auth }: HttpContext) {
/**
* Step 1: Get credentials from the request body
*/
const { accessToken, accessSecret } = request.only(['accessToken', 'accessSecret'])
const { accessToken, accessSecret } = request.only([
"accessToken",
"accessSecret",
]) as { accessToken: string; accessSecret: string };
try {
const usosClient = createClient({
token: accessToken,
secret: accessSecret,
})
const profile: any = await usosClient.get(
'users/user?fields=id|student_number|first_name|last_name'
)
let user = await User.findBy('usos_id', profile.id)
if (!user) {
});
const profile = await usosClient.get<{
id: string;
student_number: string;
first_name: string;
last_name: string;
}>("users/user?fields=id|student_number|first_name|last_name");
let user = await User.findBy("usos_id", profile.id);
if (user === null) {
user = await User.create({
usos_id: profile.id,
studentNumber: profile.student_number,
firstName: profile.first_name,
lastName: profile.last_name,
})
});
}
await auth.use('jwt').generate(user)

await auth.use("jwt").generate(user);

return response.ok({
...user.serialize(),
})
});
} catch (error) {
return response.unauthorized({ message: 'Login failed.', error: error.message })
assert(error instanceof Error);
return response.unauthorized({
message: "Login failed.",
error: error.message,
});
}
}
async destroy({ response }: HttpContext) {
try {
response.clearCookie('token')
response.clearCookie("token");

return response.ok({ message: 'Successfully logged out' })
return response.ok({ message: "Successfully logged out" });
} catch (error) {
return response.internalServerError({ message: 'Logout failed', error: error.message })
assert(error instanceof Error);
return response.internalServerError({
message: "Logout failed",
error: error.message,
});
}
}
}
69 changes: 42 additions & 27 deletions backend/app/controllers/courses_controller.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,83 @@
import Course from '#models/course'
import { createCourseValidator } from '#validators/course'
import type { HttpContext } from '@adonisjs/core/http'
import assert from "node:assert";

import type { HttpContext } from "@adonisjs/core/http";

import Course from "#models/course";
import { createCourseValidator } from "#validators/course";

export default class CoursesController {
/**
* Display a list of courses in matching registration
*/
async index({ params }: HttpContext) {
const registrationId = decodeURIComponent(params.registration_id)
assert(typeof params.registration_id === "string");

const registrationId = decodeURIComponent(params.registration_id);
if (registrationId) {
return await Course.query().where('registrationId', registrationId).preload('groups')
return await Course.query()
.where("registrationId", registrationId)
.preload("groups");
}
return []
return [];
}

/**
* Handle form submission for the create action
*/
async store({ request, params }: HttpContext) {
const registrationId = decodeURIComponent(params.registration_id)
const payload = await request.validateUsing(createCourseValidator)
const course = await Course.create({ ...payload, registrationId })
return { message: 'Course created.', course }
assert(typeof params.registration_id === "string");

const registrationId = decodeURIComponent(params.registration_id);
const payload = await request.validateUsing(createCourseValidator);
const course = await Course.create({ ...payload, registrationId });
return { message: "Course created.", course };
}

/**
* Show individual record of course in matching registration
*/
async show({ params }: HttpContext) {
const registrationId = decodeURIComponent(params.registration_id)
assert(typeof params.registration_id === "string");
const registrationId = decodeURIComponent(params.registration_id);
if (registrationId) {
assert(typeof params.id === "string");

return await Course.query()
.where('registrationId', registrationId)
.andWhere('id', params.id)
.preload('groups')
.firstOrFail()
.where("registrationId", registrationId)
.andWhere("id", params.id)
.preload("groups")
.firstOrFail();
}
return {}
return {};
}

/**
* Handle form submission for the edit action
*/
async update({ params, request }: HttpContext) {
const registrationId = decodeURIComponent(params.registration_id)
const payload = await request.validateUsing(createCourseValidator)
assert(typeof params.registration_id === "string");
assert(typeof params.id === "string");

const registrationId = decodeURIComponent(params.registration_id);
const payload = await request.validateUsing(createCourseValidator);

const course = await Course.query()
.where('registrationId', registrationId)
.andWhere('id', params.id)
.firstOrFail()
.where("registrationId", registrationId)
.andWhere("id", params.id)
.firstOrFail();

course.merge(payload)
await course.save()
course.merge(payload);
await course.save();

return { message: 'Course updated successfully.', course }
return { message: "Course updated successfully.", course };
}

/**
* Delete record
*/
async destroy({ params }: HttpContext) {
const course = await Course.findOrFail(params.id)
await course.delete()
return { message: 'Course successfully deleted.' }
const course = await Course.findOrFail(params.id);
await course.delete();
return { message: "Course successfully deleted." };
}
}
Loading
Loading