diff options
Diffstat (limited to 'pages')
-rw-r--r-- | pages/_app.js | 20 | ||||
-rw-r--r-- | pages/api/login.js | 29 | ||||
-rw-r--r-- | pages/api/logout.js | 6 | ||||
-rw-r--r-- | pages/api/note/[id].js | 30 | ||||
-rw-r--r-- | pages/api/notes.js | 101 | ||||
-rw-r--r-- | pages/api/register.js | 35 | ||||
-rw-r--r-- | pages/api/user.js | 22 | ||||
-rw-r--r-- | pages/api/verify.js | 45 | ||||
-rw-r--r-- | pages/index.js | 67 | ||||
-rw-r--r-- | pages/login.js | 26 | ||||
-rw-r--r-- | pages/register.js | 24 | ||||
-rw-r--r-- | pages/verify.js | 109 |
12 files changed, 514 insertions, 0 deletions
diff --git a/pages/_app.js b/pages/_app.js new file mode 100644 index 0000000..5196b22 --- /dev/null +++ b/pages/_app.js @@ -0,0 +1,20 @@ +import { SWRConfig } from 'swr' +import fetchJson from 'lib/fetchJson' +import '/styles/styles.scss' + +function MyApp({Component, pageProps}) { + return ( + <SWRConfig + value={{ + fetcher: fetchJson, + onError: (err) => { + console.error(err) + }, + }} + > + <Component {...pageProps} /> + </SWRConfig> + ) +} + +export default MyApp diff --git a/pages/api/login.js b/pages/api/login.js new file mode 100644 index 0000000..30edda1 --- /dev/null +++ b/pages/api/login.js @@ -0,0 +1,29 @@ +import withSession from 'lib/withSession' +import dbConnect from 'lib/dbConnect' +import User from 'models/User' + +export default withSession(async (req, res) => { + const {method} = req + + await dbConnect() + + switch (method) { + case 'POST': + try { + const {_id, email, isVerified, noteList} = await User.findByCredentials(req.body.email, req.body.password); + if (!email) { throw new Error('Something went wrong') } + + const user = {_id, email, isVerified, isLoggedIn: true, noteList} + req.session.set('user', user) + await req.session.save() + res.status(201).json(user) + } catch (error) { + res.status(400).json({isLoggedIn: false}) + } + break + default: + res.status(400).send({isLoggedIn: false}) + break + } +}) + diff --git a/pages/api/logout.js b/pages/api/logout.js new file mode 100644 index 0000000..8c7a766 --- /dev/null +++ b/pages/api/logout.js @@ -0,0 +1,6 @@ +import withSession from 'lib/withSession' + +export default withSession(async (req, res) => { + req.session.destroy() + res.json({isLoggedIn: false}) +}) diff --git a/pages/api/note/[id].js b/pages/api/note/[id].js new file mode 100644 index 0000000..8ba3b70 --- /dev/null +++ b/pages/api/note/[id].js @@ -0,0 +1,30 @@ +import dbConnect from 'lib/dbConnect' +import withSession from 'lib/withSession' +import Note from 'models/Note' + +export default withSession(async (req, res) => { + const { id } = req.query + await dbConnect() + + switch (req.method) { + case 'GET': + try { + const user = req.session.get('user') + + if (!user || !user?.isVerified || !id ) { + throw new Error('Something went wrong') + } + + const note = await Note.findById(id) + + if (!note) { + throw new Error('Something went wrong') + } + + res.status(200).json(note) + } catch (error) { + res.status(400).json({error: true}) + } + break + } +}) diff --git a/pages/api/notes.js b/pages/api/notes.js new file mode 100644 index 0000000..73a7217 --- /dev/null +++ b/pages/api/notes.js @@ -0,0 +1,101 @@ +import dbConnect from 'lib/dbConnect' +import withSession from 'lib/withSession' +import NoteList from 'models/NoteList' +import Note from 'models/Note' + +export default withSession(async (req, res) => { + await dbConnect() + + switch (req.method) { + case 'GET': + try { + const user = req.session.get('user') + + if (!user || !user.isVerified) { + throw new Error('Something went wrong') + } + + const {notes} = await NoteList.findById(user.noteList) + + res.status(200).json(notes) + } catch (error) { + res.status(400).json([]) + } + break + case 'POST': + try { + const user = req.session.get('user') + const {title, content} = req.body + + if (!user || !user?.isVerified || !content) { + throw new Error('Something went wrong') + } + + const note = await Note.create({content}) + + const noteList = await NoteList.findById(user.noteList) + noteList.notes.push({title: title ? title : 'No title', noteId: note}) + await noteList.save() + + res.status(200).json(noteList.notes) + } catch (error) { + res.status(400).json([]) + } + break + case 'DELETE': + try { + const user = req.session.get('user') + const {_id} = req.body + + if (!user || !user?.isVerified || !_id) { + throw new Error('Something went wrong') + } + + const noteList = await NoteList.findById(user.noteList) + const noteId = noteList.notes + .find(n => n._id.toString() === _id.toString()).noteId + + if (!noteList || !noteId) { + throw new Error('Something went wrong') + } + + await Note.findByIdAndDelete(noteId) + + noteList.notes.pull(_id) + await noteList.save() + + res.status(200).json(noteList.notes) + } catch (error) { + res.status(400).json([]) + } + break + case 'PUT': + try { + const user = req.session.get('user') + const {_id, title, noteId, content} = req.body + + if (!user || !user?.isVerified || !_id || !content) { + throw new Error('Something went wrong') + } + + await Note.findByIdAndUpdate(noteId, {content}, {new: true}) + + const notes = await NoteList.findOneAndUpdate( + { _id: user.noteList, "notes.noteId": noteId }, + { $set: { "notes.$.title": title ? title : 'No title' } }, + { new: true } + ) + + res.status(200).json(notes) + } catch (error) { + console.log(error) + res.status(400).json([]) + } + break + default: + res.status(400).send() + break + } +}) + + diff --git a/pages/api/register.js b/pages/api/register.js new file mode 100644 index 0000000..f89e347 --- /dev/null +++ b/pages/api/register.js @@ -0,0 +1,35 @@ +import dbConnect from 'lib/dbConnect' +import withSession from 'lib/withSession' +import sendMail from 'lib/sendMail' +import {subject, text, html} from 'helpers/email' +import User from 'models/User' +import NoteList from 'models/NoteList' + +export default withSession(async (req, res) => { + await dbConnect() + + switch (req.method) { + case 'POST': + try { + const noteList = await NoteList.create({}) + + const {_id, email, verificationKey: key} = await User.create({...req.body, noteList}) + if (!email) { throw new Error('Something went wrong') } + + sendMail(req.body.email, subject, text(key), html(key)) + + const user = {_id, email, noteList, isVerified: false, isLoggedIn: true} + req.session.set('user', user) + await req.session.save() + res.status(201).json(user) + } catch (error) { + console.log(error) + res.status(400).json({isLoggedIn: false}) + } + break + default: + res.status(400).json({isLoggedIn: false}) + break + } +}) + diff --git a/pages/api/user.js b/pages/api/user.js new file mode 100644 index 0000000..0b308f7 --- /dev/null +++ b/pages/api/user.js @@ -0,0 +1,22 @@ +import dbConnect from 'lib/dbConnect' +import withSession from 'lib/withSession' +import User from 'models/User' + +export default withSession(async (req, res) => { + await dbConnect() + + const user = req.session.get('user') + const state = await User.state(user._id) + + if (user) { + res.json({ + isLoggedIn: true, + ...user, + ...state + }) + } else { + res.json({ + isLoggedIn: false, + }) + } +}) diff --git a/pages/api/verify.js b/pages/api/verify.js new file mode 100644 index 0000000..1606dbc --- /dev/null +++ b/pages/api/verify.js @@ -0,0 +1,45 @@ +import withSession from 'lib/withSession' +import dbConnect from 'lib/dbConnect' +import sendMail from 'lib/sendMail' +import User from 'models/User' +import {subject, text, html} from 'helpers/email' + +export default withSession(async (req, res) => { + await dbConnect() + + switch (req.method) { + case 'GET': + try { + const {email} = req.session.get('user') + if (!email) { throw new Error('Something went wrong') } + + const key = await User.getVerificationKey(email) + if (!key) { throw new Error('Something went wrong') } + + const response = await sendMail(email, subject, text(key), html(key)) + if (!response?.accepted?.length) { throw new Error('Something went wrong') } + + res.status(204).send() + } catch (error) { + res.status(400).send() + } + break + case 'POST': + try { + if (req.body.key) { + const user = await User.verifyUser(req.body._id, req.body.key) + req.session.set('user', user) + await req.session.save() + res.status(200).json(user) + } + } catch (error) { + console.log(error) + res.status(400).send() + } + break + default: + res.status(400).send() + break + } +}) + diff --git a/pages/index.js b/pages/index.js new file mode 100644 index 0000000..9df0acf --- /dev/null +++ b/pages/index.js @@ -0,0 +1,67 @@ +import React, { useState ,useRef } from 'react' +import useUser from 'lib/useUser' +import {Layout, App} from 'components' +import {open} from 'helpers/windowActions' +import {Notes} from 'apps' + +const appList = { + Notes, +}; + +const Home = () => { + const [apps, setApps] = useState([]) + const {mutateUser} = useUser({ + redirectToLogin: true, + redirectToVerify: true, + redirectToApps: true, + }) + + return ( + <Layout apps={apps} setApps={setApps}> + <> + { + Object.keys(appList).map(appName => ( + <div key={`${appName}_icon`} onClick={() => open(appName, apps, setApps)}> + <img src={`./${appName.toLowerCase()}.svg`} alt={`${appName} Icon`} /> + <p>{appName}</p> + </div> + )) + } + { apps && apps.map(app => { + const AppName = appList[app.name] + + return ( + <App + key={`${app.name}_app`} + app={app} + apps={apps} + setApps={setApps} + > + <AppName /> + </App> + ); + })} + <style jsx>{` + div { + text-decoration: none; + display: inline-block; + width: 4em; + cursor: pointer; + } + + img { + width: 4em; + } + + p { + padding-top: .5em; + text-align: center; + color: #333; + } + `}</style> + </> + </Layout> + ) +} + +export default Home diff --git a/pages/login.js b/pages/login.js new file mode 100644 index 0000000..3fb2df6 --- /dev/null +++ b/pages/login.js @@ -0,0 +1,26 @@ +import { useState } from 'react' +import useUser from 'lib/useUser' +import submitForm from 'helpers/submitForm' +import {Layout, Form} from 'components' + +const Login = () => { + const {mutateUser} = useUser({ + redirectToVerify: true, + redirectToApps: true, + }) + + const [errorMsg, setErrorMsg] = useState('') + const handleSubmit = e => submitForm(e, '/api/login', mutateUser, setErrorMsg) + + return ( + <Layout> + <Form + isLogin + errorMessage={errorMsg} + onSubmit={handleSubmit} + /> + </Layout> + ) +} + +export default Login diff --git a/pages/register.js b/pages/register.js new file mode 100644 index 0000000..3a00504 --- /dev/null +++ b/pages/register.js @@ -0,0 +1,24 @@ +import {useState } from 'react' +import useUser from 'lib/useUser' +import submitForm from 'helpers/submitForm' +import {Layout, Form} from 'components' + +const Register = () => { + const { mutateUser } = useUser({ + redirectToVerify: true, + }) + + const [errorMsg, setErrorMsg] = useState('') + const handleSubmit = e => submitForm(e, '/api/register', mutateUser, setErrorMsg) + + return ( + <Layout> + <Form + errorMessage={errorMsg} + onSubmit={handleSubmit} + /> + </Layout> + ) +} + +export default Register diff --git a/pages/verify.js b/pages/verify.js new file mode 100644 index 0000000..1d89e22 --- /dev/null +++ b/pages/verify.js @@ -0,0 +1,109 @@ +import {useState, useEffect} from 'react' +import {useRouter} from 'next/router' +import useUser from 'lib/useUser' +import fetchJson from 'lib/fetchJson' +import {Layout} from 'components' + +const Verify = () => { + const router = useRouter() + const {user, mutateUser} = useUser({ + redirectToLogin: true, + redirectToApps: true, + }) + const [errorMsg, setErrorMsg] = useState('') + const [sending, setSending] = useState(false) + const [resent, setResent] = useState(false) + + const handleSendMail = async e => { + e.preventDefault() + setErrorMsg('') + setSending(true) + try { + await fetch('/api/verify') + setResent(true) + } catch (error) { + setErrorMsg('Could not send verification email') + } finally { + setSending(false) + } + } + + const handleKey = async e => { + e.preventDefault() + const key = e.currentTarget.key.value + try { + mutateUser( + await fetchJson('/api/verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({_id: user._id, key}), + }) + ) + } catch (err) { + setErrorMsg('Could not verify user') + } + } + + return ( + <Layout> + {!user + ? ( + <div className="window window--popup"> + <p>Loading...</p> + </div> + ) : ( + <div className="window window--popup"> + <p>One last step missing</p> + <p>{`To start using Notes App type the verification code we sent to your email (${user.email}):`}</p> + <form onSubmit={handleKey}> + <input type="text" placeholder="Verification key" name="key" /> + <button className="window__button" type="submit">Send</button> + </form> + { + sending + ? ( + <p>Sending...</p> + ) : ( + resent + ? ( + <p>Mail was successfully sent again, check your mailbox!</p> + ) : ( + <p>If you didn't get verification email + <span className="verify__sendEmail" onClick={handleSendMail}>send it again</span>. + </p> + ) + ) + } + {errorMsg && <p className="verify__errorMsg">{errorMsg}</p>} + </div> + ) + } + <style jsx>{` + form { + padding .5em 0; + } + + input { + padding: .75em; + border: 1px dashed #333; + border-radius: .5em; + } + + p { + line-height: 1.33; + padding: .5em 0; + } + + .verify__sendEmail { + color: blue; + cursor: pointer; + } + .verify__errorMsg { + color: brown; + } + `}</style> + </Layout> + ) +} + +export default Verify |