From 25903bf3cd7ca9c02c37badd8017f1a446b1902e Mon Sep 17 00:00:00 2001 From: poeti8 Date: Thu, 30 Jan 2020 18:51:52 +0330 Subject: [PATCH] feat: api v2 --- .eslintrc | 2 +- client/components/Icon/Icon.tsx | 6 +- client/components/Icon/Stop.tsx | 22 + client/components/LinksTable.tsx | 351 +- client/components/Settings/SettingsBan.tsx | 83 - client/components/Settings/SettingsDomain.tsx | 30 +- .../components/Settings/SettingsPassword.tsx | 4 +- client/components/Shortener.tsx | 6 +- client/components/Text.tsx | 27 +- client/consts/consts.ts | 22 +- client/pages/login.tsx | 4 +- .../{url-password.tsx => protected/[id].tsx} | 34 +- client/pages/report.tsx | 4 +- client/pages/reset-password.tsx | 4 +- client/pages/settings.tsx | 18 +- client/pages/stats.tsx | 8 +- client/pages/url-info.tsx | 19 +- client/store/auth.ts | 6 +- client/store/links.ts | 32 +- client/store/settings.ts | 62 +- global.d.ts | 35 + package-lock.json | 5598 ++++++++++------- package.json | 112 +- .../{ => __v1}/controllers/linkController.ts | 50 +- .../controllers/validateBodyController.ts | 30 +- server/{ => __v1}/db/domain.ts | 6 +- server/{ => __v1}/db/host.ts | 6 +- server/{ => __v1}/db/ip.ts | 10 +- server/{ => __v1}/db/link.ts | 6 +- server/{ => __v1}/db/user.ts | 6 +- server/__v1/index.ts | 63 + server/configToEnv.ts | 62 - server/controllers/authController.ts | 274 - server/cron.ts | 7 +- server/env.ts | 41 + server/handlers/auth.ts | 138 +- server/handlers/domains.ts | 31 + server/handlers/helpers.ts | 41 +- server/handlers/links.ts | 431 +- server/handlers/sanitizers.ts | 21 - server/handlers/types.d.ts | 11 + server/handlers/users.ts | 14 + server/handlers/validators.ts | 323 +- server/knex.ts | 14 +- server/mail/index.ts | 1 + server/mail/mail.ts | 66 +- server/migration/01_host.ts | 15 +- server/migration/02_users.ts | 17 +- server/migration/03_domains.ts | 17 +- server/migration/04_links.ts | 19 +- server/migration/neo4j_delete_duplicated.ts | 7 +- server/models/domain.ts | 12 + server/passport.ts | 15 +- server/queries/domain.ts | 92 +- server/queries/host.ts | 62 + server/queries/index.ts | 15 + server/queries/ip.ts | 35 + server/queries/link.ts | 213 +- server/queries/user.ts | 75 + server/queries/visit.ts | 245 + server/queues/index.ts | 6 +- server/queues/queues.ts | 16 +- server/queues/{visitQueue.ts => visit.ts} | 7 +- server/redis.ts | 37 +- server/routes/auth.ts | 43 + server/routes/domains.ts | 29 + server/routes/index.ts | 12 +- server/routes/links.ts | 53 +- server/routes/routes.ts | 17 + server/routes/users.ts | 16 + server/server.ts | 195 +- server/utils/index.ts | 119 +- 72 files changed, 6033 insertions(+), 3497 deletions(-) create mode 100644 client/components/Icon/Stop.tsx delete mode 100644 client/components/Settings/SettingsBan.tsx rename client/pages/{url-password.tsx => protected/[id].tsx} (73%) rename server/{ => __v1}/controllers/linkController.ts (88%) rename server/{ => __v1}/controllers/validateBodyController.ts (91%) rename server/{ => __v1}/db/domain.ts (96%) rename server/{ => __v1}/db/host.ts (90%) rename server/{ => __v1}/db/ip.ts (81%) rename server/{ => __v1}/db/link.ts (99%) rename server/{ => __v1}/db/user.ts (97%) create mode 100644 server/__v1/index.ts delete mode 100644 server/configToEnv.ts delete mode 100644 server/controllers/authController.ts create mode 100644 server/env.ts create mode 100644 server/handlers/domains.ts delete mode 100644 server/handlers/sanitizers.ts create mode 100644 server/handlers/types.d.ts create mode 100644 server/handlers/users.ts create mode 100644 server/mail/index.ts create mode 100644 server/queries/host.ts create mode 100644 server/queries/index.ts create mode 100644 server/queries/ip.ts create mode 100644 server/queries/user.ts create mode 100644 server/queries/visit.ts rename server/queues/{visitQueue.ts => visit.ts} (89%) create mode 100644 server/routes/auth.ts create mode 100644 server/routes/domains.ts create mode 100644 server/routes/routes.ts create mode 100644 server/routes/users.ts diff --git a/.eslintrc b/.eslintrc index c282f2e..ecdd233 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,7 +15,7 @@ "no-var": "warn", "no-console": "warn", "max-len": ["warn", { "comments": 80 }], - "no-param-reassign": ["warn", { "props": false }], + "no-param-reassign": 0, "require-atomic-updates": 0, "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/no-unused-vars": "off", // "warn" for production diff --git a/client/components/Icon/Icon.tsx b/client/components/Icon/Icon.tsx index 7b420f3..bf24005 100644 --- a/client/components/Icon/Icon.tsx +++ b/client/components/Icon/Icon.tsx @@ -18,6 +18,7 @@ import Trash from "./Trash"; import Check from "./Check"; import Login from "./Login"; import Heart from "./Heart"; +import Stop from "./Stop"; import Plus from "./Plus"; import Lock from "./Lock"; import Edit from "./Edit"; @@ -33,10 +34,9 @@ const icons = { chevronLeft: ChevronLeft, chevronRight: ChevronRight, clipboard: Clipboard, - shuffle: Shuffle, copy: Copy, - heart: Heart, edit: Edit, + heart: Heart, key: Key, lock: Lock, login: Login, @@ -45,8 +45,10 @@ const icons = { qrcode: QRCode, refresh: Refresh, send: Send, + shuffle: Shuffle, signup: Signup, spinner: Spinner, + stop: Stop, trash: Trash, x: X, zap: Zap diff --git a/client/components/Icon/Stop.tsx b/client/components/Icon/Stop.tsx new file mode 100644 index 0000000..45b01a9 --- /dev/null +++ b/client/components/Icon/Stop.tsx @@ -0,0 +1,22 @@ +import React from "react"; + +function Stop() { + return ( + + + + + ); +} + +export default React.memo(Stop); diff --git a/client/components/LinksTable.tsx b/client/components/LinksTable.tsx index b422b42..152cb3e 100644 --- a/client/components/LinksTable.tsx +++ b/client/components/LinksTable.tsx @@ -4,16 +4,18 @@ import React, { FC, useState, useEffect } from "react"; import { useFormState } from "react-use-form-state"; import { Flex } from "reflexbox/styled-components"; import styled, { css } from "styled-components"; +import { ifProp } from "styled-tools"; import QRCode from "qrcode.react"; import Link from "next/link"; -import { useStoreActions, useStoreState } from "../store"; import { removeProtocol, withComma, errorMessage } from "../utils"; +import { useStoreActions, useStoreState } from "../store"; +import { Link as LinkType } from "../store/links"; import { Checkbox, TextInput } from "./Input"; import { NavButton, Button } from "./Button"; +import Text, { H2, H4, Span } from "./Text"; import { Col, RowCenter } from "./Layout"; -import Text, { H2, Span } from "./Text"; -import { ifProp } from "styled-tools"; +import { useMessage } from "../hooks"; import Animation from "./Animation"; import { Colors } from "../consts"; import Tooltip from "./Tooltip"; @@ -21,7 +23,6 @@ import Table from "./Table"; import ALink from "./ALink"; import Modal from "./Modal"; import Icon from "./Icon"; -import { useMessage } from "../hooks"; const Tr = styled(Flex).attrs({ as: "tr", px: [12, 12, 2] })``; const Th = styled(Flex)``; @@ -87,6 +88,218 @@ const viewsFlex = { }; const actionsFlex = { flexGrow: [1, 1, 2.5], flexShrink: [1, 1, 2.5] }; +interface RowProps { + index: number; + link: LinkType; + setDeleteModal: (number) => void; +} + +interface BanForm { + host: boolean; + user: boolean; + userLinks: boolean; + domain: boolean; +} + +const Row: FC = ({ index, link, setDeleteModal }) => { + const isAdmin = useStoreState(s => s.auth.isAdmin); + const ban = useStoreActions(s => s.links.ban); + const [formState, { checkbox }] = useFormState(); + const [copied, setCopied] = useState(false); + const [qrModal, setQRModal] = useState(false); + const [banModal, setBanModal] = useState(false); + const [banLoading, setBanLoading] = useState(false); + const [banMessage, setBanMessage] = useMessage(); + + const onCopy = () => { + setCopied(true); + setTimeout(() => { + setCopied(false); + }, 1500); + }; + + const onBan = async () => { + setBanLoading(true); + try { + const res = await ban({ id: link.id, ...formState.values }); + setBanMessage(res.message, "green"); + setTimeout(() => { + setBanModal(false); + }, 2000); + } catch (err) { + setBanMessage(errorMessage(err)); + } + setBanLoading(false); + }; + + return ( + <> + + + {link.target} + + {`${formatDistanceToNow( + new Date(link.created_at) + )} ago`} + + {copied ? ( + + + + ) : ( + + + + + + )} + {removeProtocol(link.link)} + + {withComma(link.visit_count)} + + {link.password && ( + <> + + Password protected + + + + )} + {link.banned && ( + <> + Banned + + + )} + {link.visit_count > 0 && ( + + + + + + )} + setQRModal(true)} + /> + {isAdmin && !link.banned && ( + setBanModal(true)} + /> + )} + setDeleteModal(index)} + /> + + + setQRModal(false)} + > + + + + + setBanModal(false)} + > + <> +

+ Ban link? +

+ + Are you sure do you want to ban the link{" "} + "{removeProtocol(link.link)}"? + + + + + + + + + {banLoading ? ( + <> + + + ) : banMessage.text ? ( + + {banMessage.text} + + ) : ( + <> + + + + )} + + +
+ + ); +}; + interface Form { all: boolean; limit: string; @@ -97,10 +310,8 @@ interface Form { const LinksTable: FC = () => { const isAdmin = useStoreState(s => s.auth.isAdmin); const links = useStoreState(s => s.links); - const { get, deleteOne } = useStoreActions(s => s.links); + const { get, remove } = useStoreActions(s => s.links); const [tableMessage, setTableMessage] = useState("No links to show."); - const [copied, setCopied] = useState([]); - const [qrModal, setQRModal] = useState(-1); const [deleteModal, setDeleteModal] = useState(-1); const [deleteLoading, setDeleteLoading] = useState(false); const [deleteMessage, setDeleteMessage] = useMessage(); @@ -113,7 +324,9 @@ const LinksTable: FC = () => { const linkToDelete = links.items[deleteModal]; useEffect(() => { - get(options).catch(err => setTableMessage(err?.response?.data?.error)); + get(options).catch(err => + setTableMessage(err?.response?.data?.error || "An error occurred.") + ); }, [options.limit, options.skip, options.all]); const onSubmit = e => { @@ -121,20 +334,10 @@ const LinksTable: FC = () => { get(options); }; - const onCopy = (index: number) => () => { - setCopied([index]); - setTimeout(() => { - setCopied(s => s.filter(i => i !== index)); - }, 1500); - }; - const onDelete = async () => { setDeleteLoading(true); try { - await deleteOne({ - id: linkToDelete.address, - domain: linkToDelete.domain - }); + await remove(linkToDelete.id); await get(options); setDeleteModal(-1); } catch (err) { @@ -254,98 +457,12 @@ const LinksTable: FC = () => { ) : ( <> - {links.items.map((l, index) => ( - - - {l.target} - - {`${formatDistanceToNow( - new Date(l.created_at) - )} ago`} - - {copied.includes(index) ? ( - - - - ) : ( - - - - - - )} - {removeProtocol(l.link)} - - {withComma(l.visit_count)} - - {l.password && ( - <> - - Password protected - - - - )} - {l.visit_count > 0 && ( - - - - - - )} - setQRModal(index)} - /> - setDeleteModal(index)} - /> - - + {links.items.map((link, index) => ( + ))} )} @@ -354,18 +471,6 @@ const LinksTable: FC = () => { {Nav} - -1} - closeHandler={() => setQRModal(-1)} - > - {links.items[qrModal] && ( - - - - )} - -1} diff --git a/client/components/Settings/SettingsBan.tsx b/client/components/Settings/SettingsBan.tsx deleted file mode 100644 index 7739ffb..0000000 --- a/client/components/Settings/SettingsBan.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useFormState } from "react-use-form-state"; -import { Flex } from "reflexbox/styled-components"; -import React, { FC, useState } from "react"; -import axios from "axios"; - -import { Checkbox, TextInput } from "../Input"; -import { getAxiosConfig } from "../../utils"; -import { useMessage } from "../../hooks"; -import { API } from "../../consts"; -import { Button } from "../Button"; -import Text, { H2 } from "../Text"; -import { Col } from "../Layout"; -import Icon from "../Icon"; - -interface BanForm { - id: string; - user: boolean; - domain: boolean; - host: boolean; -} - -const SettingsBan: FC = () => { - const [submitting, setSubmitting] = useState(false); - const [message, setMessage] = useMessage(3000); - const [formState, { checkbox, text }] = useFormState(); - - const onSubmit = async e => { - e.preventDefault(); - setSubmitting(true); - setMessage(); - try { - const { data } = await axios.post( - API.BAN_LINK, - formState.values, - getAxiosConfig() - ); - setMessage(data.message, "green"); - formState.clear(); - } catch (err) { - setMessage(err?.response?.data?.error || "Couldn't ban the link."); - } - setSubmitting(false); - }; - - return ( - -

- Ban link -

- - - - - - - - - - {message.text} - - - - ); -}; - -export default SettingsBan; diff --git a/client/components/Settings/SettingsDomain.tsx b/client/components/Settings/SettingsDomain.tsx index f735bd5..027d89b 100644 --- a/client/components/Settings/SettingsDomain.tsx +++ b/client/components/Settings/SettingsDomain.tsx @@ -5,6 +5,7 @@ import styled from "styled-components"; import { useStoreState, useStoreActions } from "../../store"; import { Domain } from "../../store/settings"; +import { errorMessage } from "../../utils"; import { useMessage } from "../../hooks"; import Text, { H2, Span } from "../Text"; import { Colors } from "../../consts"; @@ -14,7 +15,6 @@ import { Col } from "../Layout"; import Table from "../Table"; import Modal from "../Modal"; import Icon from "../Icon"; -import { errorMessage } from "../../utils"; const Th = styled(Flex).attrs({ as: "th", py: 3, px: 3 })` font-size: 15px; @@ -24,15 +24,15 @@ const Td = styled(Flex).attrs({ as: "td", py: 12, px: 3 })` `; const SettingsDomain: FC = () => { - const [modal, setModal] = useState(false); - const [loading, setLoading] = useState(false); - const [deleteLoading, setDeleteLoading] = useState(false); + const { saveDomain, deleteDomain } = useStoreActions(s => s.settings); const [domainToDelete, setDomainToDelete] = useState(null); - const [message, setMessage] = useMessage(2000); + const [deleteLoading, setDeleteLoading] = useState(false); const domains = useStoreState(s => s.settings.domains); - const { saveDomain, deleteDomain } = useStoreActions(s => s.settings); + const [message, setMessage] = useMessage(2000); + const [loading, setLoading] = useState(false); + const [modal, setModal] = useState(false); const [formState, { label, text }] = useFormState<{ - customDomain: string; + address: string; homepage: string; }>(null, { withIds: true }); @@ -56,7 +56,7 @@ const SettingsDomain: FC = () => { const onDelete = async () => { setDeleteLoading(true); - await deleteDomain().catch(err => + await deleteDomain(domainToDelete.id).catch(err => setMessage(errorMessage(err, "Couldn't delete the domain.")) ); setMessage("Domain has been deleted successfully.", "green"); @@ -88,9 +88,11 @@ const SettingsDomain: FC = () => { {domains.map(d => ( - - {d.customDomain} - {d.homepage || "default"} + + {d.address} + + {d.homepage || process.env.DEFAULT_DOMAIN} + { { Domain { Are you sure do you want to delete the domain{" "} - "{domainToDelete && domainToDelete.customDomain}"? + "{domainToDelete && domainToDelete.address}"? {deleteLoading ? ( diff --git a/client/components/Settings/SettingsPassword.tsx b/client/components/Settings/SettingsPassword.tsx index a5a8b37..0be9ebc 100644 --- a/client/components/Settings/SettingsPassword.tsx +++ b/client/components/Settings/SettingsPassword.tsx @@ -6,7 +6,7 @@ import axios from "axios"; import { getAxiosConfig } from "../../utils"; import { useMessage } from "../../hooks"; import { TextInput } from "../Input"; -import { API } from "../../consts"; +import { APIv2 } from "../../consts"; import { Button } from "../Button"; import Text, { H2 } from "../Text"; import { Col } from "../Layout"; @@ -30,7 +30,7 @@ const SettingsPassword: FC = () => { setMessage(); try { const res = await axios.post( - API.CHANGE_PASSWORD, + APIv2.AuthChangePassword, formState.values, getAxiosConfig() ); diff --git a/client/components/Shortener.tsx b/client/components/Shortener.tsx index c3b0785..9f8abf8 100644 --- a/client/components/Shortener.tsx +++ b/client/components/Shortener.tsx @@ -1,7 +1,7 @@ import { CopyToClipboard } from "react-copy-to-clipboard"; import { useFormState } from "react-use-form-state"; import { Flex } from "reflexbox/styled-components"; -import React, { useState } from "react"; +import React, { FC, useState } from "react"; import styled from "styled-components"; import { useStoreActions, useStoreState } from "../store"; @@ -260,8 +260,8 @@ const Shortener = () => { options={[ { key: defaultDomain, value: "" }, ...domains.map(d => ({ - key: d.customDomain, - value: d.customDomain + key: d.address, + value: d.address })) ]} /> diff --git a/client/components/Text.tsx b/client/components/Text.tsx index 9993593..8a13698 100644 --- a/client/components/Text.tsx +++ b/client/components/Text.tsx @@ -1,17 +1,19 @@ import { switchProp, ifNotProp, ifProp } from "styled-tools"; -import { Box } from "reflexbox/styled-components"; +import { Box, BoxProps } from "reflexbox/styled-components"; import styled, { css } from "styled-components"; +import { FC, CSSProperties } from "react"; import { Colors } from "../consts"; -import { FC, ComponentProps } from "react"; -interface Props { +interface Props extends Omit { + as?: string; htmlFor?: string; light?: boolean; normal?: boolean; bold?: boolean; + style?: CSSProperties; } -const Text = styled(Box)` +const Text: FC = styled(Box)` font-weight: 400; ${ifNotProp( "fontSize", @@ -50,18 +52,15 @@ const Text = styled(Box)` `; Text.defaultProps = { - as: "p", color: Colors.Text }; export default Text; -type TextProps = ComponentProps; - -export const H1: FC = props => ; -export const H2: FC = props => ; -export const H3: FC = props => ; -export const H4: FC = props => ; -export const H5: FC = props => ; -export const H6: FC = props => ; -export const Span: FC = props => ; +export const H1: FC = props => ; +export const H2: FC = props => ; +export const H3: FC = props => ; +export const H4: FC = props => ; +export const H5: FC = props => ; +export const H6: FC = props => ; +export const Span: FC = props => ; diff --git a/client/consts/consts.ts b/client/consts/consts.ts index 87f1062..6f25cd1 100644 --- a/client/consts/consts.ts +++ b/client/consts/consts.ts @@ -1,21 +1,17 @@ export enum API { - LOGIN = "/api/auth/login", - SIGNUP = "/api/auth/signup", - RENEW = "/api/auth/renew", - REPORT = "/api/url/report", - RESET_PASSWORD = "/api/auth/resetpassword", - CHANGE_PASSWORD = "/api/auth/changepassword", BAN_LINK = "/api/url/admin/ban", - CUSTOM_DOMAIN = "/api/url/customdomain", - GENERATE_APIKEY = "/api/auth/generateapikey", - SETTINGS = "/api/auth/usersettings", - SUBMIT = "/api/url/submit", - GET_LINKS = "/api/url/geturls", - DELETE_LINK = "/api/url/deleteurl", STATS = "/api/url/stats" } export enum APIv2 { + AuthLogin = "/api/v2/auth/login", + AuthSignup = "/api/v2/auth/signup", + AuthRenew = "/api/v2/auth/renew", + AuthResetPassword = "/api/v2/auth/reset-password", + AuthChangePassword = "/api/v2/auth/change-password", + AuthGenerateApikey = "/api/v2/auth/apikey", + Users = "/api/v2/users", + Domains = "/api/v2/domains", Links = "/api/v2/links" } @@ -32,6 +28,8 @@ export enum Colors { CheckIcon = "hsl(144, 50%, 60%)", TrashIcon = "hsl(0, 100%, 69%)", TrashIconBg = "hsl(0, 100%, 96%)", + StopIcon = "hsl(10, 100%, 40%)", + StopIconBg = "hsl(10, 100%, 96%)", QrCodeIcon = "hsl(0, 0%, 35%)", QrCodeIconBg = "hsl(0, 0%, 94%)", PieIcon = "hsl(260, 100%, 69%)", diff --git a/client/pages/login.tsx b/client/pages/login.tsx index e2184eb..55ae2e6 100644 --- a/client/pages/login.tsx +++ b/client/pages/login.tsx @@ -16,7 +16,7 @@ import { Button } from "../components/Button"; import Text, { H2 } from "../components/Text"; import ALink from "../components/ALink"; import Icon from "../components/Icon"; -import { API } from "../consts"; +import { APIv2 } from "../consts"; const LoginForm = styled(Flex).attrs({ as: "form", @@ -80,7 +80,7 @@ const LoginPage = () => { if (type === "signup") { setLoading(s => ({ ...s, signup: true })); try { - await axios.post(API.SIGNUP, { email, password }); + await axios.post(APIv2.AuthSignup, { email, password }); setVerifying(true); } catch (error) { setError(error.response.data.error); diff --git a/client/pages/url-password.tsx b/client/pages/protected/[id].tsx similarity index 73% rename from client/pages/url-password.tsx rename to client/pages/protected/[id].tsx index ba4136b..84bbc94 100644 --- a/client/pages/url-password.tsx +++ b/client/pages/protected/[id].tsx @@ -2,20 +2,23 @@ import { useFormState } from "react-use-form-state"; import { Flex } from "reflexbox/styled-components"; import React, { useState } from "react"; import { NextPage } from "next"; +import { useRouter } from "next/router"; import axios from "axios"; -import AppWrapper from "../components/AppWrapper"; -import { TextInput } from "../components/Input"; -import { Button } from "../components/Button"; -import Text, { H2 } from "../components/Text"; -import { Col } from "../components/Layout"; -import Icon from "../components/Icon"; +import AppWrapper from "../../components/AppWrapper"; +import { TextInput } from "../../components/Input"; +import { Button } from "../../components/Button"; +import Text, { H2 } from "../../components/Text"; +import { Col } from "../../components/Layout"; +import Icon from "../../components/Icon"; +import { APIv2 } from "../../consts"; interface Props { protectedLink?: string; } -const UrlPasswordPage: NextPage = ({ protectedLink }) => { +const ProtectedPage: NextPage = () => { + const router = useRouter(); const [loading, setLoading] = useState(false); const [formState, { password }] = useFormState<{ password: string }>(); const [error, setError] = useState(); @@ -30,12 +33,13 @@ const UrlPasswordPage: NextPage = ({ protectedLink }) => { setError(""); setLoading(true); - // TODO: better api calls try { - const { data } = await axios.post("/api/url/requesturl", { - id: protectedLink, - password - }); + const { data } = await axios.post( + `${APIv2.Links}/${router.query.id}/protected`, + { + password + } + ); window.location.replace(data.target); } catch ({ response }) { setError(response.data.error); @@ -45,7 +49,7 @@ const UrlPasswordPage: NextPage = ({ protectedLink }) => { return ( - {!protectedLink ? ( + {!router.query.id ? (

404 | Link could not be found.

@@ -84,10 +88,10 @@ const UrlPasswordPage: NextPage = ({ protectedLink }) => { ); }; -UrlPasswordPage.getInitialProps = async ({ req }) => { +ProtectedPage.getInitialProps = async ({ req }) => { return { protectedLink: req && (req as any).protectedLink }; }; -export default UrlPasswordPage; +export default ProtectedPage; diff --git a/client/pages/report.tsx b/client/pages/report.tsx index f66ef04..ced5b67 100644 --- a/client/pages/report.tsx +++ b/client/pages/report.tsx @@ -10,7 +10,7 @@ import { Button } from "../components/Button"; import { Col } from "../components/Layout"; import Icon from "../components/Icon"; import { useMessage } from "../hooks"; -import { API } from "../consts"; +import { APIv2 } from "../consts"; const ReportPage = () => { const [formState, { text }] = useFormState<{ url: string }>(); @@ -22,7 +22,7 @@ const ReportPage = () => { setLoading(true); setMessage(); try { - await axios.post(API.REPORT, { link: formState.values.url }); // TODO: better api calls + await axios.post(`${APIv2.Links}/report`, { link: formState.values.url }); setMessage("Thanks for the report, we'll take actions shortly.", "green"); formState.clear(); } catch (error) { diff --git a/client/pages/reset-password.tsx b/client/pages/reset-password.tsx index 680d43f..c91e6e8 100644 --- a/client/pages/reset-password.tsx +++ b/client/pages/reset-password.tsx @@ -16,7 +16,7 @@ import { Col } from "../components/Layout"; import { TokenPayload } from "../types"; import { useMessage } from "../hooks"; import Icon from "../components/Icon"; -import { API } from "../consts"; +import { API, APIv2 } from "../consts"; interface Props { token?: string; @@ -51,7 +51,7 @@ const ResetPassword: NextPage = ({ token }) => { setLoading(true); setMessage(); try { - await axios.post(API.RESET_PASSWORD, { + await axios.post(APIv2.AuthResetPassword, { email: formState.values.email }); setMessage("Reset password email has been sent.", "green"); diff --git a/client/pages/settings.tsx b/client/pages/settings.tsx index a989733..31ad05e 100644 --- a/client/pages/settings.tsx +++ b/client/pages/settings.tsx @@ -1,20 +1,18 @@ -import { Flex } from "reflexbox/styled-components"; -import React, { useEffect } from "react"; import { NextPage } from "next"; +import React from "react"; import SettingsPassword from "../components/Settings/SettingsPassword"; import SettingsDomain from "../components/Settings/SettingsDomain"; -import SettingsBan from "../components/Settings/SettingsBan"; import SettingsApi from "../components/Settings/SettingsApi"; -import { useStoreState, useStoreActions } from "../store"; import AppWrapper from "../components/AppWrapper"; import { H1, Span } from "../components/Text"; import Divider from "../components/Divider"; -import Footer from "../components/Footer"; import { Col } from "../components/Layout"; +import Footer from "../components/Footer"; +import { useStoreState } from "../store"; -const SettingsPage: NextPage = props => { - const { email, isAdmin } = useStoreState(s => s.auth); +const SettingsPage: NextPage = () => { + const email = useStoreState(s => s.auth.email); return ( @@ -27,12 +25,6 @@ const SettingsPage: NextPage = props => { . - {isAdmin && ( - <> - - - - )} diff --git a/client/pages/stats.tsx b/client/pages/stats.tsx index f0d7678..1e1e37b 100644 --- a/client/pages/stats.tsx +++ b/client/pages/stats.tsx @@ -15,15 +15,14 @@ import AppWrapper from "../components/AppWrapper"; import Divider from "../components/Divider"; import { useStoreState } from "../store"; import ALink from "../components/ALink"; -import { API, Colors } from "../consts"; +import { APIv2, Colors } from "../consts"; import Icon from "../components/Icon"; interface Props { - domain?: string; id?: string; } -const StatsPage: NextPage = ({ domain, id }) => { +const StatsPage: NextPage = ({ id }) => { const { isAuthenticated } = useStoreState(s => s.auth); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); @@ -35,7 +34,7 @@ const StatsPage: NextPage = ({ domain, id }) => { useEffect(() => { if (!id || !isAuthenticated) return; axios - .get(`${API.STATS}?id=${id}&domain=${domain}`, getAxiosConfig()) + .get(`${APIv2.Links}/${id}/stats`, getAxiosConfig()) .then(({ data }) => { setLoading(false); setError(!data); @@ -208,7 +207,6 @@ StatsPage.getInitialProps = ({ query }) => { }; StatsPage.defaultProps = { - domain: "", id: "" }; diff --git a/client/pages/url-info.tsx b/client/pages/url-info.tsx index 14fde1c..907fb58 100644 --- a/client/pages/url-info.tsx +++ b/client/pages/url-info.tsx @@ -1,21 +1,16 @@ +import { useRouter } from "next/router"; import React from "react"; -import styled from "styled-components"; -import { Flex } from "reflexbox/styled-components"; -import { NextPage } from "next"; import AppWrapper from "../components/AppWrapper"; import Footer from "../components/Footer"; import { H2, H4 } from "../components/Text"; import { Col } from "../components/Layout"; -interface Props { - linkTarget?: string; -} - -const UrlInfoPage: NextPage = ({ linkTarget }) => { +const UrlInfoPage = () => { + const { query } = useRouter(); return ( - {!linkTarget ? ( + {!query.target ? (

404 | Link could not be found.

@@ -25,7 +20,7 @@ const UrlInfoPage: NextPage = ({ linkTarget }) => {

Target:

-

{linkTarget}

+

{query.target}