feat: v3.2.0 - custom themes & curated themes
This commit is contained in:
parent
47db6cf1bd
commit
c5cef56e2a
17 changed files with 372 additions and 33 deletions
|
@ -17,9 +17,8 @@ Fast & lightweight file uploading.
|
|||
- Configurable
|
||||
- Fast
|
||||
- Built with Next.js & React
|
||||
- Support for **multible database types**
|
||||
- Token protected uploading
|
||||
- Easy setup instructions on [docs](https://zipline.diced.me)
|
||||
- Easy setup instructions on [docs](https://zipline.diced.me) (One command install `docker-compose up`)
|
||||
|
||||
# Installing
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "zip3",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"scripts": {
|
||||
"prepare": "husky install",
|
||||
"dev": "NODE_ENV=development node server",
|
||||
|
|
25
prisma/migrations/20210826034827_custom_themes/migration.sql
Normal file
25
prisma/migrations/20210826034827_custom_themes/migration.sql
Normal file
|
@ -0,0 +1,25 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "systemTheme" TEXT NOT NULL DEFAULT E'dark_blue';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Theme" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"primary" TEXT NOT NULL,
|
||||
"secondary" TEXT NOT NULL,
|
||||
"error" TEXT NOT NULL,
|
||||
"warning" TEXT NOT NULL,
|
||||
"info" TEXT NOT NULL,
|
||||
"border" TEXT NOT NULL,
|
||||
"mainBackground" TEXT NOT NULL,
|
||||
"paperBackground" TEXT NOT NULL,
|
||||
"userId" INTEGER NOT NULL,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Theme_userId_unique" ON "Theme"("userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Theme" ADD FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -13,12 +13,29 @@ model User {
|
|||
password String
|
||||
token String
|
||||
administrator Boolean @default(false)
|
||||
systemTheme String @default("dark_blue")
|
||||
customTheme Theme?
|
||||
embedTitle String?
|
||||
embedColor String @default("#2f3136")
|
||||
images Image[]
|
||||
urls Url[]
|
||||
}
|
||||
|
||||
model Theme {
|
||||
id Int @id @default(autoincrement())
|
||||
type String
|
||||
primary String
|
||||
secondary String
|
||||
error String
|
||||
warning String
|
||||
info String
|
||||
border String
|
||||
mainBackground String
|
||||
paperBackground String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
}
|
||||
|
||||
model Image {
|
||||
id Int @id @default(autoincrement())
|
||||
file String
|
||||
|
|
|
@ -3,9 +3,8 @@ const prismaRun = require('./prisma-run');
|
|||
|
||||
module.exports = async (config) => {
|
||||
try {
|
||||
await prismaRun(config.database.url, ['migrate', 'deploy', '--schema=prisma/schema.prisma']);
|
||||
await prismaRun(config.database.url, ['generate', '--schema=prisma/schema.prisma']);
|
||||
await prismaRun(config.database.url, ['db', 'seed', '--preview-feature', '--schema=prisma/schema.prisma']);
|
||||
await prismaRun(config.database.url, ['migrate', 'deploy']);
|
||||
await prismaRun(config.database.url, ['generate']);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
Logger.get('db').error('there was an error.. exiting..');
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
Select,
|
||||
} from '@material-ui/core';
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
|
@ -33,10 +34,15 @@ import {
|
|||
ContentCopy as CopyIcon,
|
||||
Autorenew as ResetIcon,
|
||||
Logout as LogoutIcon,
|
||||
PeopleAlt as UsersIcon
|
||||
PeopleAlt as UsersIcon,
|
||||
Brush as BrushIcon
|
||||
} from '@material-ui/icons';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import Backdrop from './Backdrop';
|
||||
import { friendlyThemeName, themes } from './Theming';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useStoreDispatch } from 'lib/redux/store';
|
||||
import { updateUser } from 'lib/redux/reducers/user';
|
||||
|
||||
const items = [
|
||||
{
|
||||
|
@ -122,11 +128,14 @@ function ResetTokenDialog({ open, setOpen, setToken }) {
|
|||
}
|
||||
|
||||
export default function Layout({ children, user, loading, noPaper }) {
|
||||
const [systemTheme, setSystemTheme] = useState(user.systemTheme || 'dark_blue');
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [copyOpen, setCopyOpen] = useState(false);
|
||||
const [resetOpen, setResetOpen] = useState(false);
|
||||
const [token, setToken] = useState(user?.token);
|
||||
const router = useRouter();
|
||||
const dispatch = useStoreDispatch();
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = e => setAnchorEl(e.currentTarget);
|
||||
|
@ -142,6 +151,17 @@ export default function Layout({ children, user, loading, noPaper }) {
|
|||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleUpdateTheme = async (event: React.ChangeEvent<{ value: string }>) => {
|
||||
const newUser = await useFetch('/api/user', 'PATCH', {
|
||||
systemTheme: event.target.value || 'dark_blue'
|
||||
});
|
||||
|
||||
setSystemTheme(newUser.systemTheme);
|
||||
dispatch(updateUser(newUser));
|
||||
|
||||
router.replace(router.pathname);
|
||||
};
|
||||
|
||||
const drawer = (
|
||||
<div>
|
||||
<CopyTokenDialog open={copyOpen} setOpen={setCopyOpen} token={token} />
|
||||
|
@ -221,6 +241,22 @@ export default function Layout({ children, user, loading, noPaper }) {
|
|||
</MenuItem>
|
||||
</a>
|
||||
</Link>
|
||||
<MenuItem>
|
||||
<BrushIcon sx={{ mr: 2 }} />
|
||||
<Select
|
||||
variant='standard'
|
||||
label='Theme'
|
||||
value={systemTheme}
|
||||
onChange={handleUpdateTheme}
|
||||
fullWidth
|
||||
>
|
||||
{Object.keys(themes).map(t => (
|
||||
<MenuItem value={t} key={t}>
|
||||
{friendlyThemeName[t]}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
)}
|
||||
|
|
53
src/components/Theming.tsx
Normal file
53
src/components/Theming.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from 'react';
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { CssBaseline } from '@material-ui/core';
|
||||
import dark_blue from 'lib/themes/dark_blue';
|
||||
import dark from 'lib/themes/dark';
|
||||
import ayu_dark from 'lib/themes/ayu_dark';
|
||||
import { useStoreSelector } from 'lib/redux/store';
|
||||
import createTheme from 'lib/themes';
|
||||
|
||||
export const themes = {
|
||||
'dark_blue': dark_blue,
|
||||
'dark': dark,
|
||||
'ayu_dark': ayu_dark
|
||||
};
|
||||
|
||||
export const friendlyThemeName = {
|
||||
'dark_blue': 'Dark Blue',
|
||||
'dark': 'Very Dark',
|
||||
'ayu_dark': 'Ayu Dark'
|
||||
};
|
||||
|
||||
export default function ZiplineTheming({ Component, pageProps }) {
|
||||
let t;
|
||||
|
||||
const user = useStoreSelector(state => state.user);
|
||||
if (!user) t = themes.dark_blue;
|
||||
else {
|
||||
if (user.customTheme) {
|
||||
t = createTheme({
|
||||
type: 'dark',
|
||||
primary: user.customTheme.primary,
|
||||
secondary: user.customTheme.secondary,
|
||||
error: user.customTheme.error,
|
||||
warning: user.customTheme.warning,
|
||||
info: user.customTheme.info,
|
||||
border: user.customTheme.border,
|
||||
background: {
|
||||
main: user.customTheme.mainBackground,
|
||||
paper: user.customTheme.paperBackground
|
||||
}
|
||||
});
|
||||
} else {
|
||||
t = themes[user.systemTheme] ?? themes.dark_blue;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={t}>
|
||||
<CssBaseline />
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
|
@ -24,6 +24,7 @@ type Aligns = 'inherit' | 'right' | 'left' | 'center' | 'justify';
|
|||
|
||||
export function bytesToRead(bytes: number) {
|
||||
if (isNaN(bytes)) return '0.0 B';
|
||||
if (bytes === Infinity) return '0.0 B';
|
||||
const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
||||
let num = 0;
|
||||
|
||||
|
@ -96,7 +97,7 @@ export default function Dashboard() {
|
|||
const imgs = await useFetch('/api/user/images');
|
||||
const stts = await useFetch('/api/stats');
|
||||
setImages(imgs);
|
||||
setStats(stts);
|
||||
setStats(stts);console.log(stts);
|
||||
|
||||
setApiLoading(false);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { TextField, Button, Box, Typography } from '@material-ui/core';
|
||||
import { TextField, Button, Box, Typography, Select, MenuItem } from '@material-ui/core';
|
||||
|
||||
import { useFormik } from 'formik';
|
||||
import * as yup from 'yup';
|
||||
|
@ -8,6 +8,7 @@ import Backdrop from 'components/Backdrop';
|
|||
import Alert from 'components/Alert';
|
||||
import { useStoreDispatch, useStoreSelector } from 'lib/redux/store';
|
||||
import { updateUser } from 'lib/redux/reducers/user';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const validationSchema = yup.object({
|
||||
username: yup
|
||||
|
@ -15,6 +16,45 @@ const validationSchema = yup.object({
|
|||
.required('Username is required')
|
||||
});
|
||||
|
||||
const themeValidationSchema = yup.object({
|
||||
type: yup
|
||||
.string()
|
||||
.required('Type (dark, light) is required is required'),
|
||||
primary: yup
|
||||
.string()
|
||||
.required('Primary color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
secondary: yup
|
||||
.string()
|
||||
.required('Secondary color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
error: yup
|
||||
.string()
|
||||
.required('Error color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
warning: yup
|
||||
.string()
|
||||
.required('Warning color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
info: yup
|
||||
.string()
|
||||
.required('Info color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
border: yup
|
||||
.string()
|
||||
.required('Border color is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
mainBackground: yup
|
||||
.string()
|
||||
.required('Main Background is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
paperBackground: yup
|
||||
.string()
|
||||
.required('Paper Background is required')
|
||||
.matches(/\#[0-9A-Fa-f]{6}/g, { message: 'Not a valid hex color' }),
|
||||
|
||||
});
|
||||
|
||||
function TextInput({ id, label, formik, ...other }) {
|
||||
return (
|
||||
<TextField
|
||||
|
@ -36,6 +76,7 @@ function TextInput({ id, label, formik, ...other }) {
|
|||
export default function Manage() {
|
||||
const user = useStoreSelector(state => state.user);
|
||||
const dispatch = useStoreDispatch();
|
||||
const router = useRouter();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
@ -84,6 +125,41 @@ export default function Manage() {
|
|||
}
|
||||
});
|
||||
|
||||
const customThemeFormik = useFormik({
|
||||
initialValues: {
|
||||
type: user.customTheme?.type || 'dark',
|
||||
primary: user.customTheme?.primary || '',
|
||||
secondary: user.customTheme?.secondary || '',
|
||||
error: user.customTheme?.error || '',
|
||||
warning: user.customTheme?.warning || '',
|
||||
info: user.customTheme?.info || '',
|
||||
border: user.customTheme?.border || '',
|
||||
mainBackground: user.customTheme?.mainBackground || '',
|
||||
paperBackground: user.customTheme?.paperBackground || '',
|
||||
},
|
||||
validationSchema: themeValidationSchema,
|
||||
onSubmit: async values => {
|
||||
setLoading(true);
|
||||
|
||||
const newUser = await useFetch('/api/user', 'PATCH', { customTheme: values });
|
||||
console.log(newUser);
|
||||
|
||||
if (newUser.error) {
|
||||
setLoading(false);
|
||||
setMessage('An error occured');
|
||||
setSeverity('error');
|
||||
setOpen(true);
|
||||
} else {
|
||||
dispatch(updateUser(newUser));
|
||||
router.replace(router.pathname);
|
||||
setLoading(false);
|
||||
setMessage('Saved theme');
|
||||
setSeverity('success');
|
||||
setOpen(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop open={loading}/>
|
||||
|
@ -104,7 +180,42 @@ export default function Manage() {
|
|||
<Button
|
||||
variant='contained'
|
||||
type='submit'
|
||||
>Save</Button>
|
||||
>Save User</Button>
|
||||
</Box>
|
||||
</form>
|
||||
<Typography variant='h4' py={2}>Manage Theme</Typography>
|
||||
<form onSubmit={customThemeFormik.handleSubmit}>
|
||||
<Select
|
||||
id='type'
|
||||
name='type'
|
||||
label='Type'
|
||||
value={customThemeFormik.values['type']}
|
||||
onChange={customThemeFormik.handleChange}
|
||||
error={customThemeFormik.touched['type'] && Boolean(customThemeFormik.errors['type'])}
|
||||
variant='standard'
|
||||
fullWidth
|
||||
>
|
||||
<MenuItem value='dark'>Dark Theme</MenuItem>
|
||||
<MenuItem value='light'>Light Theme</MenuItem>
|
||||
</Select>
|
||||
<TextInput id='primary' label='Primary Color' formik={customThemeFormik} />
|
||||
<TextInput id='secondary' label='Secondary Color' formik={customThemeFormik} />
|
||||
<TextInput id='error' label='Error Color' formik={customThemeFormik} />
|
||||
<TextInput id='warning' label='Warning Color' formik={customThemeFormik} />
|
||||
<TextInput id='info' label='Info Color' formik={customThemeFormik} />
|
||||
<TextInput id='border' label='Border Color' formik={customThemeFormik} />
|
||||
<TextInput id='mainBackground' label='Main Background' formik={customThemeFormik} />
|
||||
<TextInput id='paperBackground' label='Paper Background' formik={customThemeFormik} />
|
||||
<Box
|
||||
display='flex'
|
||||
justifyContent='right'
|
||||
alignItems='right'
|
||||
pt={2}
|
||||
>
|
||||
<Button
|
||||
variant='contained'
|
||||
type='submit'
|
||||
>Save Theme</Button>
|
||||
</Box>
|
||||
</form>
|
||||
</>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { CookieSerializeOptions } from 'cookie';
|
||||
import type { User } from '@prisma/client';
|
||||
import type { Image, Theme, User } from '@prisma/client';
|
||||
|
||||
import { serialize } from 'cookie';
|
||||
import { sign64, unsign64 } from '../util';
|
||||
|
@ -17,7 +17,18 @@ export interface NextApiFile {
|
|||
}
|
||||
|
||||
export type NextApiReq = NextApiRequest & {
|
||||
user: () => Promise<User | null | void>;
|
||||
user: () => Promise<{
|
||||
username: string;
|
||||
token: string;
|
||||
embedTitle: string;
|
||||
embedColor: string;
|
||||
systemTheme: string;
|
||||
customTheme?: Theme;
|
||||
administrator: boolean;
|
||||
id: number;
|
||||
images: Image[];
|
||||
password: string;
|
||||
} | null | void>;
|
||||
getCookie: (name: string) => string | null;
|
||||
cleanCookie: (name: string) => void;
|
||||
file?: NextApiFile;
|
||||
|
@ -83,6 +94,18 @@ export const withZipline = (handler: (req: NextApiRequest, res: NextApiResponse)
|
|||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id: Number(userId)
|
||||
},
|
||||
select: {
|
||||
administrator: true,
|
||||
embedColor: true,
|
||||
embedTitle: true,
|
||||
id: true,
|
||||
images: true,
|
||||
password: true,
|
||||
systemTheme: true,
|
||||
customTheme: true,
|
||||
token: true,
|
||||
username: true
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Theme } from '@prisma/client';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
export interface User {
|
||||
|
@ -5,6 +6,8 @@ export interface User {
|
|||
token: string;
|
||||
embedTitle: string;
|
||||
embedColor: string;
|
||||
systemTheme: string;
|
||||
customTheme?: Theme;
|
||||
}
|
||||
|
||||
const initialState: User = null;
|
||||
|
|
17
src/lib/themes/ayu_dark.ts
Normal file
17
src/lib/themes/ayu_dark.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// https://github.com/AlphaNecron/
|
||||
// https://github.com/ayu-theme/ayu-colors
|
||||
import createTheme from '.';
|
||||
|
||||
export default createTheme({
|
||||
type: 'dark',
|
||||
primary: '#E6B450',
|
||||
secondary: '#FFEE99',
|
||||
error: '#FF3333',
|
||||
warning: '#F29668',
|
||||
info: '#95E6CB',
|
||||
border: '#0A0E14',
|
||||
background: {
|
||||
main: '#0D1016',
|
||||
paper: '#0A0E14'
|
||||
}
|
||||
});
|
15
src/lib/themes/dark.ts
Normal file
15
src/lib/themes/dark.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import createTheme from '.';
|
||||
|
||||
export default createTheme({
|
||||
type: 'dark',
|
||||
primary: '#2c39a6',
|
||||
secondary: '#7344e2',
|
||||
error: '#ff4141',
|
||||
warning: '#ff9800',
|
||||
info: '#2f6fb9',
|
||||
border: '#2b2b2b',
|
||||
background: {
|
||||
main: '#000000',
|
||||
paper: '#060606'
|
||||
}
|
||||
});
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import { Provider } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import Head from 'next/head';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import theme from 'lib/themes/dark_blue';
|
||||
import Theming from 'components/Theming';
|
||||
import { useStore } from 'lib/redux/store';
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
|
@ -22,10 +20,10 @@ export default function MyApp({ Component, pageProps }) {
|
|||
<meta name='description' content='Zipline' />
|
||||
<meta name='viewport' content='minimum-scale=1, initial-scale=1, width=device-width' />
|
||||
</Head>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
<Theming
|
||||
Component={Component}
|
||||
pageProps={pageProps}
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,18 @@ import prisma from 'lib/prisma';
|
|||
import { NextApiReq, NextApiRes, withZipline } from 'middleware/withZipline';
|
||||
import { checkPassword } from 'lib/util';
|
||||
import Logger from 'lib/logger';
|
||||
import prismaRun from '../../../../scripts/prisma-run';
|
||||
import config from 'lib/config';
|
||||
|
||||
async function handler(req: NextApiReq, res: NextApiRes) {
|
||||
if (req.method !== 'POST') return res.status(405).end();
|
||||
const { username, password } = req.body as { username: string, password: string };
|
||||
|
||||
const users = await prisma.user.findMany();
|
||||
if (users.length === 0) {
|
||||
await prismaRun(config.database.url, ['db', 'seed', '--preview-feature']);
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
username
|
||||
|
|
|
@ -27,7 +27,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
} else {
|
||||
const images = await prisma.image.findMany({
|
||||
where: {
|
||||
user
|
||||
userId: user.id
|
||||
},
|
||||
select: {
|
||||
created_at: true,
|
||||
|
|
|
@ -31,14 +31,49 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
data: { embedColor: req.body.embedColor }
|
||||
});
|
||||
|
||||
if (req.body.systemTheme) await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { systemTheme: req.body.systemTheme }
|
||||
});
|
||||
|
||||
if (req.body.customTheme) {
|
||||
if (user.customTheme) await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
customTheme: {
|
||||
update: {
|
||||
...req.body.customTheme
|
||||
}
|
||||
}
|
||||
}
|
||||
}); else await prisma.theme.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
...req.body.customTheme
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const newUser = await prisma.user.findFirst({
|
||||
where: { id: user.id }
|
||||
where: {
|
||||
id: Number(user.id)
|
||||
},
|
||||
select: {
|
||||
administrator: true,
|
||||
embedColor: true,
|
||||
embedTitle: true,
|
||||
id: true,
|
||||
images: false,
|
||||
password: false,
|
||||
systemTheme: true,
|
||||
customTheme: true,
|
||||
token: true,
|
||||
username: true
|
||||
}
|
||||
});
|
||||
|
||||
Logger.get('user').info(`User ${user.username} (${newUser.username}) (${newUser.id}) was updated`);
|
||||
|
||||
delete newUser.password;
|
||||
|
||||
return res.json(newUser);
|
||||
} else {
|
||||
delete user.password;
|
||||
|
|
Loading…
Reference in a new issue