colocate components

This commit is contained in:
Max Leiter 2022-11-12 00:58:21 -08:00
parent 96c4023c14
commit 96da95818f
98 changed files with 70 additions and 360 deletions

View file

@ -3,7 +3,7 @@
import { FormEvent, useState } from "react"
import styles from "./auth.module.css"
import { useRouter } from "next/navigation"
import Link from "../link"
import Link from "../../components/link"
import { Button, Input, Note } from "@geist-ui/core/dist"
import { signIn } from "next-auth/react"
import { Github as GithubIcon } from "@geist-ui/icons"

View file

@ -1,4 +1,4 @@
import PageSeo from "@components/page-seo"
import PageSeo from "app/components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign In" />

View file

@ -1,5 +1,5 @@
import Auth from "@components/auth"
import Header from "@components/header"
import Auth from "../components"
import Header from "app/components/header"
export default function SignInPage() {
return (

View file

@ -0,0 +1,5 @@
import PageSeo from "app/components/page-seo"
export default function AuthHead() {
return <PageSeo title="Sign Up" />
}

View file

@ -1,5 +1,5 @@
import Auth from "@components/auth"
import Header from "@components/header"
import Auth from "../components"
import Header from "app/components/header"
import { getRequiresPasscode } from "pages/api/auth/requires-passcode"
const getPasscode = async () => {

View file

@ -1,4 +1,4 @@
import ShiftBy from "@components/shift-by"
import ShiftBy from "app/components/shift-by"
import { Button, Popover } from "@geist-ui/core/dist"
import ChevronDown from "@geist-ui/icons/chevronDown"
import CodeIcon from "@geist-ui/icons/fileFunction"

View file

@ -2,10 +2,10 @@ import { File } from "@lib/types"
import FileIcon from "@geist-ui/icons/fileText"
import CodeIcon from "@geist-ui/icons/fileLambda"
import styles from "./file-tree.module.css"
import ShiftBy from "@components/shift-by"
import ShiftBy from "app/components/shift-by"
import { useEffect, useState } from "react"
import { codeFileExtensions } from "@lib/constants"
import Link from "@components/link"
import Link from "app/components/link"
type Item = File & {
icon: JSX.Element

View file

@ -12,7 +12,7 @@ import FormattingIcons from "./formatting-icons"
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor"
import { Button, Input, Spacer, Tabs, Textarea } from "@geist-ui/core/dist"
import Preview from "@components/preview"
import Preview from "./preview"
// import Link from "next/link"
type Props = {

View file

@ -8,7 +8,6 @@ type Props = {
fileId?: string
content?: string
title?: string
// file extensions we can highlight
}
const MarkdownPreview = ({ height = 500, fileId, content, title }: Props) => {
@ -26,18 +25,13 @@ const MarkdownPreview = ({ height = 500, fileId, content, title }: Props) => {
setIsLoading(false)
}
} else if (content) {
// add title and query to url params
const urlQuery = new URLSearchParams({
title: title || "",
content
})
const resp = await fetch(`/api/files/get-html?${urlQuery}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${getCookie(TOKEN_COOKIE_NAME)}`
}
method: "GET"
})
if (resp.ok) {

View file

@ -1,5 +1,5 @@
import type { Document } from "@lib/types"
import DocumentComponent from "@components/edit-document"
import DocumentComponent from "./edit-document"
import { ChangeEvent, memo, useCallback } from "react"
const DocumentList = ({

View file

@ -4,17 +4,16 @@ import { Button, useToasts, Input, ButtonDropdown } from "@geist-ui/core/dist"
import { useRouter } from "next/navigation"
import { useCallback, useState } from "react"
import generateUUID from "@lib/generate-uuid"
import FileDropzone from "./drag-and-drop"
import styles from "./post.module.css"
import Title from "./title"
import type { PostVisibility, Document as DocumentType } from "@lib/types"
import PasswordModal from "./password-modal"
import EditDocumentList from "@components/edit-document-list"
import EditDocumentList from "./edit-document-list"
import { ChangeEvent } from "react"
import DatePicker from "react-datepicker"
import getTitleForPostCopy from "@lib/get-title-for-post-copy"
import Description from "./description"
import { PostWithFiles } from "@lib/server/prisma"
import PasswordModal from "./password-modal"
import Title from "./title"
const emptyDoc = {
title: "",

View file

@ -1,7 +1,7 @@
import { ChangeEvent, memo, useEffect, useState } from "react"
import { Text } from "@geist-ui/core/dist"
import ShiftBy from "@components/shift-by"
import ShiftBy from "app/components/shift-by"
import styles from "../post.module.css"
import { Input } from "@geist-ui/core/dist"

View file

@ -1,7 +1,7 @@
import NewPost from "@components/new-post"
import NewPost from "app/(posts)/new/components/new"
import { useRouter } from "next/navigation"
import { getPostWithFiles } from "@lib/server/prisma"
import Header from "@components/header"
import Header from "app/components/header"
const NewFromExisting = async ({
params

View file

@ -1,4 +1,4 @@
import PageSeo from "@components/page-seo"
import PageSeo from "app/components/page-seo"
export default function NewPostHead() {
return <PageSeo title="Create a new Drift" />

View file

@ -1,5 +1,5 @@
import Header from "@components/header"
import NewPost from "@components/new-post"
import Header from "app/components/header"
import NewPost from "app/(posts)/new/components/new"
import "@styles/react-datepicker.css"
const New = () => <>

View file

@ -1,7 +1,7 @@
"use client"
import VisibilityBadge from "@components/badges/visibility-badge"
import DocumentComponent from "@components/view-document"
import VisibilityBadge from "app/components/badges/visibility-badge"
import DocumentComponent from "./view-document"
import styles from "./post-page.module.css"
import type { PostVisibility } from "@lib/types"
@ -10,15 +10,15 @@ import { useEffect, useState } from "react"
import Archive from "@geist-ui/icons/archive"
import Edit from "@geist-ui/icons/edit"
import Parent from "@geist-ui/icons/arrowUpCircle"
import FileDropdown from "@components/file-dropdown"
import ScrollToTop from "@components/scroll-to-top"
import FileDropdown from "app/(posts)/components/file-dropdown"
import ScrollToTop from "app/components/scroll-to-top"
import { useRouter } from "next/navigation"
import ExpirationBadge from "@components/badges/expiration-badge"
import CreatedAgoBadge from "@components/badges/created-ago-badge"
import ExpirationBadge from "app/components/badges/expiration-badge"
import CreatedAgoBadge from "app/components/badges/created-ago-badge"
import PasswordModalPage from "./password-modal-wrapper"
import VisibilityControl from "@components/badges/visibility-control"
import VisibilityControl from "app/components/badges/visibility-control"
import { File, PostWithFiles } from "@lib/server/prisma"
import Header from "@components/header"
import Header from "app/components/header"
type Props = {
post: PostWithFiles

View file

@ -14,8 +14,8 @@ import {
Tooltip,
Tag
} from "@geist-ui/core/dist"
import HtmlPreview from "@components/preview"
import FadeIn from "@components/fade-in"
import HtmlPreview from "app/(posts)/new/components/edit-document-list/edit-document/preview"
import FadeIn from "app/components/fade-in"
// import Link from "next/link"
type Props = {

View file

@ -1,4 +1,4 @@
import PageSeo from "@components/page-seo"
import PageSeo from "app/components/page-seo"
import { getPostById } from "@lib/server/prisma"
export default async function Head({

View file

@ -1,12 +1,12 @@
import type { GetServerSideProps } from "next"
import type { Post } from "@lib/types"
import PostPage from "@components/post-page"
import PostPage from "app/(posts)/post/[id]/components/post-page"
import { USER_COOKIE_NAME } from "@lib/constants"
import { notFound } from "next/navigation"
import { getPostById } from "@lib/server/prisma"
import { getCurrentUser, getSession } from "@lib/server/session"
import Header from "@components/header"
import Header from "app/components/header"
export type PostProps = {
post: Post

View file

@ -4,7 +4,7 @@ import byteToMB from "@lib/byte-to-mb"
import { Post } from "@lib/types"
import Table from "rc-table"
import { useEffect, useMemo, useState } from "react"
import { adminFetcher } from "."
import { adminFetcher } from "./admin"
import ActionDropdown from "./action-dropdown"
const PostTable = () => {

View file

@ -1,7 +1,7 @@
import { Fieldset, useToasts } from "@geist-ui/core/dist"
import { User } from "@lib/types"
import { useEffect, useMemo, useState } from "react"
import { adminFetcher } from "."
import { adminFetcher } from "./admin"
import Table from "rc-table"
import SettingsGroup from "@components/settings-group"
import ActionDropdown from "./action-dropdown"

View file

@ -1,8 +1,5 @@
import Admin from "@components/admin"
import { TOKEN_COOKIE_NAME } from "@lib/constants"
import { isUserAdmin } from "@lib/server/prisma"
import Admin from "./components/admin"
import { getCurrentUser } from "@lib/server/session"
import { cookies } from "next/headers"
import { notFound } from "next/navigation"
const AdminPage = async () => {

View file

@ -27,6 +27,7 @@ import { useTheme } from "next-themes"
// import useUserData from "@lib/hooks/use-user-data"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { signOut } from "next-auth/react"
type Tab = {
name: string
@ -99,7 +100,7 @@ const Header = ({ signedIn = false }) => {
name: "sign out",
icon: <SignOutIcon />,
value: "signout",
href: "/signout"
onClick: () => signOut()
},
...defaultPages
])

View file

@ -1,5 +1,5 @@
"use client"
import ShiftBy from "@components/shift-by"
import ShiftBy from "app/components/shift-by"
import { Spacer, Tabs, Card, Textarea, Text } from "@geist-ui/core/dist"
import Image from "next/image"
import styles from "./home.module.css"

View file

@ -7,7 +7,7 @@ import ListItemSkeleton from "./list-item-skeleton"
import ListItem from "./list-item"
import { ChangeEvent, useCallback, useEffect, useState } from "react"
import useDebounce from "@lib/hooks/use-debounce"
import Link from "@components/link"
import Link from "app/components/link"
import { TOKEN_COOKIE_NAME } from "@lib/constants"
import { getCookie } from "cookies-next"
import type { PostWithFiles } from "@lib/server/prisma"

View file

@ -7,15 +7,15 @@ import {
Badge,
Button
} from "@geist-ui/core/dist"
import FadeIn from "@components/fade-in"
import FadeIn from "app/components/fade-in"
import Trash from "@geist-ui/icons/trash"
import ExpirationBadge from "@components/badges/expiration-badge"
import CreatedAgoBadge from "@components/badges/created-ago-badge"
import ExpirationBadge from "app/components/badges/expiration-badge"
import CreatedAgoBadge from "app/components/badges/created-ago-badge"
import Edit from "@geist-ui/icons/edit"
import { useRouter } from "next/navigation"
import Parent from "@geist-ui/icons/arrowUpCircle"
import styles from "./list-item.module.css"
import Link from "@components/link"
import Link from "app/components/link"
import type { PostWithFiles } from "@lib/server/prisma"
import type { PostVisibility } from "@lib/types"
import type { File } from "@lib/server/prisma"
@ -40,10 +40,6 @@ const ListItem = ({
router.push(`/post/${post.parentId}`)
}
{
console.log(post)
}
return (
<FadeIn>
<li key={post.id}>

View file

@ -1,4 +1,4 @@
import PageSeo from "@components/page-seo"
import PageSeo from "app/components/page-seo"
export default function Head() {
return <PageSeo title="Drift - Your profile" isPrivate />

View file

@ -1,8 +1,8 @@
import { redirect } from "next/navigation"
import { getPostsByUser } from "@lib/server/prisma"
import PostList from "@components/post-list"
import PostList from "app/components/post-list"
import { getCurrentUser } from "@lib/server/session"
import Header from "@components/header"
import Header from "app/components/header"
import { authOptions } from "@lib/server/auth"
export default async function Mine() {

View file

@ -1,7 +1,7 @@
import Header from "@components/header"
import Header from "app/components/header"
import { getCurrentUser } from "@lib/server/session"
import { getWelcomeContent } from "pages/api/welcome"
import Home from "./home"
import Home from "./components/home"
const getWelcomeData = async () => {
const welcomeContent = await getWelcomeContent()

View file

@ -1,4 +1,4 @@
import PageSeo from "@components/page-seo"
import PageSeo from "app/components/page-seo"
export default function Head() {
return <PageSeo title="Drift - Settings" isPrivate />

View file

@ -1,7 +1,7 @@
import Header from "@components/header"
import SettingsGroup from "@components/settings-group"
import Password from "@components/settings/sections/password"
import Profile from "@components/settings/sections/profile"
import Header from "app/components/header"
import SettingsGroup from "./components/settings-group"
import Password from "app/settings/components/sections/password"
import Profile from "app/settings/components/sections/profile"
import { authOptions } from "@lib/server/auth"
import { getCurrentUser } from "@lib/server/session"
import { redirect } from "next/navigation"

View file

@ -1,66 +0,0 @@
import Header from "@components/header"
import { GeistProvider, CssBaseline, Themes, Page } from "@geist-ui/core/dist"
import type { NextComponentType, NextPageContext } from "next"
import { SkeletonTheme } from "react-loading-skeleton"
const App = ({
Component,
pageProps
}: {
Component: NextComponentType<NextPageContext, any, any>
pageProps: any
}) => {
const skeletonBaseColor = "var(--light-gray)"
const skeletonHighlightColor = "var(--lighter-gray)"
const customTheme = Themes.createFromLight({
type: "custom",
palette: {
background: "var(--bg)",
foreground: "var(--fg)",
accents_1: "var(--lightest-gray)",
accents_2: "var(--lighter-gray)",
accents_3: "var(--light-gray)",
accents_4: "var(--gray)",
accents_5: "var(--darker-gray)",
accents_6: "var(--darker-gray)",
accents_7: "var(--darkest-gray)",
accents_8: "var(--darkest-gray)",
border: "var(--light-gray)",
warning: "var(--warning)"
},
expressiveness: {
dropdownBoxShadow: "0 0 0 1px var(--light-gray)",
shadowSmall: "0 0 0 1px var(--light-gray)",
shadowLarge: "0 0 0 1px var(--light-gray)",
shadowMedium: "0 0 0 1px var(--light-gray)"
},
layout: {
gap: "var(--gap)",
gapHalf: "var(--gap-half)",
gapQuarter: "var(--gap-quarter)",
gapNegative: "var(--gap-negative)",
gapHalfNegative: "var(--gap-half-negative)",
gapQuarterNegative: "var(--gap-quarter-negative)",
radius: "var(--radius)"
},
font: {
mono: "var(--font-mono)",
sans: "var(--font-sans)"
}
})
return (
<GeistProvider themes={[customTheme]} themeType={"custom"}>
<SkeletonTheme
baseColor={skeletonBaseColor}
highlightColor={skeletonHighlightColor}
>
<CssBaseline />
<Header />
<Component {...pageProps} />
</SkeletonTheme>
</GeistProvider>
)
}
export default App

View file

@ -1,14 +1,14 @@
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { NextAuthOptions } from "next-auth"
import GitHubProvider from "next-auth/providers/github"
import prisma from "lib/server/prisma"
import prisma from "@lib/server/prisma"
import config from "@lib/config"
const providers: NextAuthOptions["providers"] = [
GitHubProvider({
clientId: config.GITHUB_CLIENT_ID,
clientSecret: config.GITHUB_CLIENT_SECRET
}),
})
]
export const authOptions: NextAuthOptions = {
@ -41,7 +41,7 @@ export const authOptions: NextAuthOptions = {
})
if (!dbUser) {
// TODO: user should be defined?
// TODO: user should be defined? should we invalidate/signout?
if (user) {
token.id = user.id
token.role = "user"
@ -59,4 +59,3 @@ export const authOptions: NextAuthOptions = {
}
}
} as const

View file

@ -1,27 +0,0 @@
import config from "@lib/config"
import { User } from "@prisma/client"
import prisma from "@lib/server/prisma"
import { sign } from "jsonwebtoken"
export async function generateAndExpireAccessToken(userId: User["id"]) {
const token = sign({ id: userId }, config.jwt_secret, { expiresIn: "2d" })
await prisma.authTokens.create({
data: {
userId: userId,
token: token
}
})
// TODO: set expiredReason?
prisma.authTokens.deleteMany({
where: {
userId: userId,
token: {
not: token
}
}
})
return token
}

View file

@ -4,8 +4,6 @@ declare global {
import config from "@lib/config"
import { Post, PrismaClient, File, User } from "@prisma/client"
import { cache } from "react"
import { generateAndExpireAccessToken } from "./generate-access-token"
const prisma = new PrismaClient()
@ -140,23 +138,11 @@ export const createUser = async (
throw new Error("Wrong registration password")
}
// const salt = await genSalt(10)
// the first user is the admin
const isUserAdminByDefault =
config.enable_admin && (await prisma.user.count()) === 0
const userRole = isUserAdminByDefault ? "admin" : "user"
// const user = await prisma.user.create({
// data: {
// username,
// password: await bcrypt.hash(password, salt),
// role: userRole,
// },
// })
// const token = await generateAndExpireAccessToken(user.id)
return {
// user,
// token

View file

@ -1,59 +0,0 @@
import "@styles/globals.css"
import type { AppProps as NextAppProps } from "next/app"
import "react-loading-skeleton/dist/skeleton.css"
import Head from "next/head"
import { ThemeProvider } from "next-themes"
import App from "@components/app"
import React from "react"
type AppProps<P = any> = {
pageProps: P
} & Omit<NextAppProps<P>, "pageProps">
function MyApp({ Component, pageProps }: AppProps) {
return (
<div>
<Head>
<meta charSet="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/assets/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<link
rel="mask-icon"
href="/assets/safari-pinned-tab.svg"
color="#5bbad5"
/>
<meta name="apple-mobile-web-app-title" content="Drift" />
<meta name="application-name" content="Drift" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Drift</title>
</Head>
<ThemeProvider defaultTheme="system" disableTransitionOnChange>
<App Component={Component} pageProps={pageProps} />
</ThemeProvider>
</div>
)
}
export default MyApp

View file

@ -1,39 +0,0 @@
import { CssBaseline } from "@geist-ui/core/dist"
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext
} from "next/document"
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx)
const styles = CssBaseline.flush()
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{styles}
</> // TODO: Investigate typescript
) as any
}
}
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

View file

@ -1,12 +0,0 @@
import ErrorComponent from "@components/error"
function Error({ statusCode }: { statusCode: number }) {
return <ErrorComponent status={statusCode} />
}
Error.getInitialProps = ({ res, err }: { res: any; err: any }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
return { statusCode }
}
export default Error

View file

@ -1,51 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next"
import prisma from "@lib/server/prisma"
import bcrypt from "bcrypt"
import { signin } from "@lib/server/signin"
import { setCookie } from "cookies-next"
import { TOKEN_COOKIE_NAME, USER_COOKIE_NAME } from "@lib/constants"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { username, password } = req.body
if (!username || !password) {
return res.status(400).json({ error: "Missing param" })
}
const user = await prisma.user.findFirst({
where: {
username
}
})
if (!user || !user.password) {
return res.status(401).json({ error: "Unauthorized" })
}
const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) {
return res.status(401).json({ error: "Unauthorized" })
}
const token = await signin(user.id, req, res)
setCookie(TOKEN_COOKIE_NAME, token, {
path: "/",
maxAge: 60 * 60 * 24 * 7, // 1 week
httpOnly: true,
secure: process.env.NODE_ENV === "production",
req,
res
})
setCookie(USER_COOKIE_NAME, user.id, {
path: "/",
maxAge: 60 * 60 * 24 * 7, // 1 week
httpOnly: true,
secure: process.env.NODE_ENV === "production",
req,
res
})
return res.status(201).json({ token: token, userId: user.id })
}

View file

@ -1,12 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next"
import { createUser } from "@lib/server/prisma"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { username, password, serverPassword } = req.body
const { user, token } = await createUser(username, password, serverPassword)
return res.status(201).json({ token: token, userId: user.id })
}

View file

@ -1,5 +1,5 @@
import { NextApiRequest, NextApiResponse } from "next"
import prisma from "lib/server/prisma"
import prisma from "@lib/server/prisma"
import { parseQueryParam } from "@lib/server/parse-query-param"
const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {

View file

@ -9,10 +9,10 @@ import {
import { authOptions } from "@lib/server/auth"
import { CreatePostSchema } from "@lib/validations/post"
import { Post } from "@prisma/client"
import prisma, { getPostById } from "lib/server/prisma"
import prisma, { getPostById } from "@lib/server/prisma"
import { NextApiRequest, NextApiResponse } from "next"
import { unstable_getServerSession } from "next-auth/next"
import { File } from "lib/server/prisma"
import { File } from "@lib/server/prisma"
import * as crypto from "crypto"
import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file"
import { getSession } from "next-auth/react"

View file

@ -113,7 +113,6 @@ model User {
role String? @default("user")
password String? @db.Text
@@map("users")
}

View file

@ -39,13 +39,13 @@
"baseUrl": ".",
"paths": {
"@components/*": [
"components/*"
"app/components/*"
],
"@lib/*": [
"lib/*"
],
"@styles/*": [
"styles/*"
"app/styles/*"
]
}
},