feat: external links & bug fixes
This commit is contained in:
parent
45541a3cdd
commit
a454a4f4a8
22 changed files with 130 additions and 169 deletions
|
@ -11,6 +11,14 @@ module.exports = {
|
|||
},
|
||||
];
|
||||
},
|
||||
webpack(config) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
fs: false, // the solution
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
poweredByHeader: false,
|
||||
reactStrictMode: true,
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "zipline",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "REACT_EDITOR=code NODE_ENV=development tsx src/server",
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { PrismaClient } from '@prisma/client';
|
||||
import { hashPassword, createToken } from '../src/lib/util';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username: 'administrator',
|
||||
password: await hashPassword('password'),
|
||||
token: createToken(),
|
||||
administrator: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`
|
||||
When logging into Zipline for the first time, use these credentials:
|
||||
|
||||
Username: "${user.username}"
|
||||
Password: "password"
|
||||
`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
|
@ -8,7 +8,7 @@ import { useStoreDispatch } from 'lib/redux/store';
|
|||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ActivityIcon, CheckIcon, CopyIcon, CrossIcon, DeleteIcon, FileIcon, HomeIcon, LinkIcon, LogoutIcon, PencilIcon, SettingsIcon, TagIcon, TypeIcon, UploadIcon, UserIcon } from './icons';
|
||||
import { ExternalLinkIcon, ActivityIcon, CheckIcon, CopyIcon, CrossIcon, DeleteIcon, FileIcon, HomeIcon, LinkIcon, LogoutIcon, PencilIcon, SettingsIcon, TagIcon, TypeIcon, UploadIcon, UserIcon } from './icons';
|
||||
import { friendlyThemeName, themes } from './Theming';
|
||||
|
||||
function MenuItemLink(props) {
|
||||
|
@ -108,7 +108,10 @@ const admin_items = [
|
|||
},
|
||||
];
|
||||
|
||||
export default function Layout({ children, user, title }) {
|
||||
export default function Layout({ children, user, props }) {
|
||||
const { title } = props;
|
||||
const external_links = JSON.parse(props.external_links ?? '[]');
|
||||
|
||||
const [token, setToken] = useState(user?.token);
|
||||
const [systemTheme, setSystemTheme] = useState(user.systemTheme ?? 'system');
|
||||
const [version, setVersion] = useState<{ local: string, upstream: string }>(null);
|
||||
|
@ -248,6 +251,19 @@ export default function Layout({ children, user, title }) {
|
|||
</NavLink>
|
||||
)}
|
||||
</Navbar.Section>
|
||||
<Navbar.Section>
|
||||
{external_links.length ? external_links.map(({ label, link }, i) => (
|
||||
<Link href={link} passHref key={i}>
|
||||
<NavLink
|
||||
label={label}
|
||||
component='a'
|
||||
target='_blank'
|
||||
variant='light'
|
||||
icon={<ExternalLinkIcon />}
|
||||
/>
|
||||
</Link>
|
||||
)) : null}
|
||||
</Navbar.Section>
|
||||
{version ? (
|
||||
<Navbar.Section>
|
||||
<Tooltip
|
||||
|
@ -319,8 +335,8 @@ export default function Layout({ children, user, title }) {
|
|||
{user.username}
|
||||
</Text>
|
||||
<MenuItemLink icon={<SettingsIcon />} href='/dashboard/manage'>Manage Account</MenuItemLink>
|
||||
<MenuItem icon={<CopyIcon />} onClick={() => {setOpen(false);openCopyToken();}}>Copy Token</MenuItem>
|
||||
<MenuItem icon={<DeleteIcon />} onClick={() => {setOpen(false);openResetToken();}} color='red'>Reset Token</MenuItem>
|
||||
<MenuItem icon={<CopyIcon />} onClick={() => { setOpen(false); openCopyToken(); }}>Copy Token</MenuItem>
|
||||
<MenuItem icon={<DeleteIcon />} onClick={() => { setOpen(false); openResetToken(); }} color='red'>Reset Token</MenuItem>
|
||||
<MenuItemLink icon={<LogoutIcon />} href='/auth/logout' color='red'>Logout</MenuItemLink>
|
||||
<Divider
|
||||
variant='solid'
|
||||
|
|
5
src/components/icons/ExternalLinkIcon.tsx
Normal file
5
src/components/icons/ExternalLinkIcon.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { ExternalLink } from 'react-feather';
|
||||
|
||||
export default function ExternalLinkIcon({ ...props }) {
|
||||
return <ExternalLink size={15} {...props} />;
|
||||
}
|
|
@ -23,6 +23,7 @@ import CalendarIcon from './CalendarIcon';
|
|||
import HashIcon from './HashIcon';
|
||||
import TagIcon from './TagIcon';
|
||||
import ClockIcon from './ClockIcon';
|
||||
import ExternalLinkIcon from './ExternalLinkIcon';
|
||||
|
||||
export {
|
||||
ActivityIcon,
|
||||
|
@ -50,4 +51,5 @@ export {
|
|||
HashIcon,
|
||||
TagIcon,
|
||||
ClockIcon,
|
||||
ExternalLinkIcon,
|
||||
};
|
|
@ -62,6 +62,13 @@ export interface ConfigWebsite {
|
|||
title: string;
|
||||
show_files_per_user: boolean;
|
||||
show_version: boolean;
|
||||
|
||||
external_links: ConfigWebsiteExternalLinks[];
|
||||
}
|
||||
|
||||
export interface ConfigWebsiteExternalLinks {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ConfigDiscord {
|
||||
|
|
|
@ -24,7 +24,7 @@ function set(object: Record<string, any>, property: string, value: any) {
|
|||
return object;
|
||||
}
|
||||
|
||||
function map(env: string, type: 'string' | 'number' | 'boolean' | 'array', path: string) {
|
||||
function map(env: string, type: 'string' | 'number' | 'boolean' | 'array' | 'json-array', path: string) {
|
||||
return {
|
||||
env,
|
||||
type,
|
||||
|
@ -85,6 +85,7 @@ export default function readConfig() {
|
|||
map('WEBSITE_TITLE', 'string', 'website.title'),
|
||||
map('WEBSITE_SHOW_FILES_PER_USER', 'boolean', 'website.show_files_per_user'),
|
||||
map('WEBSITE_SHOW_VERSION', 'boolean', 'website.show_version'),
|
||||
map('WEBSITE_EXTERNAL_LINKS', 'json-array', 'website.external_links'),
|
||||
|
||||
map('DISCORD_URL', 'string', 'discord.url'),
|
||||
map('DISCORD_USERNAME', 'string', 'discord.username'),
|
||||
|
@ -128,6 +129,12 @@ export default function readConfig() {
|
|||
case 'boolean':
|
||||
parsed = value === 'true';
|
||||
break;
|
||||
case 'json-array':
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
} catch (e) {
|
||||
parsed = [];
|
||||
}
|
||||
default:
|
||||
parsed = value;
|
||||
};
|
||||
|
|
|
@ -68,6 +68,13 @@ const validator = object({
|
|||
title: string().default('Zipline'),
|
||||
show_files_per_user: boolean().default(true),
|
||||
show_version: boolean().default(true),
|
||||
external_links: array(object({
|
||||
label: string(),
|
||||
link: string(),
|
||||
})).default([
|
||||
{ label: 'Zipline', link: 'https://github.com/diced/zipline' },
|
||||
{ label: 'Documentation', link: 'https://zipline.diced.tech/' },
|
||||
]),
|
||||
}),
|
||||
discord: object({
|
||||
url: string(),
|
||||
|
|
11
src/lib/middleware/getServerSideProps.ts
Normal file
11
src/lib/middleware/getServerSideProps.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import config from 'lib/config';
|
||||
import { GetServerSideProps } from 'next';
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async context => {
|
||||
return {
|
||||
props: {
|
||||
title: config.website.title,
|
||||
external_links: JSON.stringify(config.website.external_links),
|
||||
},
|
||||
};
|
||||
};
|
|
@ -46,7 +46,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
|
||||
Logger.get('url').info(`User ${user.username} (${user.id}) shortenned a url ${url.destination} (${url.id})`);
|
||||
|
||||
if (config.discord.shorten) {
|
||||
if (config.discord?.shorten) {
|
||||
await sendShorten(user, url, `${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${zconfig.urls.route}/${req.body.vanity ? req.body.vanity : invis ? invis.invis : url.id}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ async function handler(req: NextApiReq, res: NextApiRes) {
|
|||
response.files.push(`${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${zconfig.uploader.route === '/' ? '' : zconfig.uploader.route}/${invis ? invis.invis : image.file}`);
|
||||
}
|
||||
|
||||
if (zconfig.discord.upload) {
|
||||
if (zconfig.discord?.upload) {
|
||||
await sendUpload(user, image, `${zconfig.core.https ? 'https' : 'http'}://${req.headers.host}${zconfig.uploader.route === '/' ? '' : zconfig.uploader.route}/${invis ? invis.invis : image.file}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Files from 'components/pages/Files';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function FilesPage({ title }) {
|
||||
export default function FilesPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,23 +14,15 @@ export default function FilesPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Files</title>
|
||||
<title>{props.title} - Files</title>
|
||||
</Head>
|
||||
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Files />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Dashboard from 'components/pages/Dashboard';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function DashboardPage({ title, meta }) {
|
||||
export default function DashboardPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function DashboardPage({ title, meta }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
<title>{props.title}</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Dashboard />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -4,9 +4,9 @@ import Layout from 'components/Layout';
|
|||
import Invites from 'components/pages/Invites';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import Head from 'next/head';
|
||||
import { GetServerSideProps } from 'next';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function InvitesPage({ title }) {
|
||||
export default function InvitesPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function InvitesPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Invites</title>
|
||||
<title>{props.title} - Invites</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Invites />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Manage from 'components/pages/Manage';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function ManagePage({ title }) {
|
||||
export default function ManagePage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function ManagePage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Manage User</title>
|
||||
<title>{props.title} - Manage User</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Manage />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Stats from 'components/pages/Stats';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function StatsPage({ title }) {
|
||||
export default function StatsPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function StatsPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Stats</title>
|
||||
<title>{props.title} - Stats</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Stats />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import UploadText from 'components/pages/UploadText';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function UploadTextPage({ title }) {
|
||||
export default function UploadTextPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function UploadTextPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Upload Text</title>
|
||||
<title>{props.title} - Upload Text</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<UploadText/>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Upload from 'components/pages/Upload';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function UploadPage({ title }) {
|
||||
export default function UploadPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function UploadPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Upload</title>
|
||||
<title>{props.title} - Upload</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Upload/>
|
||||
<Upload />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Urls from 'components/pages/Urls';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function UrlsPage({ title }) {
|
||||
export default function UrlsPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function UrlsPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - URLs</title>
|
||||
<title>{props.title} - URLs</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Urls />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -3,10 +3,10 @@ import useLogin from 'hooks/useLogin';
|
|||
import Layout from 'components/Layout';
|
||||
import Users from 'components/pages/Users';
|
||||
import { LoadingOverlay } from '@mantine/core';
|
||||
import { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
export { getServerSideProps } from 'middleware/getServerSideProps';
|
||||
|
||||
export default function UsersPage({ title }) {
|
||||
export default function UsersPage(props) {
|
||||
const { user, loading } = useLogin();
|
||||
|
||||
if (loading) return <LoadingOverlay visible={loading} />;
|
||||
|
@ -14,22 +14,14 @@ export default function UsersPage({ title }) {
|
|||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title} - Users</title>
|
||||
<title>{props.title} - Users</title>
|
||||
</Head>
|
||||
<Layout
|
||||
user={user}
|
||||
title={title}
|
||||
props={props}
|
||||
>
|
||||
<Users />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async () => {
|
||||
return {
|
||||
props: {
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -10,6 +10,7 @@ import { useStoreDispatch } from 'lib/redux/store';
|
|||
import { updateUser } from 'lib/redux/reducers/user';
|
||||
import { useRouter } from 'next/router';
|
||||
import Head from 'next/head';
|
||||
import config from 'lib/config';
|
||||
|
||||
export default function Invite({ code, title }) {
|
||||
const [active, setActive] = useState(0);
|
||||
|
@ -146,8 +147,8 @@ export const getServerSideProps: GetServerSideProps = async context => {
|
|||
|
||||
return {
|
||||
props: {
|
||||
title: config.website.title,
|
||||
code: invite.code,
|
||||
title: global.config.website.title,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue