2 Commits

Author SHA1 Message Date
Mathis HERRIOT
02796e4e1f chore: bump version to 1.2.1
All checks were successful
CI/CD Pipeline / Valider backend (push) Successful in 1m35s
CI/CD Pipeline / Valider documentation (push) Successful in 2m7s
CI/CD Pipeline / Valider frontend (push) Successful in 2m4s
CI/CD Pipeline / Déploiement en Production (push) Successful in 17s
2026-01-21 11:38:47 +01:00
Mathis HERRIOT
951b38db67 chore: update lint scripts and improve formatting consistency
- Added `lint:fix` scripts for backend, frontend, and documentation.
- Enabled `biome check --write` for unsafe fixes in backend scripts.
- Fixed imports, formatting, and logging for improved code clarity.
- Adjusted service unit tests for better readability and maintainability.
2026-01-21 11:38:25 +01:00
10 changed files with 65 additions and 25 deletions

View File

@@ -24,7 +24,8 @@
"rules": {
"recommended": true,
"suspicious": {
"noUnknownAtRules": "off"
"noUnknownAtRules": "off",
"noExplicitAny": "off"
},
"style": {
"useImportType": "off"

View File

@@ -1,6 +1,6 @@
{
"name": "@memegoat/backend",
"version": "1.2.0",
"version": "1.2.1",
"description": "",
"author": "",
"private": true,
@@ -13,7 +13,7 @@
"scripts": {
"build": "nest build",
"lint": "biome check",
"lint:write": "biome check --write",
"lint:write": "biome check --write --unsafe",
"format": "biome format --write",
"start": "nest start",
"start:dev": "nest start --watch",

View File

@@ -24,6 +24,7 @@ import { ConfigService } from "@nestjs/config";
import { Test, TestingModule } from "@nestjs/testing";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { BootstrapService } from "./bootstrap.service";
jest.mock("iron-session", () => ({
getIronSession: jest.fn().mockResolvedValue({
@@ -44,6 +45,10 @@ describe("AuthController", () => {
refresh: jest.fn(),
};
const mockBootstrapService = {
consumeToken: jest.fn(),
};
const mockConfigService = {
get: jest
.fn()
@@ -55,6 +60,7 @@ describe("AuthController", () => {
controllers: [AuthController],
providers: [
{ provide: AuthService, useValue: mockAuthService },
{ provide: BootstrapService, useValue: mockBootstrapService },
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile();
@@ -75,7 +81,6 @@ describe("AuthController", () => {
password: "password",
username: "test",
};
// biome-ignore lint/suspicious/noExplicitAny: Necessary to avoid defining full DTO in test
await controller.register(dto as any);
expect(authService.register).toHaveBeenCalledWith(dto);
});

View File

@@ -1,4 +1,13 @@
import { Body, Controller, Get, Headers, Post, Query, Req, Res } from "@nestjs/common";
import {
Body,
Controller,
Get,
Headers,
Post,
Query,
Req,
Res,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Throttle } from "@nestjs/throttler";
import type { Request, Response } from "express";

View File

@@ -1,14 +1,14 @@
import { Test, TestingModule } from "@nestjs/testing";
import { ConfigService } from "@nestjs/config";
import { UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Test, TestingModule } from "@nestjs/testing";
import { UsersService } from "../users/users.service";
import { BootstrapService } from "./bootstrap.service";
import { RbacService } from "./rbac.service";
import { UsersService } from "../users/users.service";
describe("BootstrapService", () => {
let service: BootstrapService;
let rbacService: RbacService;
let usersService: UsersService;
let _usersService: UsersService;
const mockRbacService = {
countAdmins: jest.fn(),
@@ -36,7 +36,7 @@ describe("BootstrapService", () => {
service = module.get<BootstrapService>(BootstrapService);
rbacService = module.get<RbacService>(RbacService);
usersService = module.get<UsersService>(UsersService);
_usersService = module.get<UsersService>(UsersService);
});
it("should be defined", () => {
@@ -46,7 +46,10 @@ describe("BootstrapService", () => {
describe("onApplicationBootstrap", () => {
it("should generate a token if no admin exists", async () => {
mockRbacService.countAdmins.mockResolvedValue(0);
const generateTokenSpy = jest.spyOn(service as any, "generateBootstrapToken");
const generateTokenSpy = jest.spyOn(
service as any,
"generateBootstrapToken",
);
await service.onApplicationBootstrap();
@@ -56,7 +59,10 @@ describe("BootstrapService", () => {
it("should not generate a token if admin exists", async () => {
mockRbacService.countAdmins.mockResolvedValue(1);
const generateTokenSpy = jest.spyOn(service as any, "generateBootstrapToken");
const generateTokenSpy = jest.spyOn(
service as any,
"generateBootstrapToken",
);
await service.onApplicationBootstrap();
@@ -70,9 +76,9 @@ describe("BootstrapService", () => {
mockRbacService.countAdmins.mockResolvedValue(0);
await service.onApplicationBootstrap();
await expect(
service.consumeToken("wrong-token", "user1"),
).rejects.toThrow(UnauthorizedException);
await expect(service.consumeToken("wrong-token", "user1")).rejects.toThrow(
UnauthorizedException,
);
});
it("should throw UnauthorizedException if user not found", async () => {
@@ -97,7 +103,10 @@ describe("BootstrapService", () => {
const result = await service.consumeToken(token, "user1");
expect(rbacService.assignRoleToUser).toHaveBeenCalledWith("user-uuid", "admin");
expect(rbacService.assignRoleToUser).toHaveBeenCalledWith(
"user-uuid",
"admin",
);
expect((service as any).bootstrapToken).toBeNull();
expect(result.message).toContain("user1 is now an administrator");
});

View File

@@ -1,3 +1,4 @@
import * as crypto from "node:crypto";
import {
Injectable,
Logger,
@@ -5,7 +6,6 @@ import {
UnauthorizedException,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as crypto from "node:crypto";
import { UsersService } from "../users/users.service";
import { RbacService } from "./rbac.service";
@@ -34,9 +34,15 @@ export class BootstrapService implements OnApplicationBootstrap {
const url = `${protocol}://${domain}/auth/bootstrap-admin`;
this.logger.warn("SECURITY ALERT: No administrator found in database.");
this.logger.warn("To create the first administrator, use the following endpoint:");
this.logger.warn(`Endpoint: GET ${url}?token=${this.bootstrapToken}&username=votre_nom_utilisateur`);
this.logger.warn("Exemple: curl -X GET \"http://localhost/auth/bootstrap-admin?token=...&username=...\"");
this.logger.warn(
"To create the first administrator, use the following endpoint:",
);
this.logger.warn(
`Endpoint: GET ${url}?token=${this.bootstrapToken}&username=votre_nom_utilisateur`,
);
this.logger.warn(
'Exemple: curl -X GET "http://localhost/auth/bootstrap-admin?token=...&username=..."',
);
this.logger.warn("This token is one-time use only.");
}
@@ -53,7 +59,9 @@ export class BootstrapService implements OnApplicationBootstrap {
await this.rbacService.assignRoleToUser(user.uuid, "admin");
this.bootstrapToken = null; // One-time use
this.logger.log(`User ${username} has been promoted to administrator via bootstrap token.`);
this.logger.log(
`User ${username} has been promoted to administrator via bootstrap token.`,
);
return { message: `User ${username} is now an administrator` };
}
}

View File

@@ -18,7 +18,11 @@ export class RbacService implements OnApplicationBootstrap {
if (count === 0) {
this.logger.log("No roles found, seeding default roles...");
const defaultRoles = [
{ name: "Administrator", slug: "admin", description: "Full system access" },
{
name: "Administrator",
slug: "admin",
description: "Full system access",
},
{
name: "Moderator",
slug: "moderator",

View File

@@ -8,7 +8,6 @@ jest.mock("minio");
describe("S3Service", () => {
let service: S3Service;
let configService: ConfigService;
// biome-ignore lint/suspicious/noExplicitAny: Fine for testing purposes
let minioClient: any;
beforeEach(async () => {

View File

@@ -1,12 +1,13 @@
{
"name": "@memegoat/frontend",
"version": "1.2.0",
"version": "1.2.1",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check",
"lint:write": "biome check --write",
"format": "biome format --write"
},
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@memegoat/source",
"version": "1.2.0",
"version": "1.2.1",
"description": "",
"scripts": {
"version:get": "cmake -P version.cmake GET",
@@ -13,9 +13,13 @@
"build:back": "pnpm run -F @memegoat/backend build",
"build:docs": "pnpm run -F @memegoat/documentation build",
"lint": "pnpm run lint:back && pnpm run lint:front && pnpm run lint:docs",
"lint:fix": "pnpm run lint:back:fix && pnpm run lint:front:fix && pnpm run lint:docs:fix",
"lint:back": "pnpm run -F @memegoat/backend lint",
"lint:back:fix": "pnpm run -F @memegoat/backend lint:write",
"lint:front": "pnpm run -F @memegoat/frontend lint",
"lint:front:fix": "pnpm run -F @memegoat/frontend lint:write",
"lint:docs": "pnpm run -F @memegoat/documentation lint",
"lint:docs:fix": "pnpm run -F @memegoat/documentation lint:write",
"test": "pnpm run test:back && pnpm run test:front",
"test:back": "pnpm run -F @memegoat/backend test",
"test:front": "pnpm run -F @memegoat/frontend test",