Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81c92f51d8 | |||
| 7d8c6bf21c | |||
| c769c81207 | |||
| b821ab7a6e | |||
| d3e91bfcb1 | |||
| 8f51c75f0a | |||
| 163738867b | |||
| 84fee6afcb | |||
| 0cc515971a | |||
| e3fae2f8bb | |||
| 2f9e623af6 | |||
| 1af0e0d7de | |||
| 2dc553ee1c | |||
| 21af80181f | |||
|
3fecd548a4
|
|||
| 6d5b0581a5 | |||
| d7cd3ccd99 | |||
| ff0ae73fa7 | |||
| b245f1984e | |||
| 6545f46ba6 | |||
| 8215fda5ff |
@@ -22,8 +22,8 @@ export async function app(fastify: FastifyInstance, opts: AppOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
// Log unauthorized access attempts
|
||||
if (error.statusCode === 401 || error.statusCode === 403) {
|
||||
// Log unauthorized access attempts (exclude /api/auth/me as 401s there are expected during token refresh)
|
||||
if ((error.statusCode === 401 || error.statusCode === 403) && request.url !== '/api/auth/me') {
|
||||
await AuditService.log({
|
||||
action: AuditAction.unauthorizedAccess,
|
||||
category: AuditCategory.security,
|
||||
|
||||
@@ -14,11 +14,11 @@ const helmetPlugin: FastifyPluginAsync = async (app) => {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
// Angular uses inline styles for component encapsulation, so we need to allow them
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
||||
imgSrc: ["'self'", "data:", "https:"],
|
||||
scriptSrc: ["'self'"],
|
||||
connectSrc: ["'self'", process.env.FRONTEND_URL ?? "http://localhost:4200"],
|
||||
fontSrc: ["'self'", "data:"],
|
||||
fontSrc: ["'self'", "data:", "https://fonts.gstatic.com"],
|
||||
objectSrc: ["'none'"],
|
||||
baseUri: ["'self'"],
|
||||
formAction: ["'self'"],
|
||||
|
||||
@@ -36,10 +36,6 @@ export class BookService {
|
||||
if (!validateStringLength(data.notes, MAX_LENGTHS.NOTES)) {
|
||||
throw new Error(`Notes must be ${MAX_LENGTHS.NOTES} characters or less.`);
|
||||
}
|
||||
if (!validateStringLength(data.coverImage, MAX_LENGTHS.URL)) {
|
||||
throw new Error(`Cover image URL must be ${MAX_LENGTHS.URL} characters or less.`);
|
||||
}
|
||||
|
||||
// Validate rating
|
||||
if (!validateRating(data.rating)) {
|
||||
throw new Error("Rating must be an integer between 0 and 10.");
|
||||
@@ -47,7 +43,11 @@ export class BookService {
|
||||
|
||||
if (data.coverImage) {
|
||||
if (data.coverImage.startsWith("data:")) {
|
||||
const sizeInBytes = data.coverImage.length * 0.75;
|
||||
const base64Data = data.coverImage.split(",")[1];
|
||||
if (!base64Data) {
|
||||
throw new Error("Invalid image data URL format.");
|
||||
}
|
||||
const sizeInBytes = base64Data.length * 0.75;
|
||||
if (sizeInBytes > MAX_LENGTHS.DATA_URL) {
|
||||
throw new Error("Cover image must be under 5MB.");
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ export class MangaService {
|
||||
if (!validateStringLength(data.notes, MAX_LENGTHS.NOTES)) {
|
||||
throw new Error(`Notes must be ${MAX_LENGTHS.NOTES} characters or less.`);
|
||||
}
|
||||
if (!validateStringLength(data.coverImage, MAX_LENGTHS.URL)) {
|
||||
throw new Error(`Cover image URL must be ${MAX_LENGTHS.URL} characters or less.`);
|
||||
}
|
||||
|
||||
// Validate rating
|
||||
if (!validateRating(data.rating)) {
|
||||
throw new Error("Rating must be an integer between 0 and 10.");
|
||||
@@ -45,7 +41,11 @@ export class MangaService {
|
||||
// Validate cover image URL
|
||||
if (data.coverImage) {
|
||||
if (data.coverImage.startsWith("data:")) {
|
||||
const sizeInBytes = data.coverImage.length * 0.75;
|
||||
const base64Data = data.coverImage.split(",")[1];
|
||||
if (!base64Data) {
|
||||
throw new Error("Invalid image data URL format.");
|
||||
}
|
||||
const sizeInBytes = base64Data.length * 0.75;
|
||||
if (sizeInBytes > MAX_LENGTHS.DATA_URL) {
|
||||
throw new Error("Cover image must be under 5MB.");
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ export class MusicService {
|
||||
if (!validateStringLength(data.notes, MAX_LENGTHS.NOTES)) {
|
||||
throw new Error(`Notes must be ${MAX_LENGTHS.NOTES} characters or less.`);
|
||||
}
|
||||
if (!validateStringLength(data.coverArt, MAX_LENGTHS.URL)) {
|
||||
throw new Error(`Cover art URL must be ${MAX_LENGTHS.URL} characters or less.`);
|
||||
}
|
||||
|
||||
// Validate rating
|
||||
if (data.rating !== undefined && !validateRating(data.rating)) {
|
||||
throw new Error("Rating must be an integer between 0 and 10.");
|
||||
@@ -45,7 +41,11 @@ export class MusicService {
|
||||
// Validate cover art URL
|
||||
if (data.coverArt) {
|
||||
if (data.coverArt.startsWith("data:")) {
|
||||
const sizeInBytes = data.coverArt.length * 0.75;
|
||||
const base64Data = data.coverArt.split(",")[1];
|
||||
if (!base64Data) {
|
||||
throw new Error("Invalid image data URL format.");
|
||||
}
|
||||
const sizeInBytes = base64Data.length * 0.75;
|
||||
if (sizeInBytes > MAX_LENGTHS.DATA_URL) {
|
||||
throw new Error("Cover image must be under 5MB.");
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ export class ShowService {
|
||||
if (!validateStringLength(data.notes, MAX_LENGTHS.NOTES)) {
|
||||
throw new Error(`Notes must be ${MAX_LENGTHS.NOTES} characters or less.`);
|
||||
}
|
||||
if (!validateStringLength(data.coverImage, MAX_LENGTHS.URL)) {
|
||||
throw new Error(`Cover image URL must be ${MAX_LENGTHS.URL} characters or less.`);
|
||||
}
|
||||
|
||||
// Validate rating
|
||||
if (!validateRating(data.rating)) {
|
||||
throw new Error("Rating must be an integer between 0 and 10.");
|
||||
@@ -42,7 +38,11 @@ export class ShowService {
|
||||
// Validate cover image URL
|
||||
if (data.coverImage) {
|
||||
if (data.coverImage.startsWith("data:")) {
|
||||
const sizeInBytes = data.coverImage.length * 0.75;
|
||||
const base64Data = data.coverImage.split(",")[1];
|
||||
if (!base64Data) {
|
||||
throw new Error("Invalid image data URL format.");
|
||||
}
|
||||
const sizeInBytes = base64Data.length * 0.75;
|
||||
if (sizeInBytes > MAX_LENGTHS.DATA_URL) {
|
||||
throw new Error("Cover image must be under 5MB.");
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": {
|
||||
"styles": {
|
||||
"inlineCritical": false
|
||||
}
|
||||
},
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
|
||||
|
After Width: | Height: | Size: 8.3 MiB |
|
After Width: | Height: | Size: 8.5 MiB |
|
After Width: | Height: | Size: 8.3 MiB |
|
After Width: | Height: | Size: 9.1 MiB |
|
After Width: | Height: | Size: 8.1 MiB |
|
After Width: | Height: | Size: 8.2 MiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 233 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 392 KiB After Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 381 KiB |
|
After Width: | Height: | Size: 8.6 MiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -23,6 +23,10 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
||||
imports: [CommonModule, FormsModule, RouterModule, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/art-avatar.jpg" alt="Art avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>Art Gallery</h2>
|
||||
<p class="subtitle">Artwork of Naomi</p>
|
||||
@@ -458,6 +462,26 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #fdcb6e;
|
||||
box-shadow: 0 4px 12px rgba(253, 203, 110, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(253, 203, 110, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -23,6 +23,10 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
||||
imports: [CommonModule, FormsModule, RouterLink, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/books-avatar.jpg" alt="Books avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>My Book Collection</h2>
|
||||
@if (authService.isAdmin()) {
|
||||
@@ -701,6 +705,26 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #8b6f47;
|
||||
box-shadow: 0 4px 12px rgba(139, 111, 71, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(139, 111, 71, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -23,6 +23,10 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
||||
imports: [CommonModule, FormsModule, RouterModule, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/games-avatar.jpg" alt="Gaming avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>My Game Collection</h2>
|
||||
@if (authService.isAdmin()) {
|
||||
@@ -684,6 +688,26 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #ff6b6b;
|
||||
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(255, 107, 107, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ApiService } from '../../services/api.service';
|
||||
<header class="header">
|
||||
<nav class="navbar" aria-label="Main navigation">
|
||||
<div class="nav-brand">
|
||||
<img src="/assets/icons/icon-72x72.png" alt="" class="brand-icon" role="presentation" />
|
||||
<img src="/assets/nav-icon.jpg" alt="" class="brand-icon" role="presentation" />
|
||||
<h1><a routerLink="/">Naomi's Library</a></h1>
|
||||
@if (version()) {
|
||||
<span class="version" aria-label="Version {{ version() }}">v{{ version() }}</span>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType, Manga, MangaStatu
|
||||
<div class="container">
|
||||
<div class="hero">
|
||||
<h1>Welcome to Naomi's Library</h1>
|
||||
<img src="/assets/nav-icon.jpg" alt="Naomi's avatar" class="hero-avatar" />
|
||||
<p class="tagline">A personal collection of games, books, music, manga, shows, and art</p>
|
||||
</div>
|
||||
|
||||
@@ -190,10 +191,28 @@ import { Game, GameStatus, Book, BookStatus, Music, MusicType, Manga, MangaStatu
|
||||
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--witch-purple);
|
||||
}
|
||||
|
||||
.hero-avatar {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 4px solid var(--witch-lavender);
|
||||
box-shadow: 0 4px 12px var(--witch-shadow);
|
||||
margin: 1rem auto;
|
||||
display: block;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.hero-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(157, 78, 221, 0.5);
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 1.2rem;
|
||||
color: var(--witch-plum);
|
||||
|
||||
@@ -23,6 +23,10 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
||||
imports: [CommonModule, FormsModule, RouterLink, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/manga-avatar.jpg" alt="Manga avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>My Manga Collection</h2>
|
||||
@if (authService.isAdmin()) {
|
||||
@@ -619,6 +623,26 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #00b894;
|
||||
box-shadow: 0 4px 12px rgba(0, 184, 148, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(0, 184, 148, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -23,6 +23,10 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
||||
imports: [CommonModule, FormsModule, RouterLink, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/music-avatar.jpg" alt="Music avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>My Music Collection</h2>
|
||||
@if (authService.isAdmin()) {
|
||||
@@ -689,6 +693,26 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #74b9ff;
|
||||
box-shadow: 0 4px 12px rgba(116, 185, 255, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(116, 185, 255, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -23,6 +23,10 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
||||
imports: [CommonModule, RouterLink, FormsModule, PaginationComponent, LikeButtonComponent],
|
||||
template: `
|
||||
<div class="container">
|
||||
<div class="page-hero">
|
||||
<img src="/assets/avatars/shows-avatar.jpg" alt="Shows avatar" class="page-avatar" />
|
||||
</div>
|
||||
|
||||
<div class="header-section">
|
||||
<h2>My Shows & Films</h2>
|
||||
@if (authService.isAdmin()) {
|
||||
@@ -615,6 +619,26 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.page-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.page-avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #e84393;
|
||||
box-shadow: 0 4px 12px rgba(232, 67, 147, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.page-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 16px rgba(232, 67, 147, 0.5);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -12,6 +12,11 @@ export class GlobalErrorHandler implements ErrorHandler {
|
||||
private toast = inject(ToastService);
|
||||
|
||||
handleError(error: Error): void {
|
||||
if (error.name === 'ChunkLoadError' || error.message.includes('Loading chunk')) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Global error caught:', error);
|
||||
|
||||
// Show user-friendly error message
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<meta name="description" content="Naomi's curated collection of games, books, music, shows, manga, and art. Browse, engage, and suggest new additions!" />
|
||||
<meta name="theme-color" content="#9d4edd" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Creepster&family=Griffy&family=Henny+Penny&family=Kalam:wght@300;400;700&display=swap" rel="stylesheet" />
|
||||
<script defer src="https://analytics.nhcarrigan.com/js/pa-YUXAn1vhhRttySUAw_LMN.js"></script>
|
||||
<script>
|
||||
window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
font-family: 'Kalam', cursive;
|
||||
font-size: 14pt;
|
||||
line-height: 1.6;
|
||||
color: var(--foreground);
|
||||
@@ -75,10 +75,30 @@ body::after {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
0%, 100% { transform: rotate(-2deg); }
|
||||
50% { transform: rotate(2deg); }
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
line-height: 1.2;
|
||||
color: var(--witch-purple);
|
||||
font-family: 'Griffy', cursive;
|
||||
}
|
||||
|
||||
h1 {
|
||||
animation: wiggle 4s ease-in-out infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.witchy-accent,
|
||||
.spooky-title {
|
||||
font-family: 'Creepster', cursive;
|
||||
}
|
||||
|
||||
.mystical-text {
|
||||
font-family: 'Henny Penny', cursive;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -121,6 +141,7 @@ select:focus {
|
||||
|
||||
// Button base styles
|
||||
button {
|
||||
font-family: inherit;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@library/source",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "nx run-many --target=build --all && NODE_ENV=production op run --env-file=dev.env -- node dist/api/main.js",
|
||||
@@ -31,7 +31,7 @@
|
||||
"@fastify/csrf-protection": "7.1.0",
|
||||
"@fastify/helmet": "13.0.2",
|
||||
"@fastify/jwt": "10.0.0",
|
||||
"@fastify/oauth2": "8.1.2",
|
||||
"@fastify/oauth2": "8.2.0",
|
||||
"@fastify/rate-limit": "10.3.0",
|
||||
"@fastify/sensible": "6.0.4",
|
||||
"@fastify/static": "9.0.0",
|
||||
@@ -44,8 +44,8 @@
|
||||
"dompurify": "3.3.1",
|
||||
"fastify": "5.7.3",
|
||||
"fastify-plugin": "5.0.1",
|
||||
"jsdom": "28.0.0",
|
||||
"marked": "17.0.1",
|
||||
"jsdom": "28.1.0",
|
||||
"marked": "17.0.3",
|
||||
"rxjs": "7.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -76,7 +76,7 @@
|
||||
"@types/jsdom": "27.0.0",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/node": "20.19.9",
|
||||
"@typescript-eslint/utils": "8.54.0",
|
||||
"@typescript-eslint/utils": "8.56.1",
|
||||
"angular-eslint": "21.1.0",
|
||||
"cypress": "15.9.0",
|
||||
"esbuild": "0.19.12",
|
||||
@@ -91,6 +91,6 @@
|
||||
"ts-node": "10.9.1",
|
||||
"tslib": "2.8.1",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.54.0"
|
||||
"typescript-eslint": "8.56.0"
|
||||
}
|
||||
}
|
||||
|
||||