mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-07-02 07:20:38 +02:00
feat: add add new config strategy to frontend
This commit is contained in:
parent
1b5e53ff7e
commit
493705e4ef
18
.env.example
18
.env.example
|
@ -1,18 +0,0 @@
|
||||||
# Read what every environment variable does: https://github.com/stonith404/pingvin-share#environment-variables
|
|
||||||
|
|
||||||
# General
|
|
||||||
APP_URL=http://localhost:3000
|
|
||||||
SHOW_HOME_PAGE=true
|
|
||||||
ALLOW_REGISTRATION=true
|
|
||||||
ALLOW_UNAUTHENTICATED_SHARES=false
|
|
||||||
MAX_FILE_SIZE=1000000000
|
|
||||||
|
|
||||||
# Security
|
|
||||||
JWT_SECRET=long-random-string
|
|
||||||
|
|
||||||
# Email
|
|
||||||
EMAIL_RECIPIENTS_ENABLED=false
|
|
||||||
SMTP_HOST=smtp.example.com
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_EMAIL=pingvin-share@example.com
|
|
||||||
SMTP_PASSWORD=example
|
|
|
@ -1,15 +0,0 @@
|
||||||
# General
|
|
||||||
APP_URL=http://localhost:3000
|
|
||||||
ALLOW_REGISTRATION=true
|
|
||||||
MAX_FILE_SIZE=5000000000
|
|
||||||
ALLOW_UNAUTHENTICATED_SHARES=false
|
|
||||||
|
|
||||||
# Security
|
|
||||||
JWT_SECRET=random-string
|
|
||||||
|
|
||||||
# Email
|
|
||||||
EMAIL_RECIPIENTS_ENABLED=false
|
|
||||||
SMTP_HOST=smtp.example.com
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_EMAIL=pingvin-share@example.com
|
|
||||||
SMTP_PASSWORD=example
|
|
|
@ -5,6 +5,7 @@ import { User } from "@prisma/client";
|
||||||
export class AdministratorGuard implements CanActivate {
|
export class AdministratorGuard implements CanActivate {
|
||||||
canActivate(context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const { user }: { user: User } = context.switchToHttp().getRequest();
|
const { user }: { user: User } = context.switchToHttp().getRequest();
|
||||||
|
if (!user) return false;
|
||||||
return user.isAdministrator;
|
return user.isAdministrator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { Controller, Get } from "@nestjs/common";
|
import { Body, Controller, Get, Param, Patch, UseGuards } from "@nestjs/common";
|
||||||
|
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
|
||||||
import { ConfigService } from "./config.service";
|
import { ConfigService } from "./config.service";
|
||||||
|
import { AdminConfigDTO } from "./dto/adminConfig.dto";
|
||||||
import { ConfigDTO } from "./dto/config.dto";
|
import { ConfigDTO } from "./dto/config.dto";
|
||||||
|
import UpdateConfigDTO from "./dto/updateConfig.dto";
|
||||||
|
|
||||||
@Controller("configs")
|
@Controller("configs")
|
||||||
export class ConfigController {
|
export class ConfigController {
|
||||||
|
@ -8,11 +11,22 @@ export class ConfigController {
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async list() {
|
async list() {
|
||||||
return new ConfigDTO().fromList(await this.configService.list())
|
return new ConfigDTO().fromList(await this.configService.list());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("admin")
|
@Get("admin")
|
||||||
|
@UseGuards(AdministratorGuard)
|
||||||
async listForAdmin() {
|
async listForAdmin() {
|
||||||
return await this.configService.listForAdmin();
|
return new AdminConfigDTO().fromList(
|
||||||
|
await this.configService.listForAdmin()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch("admin/:key")
|
||||||
|
@UseGuards(AdministratorGuard)
|
||||||
|
async update(@Param("key") key: string, @Body() data: UpdateConfigDTO) {
|
||||||
|
return new AdminConfigDTO().from(
|
||||||
|
await this.configService.update(key, data.value)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { Inject, Injectable } from "@nestjs/common";
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
} from "@nestjs/common";
|
||||||
import { Config } from "@prisma/client";
|
import { Config } from "@prisma/client";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
|
@ -38,4 +43,23 @@ export class ConfigService {
|
||||||
return configVariable;
|
return configVariable;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update(key: string, value: string | number | boolean) {
|
||||||
|
const configVariable = await this.prisma.config.findUnique({
|
||||||
|
where: { key },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!configVariable || configVariable.locked)
|
||||||
|
throw new NotFoundException("Config variable not found");
|
||||||
|
|
||||||
|
if (typeof value != configVariable.type)
|
||||||
|
throw new BadRequestException(
|
||||||
|
`Config variable must be of type ${configVariable.type}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return await this.prisma.config.update({
|
||||||
|
where: { key },
|
||||||
|
data: { value: value.toString() },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
23
backend/src/config/dto/adminConfig.dto.ts
Normal file
23
backend/src/config/dto/adminConfig.dto.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Expose, plainToClass } from "class-transformer";
|
||||||
|
import { ConfigDTO } from "./config.dto";
|
||||||
|
|
||||||
|
export class AdminConfigDTO extends ConfigDTO {
|
||||||
|
@Expose()
|
||||||
|
default: string;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
secret: boolean;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
from(partial: Partial<AdminConfigDTO>) {
|
||||||
|
return plainToClass(AdminConfigDTO, partial, { excludeExtraneousValues: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fromList(partial: Partial<AdminConfigDTO>[]) {
|
||||||
|
return partial.map((part) =>
|
||||||
|
plainToClass(AdminConfigDTO, part, { excludeExtraneousValues: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
8
backend/src/config/dto/updateConfig.dto.ts
Normal file
8
backend/src/config/dto/updateConfig.dto.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { IsNotEmpty } from "class-validator";
|
||||||
|
|
||||||
|
class UpdateConfigDTO {
|
||||||
|
@IsNotEmpty()
|
||||||
|
value: string | number | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UpdateConfigDTO;
|
|
@ -1,5 +0,0 @@
|
||||||
SHOW_HOME_PAGE=true
|
|
||||||
ALLOW_REGISTRATION=true
|
|
||||||
MAX_FILE_SIZE=1000000000
|
|
||||||
ALLOW_UNAUTHENTICATED_SHARES=false
|
|
||||||
EMAIL_RECIPIENTS_ENABLED=false
|
|
|
@ -1,14 +1,5 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
|
|
||||||
const nextConfig = {
|
|
||||||
publicRuntimeConfig: {
|
|
||||||
ALLOW_REGISTRATION: process.env.ALLOW_REGISTRATION,
|
|
||||||
SHOW_HOME_PAGE: process.env.SHOW_HOME_PAGE,
|
|
||||||
MAX_FILE_SIZE: process.env.MAX_FILE_SIZE,
|
|
||||||
ALLOW_UNAUTHENTICATED_SHARES: process.env.ALLOW_UNAUTHENTICATED_SHARES,
|
|
||||||
EMAIL_RECIPIENTS_ENABLED: process.env.EMAIL_RECIPIENTS_ENABLED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const withPWA = require("next-pwa")({
|
const withPWA = require("next-pwa")({
|
||||||
dest: "public",
|
dest: "public",
|
||||||
|
@ -16,4 +7,4 @@ const withPWA = require("next-pwa")({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = withPWA(nextConfig);
|
module.exports = withPWA();
|
||||||
|
|
|
@ -9,15 +9,15 @@ import {
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
import getConfig from "next/config";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
import useConfig from "../../hooks/config.hook";
|
||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
import toast from "../../utils/toast.util";
|
import toast from "../../utils/toast.util";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const AuthForm = ({ mode }: { mode: "signUp" | "signIn" }) => {
|
const AuthForm = ({ mode }: { mode: "signUp" | "signIn" }) => {
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const validationSchema = yup.object().shape({
|
||||||
email: yup.string().email().required(),
|
email: yup.string().email().required(),
|
||||||
password: yup.string().min(8).required(),
|
password: yup.string().min(8).required(),
|
||||||
|
@ -55,7 +55,7 @@ const AuthForm = ({ mode }: { mode: "signUp" | "signIn" }) => {
|
||||||
>
|
>
|
||||||
{mode == "signUp" ? "Sign up" : "Welcome back"}
|
{mode == "signUp" ? "Sign up" : "Welcome back"}
|
||||||
</Title>
|
</Title>
|
||||||
{publicRuntimeConfig.ALLOW_REGISTRATION == "true" && (
|
{config.get("allowRegistration") && (
|
||||||
<Text color="dimmed" size="sm" align="center" mt={5}>
|
<Text color="dimmed" size="sm" align="center" mt={5}>
|
||||||
{mode == "signUp"
|
{mode == "signUp"
|
||||||
? "You have an account already?"
|
? "You have an account already?"
|
||||||
|
|
|
@ -11,15 +11,13 @@ import {
|
||||||
Transition,
|
Transition,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
import getConfig from "next/config";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
|
import useConfig from "../../hooks/config.hook";
|
||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
import Logo from "../Logo";
|
import Logo from "../Logo";
|
||||||
import ActionAvatar from "./ActionAvatar";
|
import ActionAvatar from "./ActionAvatar";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const HEADER_HEIGHT = 60;
|
const HEADER_HEIGHT = 60;
|
||||||
|
|
||||||
type NavLink = {
|
type NavLink = {
|
||||||
|
@ -110,6 +108,8 @@ const useStyles = createStyles((theme) => ({
|
||||||
|
|
||||||
const NavBar = () => {
|
const NavBar = () => {
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
const [opened, toggleOpened] = useDisclosure(false);
|
const [opened, toggleOpened] = useDisclosure(false);
|
||||||
|
|
||||||
const authenticatedLinks = [
|
const authenticatedLinks = [
|
||||||
|
@ -130,7 +130,7 @@ const NavBar = () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (publicRuntimeConfig.SHOW_HOME_PAGE == "true")
|
if (config.get("showHomePage"))
|
||||||
setUnauthenticatedLinks((array) => [
|
setUnauthenticatedLinks((array) => [
|
||||||
{
|
{
|
||||||
link: "/",
|
link: "/",
|
||||||
|
@ -139,7 +139,7 @@ const NavBar = () => {
|
||||||
...array,
|
...array,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (publicRuntimeConfig.ALLOW_REGISTRATION == "true")
|
if (config.get("allowRegistration"))
|
||||||
setUnauthenticatedLinks((array) => [
|
setUnauthenticatedLinks((array) => [
|
||||||
...array,
|
...array,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import { Button, Center, createStyles, Group, Text } from "@mantine/core";
|
import { Button, Center, createStyles, Group, Text } from "@mantine/core";
|
||||||
import { Dropzone as MantineDropzone } from "@mantine/dropzone";
|
import { Dropzone as MantineDropzone } from "@mantine/dropzone";
|
||||||
import getConfig from "next/config";
|
|
||||||
import { Dispatch, ForwardedRef, SetStateAction, useRef } from "react";
|
import { Dispatch, ForwardedRef, SetStateAction, useRef } from "react";
|
||||||
import { TbCloudUpload, TbUpload } from "react-icons/tb";
|
import { TbCloudUpload, TbUpload } from "react-icons/tb";
|
||||||
|
import useConfig from "../../hooks/config.hook";
|
||||||
import { FileUpload } from "../../types/File.type";
|
import { FileUpload } from "../../types/File.type";
|
||||||
import { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
|
import { byteStringToHumanSizeString } from "../../utils/math/byteStringToHumanSizeString.util";
|
||||||
import toast from "../../utils/toast.util";
|
import toast from "../../utils/toast.util";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
wrapper: {
|
wrapper: {
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
@ -40,12 +38,14 @@ const Dropzone = ({
|
||||||
isUploading: boolean;
|
isUploading: boolean;
|
||||||
setFiles: Dispatch<SetStateAction<FileUpload[]>>;
|
setFiles: Dispatch<SetStateAction<FileUpload[]>>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const openRef = useRef<() => void>();
|
const openRef = useRef<() => void>();
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<MantineDropzone
|
<MantineDropzone
|
||||||
maxSize={parseInt(publicRuntimeConfig.MAX_FILE_SIZE!)}
|
maxSize={parseInt(config.get("maxFileSize"))}
|
||||||
onReject={(e) => {
|
onReject={(e) => {
|
||||||
toast.error(e[0].errors[0].message);
|
toast.error(e[0].errors[0].message);
|
||||||
}}
|
}}
|
||||||
|
@ -75,8 +75,7 @@ const Dropzone = ({
|
||||||
<Text align="center" size="sm" mt="xs" color="dimmed">
|
<Text align="center" size="sm" mt="xs" color="dimmed">
|
||||||
Drag'n'drop files here to start your share. We can accept
|
Drag'n'drop files here to start your share. We can accept
|
||||||
only files that are less than{" "}
|
only files that are less than{" "}
|
||||||
{byteStringToHumanSizeString(publicRuntimeConfig.MAX_FILE_SIZE)} in
|
{byteStringToHumanSizeString(config.get("maxFileSize"))} in size.
|
||||||
size.
|
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</MantineDropzone>
|
</MantineDropzone>
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
import { useModals } from "@mantine/modals";
|
import { useModals } from "@mantine/modals";
|
||||||
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
||||||
import getConfig from "next/config";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { TbAlertCircle } from "react-icons/tb";
|
import { TbAlertCircle } from "react-icons/tb";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
@ -26,11 +25,13 @@ import shareService from "../../../services/share.service";
|
||||||
import { ShareSecurity } from "../../../types/share.type";
|
import { ShareSecurity } from "../../../types/share.type";
|
||||||
import ExpirationPreview from "../ExpirationPreview";
|
import ExpirationPreview from "../ExpirationPreview";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const showCreateUploadModal = (
|
const showCreateUploadModal = (
|
||||||
modals: ModalsContextProps,
|
modals: ModalsContextProps,
|
||||||
isSignedIn: boolean,
|
options: {
|
||||||
|
isUserSignedIn: boolean;
|
||||||
|
allowUnauthenticatedShares: boolean;
|
||||||
|
emailRecipientsEnabled: boolean;
|
||||||
|
},
|
||||||
uploadCallback: (
|
uploadCallback: (
|
||||||
id: string,
|
id: string,
|
||||||
expiration: string,
|
expiration: string,
|
||||||
|
@ -42,7 +43,7 @@ const showCreateUploadModal = (
|
||||||
title: <Title order={4}>Share</Title>,
|
title: <Title order={4}>Share</Title>,
|
||||||
children: (
|
children: (
|
||||||
<CreateUploadModalBody
|
<CreateUploadModalBody
|
||||||
isSignedIn={isSignedIn}
|
options={options}
|
||||||
uploadCallback={uploadCallback}
|
uploadCallback={uploadCallback}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
@ -51,7 +52,7 @@ const showCreateUploadModal = (
|
||||||
|
|
||||||
const CreateUploadModalBody = ({
|
const CreateUploadModalBody = ({
|
||||||
uploadCallback,
|
uploadCallback,
|
||||||
isSignedIn,
|
options,
|
||||||
}: {
|
}: {
|
||||||
uploadCallback: (
|
uploadCallback: (
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -59,12 +60,16 @@ const CreateUploadModalBody = ({
|
||||||
recipients: string[],
|
recipients: string[],
|
||||||
security: ShareSecurity
|
security: ShareSecurity
|
||||||
) => void;
|
) => void;
|
||||||
isSignedIn: boolean;
|
options: {
|
||||||
|
isUserSignedIn: boolean;
|
||||||
|
allowUnauthenticatedShares: boolean;
|
||||||
|
emailRecipientsEnabled: boolean;
|
||||||
|
};
|
||||||
}) => {
|
}) => {
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
|
|
||||||
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(
|
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(
|
||||||
publicRuntimeConfig.ALLOW_UNAUTHENTICATED_SHARES == "true"
|
options.emailRecipientsEnabled
|
||||||
);
|
);
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const validationSchema = yup.object().shape({
|
||||||
|
@ -93,7 +98,7 @@ const CreateUploadModalBody = ({
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Group>
|
<Group>
|
||||||
{showNotSignedInAlert && !isSignedIn && (
|
{showNotSignedInAlert && !options.isUserSignedIn && (
|
||||||
<Alert
|
<Alert
|
||||||
withCloseButton
|
withCloseButton
|
||||||
onClose={() => setShowNotSignedInAlert(false)}
|
onClose={() => setShowNotSignedInAlert(false)}
|
||||||
|
@ -225,7 +230,7 @@ const CreateUploadModalBody = ({
|
||||||
{ExpirationPreview({ form })}
|
{ExpirationPreview({ form })}
|
||||||
</Text>
|
</Text>
|
||||||
<Accordion>
|
<Accordion>
|
||||||
{publicRuntimeConfig.EMAIL_RECIPIENTS_ENABLED == "true" && (
|
{options.emailRecipientsEnabled && (
|
||||||
<Accordion.Item value="recipients" sx={{ borderBottom: "none" }}>
|
<Accordion.Item value="recipients" sx={{ borderBottom: "none" }}>
|
||||||
<Accordion.Control>Email recipients</Accordion.Control>
|
<Accordion.Control>Email recipients</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
|
|
14
frontend/src/hooks/config.hook.ts
Normal file
14
frontend/src/hooks/config.hook.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
import configService from "../services/config.service";
|
||||||
|
import Config from "../types/config.type";
|
||||||
|
|
||||||
|
export const ConfigContext = createContext<Config[] | null>(null);
|
||||||
|
|
||||||
|
const useConfig = () => {
|
||||||
|
const configVariables = useContext(ConfigContext) as Config[];
|
||||||
|
return {
|
||||||
|
get: (key: string) => configService.get(key, configVariables),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useConfig;
|
|
@ -10,11 +10,14 @@ import { NotificationsProvider } from "@mantine/notifications";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Header from "../components/navBar/NavBar";
|
import Header from "../components/navBar/NavBar";
|
||||||
|
import { ConfigContext } from "../hooks/config.hook";
|
||||||
import { UserContext } from "../hooks/user.hook";
|
import { UserContext } from "../hooks/user.hook";
|
||||||
import authService from "../services/auth.service";
|
import authService from "../services/auth.service";
|
||||||
|
import configService from "../services/config.service";
|
||||||
import userService from "../services/user.service";
|
import userService from "../services/user.service";
|
||||||
import GlobalStyle from "../styles/global.style";
|
import GlobalStyle from "../styles/global.style";
|
||||||
import globalStyle from "../styles/mantine.style";
|
import globalStyle from "../styles/mantine.style";
|
||||||
|
import Config from "../types/config.type";
|
||||||
import { CurrentUser } from "../types/user.type";
|
import { CurrentUser } from "../types/user.type";
|
||||||
import { GlobalLoadingContext } from "../utils/loading.util";
|
import { GlobalLoadingContext } from "../utils/loading.util";
|
||||||
|
|
||||||
|
@ -24,9 +27,11 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
const [colorScheme, setColorScheme] = useState<ColorScheme>();
|
const [colorScheme, setColorScheme] = useState<ColorScheme>();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [user, setUser] = useState<CurrentUser | null>(null);
|
const [user, setUser] = useState<CurrentUser | null>(null);
|
||||||
|
const [config, setConfig] = useState<Config[] | null>(null);
|
||||||
|
|
||||||
const getInitalData = async () => {
|
const getInitalData = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
setConfig(await configService.getAll());
|
||||||
await authService.refreshAccessToken();
|
await authService.refreshAccessToken();
|
||||||
setUser(await userService.getCurrentUser());
|
setUser(await userService.getCurrentUser());
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
@ -54,13 +59,15 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoadingOverlay visible overlayOpacity={1} />
|
<LoadingOverlay visible overlayOpacity={1} />
|
||||||
) : (
|
) : (
|
||||||
<UserContext.Provider value={user}>
|
<ConfigContext.Provider value={config}>
|
||||||
<LoadingOverlay visible={isLoading} overlayOpacity={1} />
|
<UserContext.Provider value={user}>
|
||||||
<Header />
|
<LoadingOverlay visible={isLoading} overlayOpacity={1} />
|
||||||
<Container>
|
<Header />
|
||||||
<Component {...pageProps} />
|
<Container>
|
||||||
</Container>
|
<Component {...pageProps} />
|
||||||
</UserContext.Provider>
|
</Container>
|
||||||
|
</UserContext.Provider>{" "}
|
||||||
|
</ConfigContext.Provider>
|
||||||
)}
|
)}
|
||||||
</GlobalLoadingContext.Provider>
|
</GlobalLoadingContext.Provider>
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
|
@ -69,9 +76,4 @@ function App({ Component, pageProps }: AppProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opts out of static site generation to use publicRuntimeConfig
|
|
||||||
App.getInitialProps = () => {
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import getConfig from "next/config";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import AuthForm from "../../components/auth/AuthForm";
|
import AuthForm from "../../components/auth/AuthForm";
|
||||||
import Meta from "../../components/Meta";
|
import Meta from "../../components/Meta";
|
||||||
|
import useConfig from "../../hooks/config.hook";
|
||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const SignUp = () => {
|
const SignUp = () => {
|
||||||
|
const config = useConfig();
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
if (user) {
|
if (user) {
|
||||||
router.replace("/");
|
router.replace("/");
|
||||||
} else if (publicRuntimeConfig.ALLOW_REGISTRATION == "false") {
|
} else if (config.get("allowRegistration") == "false") {
|
||||||
router.replace("/auth/signIn");
|
router.replace("/auth/signIn");
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,16 +8,14 @@ import {
|
||||||
ThemeIcon,
|
ThemeIcon,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import getConfig from "next/config";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { TbCheck } from "react-icons/tb";
|
import { TbCheck } from "react-icons/tb";
|
||||||
import Meta from "../components/Meta";
|
import Meta from "../components/Meta";
|
||||||
|
import useConfig from "../hooks/config.hook";
|
||||||
import useUser from "../hooks/user.hook";
|
import useUser from "../hooks/user.hook";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
inner: {
|
inner: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -71,13 +69,14 @@ const useStyles = createStyles((theme) => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const config = useConfig();
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
|
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
if (user || publicRuntimeConfig.ALLOW_UNAUTHENTICATED_SHARES == "true") {
|
if (user || config.get("allowUnauthenticatedShares")) {
|
||||||
router.replace("/upload");
|
router.replace("/upload");
|
||||||
} else if (publicRuntimeConfig.SHOW_HOME_PAGE == "false") {
|
} else if (!config.get("showHomePage")) {
|
||||||
router.replace("/auth/signIn");
|
router.replace("/auth/signIn");
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Button, Group } from "@mantine/core";
|
import { Button, Group } from "@mantine/core";
|
||||||
import { useModals } from "@mantine/modals";
|
import { useModals } from "@mantine/modals";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import getConfig from "next/config";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Meta from "../components/Meta";
|
import Meta from "../components/Meta";
|
||||||
|
@ -9,13 +8,13 @@ import Dropzone from "../components/upload/Dropzone";
|
||||||
import FileList from "../components/upload/FileList";
|
import FileList from "../components/upload/FileList";
|
||||||
import showCompletedUploadModal from "../components/upload/modals/showCompletedUploadModal";
|
import showCompletedUploadModal from "../components/upload/modals/showCompletedUploadModal";
|
||||||
import showCreateUploadModal from "../components/upload/modals/showCreateUploadModal";
|
import showCreateUploadModal from "../components/upload/modals/showCreateUploadModal";
|
||||||
|
import useConfig from "../hooks/config.hook";
|
||||||
import useUser from "../hooks/user.hook";
|
import useUser from "../hooks/user.hook";
|
||||||
import shareService from "../services/share.service";
|
import shareService from "../services/share.service";
|
||||||
import { FileUpload } from "../types/File.type";
|
import { FileUpload } from "../types/File.type";
|
||||||
import { ShareSecurity } from "../types/share.type";
|
import { ShareSecurity } from "../types/share.type";
|
||||||
import toast from "../utils/toast.util";
|
import toast from "../utils/toast.util";
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
let share: any;
|
let share: any;
|
||||||
|
|
||||||
const Upload = () => {
|
const Upload = () => {
|
||||||
|
@ -23,6 +22,7 @@ const Upload = () => {
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
|
|
||||||
const user = useUser();
|
const user = useUser();
|
||||||
|
const config = useConfig();
|
||||||
const [files, setFiles] = useState<FileUpload[]>([]);
|
const [files, setFiles] = useState<FileUpload[]>([]);
|
||||||
const [isUploading, setisUploading] = useState(false);
|
const [isUploading, setisUploading] = useState(false);
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ const Upload = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [files]);
|
}, [files]);
|
||||||
if (!user && publicRuntimeConfig.ALLOW_UNAUTHENTICATED_SHARES == "false") {
|
if (!user && !config.get("allowUnauthenticatedShares")) {
|
||||||
router.replace("/");
|
router.replace("/");
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
@ -106,7 +106,17 @@ const Upload = () => {
|
||||||
loading={isUploading}
|
loading={isUploading}
|
||||||
disabled={files.length <= 0}
|
disabled={files.length <= 0}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
showCreateUploadModal(modals, user ? true : false, uploadFiles)
|
showCreateUploadModal(
|
||||||
|
modals,
|
||||||
|
{
|
||||||
|
isUserSignedIn: user ? true : false,
|
||||||
|
allowUnauthenticatedShares: config.get(
|
||||||
|
"allowUnauthenticatedShares"
|
||||||
|
),
|
||||||
|
emailRecipientsEnabled: config.get("emailRecipientsEnabled"),
|
||||||
|
},
|
||||||
|
uploadFiles
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Share
|
Share
|
||||||
|
|
23
frontend/src/services/config.service.ts
Normal file
23
frontend/src/services/config.service.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import Config from "../types/config.type";
|
||||||
|
import api from "./api.service";
|
||||||
|
|
||||||
|
const getAll = async (): Promise<Config[]> => {
|
||||||
|
return (await api.get("/configs")).data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const get = (key: string, configVariables: Config[]): any => {
|
||||||
|
const configVariable = configVariables.filter(
|
||||||
|
(variable) => variable.key == key
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
||||||
|
|
||||||
|
if (configVariable.type == "number") return parseInt(configVariable.value);
|
||||||
|
if (configVariable.type == "boolean") return configVariable.value == "true";
|
||||||
|
if (configVariable.type == "string") return configVariable.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAll,
|
||||||
|
get,
|
||||||
|
};
|
7
frontend/src/types/config.type.ts
Normal file
7
frontend/src/types/config.type.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
type Config = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Config;
|
Loading…
Reference in New Issue
Block a user