fix(i18n): delete local translations

This commit is contained in:
Amruth Pillai
2023-11-10 13:14:44 +01:00
parent d8c605d047
commit 48727be809
65 changed files with 143 additions and 70442 deletions

View File

@ -57,6 +57,10 @@ REDIS_URL=redis://default:password@localhost:6379
# Sentry (for error reporting, Optional) # Sentry (for error reporting, Optional)
# SENTRY_DSN= # SENTRY_DSN=
# Crowdin (Optional)
CROWDIN_DISTRIBUTION_HASH=
CROWDIN_PERSONAL_TOKEN=
# GitHub (OAuth, Optional) # GitHub (OAuth, Optional)
GITHUB_CLIENT_ID= GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET= GITHUB_CLIENT_SECRET=

View File

@ -10,20 +10,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4.1.1
- name: crowdin action - name: Sync Sources with Crowdin
uses: crowdin/github-action@v1 uses: crowdin/github-action@v1
with: with:
upload_sources: true upload_sources: true
create_pull_request: true create_pull_request: false
upload_translations: false upload_translations: false
download_translations: true download_translations: false
localization_branch_name: l10n
pull_request_base_branch_name: "v4"
pull_request_title: "New Translations"
pull_request_body: "There have been new translations added to the project. Please review and merge the changes made on Crowdin to help keep them in sync."
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}

2
.gitignore vendored
View File

@ -48,4 +48,4 @@ libs/prisma
# Lingui Compiled Messages # Lingui Compiled Messages
apps/client/src/locales/_build/ apps/client/src/locales/_build/
apps/client/src/locales/**/*.js apps/client/src/locales

View File

@ -1,6 +1,8 @@
import { i18n } from "@lingui/core"; import { i18n } from "@lingui/core";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { axios } from "./axios";
type Locale = "en-US" | "de-DE" | "zu-ZA"; type Locale = "en-US" | "de-DE" | "zu-ZA";
export const defaultLocale = "en-US"; export const defaultLocale = "en-US";
@ -20,7 +22,8 @@ export const getLocales = () => {
}; };
export async function dynamicActivate(locale: string) { export async function dynamicActivate(locale: string) {
const { messages } = await import(`../locales/${locale}/messages.po`); const response = await axios(`translation/${locale}`);
const messages = await response.data;
i18n.load(locale, messages); i18n.load(locale, messages);
i18n.activate(locale); i18n.activate(locale);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ import { MailModule } from "./mail/mail.module";
import { PrinterModule } from "./printer/printer.module"; import { PrinterModule } from "./printer/printer.module";
import { ResumeModule } from "./resume/resume.module"; import { ResumeModule } from "./resume/resume.module";
import { StorageModule } from "./storage/storage.module"; import { StorageModule } from "./storage/storage.module";
import { TranslationModule } from "./translation/translation.module";
import { UserModule } from "./user/user.module"; import { UserModule } from "./user/user.module";
import { UtilsModule } from "./utils/utils.module"; import { UtilsModule } from "./utils/utils.module";
@ -34,6 +35,7 @@ import { UtilsModule } from "./utils/utils.module";
ResumeModule, ResumeModule,
StorageModule, StorageModule,
PrinterModule, PrinterModule,
TranslationModule,
// Static Assets // Static Assets
ServeStaticModule.forRoot({ ServeStaticModule.forRoot({

View File

@ -1,8 +1,6 @@
import { CacheModule as NestCacheModule } from "@nestjs/cache-manager"; import { Module } from "@nestjs/common";
import { Logger, Module } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { RedisModule } from "@songkeys/nestjs-redis"; import { RedisModule } from "@songkeys/nestjs-redis";
import { redisStore } from "cache-manager-redis-yet";
import { Config } from "../config/schema"; import { Config } from "../config/schema";
@ -14,25 +12,6 @@ import { Config } from "../config/schema";
config: { url: configService.getOrThrow("REDIS_URL") }, config: { url: configService.getOrThrow("REDIS_URL") },
}), }),
}), }),
NestCacheModule.registerAsync({
isGlobal: true,
inject: [ConfigService],
useFactory: async (configService: ConfigService<Config>) => {
const url = configService.get("REDIS_URL");
if (!url) {
Logger.warn(
"`REDIS_URL` was not set, using in-memory cache instead. This is not suitable for production.",
"CacheModule",
);
return {};
}
return { store: await redisStore({ url }) };
},
}),
], ],
}) })
export class CacheModule {} export class CacheModule {}

View File

@ -42,6 +42,9 @@ export const configSchema = z.object({
// Sentry // Sentry
SENTRY_DSN: z.string().url().startsWith("https://").optional(), SENTRY_DSN: z.string().url().startsWith("https://").optional(),
// Crowdin (Optional)
CROWDIN_DISTRIBUTION_HASH: z.string().optional(),
// GitHub (OAuth) // GitHub (OAuth)
GITHUB_CLIENT_ID: z.string().optional(), GITHUB_CLIENT_ID: z.string().optional(),
GITHUB_CLIENT_SECRET: z.string().optional(), GITHUB_CLIENT_SECRET: z.string().optional(),

View File

@ -1,11 +1,11 @@
import { CacheInterceptor, CacheKey, CacheTTL } from "@nestjs/cache-manager"; import { Controller, Get, NotFoundException } from "@nestjs/common";
import { Controller, Get, NotFoundException, UseInterceptors } from "@nestjs/common";
import { ApiTags } from "@nestjs/swagger"; import { ApiTags } from "@nestjs/swagger";
import { HealthCheck, HealthCheckService } from "@nestjs/terminus"; import { HealthCheck, HealthCheckService } from "@nestjs/terminus";
import { RedisService } from "@songkeys/nestjs-redis"; import { RedisService } from "@songkeys/nestjs-redis";
import { RedisHealthIndicator } from "@songkeys/nestjs-redis-health"; import { RedisHealthIndicator } from "@songkeys/nestjs-redis-health";
import { configSchema } from "../config/schema"; import { configSchema } from "../config/schema";
import { UtilsService } from "../utils/utils.service";
import { BrowserHealthIndicator } from "./browser.health"; import { BrowserHealthIndicator } from "./browser.health";
import { DatabaseHealthIndicator } from "./database.health"; import { DatabaseHealthIndicator } from "./database.health";
import { StorageHealthIndicator } from "./storage.health"; import { StorageHealthIndicator } from "./storage.health";
@ -20,14 +20,10 @@ export class HealthController {
private readonly storage: StorageHealthIndicator, private readonly storage: StorageHealthIndicator,
private readonly redisService: RedisService, private readonly redisService: RedisService,
private readonly redis: RedisHealthIndicator, private readonly redis: RedisHealthIndicator,
private readonly utils: UtilsService,
) {} ) {}
@Get() private run() {
@HealthCheck()
@UseInterceptors(CacheInterceptor)
@CacheKey("health:check")
@CacheTTL(30000) // 30 seconds
check() {
return this.health.check([ return this.health.check([
() => this.database.isHealthy(), () => this.database.isHealthy(),
() => this.storage.isHealthy(), () => this.storage.isHealthy(),
@ -42,6 +38,12 @@ export class HealthController {
]); ]);
} }
@Get()
@HealthCheck()
check() {
return this.utils.getCachedOrSet(`health:check`, () => this.run(), 1000 * 30); // 30 seconds
}
@Get("environment") @Get("environment")
environment() { environment() {
if (process.env.NODE_ENV === "production") throw new NotFoundException(); if (process.env.NODE_ENV === "production") throw new NotFoundException();

View File

@ -1,4 +1,3 @@
import { CacheInterceptor, CacheKey } from "@nestjs/cache-manager";
import { import {
BadRequestException, BadRequestException,
Body, Body,
@ -11,7 +10,6 @@ import {
Patch, Patch,
Post, Post,
UseGuards, UseGuards,
UseInterceptors,
} from "@nestjs/common"; } from "@nestjs/common";
import { ApiTags } from "@nestjs/swagger"; import { ApiTags } from "@nestjs/swagger";
import { User as UserEntity } from "@prisma/client"; import { User as UserEntity } from "@prisma/client";
@ -25,6 +23,7 @@ import { User } from "@/server/user/decorators/user.decorator";
import { OptionalGuard } from "../auth/guards/optional.guard"; import { OptionalGuard } from "../auth/guards/optional.guard";
import { TwoFactorGuard } from "../auth/guards/two-factor.guard"; import { TwoFactorGuard } from "../auth/guards/two-factor.guard";
import { ErrorMessage } from "../constants/error-message"; import { ErrorMessage } from "../constants/error-message";
import { UtilsService } from "../utils/utils.service";
import { Resume } from "./decorators/resume.decorator"; import { Resume } from "./decorators/resume.decorator";
import { ResumeGuard } from "./guards/resume.guard"; import { ResumeGuard } from "./guards/resume.guard";
import { ResumeService } from "./resume.service"; import { ResumeService } from "./resume.service";
@ -32,13 +31,18 @@ import { ResumeService } from "./resume.service";
@ApiTags("Resume") @ApiTags("Resume")
@Controller("resume") @Controller("resume")
export class ResumeController { export class ResumeController {
constructor(private readonly resumeService: ResumeService) {} constructor(
private readonly resumeService: ResumeService,
private readonly utils: UtilsService,
) {}
@Get("schema") @Get("schema")
@UseInterceptors(CacheInterceptor) getSchema() {
@CacheKey("resume:schema") return this.utils.getCachedOrSet(
async getSchema() { `resume:schema`,
return zodToJsonSchema(resumeDataSchema); () => zodToJsonSchema(resumeDataSchema),
1000 * 60 * 60 * 24, // 24 hours
);
} }
@Post() @Post()

View File

@ -1,12 +1,10 @@
import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { BadRequestException, Inject, Injectable, Logger } from "@nestjs/common";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { CreateResumeDto, ImportResumeDto, ResumeDto, UpdateResumeDto } from "@reactive-resume/dto"; import { CreateResumeDto, ImportResumeDto, ResumeDto, UpdateResumeDto } from "@reactive-resume/dto";
import { defaultResumeData, ResumeData } from "@reactive-resume/schema"; import { defaultResumeData, ResumeData } from "@reactive-resume/schema";
import type { DeepPartial } from "@reactive-resume/utils"; import type { DeepPartial } from "@reactive-resume/utils";
import { generateRandomName, kebabCase } from "@reactive-resume/utils"; import { generateRandomName, kebabCase } from "@reactive-resume/utils";
import { RedisService } from "@songkeys/nestjs-redis"; import { RedisService } from "@songkeys/nestjs-redis";
import { Cache } from "cache-manager";
import deepmerge from "deepmerge"; import deepmerge from "deepmerge";
import Redis from "ioredis"; import Redis from "ioredis";
import { PrismaService } from "nestjs-prisma"; import { PrismaService } from "nestjs-prisma";
@ -28,7 +26,6 @@ export class ResumeService {
private readonly storageService: StorageService, private readonly storageService: StorageService,
private readonly redisService: RedisService, private readonly redisService: RedisService,
private readonly utils: UtilsService, private readonly utils: UtilsService,
@Inject(CACHE_MANAGER) private readonly cache: Cache,
) { ) {
this.redis = this.redisService.getClient(); this.redis = this.redisService.getClient();
} }
@ -54,8 +51,8 @@ export class ResumeService {
}); });
await Promise.all([ await Promise.all([
this.cache.del(`user:${userId}:resumes`), this.redis.del(`user:${userId}:resumes`),
this.cache.set(`user:${userId}:resume:${resume.id}`, resume), this.redis.set(`user:${userId}:resume:${resume.id}`, JSON.stringify(resume)),
]); ]);
return resume; return resume;
@ -75,8 +72,8 @@ export class ResumeService {
}); });
await Promise.all([ await Promise.all([
this.cache.del(`user:${userId}:resumes`), this.redis.del(`user:${userId}:resumes`),
this.cache.set(`user:${userId}:resume:${resume.id}`, resume), this.redis.set(`user:${userId}:resume:${resume.id}`, JSON.stringify(resume)),
]); ]);
return resume; return resume;
@ -142,10 +139,10 @@ export class ResumeService {
}); });
await Promise.all([ await Promise.all([
this.cache.set(`user:${userId}:resume:${id}`, resume), this.redis.set(`user:${userId}:resume:${id}`, JSON.stringify(resume)),
this.cache.del(`user:${userId}:resumes`), this.redis.del(`user:${userId}:resumes`),
this.cache.del(`user:${userId}:storage:resumes:${id}`), this.redis.del(`user:${userId}:storage:resumes:${id}`),
this.cache.del(`user:${userId}:storage:previews:${id}`), this.redis.del(`user:${userId}:storage:previews:${id}`),
]); ]);
return resume; return resume;
@ -163,8 +160,8 @@ export class ResumeService {
}); });
await Promise.all([ await Promise.all([
this.cache.set(`user:${userId}:resume:${id}`, resume), this.redis.set(`user:${userId}:resume:${id}`, JSON.stringify(resume)),
this.cache.del(`user:${userId}:resumes`), this.redis.del(`user:${userId}:resumes`),
]); ]);
return resume; return resume;
@ -173,8 +170,8 @@ export class ResumeService {
async remove(userId: string, id: string) { async remove(userId: string, id: string) {
await Promise.all([ await Promise.all([
// Remove cached keys // Remove cached keys
this.cache.del(`user:${userId}:resumes`), this.redis.del(`user:${userId}:resumes`),
this.cache.del(`user:${userId}:resume:${id}`), this.redis.del(`user:${userId}:resume:${id}`),
// Remove files in storage, and their cached keys // Remove files in storage, and their cached keys
this.storageService.deleteObject(userId, "resumes", id), this.storageService.deleteObject(userId, "resumes", id),

View File

@ -1,14 +1,8 @@
import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { Injectable, InternalServerErrorException, Logger, OnModuleInit } from "@nestjs/common";
import {
Inject,
Injectable,
InternalServerErrorException,
Logger,
OnModuleInit,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { createId } from "@paralleldrive/cuid2"; import { createId } from "@paralleldrive/cuid2";
import { Cache } from "cache-manager"; import { RedisService } from "@songkeys/nestjs-redis";
import { Redis } from "ioredis";
import { Client } from "minio"; import { Client } from "minio";
import { MinioService } from "nestjs-minio-client"; import { MinioService } from "nestjs-minio-client";
import sharp from "sharp"; import sharp from "sharp";
@ -44,6 +38,7 @@ const PUBLIC_ACCESS_POLICY = {
@Injectable() @Injectable()
export class StorageService implements OnModuleInit { export class StorageService implements OnModuleInit {
private readonly redis: Redis;
private readonly logger = new Logger(StorageService.name); private readonly logger = new Logger(StorageService.name);
private client: Client; private client: Client;
@ -52,8 +47,10 @@ export class StorageService implements OnModuleInit {
constructor( constructor(
private readonly configService: ConfigService<Config>, private readonly configService: ConfigService<Config>,
private readonly minioService: MinioService, private readonly minioService: MinioService,
@Inject(CACHE_MANAGER) private readonly cache: Cache, private readonly redisService: RedisService,
) {} ) {
this.redis = this.redisService.getClient();
}
async onModuleInit() { async onModuleInit() {
this.client = this.minioService.client; this.client = this.minioService.client;
@ -125,7 +122,7 @@ export class StorageService implements OnModuleInit {
await Promise.all([ await Promise.all([
this.client.putObject(this.bucketName, filepath, buffer, metadata), this.client.putObject(this.bucketName, filepath, buffer, metadata),
this.cache.set(`user:${userId}:storage:${type}:${filename}`, url), this.redis.set(`user:${userId}:storage:${type}:${filename}`, url),
]); ]);
return url; return url;
@ -140,7 +137,7 @@ export class StorageService implements OnModuleInit {
try { try {
return Promise.all([ return Promise.all([
this.cache.del(`user:${userId}:storage:${type}:${filename}`), this.redis.del(`user:${userId}:storage:${type}:${filename}`),
this.client.removeObject(this.bucketName, path), this.client.removeObject(this.bucketName, path),
]); ]);
} catch (error) { } catch (error) {

View File

@ -0,0 +1,35 @@
import { HttpService } from "@nestjs/axios";
import { Controller, Get, Header, Param } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Config } from "../config/schema";
import { UtilsService } from "../utils/utils.service";
@Controller("translation")
export class TranslationController {
constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService<Config>,
private readonly utils: UtilsService,
) {}
private async fetchTranslations(locale: string) {
const distributionHash = this.configService.get("CROWDIN_DISTRIBUTION_HASH");
const response = await this.httpService.axiosRef.get(
`https://distributions.crowdin.net/${distributionHash}/content/${locale}/messages.json`,
);
return response.data;
}
@Get("/:locale")
@Header("Content-Type", "application/octet-stream")
@Header("Content-Disposition", 'attachment; filename="messages.po"')
async getTranslation(@Param("locale") locale: string) {
return this.utils.getCachedOrSet(
`translation:${locale}`,
async () => this.fetchTranslations(locale),
1000 * 60 * 60 * 24, // 24 hours
);
}
}

View File

@ -0,0 +1,10 @@
import { HttpModule } from "@nestjs/axios";
import { Module } from "@nestjs/common";
import { TranslationController } from "./translation.controller";
@Module({
imports: [HttpModule],
controllers: [TranslationController],
})
export class TranslationModule {}

View File

@ -1,7 +1,7 @@
import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { Injectable, InternalServerErrorException } from "@nestjs/common";
import { Inject, Injectable, InternalServerErrorException } from "@nestjs/common";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { Cache } from "cache-manager"; import { RedisService } from "@songkeys/nestjs-redis";
import Redis from "ioredis";
import { PrismaService } from "nestjs-prisma"; import { PrismaService } from "nestjs-prisma";
import { ErrorMessage } from "../constants/error-message"; import { ErrorMessage } from "../constants/error-message";
@ -9,11 +9,15 @@ import { StorageService } from "../storage/storage.service";
@Injectable() @Injectable()
export class UserService { export class UserService {
private readonly redis: Redis;
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly storageService: StorageService, private readonly storageService: StorageService,
@Inject(CACHE_MANAGER) private readonly cache: Cache, private readonly redisService: RedisService,
) {} ) {
this.redis = this.redisService.getClient();
}
async findOneById(id: string) { async findOneById(id: string) {
const user = await this.prisma.user.findUniqueOrThrow({ const user = await this.prisma.user.findUniqueOrThrow({
@ -67,10 +71,7 @@ export class UserService {
} }
async deleteOneById(id: string) { async deleteOneById(id: string) {
await Promise.all([ await Promise.all([this.redis.del(`user:${id}:*`), this.storageService.deleteFolder(id)]);
...(await this.cache.store.keys(`user:${id}:*`)).map((key) => this.cache.del(key)),
this.storageService.deleteFolder(id),
]);
return this.prisma.user.delete({ where: { id } }); return this.prisma.user.delete({ where: { id } });
} }

View File

@ -1,18 +1,21 @@
import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { Injectable, InternalServerErrorException, Logger } from "@nestjs/common";
import { Inject, Injectable, InternalServerErrorException, Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { Cache } from "cache-manager"; import { RedisService } from "@songkeys/nestjs-redis";
import Redis from "ioredis";
import { Config } from "../config/schema"; import { Config } from "../config/schema";
@Injectable() @Injectable()
export class UtilsService { export class UtilsService {
private readonly redis: Redis;
logger = new Logger(UtilsService.name); logger = new Logger(UtilsService.name);
constructor( constructor(
private readonly redisService: RedisService,
private readonly configService: ConfigService<Config>, private readonly configService: ConfigService<Config>,
@Inject(CACHE_MANAGER) private readonly cache: Cache, ) {
) {} this.redis = this.redisService.getClient();
}
getUrl(): string { getUrl(): string {
const url = const url =
@ -27,28 +30,34 @@ export class UtilsService {
return url; return url;
} }
async getCachedOrSet<T>(key: string, callback: () => Promise<T>, ttl?: number): Promise<T> { async getCachedOrSet<T>(
key: string,
callback: () => Promise<T> | T,
ttl: number = 1000 * 60 * 60 * 24, // 24 hours
type: "json" | "string" = "json",
): Promise<T> {
// Try to get the value from the cache // Try to get the value from the cache
const start = performance.now(); const start = performance.now();
const cachedValue = await this.cache.get<T>(key); const cachedValue = await this.redis.get(key);
const duration = Number(performance.now() - start).toFixed(0); const duration = Number(performance.now() - start).toFixed(0);
if (cachedValue === undefined) { if (!cachedValue) {
this.logger.debug(`Cache Key "${key}": miss`); this.logger.debug(`Cache Key "${key}": miss`);
} else { } else {
this.logger.debug(`Cache Key "${key}": hit - ${duration}ms`); this.logger.debug(`Cache Key "${key}": hit - ${duration}ms`);
} }
// If the value is in the cache, return it // If the value is in the cache, return it
if (cachedValue !== undefined) { if (cachedValue) {
return cachedValue; return (type === "string" ? cachedValue : JSON.parse(cachedValue)) as T;
} }
// If the value is not in the cache, run the callback // If the value is not in the cache, run the callback
const value = await callback(); const value = await callback();
const valueToCache = (type === "string" ? value : JSON.stringify(value)) as string;
// Store the value in the cache // Store the value in the cache
await this.cache.set(key, value, ttl); await this.redis.set(key, valueToCache, "PX", ttl);
// Return the value // Return the value
return value; return value;

View File

@ -131,7 +131,6 @@
"@lingui/react": "^4.5.0", "@lingui/react": "^4.5.0",
"@nestjs-modules/mailer": "^1.9.1", "@nestjs-modules/mailer": "^1.9.1",
"@nestjs/axios": "^3.0.1", "@nestjs/axios": "^3.0.1",
"@nestjs/cache-manager": "^2.1.1",
"@nestjs/common": "^10.2.8", "@nestjs/common": "^10.2.8",
"@nestjs/config": "^3.1.1", "@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.8", "@nestjs/core": "^10.2.8",
@ -186,8 +185,6 @@
"axios": "^1.6.1", "axios": "^1.6.1",
"axios-auth-refresh": "^3.3.6", "axios-auth-refresh": "^3.3.6",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"cache-manager": "^5.2.4",
"cache-manager-redis-yet": "^4.1.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"cmdk": "^0.2.0", "cmdk": "^0.2.0",

179
pnpm-lock.yaml generated
View File

@ -41,9 +41,6 @@ dependencies:
'@nestjs/axios': '@nestjs/axios':
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.0.1(@nestjs/common@10.2.8)(axios@1.6.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) version: 3.0.1(@nestjs/common@10.2.8)(axios@1.6.1)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/cache-manager':
specifier: ^2.1.1
version: 2.1.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(cache-manager@5.2.4)(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/common': '@nestjs/common':
specifier: ^10.2.8 specifier: ^10.2.8
version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1) version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1)
@ -206,12 +203,6 @@ dependencies:
bcryptjs: bcryptjs:
specifier: ^2.4.3 specifier: ^2.4.3
version: 2.4.3 version: 2.4.3
cache-manager:
specifier: ^5.2.4
version: 5.2.4
cache-manager-redis-yet:
specifier: ^4.1.2
version: 4.1.2
class-variance-authority: class-variance-authority:
specifier: ^0.7.0 specifier: ^0.7.0
version: 0.7.0 version: 0.7.0
@ -2164,16 +2155,6 @@ packages:
conventional-changelog-conventionalcommits: 7.0.2 conventional-changelog-conventionalcommits: 7.0.2
dev: true dev: true
/@commitlint/config-validator@18.1.0:
resolution: {integrity: sha512-kbHkIuItXn93o2NmTdwi5Mk1ujyuSIysRE/XHtrcps/27GuUKEIqBJp6TdJ4Sq+ze59RlzYSHMKuDKZbfg9+uQ==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
'@commitlint/types': 18.1.0
ajv: 8.12.0
dev: true
optional: true
/@commitlint/config-validator@18.4.0: /@commitlint/config-validator@18.4.0:
resolution: {integrity: sha512-1y6qHMU3o4cYQSK+Y9EnmH6H1GRiwQGjnLIUOIKlekrmfc8MrMk1ByNmb8od4vK3qHJAaL/77/5n+1uyyIF5dA==} resolution: {integrity: sha512-1y6qHMU3o4cYQSK+Y9EnmH6H1GRiwQGjnLIUOIKlekrmfc8MrMk1ByNmb8od4vK3qHJAaL/77/5n+1uyyIF5dA==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
@ -2194,13 +2175,6 @@ packages:
lodash.upperfirst: 4.3.1 lodash.upperfirst: 4.3.1
dev: true dev: true
/@commitlint/execute-rule@18.1.0:
resolution: {integrity: sha512-w3Vt4K+O7+nSr9/gFSEfZ1exKUOPSlJaRpnk7Y+XowEhvwT7AIk1HNANH+gETf0zGZ020+hfiMW/Ome+SNCUsg==}
engines: {node: '>=v18'}
requiresBuild: true
dev: true
optional: true
/@commitlint/execute-rule@18.4.0: /@commitlint/execute-rule@18.4.0:
resolution: {integrity: sha512-g013SWki6ZWhURBLOSXTaVQGWHdA0QlPJGiW4a+YpThezmJOemvc4LiKVpn13AjSKQ40QnmBqpBrxujOaSo+3A==} resolution: {integrity: sha512-g013SWki6ZWhURBLOSXTaVQGWHdA0QlPJGiW4a+YpThezmJOemvc4LiKVpn13AjSKQ40QnmBqpBrxujOaSo+3A==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
@ -2232,28 +2206,6 @@ packages:
'@commitlint/types': 18.4.0 '@commitlint/types': 18.4.0
dev: true dev: true
/@commitlint/load@18.2.0(typescript@5.2.2):
resolution: {integrity: sha512-xjX3d3CRlOALwImhOsmLYZh14/+gW/KxsY7+bPKrzmGuFailf9K7ckhB071oYZVJdACnpY4hDYiosFyOC+MpAA==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
'@commitlint/config-validator': 18.1.0
'@commitlint/execute-rule': 18.1.0
'@commitlint/resolve-extends': 18.1.0
'@commitlint/types': 18.1.0
'@types/node': 18.18.8
chalk: 4.1.2
cosmiconfig: 8.3.6(typescript@5.2.2)
cosmiconfig-typescript-loader: 5.0.0(@types/node@18.18.8)(cosmiconfig@8.3.6)(typescript@5.2.2)
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
resolve-from: 5.0.0
transitivePeerDependencies:
- typescript
dev: true
optional: true
/@commitlint/load@18.4.0(typescript@5.2.2): /@commitlint/load@18.4.0(typescript@5.2.2):
resolution: {integrity: sha512-7unGl1HGRNMgWrUPmj8OFkJyuNUMb6xA1i53/OAFKd9l+U3C4WTfoJe3t/TUz8vKZLCaDcWWR/b2cw5HveBBFg==} resolution: {integrity: sha512-7unGl1HGRNMgWrUPmj8OFkJyuNUMb6xA1i53/OAFKd9l+U3C4WTfoJe3t/TUz8vKZLCaDcWWR/b2cw5HveBBFg==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
@ -2299,20 +2251,6 @@ packages:
minimist: 1.2.8 minimist: 1.2.8
dev: true dev: true
/@commitlint/resolve-extends@18.1.0:
resolution: {integrity: sha512-3mZpzOEJkELt7BbaZp6+bofJyxViyObebagFn0A7IHaLARhPkWTivXdjvZHS12nAORftv88Yhbh8eCPKfSvB7g==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
'@commitlint/config-validator': 18.1.0
'@commitlint/types': 18.1.0
import-fresh: 3.3.0
lodash.mergewith: 4.6.2
resolve-from: 5.0.0
resolve-global: 1.0.0
dev: true
optional: true
/@commitlint/resolve-extends@18.4.0: /@commitlint/resolve-extends@18.4.0:
resolution: {integrity: sha512-qhgU6ach+S6sJMD9NjCYiEycOObGhxzWQLQzqlScJCv9zkPs15Bg0ffLXTQ3z7ipXv46XEKYMnSJzjLRw2Tlkg==} resolution: {integrity: sha512-qhgU6ach+S6sJMD9NjCYiEycOObGhxzWQLQzqlScJCv9zkPs15Bg0ffLXTQ3z7ipXv46XEKYMnSJzjLRw2Tlkg==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
@ -2348,15 +2286,6 @@ packages:
find-up: 5.0.0 find-up: 5.0.0
dev: true dev: true
/@commitlint/types@18.1.0:
resolution: {integrity: sha512-65vGxZmbs+2OVwEItxhp3Ul7X2m2LyLfifYI/NdPwRqblmuES2w2aIRhIjb7cwUIBHHSTT8WXj4ixVHQibmvLQ==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
chalk: 4.1.2
dev: true
optional: true
/@commitlint/types@18.4.0: /@commitlint/types@18.4.0:
resolution: {integrity: sha512-MKeaFxt0I9fhqUb2E+YIzX/gZtmkuodJET/XKiZIMvXUff8Ee4Ih86eLg+yAm2jf1pwGBmU02uNOp0y094w2Uw==} resolution: {integrity: sha512-MKeaFxt0I9fhqUb2E+YIzX/gZtmkuodJET/XKiZIMvXUff8Ee4Ih86eLg+yAm2jf1pwGBmU02uNOp0y094w2Uw==}
engines: {node: '>=v18'} engines: {node: '>=v18'}
@ -3564,22 +3493,6 @@ packages:
rxjs: 7.8.1 rxjs: 7.8.1
dev: false dev: false
/@nestjs/cache-manager@2.1.1(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(cache-manager@5.2.4)(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-oYfRys4Ng0zp2HTUPNjH7gizf4vvG3PQZZ+3yGemb3xrF+p3JxDSK0cDq9NTjHzD5UmhjiyAftB9GkuL+t3r9g==}
peerDependencies:
'@nestjs/common': ^9.0.0 || ^10.0.0
'@nestjs/core': ^9.0.0 || ^10.0.0
cache-manager: <=5
reflect-metadata: ^0.1.12
rxjs: ^7.0.0
dependencies:
'@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1)
'@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(@nestjs/platform-express@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)
cache-manager: 5.2.4
reflect-metadata: 0.1.13
rxjs: 7.8.1
dev: false
/@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1): /@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1):
resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==} resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==}
peerDependencies: peerDependencies:
@ -6131,55 +6044,6 @@ packages:
'@babel/runtime': 7.23.2 '@babel/runtime': 7.23.2
dev: false dev: false
/@redis/bloom@1.2.0(@redis/client@1.5.11):
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/client@1.5.11:
resolution: {integrity: sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==}
engines: {node: '>=14'}
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
dev: false
/@redis/graph@1.1.0(@redis/client@1.5.11):
resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/json@1.0.6(@redis/client@1.5.11):
resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/search@1.1.5(@redis/client@1.5.11):
resolution: {integrity: sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@redis/time-series@1.0.5(@redis/client@1.5.11):
resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.11
dev: false
/@remirror/core-constants@2.0.2: /@remirror/core-constants@2.0.2:
resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==}
dev: false dev: false
@ -9064,27 +8928,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/cache-manager-redis-yet@4.1.2:
resolution: {integrity: sha512-pM2K1ZlOv8gQpE1Z5mcDrfLj5CsNKVRiYua/SZ12j7LEDgfDeFVntI6JSgIw0siFSR/9P/FpG30scI3frHwibA==}
engines: {node: '>= 16.17.0'}
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.5.11)
'@redis/client': 1.5.11
'@redis/graph': 1.1.0(@redis/client@1.5.11)
'@redis/json': 1.0.6(@redis/client@1.5.11)
'@redis/search': 1.1.5(@redis/client@1.5.11)
'@redis/time-series': 1.0.5(@redis/client@1.5.11)
cache-manager: 5.2.4
redis: 4.6.10
dev: false
/cache-manager@5.2.4:
resolution: {integrity: sha512-gkuCjug16NdGvKm/sydxGVx17uffrSWcEe2xraBtwRCgdYcFxwJAla4OYpASAZT2yhSoxgDiWL9XH6IAChcZJA==}
dependencies:
lodash.clonedeep: 4.5.0
lru-cache: 10.0.1
dev: false
/cacheable-lookup@5.0.4: /cacheable-lookup@5.0.4:
resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
engines: {node: '>=10.6.0'} engines: {node: '>=10.6.0'}
@ -10151,7 +9994,7 @@ packages:
longest: 2.0.1 longest: 2.0.1
word-wrap: 1.2.5 word-wrap: 1.2.5
optionalDependencies: optionalDependencies:
'@commitlint/load': 18.2.0(typescript@5.2.2) '@commitlint/load': 18.4.0(typescript@5.2.2)
transitivePeerDependencies: transitivePeerDependencies:
- typescript - typescript
dev: true dev: true
@ -12069,11 +11912,6 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true dev: true
/generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
dev: false
/gensync@1.0.0-beta.2: /gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -14359,10 +14197,6 @@ packages:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
dev: true dev: true
/lodash.clonedeep@4.5.0:
resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
dev: false
/lodash.debounce@4.0.8: /lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
@ -17670,17 +17504,6 @@ packages:
redis-errors: 1.2.0 redis-errors: 1.2.0
dev: false dev: false
/redis@4.6.10:
resolution: {integrity: sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==}
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.5.11)
'@redis/client': 1.5.11
'@redis/graph': 1.1.0(@redis/client@1.5.11)
'@redis/json': 1.0.6(@redis/client@1.5.11)
'@redis/search': 1.1.5(@redis/client@1.5.11)
'@redis/time-series': 1.0.5(@redis/client@1.5.11)
dev: false
/reflect-metadata@0.1.13: /reflect-metadata@0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}

View File

@ -18,12 +18,12 @@
"@/client/*": ["apps/client/src/*"], "@/client/*": ["apps/client/src/*"],
"@/server/*": ["apps/server/src/*"], "@/server/*": ["apps/server/src/*"],
"@/artboard/*": ["apps/artboard/src/*"], "@/artboard/*": ["apps/artboard/src/*"],
"@reactive-resume/ui": ["libs/ui/src/index.ts"],
"@reactive-resume/dto": ["libs/dto/src/index.ts"], "@reactive-resume/dto": ["libs/dto/src/index.ts"],
"@reactive-resume/utils": ["libs/utils/src/index.ts"],
"@reactive-resume/hooks": ["libs/hooks/src/index.ts"], "@reactive-resume/hooks": ["libs/hooks/src/index.ts"],
"@reactive-resume/parser": ["libs/parser/src/index.ts"], "@reactive-resume/parser": ["libs/parser/src/index.ts"],
"@reactive-resume/schema": ["libs/schema/src/index.ts"], "@reactive-resume/schema": ["libs/schema/src/index.ts"]
"@reactive-resume/ui": ["libs/ui/src/index.ts"],
"@reactive-resume/utils": ["libs/utils/src/index.ts"]
} }
}, },
"exclude": ["node_modules", "dist", "tmp", ".nx"] "exclude": ["node_modules", "dist", "tmp", ".nx"]