mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-25 17:34:52 +10:00
feat(feature-flags): fixes #1592, introduces new flags DISABLE_SIGNUPS and DISABLE_EMAIL_AUTH, renamed STORAGE_SKIP_BUCKET_CHECK
This commit is contained in:
+4
-4
@@ -46,6 +46,7 @@ STORAGE_BUCKET=default
|
|||||||
STORAGE_ACCESS_KEY=minioadmin
|
STORAGE_ACCESS_KEY=minioadmin
|
||||||
STORAGE_SECRET_KEY=minioadmin
|
STORAGE_SECRET_KEY=minioadmin
|
||||||
STORAGE_USE_SSL=false
|
STORAGE_USE_SSL=false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK=false
|
||||||
|
|
||||||
# Nx Cloud (Optional)
|
# Nx Cloud (Optional)
|
||||||
# NX_CLOUD_ACCESS_TOKEN=
|
# NX_CLOUD_ACCESS_TOKEN=
|
||||||
@@ -54,10 +55,9 @@ STORAGE_USE_SSL=false
|
|||||||
# CROWDIN_PROJECT_ID=
|
# CROWDIN_PROJECT_ID=
|
||||||
# CROWDIN_PERSONAL_TOKEN=
|
# CROWDIN_PERSONAL_TOKEN=
|
||||||
|
|
||||||
# Flags (Optional)
|
# Feature Flags (Optional)
|
||||||
# DISABLE_EMAIL_AUTH=true
|
# DISABLE_SIGNUPS=false
|
||||||
# VITE_DISABLE_SIGNUPS=false
|
# DISABLE_EMAIL_AUTH=false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK=false
|
|
||||||
|
|
||||||
# GitHub (OAuth, Optional)
|
# GitHub (OAuth, Optional)
|
||||||
# GITHUB_CLIENT_ID=
|
# GITHUB_CLIENT_ID=
|
||||||
|
|||||||
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
"upgrade": true,
|
"upgrade": true,
|
||||||
"install": "always",
|
"install": "always",
|
||||||
"packageManager": "pnpm",
|
"packageManager": "pnpm",
|
||||||
"reject": ["eslint", "@reactive-resume/*"]
|
"reject": ["eslint", "eslint-plugin-unused-imports", "@reactive-resume/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { ArrowRight } from "@phosphor-icons/react";
|
|||||||
import { loginSchema } from "@reactive-resume/dto";
|
import { loginSchema } from "@reactive-resume/dto";
|
||||||
import { usePasswordToggle } from "@reactive-resume/hooks";
|
import { usePasswordToggle } from "@reactive-resume/hooks";
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertTitle,
|
||||||
Button,
|
Button,
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -22,15 +24,13 @@ import { Link } from "react-router-dom";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { useLogin } from "@/client/services/auth";
|
import { useLogin } from "@/client/services/auth";
|
||||||
import { useAuthProviders } from "@/client/services/auth/providers";
|
import { useFeatureFlags } from "@/client/services/feature";
|
||||||
|
|
||||||
type FormValues = z.infer<typeof loginSchema>;
|
type FormValues = z.infer<typeof loginSchema>;
|
||||||
|
|
||||||
export const LoginPage = () => {
|
export const LoginPage = () => {
|
||||||
const { login, loading } = useLogin();
|
const { login, loading } = useLogin();
|
||||||
|
const { flags } = useFeatureFlags();
|
||||||
const { providers } = useAuthProviders();
|
|
||||||
const emailAuthDisabled = !providers?.includes("email");
|
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
usePasswordToggle(formRef);
|
usePasswordToggle(formRef);
|
||||||
@@ -58,7 +58,7 @@ export const LoginPage = () => {
|
|||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<h2 className="text-2xl font-semibold tracking-tight">{t`Sign in to your account`}</h2>
|
<h2 className="text-2xl font-semibold tracking-tight">{t`Sign in to your account`}</h2>
|
||||||
<h6 className={cn(emailAuthDisabled && "hidden")}>
|
<h6>
|
||||||
<span className="opacity-75">{t`Don't have an account?`}</span>
|
<span className="opacity-75">{t`Don't have an account?`}</span>
|
||||||
<Button asChild variant="link" className="px-1.5">
|
<Button asChild variant="link" className="px-1.5">
|
||||||
<Link to="/auth/register">
|
<Link to="/auth/register">
|
||||||
@@ -69,7 +69,13 @@ export const LoginPage = () => {
|
|||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn(emailAuthDisabled && "hidden")}>
|
{flags.isEmailAuthDisabled && (
|
||||||
|
<Alert variant="error">
|
||||||
|
<AlertTitle>{t`Signing in via email is currently disabled by the administrator.`}</AlertTitle>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={cn(flags.isEmailAuthDisabled && "pointer-events-none select-none blur-sm")}>
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
|
|||||||
@@ -24,17 +24,14 @@ import { Link, useNavigate } from "react-router-dom";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { useRegister } from "@/client/services/auth";
|
import { useRegister } from "@/client/services/auth";
|
||||||
import { useAuthProviders } from "@/client/services/auth/providers";
|
import { useFeatureFlags } from "@/client/services/feature";
|
||||||
|
|
||||||
type FormValues = z.infer<typeof registerSchema>;
|
type FormValues = z.infer<typeof registerSchema>;
|
||||||
|
|
||||||
export const RegisterPage = () => {
|
export const RegisterPage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { flags } = useFeatureFlags();
|
||||||
const { register, loading } = useRegister();
|
const { register, loading } = useRegister();
|
||||||
const disableSignups = import.meta.env.VITE_DISABLE_SIGNUPS === "true";
|
|
||||||
|
|
||||||
const { providers } = useAuthProviders();
|
|
||||||
const emailAuthDisabled = !providers?.includes("email");
|
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
usePasswordToggle(formRef);
|
usePasswordToggle(formRef);
|
||||||
@@ -70,7 +67,7 @@ export const RegisterPage = () => {
|
|||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<h2 className="text-2xl font-semibold tracking-tight">{t`Create a new account`}</h2>
|
<h2 className="text-2xl font-semibold tracking-tight">{t`Create a new account`}</h2>
|
||||||
<h6 className={cn(emailAuthDisabled && "hidden")}>
|
<h6>
|
||||||
<span className="opacity-75">{t`Already have an account?`}</span>
|
<span className="opacity-75">{t`Already have an account?`}</span>
|
||||||
<Button asChild variant="link" className="px-1.5">
|
<Button asChild variant="link" className="px-1.5">
|
||||||
<Link to="/auth/login">
|
<Link to="/auth/login">
|
||||||
@@ -80,18 +77,13 @@ export const RegisterPage = () => {
|
|||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{disableSignups && (
|
{flags.isSignupsDisabled && (
|
||||||
<Alert variant="error">
|
<Alert variant="error">
|
||||||
<AlertTitle>{t`Signups are currently disabled by the administrator.`}</AlertTitle>
|
<AlertTitle>{t`Signups are currently disabled by the administrator.`}</AlertTitle>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div className={cn(flags.isSignupsDisabled && "pointer-events-none select-none blur-sm")}>
|
||||||
className={cn(
|
|
||||||
emailAuthDisabled && "hidden",
|
|
||||||
disableSignups && "pointer-events-none blur-sm",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { FeatureDto } from "@reactive-resume/dto";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { axios } from "@/client/libs/axios";
|
||||||
|
|
||||||
|
export const fetchFeatureFlags = async () => {
|
||||||
|
const response = await axios.get<FeatureDto>(`/feature/flags`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFeatureFlags = () => {
|
||||||
|
const {
|
||||||
|
error,
|
||||||
|
isPending: loading,
|
||||||
|
data: flags,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["feature_flags"],
|
||||||
|
queryFn: () => fetchFeatureFlags(),
|
||||||
|
refetchOnMount: "always",
|
||||||
|
initialData: {
|
||||||
|
isSignupsDisabled: false,
|
||||||
|
isEmailAuthDisabled: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { flags, loading, error };
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./flags";
|
||||||
Vendored
+1
-6
@@ -1,13 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
|
||||||
|
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare const appVersion: string;
|
declare const appVersion: string;
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
VITE_DISABLE_SIGNUPS: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
readonly env: ImportMetaEnv;
|
readonly env: ImportMetaEnv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { AuthModule } from "./auth/auth.module";
|
|||||||
import { ConfigModule } from "./config/config.module";
|
import { ConfigModule } from "./config/config.module";
|
||||||
import { ContributorsModule } from "./contributors/contributors.module";
|
import { ContributorsModule } from "./contributors/contributors.module";
|
||||||
import { DatabaseModule } from "./database/database.module";
|
import { DatabaseModule } from "./database/database.module";
|
||||||
|
import { FeatureModule } from "./feature/feature.module";
|
||||||
import { HealthModule } from "./health/health.module";
|
import { HealthModule } from "./health/health.module";
|
||||||
import { MailModule } from "./mail/mail.module";
|
import { MailModule } from "./mail/mail.module";
|
||||||
import { PrinterModule } from "./printer/printer.module";
|
import { PrinterModule } from "./printer/printer.module";
|
||||||
@@ -33,6 +34,7 @@ import { UserModule } from "./user/user.module";
|
|||||||
ResumeModule,
|
ResumeModule,
|
||||||
StorageModule,
|
StorageModule,
|
||||||
PrinterModule,
|
PrinterModule,
|
||||||
|
FeatureModule,
|
||||||
TranslationModule,
|
TranslationModule,
|
||||||
ContributorsModule,
|
ContributorsModule,
|
||||||
|
|
||||||
|
|||||||
@@ -44,17 +44,21 @@ export const configSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((s) => s !== "false" && s !== "0"),
|
.transform((s) => s !== "false" && s !== "0"),
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: z
|
||||||
|
.string()
|
||||||
|
.default("false")
|
||||||
|
.transform((s) => s !== "false" && s !== "0"),
|
||||||
|
|
||||||
// Crowdin (Optional)
|
// Crowdin (Optional)
|
||||||
CROWDIN_PROJECT_ID: z.coerce.number().optional(),
|
CROWDIN_PROJECT_ID: z.coerce.number().optional(),
|
||||||
CROWDIN_PERSONAL_TOKEN: z.string().optional(),
|
CROWDIN_PERSONAL_TOKEN: z.string().optional(),
|
||||||
|
|
||||||
// Flags (Optional)
|
// Feature Flags (Optional)
|
||||||
DISABLE_EMAIL_AUTH: z
|
DISABLE_SIGNUPS: z
|
||||||
.string()
|
.string()
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((s) => s !== "false" && s !== "0"),
|
.transform((s) => s !== "false" && s !== "0"),
|
||||||
SKIP_STORAGE_BUCKET_CHECK: z
|
DISABLE_EMAIL_AUTH: z
|
||||||
.string()
|
.string()
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((s) => s !== "false" && s !== "0"),
|
.transform((s) => s !== "false" && s !== "0"),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ContributorDto } from "@reactive-resume/dto";
|
|||||||
import { Config } from "../config/schema";
|
import { Config } from "../config/schema";
|
||||||
|
|
||||||
type GitHubResponse = { id: number; login: string; html_url: string; avatar_url: string }[];
|
type GitHubResponse = { id: number; login: string; html_url: string; avatar_url: string }[];
|
||||||
|
|
||||||
type CrowdinContributorsResponse = {
|
type CrowdinContributorsResponse = {
|
||||||
data: { data: { id: number; username: string; avatarUrl: string } }[];
|
data: { data: { id: number; username: string; avatarUrl: string } }[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Controller, Get } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { FeatureService } from "./feature.service";
|
||||||
|
|
||||||
|
@Controller("feature")
|
||||||
|
export class FeatureController {
|
||||||
|
constructor(private readonly featureService: FeatureService) {}
|
||||||
|
|
||||||
|
@Get("/flags")
|
||||||
|
getFeatureFlags() {
|
||||||
|
return this.featureService.getFeatures();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { FeatureController } from "./feature.controller";
|
||||||
|
import { FeatureService } from "./feature.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [FeatureService],
|
||||||
|
controllers: [FeatureController],
|
||||||
|
exports: [FeatureService],
|
||||||
|
})
|
||||||
|
export class FeatureModule {}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
|
import { Config } from "../config/schema";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FeatureService {
|
||||||
|
constructor(private readonly configService: ConfigService<Config>) {}
|
||||||
|
|
||||||
|
getFeatures() {
|
||||||
|
const isSignupsDisabled = this.configService.getOrThrow<boolean>("DISABLE_SIGNUPS");
|
||||||
|
const isEmailAuthDisabled = this.configService.getOrThrow<boolean>("DISABLE_EMAIL_AUTH");
|
||||||
|
|
||||||
|
return {
|
||||||
|
isSignupsDisabled,
|
||||||
|
isEmailAuthDisabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,14 +49,13 @@ export class StorageService implements OnModuleInit {
|
|||||||
this.client = this.minioService.client;
|
this.client = this.minioService.client;
|
||||||
this.bucketName = this.configService.getOrThrow<string>("STORAGE_BUCKET");
|
this.bucketName = this.configService.getOrThrow<string>("STORAGE_BUCKET");
|
||||||
|
|
||||||
const skipBucketCheck = this.configService.getOrThrow<boolean>("SKIP_STORAGE_BUCKET_CHECK");
|
const skipBucketCheck = this.configService.getOrThrow<boolean>("STORAGE_SKIP_BUCKET_CHECK");
|
||||||
|
|
||||||
if (skipBucketCheck) {
|
if (skipBucketCheck) {
|
||||||
this.logger.log("Skipping the verification of whether the storage bucket exists.");
|
this.logger.warn("Skipping the verification of whether the storage bucket exists.");
|
||||||
this.logger.warn("Make sure that the following paths are publicly accessible: ");
|
this.logger.warn(
|
||||||
this.logger.warn("- /pictures/*");
|
"Make sure that the following paths are publicly accessible: `/{pictures,previews,resumes}/*`",
|
||||||
this.logger.warn("- /previews/*");
|
);
|
||||||
this.logger.warn("- /resumes/*");
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createZodDto } from "nestjs-zod/dto";
|
||||||
|
import { z } from "nestjs-zod/z";
|
||||||
|
|
||||||
|
export const featureSchema = z.object({
|
||||||
|
isSignupsDisabled: z.boolean().default(false),
|
||||||
|
isEmailAuthDisabled: z.boolean().default(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export class FeatureDto extends createZodDto(featureSchema) {}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
// @index('./*', f => `export * from "${f.path}";`)
|
||||||
export * from "./auth";
|
export * from "./auth";
|
||||||
export * from "./contributors";
|
export * from "./contributors";
|
||||||
|
export * from "./feature";
|
||||||
export * from "./resume";
|
export * from "./resume";
|
||||||
export * from "./secrets";
|
export * from "./secrets";
|
||||||
export * from "./statistics";
|
export * from "./statistics";
|
||||||
|
|||||||
+32
-32
@@ -30,25 +30,25 @@
|
|||||||
"messages:extract": "pnpm exec lingui extract --clean --overwrite"
|
"messages:extract": "pnpm exec lingui extract --clean --overwrite"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.5",
|
"@babel/core": "^7.24.6",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.6",
|
||||||
"@lingui/cli": "^4.11.0",
|
"@lingui/cli": "^4.11.0",
|
||||||
"@lingui/conf": "^4.11.0",
|
"@lingui/conf": "^4.11.0",
|
||||||
"@lingui/swc-plugin": "^4.0.7",
|
"@lingui/swc-plugin": "^4.0.7",
|
||||||
"@lingui/vite-plugin": "^4.11.0",
|
"@lingui/vite-plugin": "^4.11.0",
|
||||||
"@nestjs/schematics": "^10.1.1",
|
"@nestjs/schematics": "^10.1.1",
|
||||||
"@nestjs/testing": "^10.3.8",
|
"@nestjs/testing": "^10.3.8",
|
||||||
"@nx/eslint": "19.0.4",
|
"@nx/eslint": "19.1.0",
|
||||||
"@nx/eslint-plugin": "19.0.4",
|
"@nx/eslint-plugin": "19.1.0",
|
||||||
"@nx/jest": "19.0.4",
|
"@nx/jest": "19.1.0",
|
||||||
"@nx/js": "19.0.4",
|
"@nx/js": "19.1.0",
|
||||||
"@nx/nest": "19.0.4",
|
"@nx/nest": "19.1.0",
|
||||||
"@nx/node": "19.0.4",
|
"@nx/node": "19.1.0",
|
||||||
"@nx/react": "19.0.4",
|
"@nx/react": "19.1.0",
|
||||||
"@nx/vite": "19.0.4",
|
"@nx/vite": "19.1.0",
|
||||||
"@nx/web": "19.0.4",
|
"@nx/web": "19.1.0",
|
||||||
"@nx/webpack": "19.0.4",
|
"@nx/webpack": "19.1.0",
|
||||||
"@nx/workspace": "19.0.4",
|
"@nx/workspace": "19.1.0",
|
||||||
"@swc-node/register": "~1.9.1",
|
"@swc-node/register": "~1.9.1",
|
||||||
"@swc/cli": "~0.3.12",
|
"@swc/cli": "~0.3.12",
|
||||||
"@swc/core": "~1.5.7",
|
"@swc/core": "~1.5.7",
|
||||||
@@ -74,15 +74,15 @@
|
|||||||
"@types/passport-github2": "^1.2.9",
|
"@types/passport-github2": "^1.2.9",
|
||||||
"@types/passport-google-oauth20": "^2.0.16",
|
"@types/passport-google-oauth20": "^2.0.16",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
"@types/react": "18.3.2",
|
"@types/react": "18.3.3",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"@types/react-is": "18.3.0",
|
"@types/react-is": "18.3.0",
|
||||||
"@types/retry": "^0.12.5",
|
"@types/retry": "^0.12.5",
|
||||||
"@types/webfontloader": "^1.6.38",
|
"@types/webfontloader": "^1.6.38",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||||
"@typescript-eslint/parser": "^7.10.0",
|
"@typescript-eslint/parser": "^7.11.0",
|
||||||
"@vitejs/plugin-react": "~4.2.1",
|
"@vitejs/plugin-react": "~4.3.0",
|
||||||
"@vitejs/plugin-react-swc": "~3.6.0",
|
"@vitejs/plugin-react-swc": "~3.7.0",
|
||||||
"@vitest/coverage-v8": "^1.6.0",
|
"@vitest/coverage-v8": "^1.6.0",
|
||||||
"@vitest/ui": "~1.6.0",
|
"@vitest/ui": "~1.6.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
@@ -93,26 +93,26 @@
|
|||||||
"eslint-plugin-jsx-a11y": "6.8.0",
|
"eslint-plugin-jsx-a11y": "6.8.0",
|
||||||
"eslint-plugin-lingui": "^0.3.0",
|
"eslint-plugin-lingui": "^0.3.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "7.34.1",
|
"eslint-plugin-react": "7.34.2",
|
||||||
"eslint-plugin-react-hooks": "4.6.2",
|
"eslint-plugin-react-hooks": "4.6.2",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.0",
|
"eslint-plugin-simple-import-sort": "^12.1.0",
|
||||||
"eslint-plugin-tailwindcss": "^3.15.2",
|
"eslint-plugin-tailwindcss": "^3.17.0",
|
||||||
"eslint-plugin-unicorn": "^53.0.0",
|
"eslint-plugin-unicorn": "^53.0.0",
|
||||||
"eslint-plugin-unused-imports": "^3.2.0",
|
"eslint-plugin-unused-imports": "^3.2.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-node": "^29.7.0",
|
"jest-environment-node": "^29.7.0",
|
||||||
"jsdom": "~24.0.0",
|
"jsdom": "~24.1.0",
|
||||||
"nx": "19.0.4",
|
"nx": "19.1.0",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.38",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-nested": "^6.0.1",
|
"postcss-nested": "^6.0.1",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"ts-jest": "^29.1.3",
|
"ts-jest": "^29.1.4",
|
||||||
"ts-node": "10.9.2",
|
"ts-node": "10.9.2",
|
||||||
"typescript": "~5.4.5",
|
"typescript": "~5.4.5",
|
||||||
"vite": "~5.2.11",
|
"vite": "~5.2.12",
|
||||||
"vite-plugin-dts": "~3.9.1",
|
"vite-plugin-dts": "~3.9.1",
|
||||||
"vitest": "~1.6.0"
|
"vitest": "~1.6.0"
|
||||||
},
|
},
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@swc/helpers": "~0.5.11",
|
"@swc/helpers": "~0.5.11",
|
||||||
"@tanstack/react-query": "^5.37.1",
|
"@tanstack/react-query": "^5.40.0",
|
||||||
"@tiptap/extension-bold": "^2.4.0",
|
"@tiptap/extension-bold": "^2.4.0",
|
||||||
"@tiptap/extension-bullet-list": "^2.4.0",
|
"@tiptap/extension-bullet-list": "^2.4.0",
|
||||||
"@tiptap/extension-code": "^2.4.0",
|
"@tiptap/extension-code": "^2.4.0",
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
"@tiptap/react": "2.4.0",
|
"@tiptap/react": "2.4.0",
|
||||||
"@types/passport-jwt": "^4.0.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
"async-retry": "^1.3.3",
|
"async-retry": "^1.3.3",
|
||||||
"axios": "^1.7.1",
|
"axios": "^1.7.2",
|
||||||
"axios-auth-refresh": "^3.3.6",
|
"axios-auth-refresh": "^3.3.6",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
@@ -201,7 +201,7 @@
|
|||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"framer-motion": "^11.2.4",
|
"framer-motion": "^11.2.6",
|
||||||
"fuzzy": "^0.1.3",
|
"fuzzy": "^0.1.3",
|
||||||
"helmet": "^7.1.0",
|
"helmet": "^7.1.0",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
@@ -210,12 +210,12 @@
|
|||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"minio": "^8.0.0",
|
"minio": "^8.0.0",
|
||||||
"nest-raven": "^10.0.1",
|
"nest-raven": "^10.1.0",
|
||||||
"nestjs-minio-client": "^2.2.0",
|
"nestjs-minio-client": "^2.2.0",
|
||||||
"nestjs-prisma": "^0.23.0",
|
"nestjs-prisma": "^0.23.0",
|
||||||
"nestjs-zod": "^3.0.0",
|
"nestjs-zod": "^3.0.0",
|
||||||
"nodemailer": "^6.9.13",
|
"nodemailer": "^6.9.13",
|
||||||
"openai": "^4.47.1",
|
"openai": "^4.47.2",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
@@ -225,14 +225,14 @@
|
|||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"prisma": "^5.14.0",
|
"prisma": "^5.14.0",
|
||||||
"puppeteer": "^22.9.0",
|
"puppeteer": "^22.10.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-colorful": "^5.6.1",
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-helmet-async": "^2.0.5",
|
"react-helmet-async": "^2.0.5",
|
||||||
"react-hook-form": "^7.51.4",
|
"react-hook-form": "^7.51.5",
|
||||||
"react-parallax-tilt": "^1.7.226",
|
"react-parallax-tilt": "^1.7.227",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
"react-router-dom": "6.23.1",
|
"react-router-dom": "6.23.1",
|
||||||
"react-zoom-pan-pinch": "^3.4.4",
|
"react-zoom-pan-pinch": "^3.4.4",
|
||||||
|
|||||||
Generated
+1525
-1276
File diff suppressed because it is too large
Load Diff
@@ -87,15 +87,15 @@ services:
|
|||||||
STORAGE_ACCESS_KEY: minioadmin
|
STORAGE_ACCESS_KEY: minioadmin
|
||||||
STORAGE_SECRET_KEY: minioadmin
|
STORAGE_SECRET_KEY: minioadmin
|
||||||
STORAGE_USE_SSL: false
|
STORAGE_USE_SSL: false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: false
|
||||||
|
|
||||||
# -- Crowdin (Optional) --
|
# -- Crowdin (Optional) --
|
||||||
# CROWDIN_PROJECT_ID:
|
# CROWDIN_PROJECT_ID:
|
||||||
# CROWDIN_PERSONAL_TOKEN:
|
# CROWDIN_PERSONAL_TOKEN:
|
||||||
|
|
||||||
# -- Flags (Optional) --
|
# -- Feature Flags (Optional) --
|
||||||
# DISABLE_EMAIL_AUTH: true
|
# DISABLE_SIGNUPS: false
|
||||||
# VITE_DISABLE_SIGNUPS: true
|
# DISABLE_EMAIL_AUTH: false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK: false
|
|
||||||
|
|
||||||
# -- GitHub (Optional) --
|
# -- GitHub (Optional) --
|
||||||
# GITHUB_CLIENT_ID: github_client_id
|
# GITHUB_CLIENT_ID: github_client_id
|
||||||
|
|||||||
@@ -86,15 +86,15 @@ services:
|
|||||||
STORAGE_ACCESS_KEY: minioadmin
|
STORAGE_ACCESS_KEY: minioadmin
|
||||||
STORAGE_SECRET_KEY: minioadmin
|
STORAGE_SECRET_KEY: minioadmin
|
||||||
STORAGE_USE_SSL: false
|
STORAGE_USE_SSL: false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: false
|
||||||
|
|
||||||
# -- Crowdin (Optional) --
|
# -- Crowdin (Optional) --
|
||||||
# CROWDIN_PROJECT_ID:
|
# CROWDIN_PROJECT_ID:
|
||||||
# CROWDIN_PERSONAL_TOKEN:
|
# CROWDIN_PERSONAL_TOKEN:
|
||||||
|
|
||||||
# -- Email (Optional) --
|
# -- Email (Optional) --
|
||||||
# DISABLE_EMAIL_AUTH: true
|
# DISABLE_SIGNUPS: false
|
||||||
# VITE_DISABLE_SIGNUPS: true
|
# DISABLE_EMAIL_AUTH: false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK: false
|
|
||||||
|
|
||||||
# -- GitHub (Optional) --
|
# -- GitHub (Optional) --
|
||||||
# GITHUB_CLIENT_ID: github_client_id
|
# GITHUB_CLIENT_ID: github_client_id
|
||||||
|
|||||||
@@ -106,15 +106,15 @@ services:
|
|||||||
STORAGE_ACCESS_KEY: minioadmin
|
STORAGE_ACCESS_KEY: minioadmin
|
||||||
STORAGE_SECRET_KEY: minioadmin
|
STORAGE_SECRET_KEY: minioadmin
|
||||||
STORAGE_USE_SSL: false
|
STORAGE_USE_SSL: false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: false
|
||||||
|
|
||||||
# -- Crowdin (Optional) --
|
# -- Crowdin (Optional) --
|
||||||
# CROWDIN_PROJECT_ID:
|
# CROWDIN_PROJECT_ID:
|
||||||
# CROWDIN_PERSONAL_TOKEN:
|
# CROWDIN_PERSONAL_TOKEN:
|
||||||
|
|
||||||
# -- Flags (Optional) --
|
# -- Feature Flags (Optional) --
|
||||||
# DISABLE_EMAIL_AUTH: true
|
# DISABLE_SIGNUPS: false
|
||||||
# VITE_DISABLE_SIGNUPS: true
|
# DISABLE_EMAIL_AUTH: false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK: false
|
|
||||||
|
|
||||||
# -- GitHub (Optional) --
|
# -- GitHub (Optional) --
|
||||||
# GITHUB_CLIENT_ID: github_client_id
|
# GITHUB_CLIENT_ID: github_client_id
|
||||||
|
|||||||
@@ -95,15 +95,15 @@ services:
|
|||||||
STORAGE_ACCESS_KEY: minioadmin
|
STORAGE_ACCESS_KEY: minioadmin
|
||||||
STORAGE_SECRET_KEY: minioadmin
|
STORAGE_SECRET_KEY: minioadmin
|
||||||
STORAGE_USE_SSL: false
|
STORAGE_USE_SSL: false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: false
|
||||||
|
|
||||||
# -- Crowdin (Optional) --
|
# -- Crowdin (Optional) --
|
||||||
# CROWDIN_PROJECT_ID:
|
# CROWDIN_PROJECT_ID:
|
||||||
# CROWDIN_PERSONAL_TOKEN:
|
# CROWDIN_PERSONAL_TOKEN:
|
||||||
|
|
||||||
# -- Flags (Optional) --
|
# -- Feature Flags (Optional) --
|
||||||
# DISABLE_EMAIL_AUTH: true
|
# DISABLE_SIGNUPS: false
|
||||||
# VITE_DISABLE_SIGNUPS: true
|
# DISABLE_EMAIL_AUTH: false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK: false
|
|
||||||
|
|
||||||
# -- GitHub (Optional) --
|
# -- GitHub (Optional) --
|
||||||
# GITHUB_CLIENT_ID: github_client_id
|
# GITHUB_CLIENT_ID: github_client_id
|
||||||
|
|||||||
@@ -91,15 +91,15 @@ services:
|
|||||||
STORAGE_ACCESS_KEY: minioadmin
|
STORAGE_ACCESS_KEY: minioadmin
|
||||||
STORAGE_SECRET_KEY: minioadmin
|
STORAGE_SECRET_KEY: minioadmin
|
||||||
STORAGE_USE_SSL: false
|
STORAGE_USE_SSL: false
|
||||||
|
STORAGE_SKIP_BUCKET_CHECK: false
|
||||||
|
|
||||||
# -- Crowdin (Optional) --
|
# -- Crowdin (Optional) --
|
||||||
# CROWDIN_PROJECT_ID:
|
# CROWDIN_PROJECT_ID:
|
||||||
# CROWDIN_PERSONAL_TOKEN:
|
# CROWDIN_PERSONAL_TOKEN:
|
||||||
|
|
||||||
# -- Flags (Optional) --
|
# -- Feature Flags (Optional) --
|
||||||
# DISABLE_EMAIL_AUTH: true
|
# DISABLE_SIGNUPS: false
|
||||||
# VITE_DISABLE_SIGNUPS: true
|
# DISABLE_EMAIL_AUTH: false
|
||||||
# SKIP_STORAGE_BUCKET_CHECK: false
|
|
||||||
|
|
||||||
# -- GitHub (Optional) --
|
# -- GitHub (Optional) --
|
||||||
# GITHUB_CLIENT_ID: github_client_id
|
# GITHUB_CLIENT_ID: github_client_id
|
||||||
|
|||||||
Reference in New Issue
Block a user