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
13 changes: 12 additions & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ services:
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
ports:
- 3000:3000
- 3001:3000

db:
hostname: db
Expand Down Expand Up @@ -185,5 +185,16 @@ services:
ports:
- "1080:1080"

nginx:
image: nginx:1.25-alpine
hostname: nginx
depends_on:
- core
ports:
- "3000:3000" # expose nginx on localhost:3000
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
restart: unless-stopped

volumes:
postgres-data:
67 changes: 67 additions & 0 deletions .devcontainer/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# nginx.conf
# This Nginx config listens on port 3000 and appends /api to the incoming request path,
# then forwards it to the 'core' container on port 3000.
# It also preserves important headers and supports WebSocket upgrades.

events {
worker_connections 1024;
}

http {
# Basic proxy settings
proxy_redirect off;
proxy_buffering off;
client_max_body_size 50m;

# Timeouts
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

# Forwarded headers
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream core_upstream {
server core:3000; # Docker DNS resolves the 'core' service name
keepalive 32;
}

server {
listen 3000;
server_name _;

# Health check endpoint
location = /health {
return 200 "ok (nginx)\n";
add_header Content-Type text/plain;
}

# Catch-all: append /api to request path and proxy to core
# Example: GET /users -> proxied as /api/users
location / {
# Rewrite the path by inserting /api right after the leading slash
# $request_uri includes path + args; we'll rewrite just the path and preserve query string
set $new_path /api$uri;

# Proxy to core using the rewritten path; args preserved
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_http_version 1.1;

# Use proxy_pass with a URI to keep our rewritten $new_path
# We construct the full URI using the variable and keep query string via $is_args$args
proxy_pass http://core_upstream$new_path$is_args$args;
}
}
}
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ test
tmp
**/test
**/*.test.ts
**/*.spec.ts
types
docker
API Collections
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ env:
GEOLOCATION_API_HOST: http://localhost:12345

jobs:
build-node:
build-unit:
runs-on: ubuntu-latest

# Service containers to run with `container-job`
Expand Down Expand Up @@ -127,8 +127,9 @@ jobs:
run: yarn vitest

test-node:
needs: build-node
strategy:
# NOTE: switch this on if you want to have the result of all the shards to fix more tests at once.
# fail-fast: false
matrix:
shard: [1/4, 2/4, 3/4, 4/4]
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"@fastify/type-provider-typebox": "5.1.0",
"@fastify/websocket": "11.0.2",
"@graasp/etherpad-api": "2.1.1",
"@graasp/sdk": "5.16.4",
"@graasp/sdk": "5.16.5",
"@rapideditor/country-coder": "5.4.0",
"@sentry/node": "7.119.2",
"@sentry/tracing": "7.120.3",
Expand Down
3 changes: 3 additions & 0 deletions setup.node.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
import { config } from 'dotenv';
import 'reflect-metadata';

config({ path: '.env.test', override: true });
46 changes: 23 additions & 23 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,30 @@ export default async function (instance: FastifyInstance): Promise<void> {

// register some dependencies manually
registerDependencies(instance.log);

await instance.register(fp(metaPlugin));
await instance.register(metaPlugin);

await instance.register(fp(passportPlugin));
// need to be defined before member and item for auth check

await instance.register(maintenancePlugin);

await instance.register(fp(authPlugin));

await instance.register(async (instance) => {
// core API modules
await instance
// the websockets plugin must be registered before but in the same scope as the apis
// otherwise tests somehow bypass mocking the authentication through jest.spyOn(app, 'verifyAuthentication')
.register(fp(websocketsPlugin), {
prefix: '/ws',
redis: {
channelName: 'graasp-realtime-updates',
connection: REDIS_CONNECTION,
},
})
.register(fp(MemberServiceApi))
.register(fp(ItemServiceApi))
.register(tagPlugin);
});
await instance.register(
async (instance) => {
// need to be defined before member and item for auth check
await instance.register(fp(authPlugin));
// core API modules
await instance
// the websockets plugin must be registered before but in the same scope as the apis
// otherwise tests somehow bypass mocking the authentication through jest.spyOn(app, 'verifyAuthentication')
.register(fp(websocketsPlugin), {
prefix: '/ws',
redis: {
channelName: 'graasp-realtime-updates',
connection: REDIS_CONNECTION,
},
})
.register(fp(MemberServiceApi))
.register(fp(ItemServiceApi))
.register(tagPlugin)
.register(maintenancePlugin);
},
{ prefix: '/api' },
);
}
2 changes: 1 addition & 1 deletion src/langs/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"GREETINGS": "Merci de vous être inscrit-e sur notre plateforme.",
"SIGN_UP_VERIFY_EMAIL": "Vérifiez votre e-mail en cliquant sur le bouton ci-dessous.",
"SIGN_UP_TEXT": "Complétez l'inscription et connectez-vous.",
"IGNORE_EMAIL_IF_NOT_REQUESTED": "Si vous n'êtes pas l'auteur-e de cette action', vous pouvez ignorer cet e-mail.",
"IGNORE_EMAIL_IF_NOT_REQUESTED": "Si vous n'êtes pas l'auteur-e de cette action, vous pouvez ignorer cet e-mail.",
"SIGN_IN_BUTTON_TEXT": "Se connecter",
"SIGN_UP_BUTTON_TEXT": "S'inscrire",
"SIGN_IN_TEXT": "Vous nous avez demandé un lien magique pour vous connecter rapidement. Le voici :",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { faker } from '@faker-js/faker';
import { describe, expect, it } from 'vitest';

import { TRANSLATIONS } from '../../langs/constants';
import enTranslations from '../../langs/en.json';
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
return 'OK' as const;
});

fastify.get('/status', async (_, reply) => {
fastify.get('/api/status', async (_, reply) => {
const searchService = resolveDependency(SearchService);
const api = new HealthyStatus().format();
const database = (await getDBStatusCheck(fastify.log)).format();
Expand All @@ -110,7 +110,7 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
};
});

fastify.get('/version', async (_, reply) => {
fastify.get('/api/version', async (_, reply) => {
// allow request cross origin
reply.header('Access-Control-Allow-Origin', '*');
return `${APP_VERSION} @ ${BUILD_TIMESTAMP}`;
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default async function (instance: FastifyInstance): Promise<void> {
});

await instance.register(swaggerUiPlugin, {
routePrefix: '/docs',
routePrefix: '/api/docs',
transformSpecification: (swaggerObject, _request, _reply) => {
return swaggerObject;
},
Expand Down
2 changes: 0 additions & 2 deletions src/repositories/const.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/schemas/regex.test.ts → src/schemas/regex.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { EMPTY_OR_SPACED_WORDS_REGEX, ITEM_NAME_REGEX } from './regex';

describe('Globals', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { v4 as uuidV4 } from 'uuid';
import { describe, expect, it } from 'vitest';

import { seedFromJson } from '../../../test/mocks/seed';
import { db } from '../../drizzle/db';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { and, eq } from 'drizzle-orm';
import { describe, expect, it } from 'vitest';

import { Context } from '@graasp/sdk';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { expect, it } from 'vitest';

// TODO
// import type { FastifyInstance, FastifyRequest } from 'fastify';

Expand Down
1 change: 1 addition & 0 deletions src/services/action/test/fixtures/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { eq } from 'drizzle-orm';
import { expect } from 'vitest';

import { type DBConnection } from '../../../../drizzle/db';
import { actionsTable } from '../../../../drizzle/schema';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';

import { getGeolocationIp } from './actions';

describe('Action Utils', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/services/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class AuthService {

const redirectionUrl = getRedirectionLink(this.log, url);
const domain = PUBLIC_URL;
const destination = new URL('/auth', domain);
const destination = new URL('/api/auth', domain);
destination.searchParams.set(SHORT_TOKEN_PARAM, token);
destination.searchParams.set('url', redirectionUrl);
const link = destination.toString();
Expand Down Expand Up @@ -73,7 +73,7 @@ export class AuthService {

const redirectionUrl = getRedirectionLink(this.log, url);
const domain = PUBLIC_URL;
const destination = new URL('/auth', domain);
const destination = new URL('/api/auth', domain);
destination.searchParams.set(SHORT_TOKEN_PARAM, token);
destination.searchParams.set('url', redirectionUrl);
const link = destination.toString();
Expand Down
Loading
Loading