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: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
**/node_modules
/dist
/resources
/resources/exp-data
/resources/reports

18 changes: 18 additions & 0 deletions docs/development/backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Backend

Under `src/backend`, the code is separated a bit by the "pages" of the frontend,
so, end-user visible functionality, and by implementation concerns.

Functionality that may be used by multiple parts of the backend, can be provided
by files directly in `backend`.

The different features typically consist of some server-side TypeScript code
and one or more HTML fragments that use the EJS template engine.

The `backend/db` feature groups the main interactions with the database, contains the
initial structure of the DB in `db.sql`, as well as SQL scripts that perform
schema updates. These scripts are run manually, and there's currently no
migration infrastructure.

The `src/index.ts` file defines the routes to make the different features
accessible to the user.
51 changes: 51 additions & 0 deletions docs/development/frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Frontend

The frontend, i.e., the code that runs in the browser, is implemented with a
somewhat "classic" approach, trying to be in some sense of the word "low tech",
while using mature/boring libraries to make this approach a bit more convenient.

## TypeScript

The frontend is implemented in TypeScript.
During development, the builtin server will serve the JS files directly
from the `/dist` folder, and `npm run nodemon` in combination with
`npm run watch` works as one would expect.

See `/src/index.ts` where `serveStaticResource` is used, which is implemented
under `/src/backend/dev-server/server.ts`.

When testing with Docker, the Dockerfile triggers `npm run compile`,
which ensures all files are in the expected place.

## JQuery

For the interaction with the DOM, we are generally using JQuery.
Thus, instead of using `document.querySelectorAll('#id')` or similar,
we use `$('#id')`.

JQuery allows for fairly concise code.

## Serve-Side Page Generation

For each page, we decide ad hoc, whether the data is queried and rendered
on the server, or whether we add a JSON-based API endpoint, and
render the page in the browser.

I am not sure, that there is a consistently applied design principle right now.
But, when ever we can do it on the server, without obvious drawbacks, it means
we do not need to expose raw data via JSON, and have an endpoint less that
we have to worry about. So, in some ways, that's preferred.

However, some database queries are known to take more time. For these cases,
requesting the data via an API endpoint as JSON is preferred.

## API End Points

The API end points are defined as routes in `/src/index.ts` and recognizable
by the `get*AsJson` function names.

The `get*AsJson` functions wrap the HTTP aspect and the data retrieval is done
by a specific method, for instance `getProfileAsJson(.)` uses `getProfile(.)`.
The types for the data are defined in `/src/shared/view-types.ts`, which is
also used by the backend. Thus, types that are used to communicate between
frontend and backend should be defined only once in `view-types.ts`.
34 changes: 34 additions & 0 deletions docs/development/repo-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Repository Layout

The implementation is in the `/src` folder. Tests are implemented in `/tests`.

## 1. General Structure of Source under /src

- `backend` TypeScript code running on the server
- `frontend` TypeScript code that runs on the client/browser
- `shared` helper functions and type definitions shared by frontend and backend
- `views` contains HTML fragments used by multiple features


## 2. Benchmarks

The `src/benchmarks` folder provides a few integration-level benchmarks that
can be used to assess end-to-end performance.

These are run in our CI setup using ReBench and the `/rebench.conf`
configuration file. Thus, ReBenchDB is benchmarking and tracking itself.

## 3. Vendored

The `src/vendored` folder contains upstream dependencies that had to be
integrated for practical purposes.

## 4. Patches

The `/patches` folder contains in-repo maintained customizations of NPM packages,
where the vendoring did not seem necessary.

## 5. Resources

The `/resources` folder contains the `style.css` file and is used to store
the compilation results and files that need to be accessible by the frontend.
2 changes: 1 addition & 1 deletion src/backend/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { prepareTemplate } from '../templates.js';
import {
respondProjectAndSourceNotFound,
respondProjectNotFound
} from '../common/standard-responses.js';
} from '../standard-responses.js';
import {
completeRequestAndHandlePromise,
startRequest
Expand Down
2 changes: 1 addition & 1 deletion src/backend/timeline/timeline.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ParameterizedContext } from 'koa';
import { respondProjectNotFound } from '../common/standard-responses.js';
import { respondProjectNotFound } from '../standard-responses.js';
import { prepareTemplate } from '../templates.js';
import { TimelineSuite } from '../../shared/api.js';
import { Database } from '../db/db.js';
Expand Down
47 changes: 14 additions & 33 deletions src/frontend/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,31 @@ function getPreferredTheme(): string {
}

function setTheme(theme: string) {
document.documentElement.setAttribute('data-bs-theme', theme);
$('html').attr('data-bs-theme', theme);
}

function showActiveTheme(theme: string) {
const themeSwitcher = document.querySelector('#theme-switcher');

if (!themeSwitcher) {
return;
}

const themeIcons = document.querySelectorAll('.theme-icon');
for (const themeIcon of themeIcons) {
themeIcon.classList.remove('active');
}

const iconToActivate = document.querySelector(`#theme-icon-${theme}`);

if (!iconToActivate) {
const themeSwitcher = $('#theme-switcher');
if (themeSwitcher.length === 0) {
return;
}

iconToActivate.classList.add('active');
$('.theme-icon').removeClass('active');
$(`#theme-icon-${theme}`).addClass('active');
}

function toggleTheme() {
const themeIcons = document.querySelectorAll('.theme-icon');
const themeIcons = $('.theme-icon');

let currentTheme = getPreferredTheme(); // that's the fallback

// let's see what the current user setting is
for (const themeIcon of themeIcons) {
if (themeIcon.classList.contains('active')) {
if (themeIcon.id === 'theme-icon-light') {
currentTheme = 'light';
} else {
currentTheme = 'dark';
}
break;
const activeThemeIcon = themeIcons.filter('.active').first();
if (activeThemeIcon.length > 0) {
if (activeThemeIcon.attr('id') === 'theme-icon-light') {
currentTheme = 'light';
} else {
currentTheme = 'dark';
}
}

Expand All @@ -89,14 +76,8 @@ function toggleTheme() {
showActiveTheme(newTheme);
});

window.addEventListener('DOMContentLoaded', () => {
$(() => {
showActiveTheme(getPreferredTheme());

const themeSwitcher = document.querySelector('#theme-switcher');
if (!themeSwitcher) {
return;
}

themeSwitcher.addEventListener('click', toggleTheme);
$('#theme-switcher').on('click', toggleTheme);
});
})();
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, expect, it } from '@jest/globals';
import {
respondProjectAndSourceNotFound,
respondProjectNotFound
} from '../../../src/backend/common/standard-responses.js';
} from '../../src/backend/standard-responses.js';

describe('respondProjectNotFound', () => {
it('should set status to 404 and respond with text', () => {
Expand Down