Merge branch 'master' of gitlab.com:pawnstar/sos-nextjs

main
Ashelyn Dawn 5 years ago
commit 51f26f5692

@ -17,18 +17,39 @@ router.post('/', parseJSON, loginValidation, async (req, res) => {
req.body.password req.body.password
) )
// TODO: Create session if(!user){
return res.status(422).json({errors: [{
param: 'email',
msg: 'Invalid login'
},{
param: 'password',
msg: ' '
}]})
}
const session = await db.session.create(
user.uuid,
req.ip,
req.get('User Agent') || "",
req.get('Referrer') || "",
null
)
req.session.uuid = session.uuid
if(user) res.json(user)
res.json(user)
else
res.status(400).json({errors: [{param: 'password', msg: 'Invalid login'}]})
}) })
// TODO: Login link stuff // TODO: Login link stuff
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
// TODO: Get current user and session stuff res.json(req.user)
})
// TODO: de-auth session
router.get('/logout', (req, res) => {
req.session = null
res.redirect('/')
}) })
module.exports = router; module.exports = router;

@ -14,16 +14,14 @@ router.use((req, res, next)=>{
next() next()
}) })
router.use(require('cookie-session')({name: 'sos-session', secret: process.env.COOKIE_SECRET}))
router.use(require('./middleware/session'))
router.use('/auth', require('./auth')) router.use('/auth', require('./auth'))
router.use('/users/', require('./users')) router.use('/users/', require('./users'))
router.use('/items/', require('./items')) router.use('/items/', require('./items'))
router.use('/images/', require('./images')) router.use('/images/', require('./images'))
router.use('/categories/', require('./categories')) router.use('/categories/', require('./categories'))
router.get('/', (req, res)=>{
res.json({test: true})
})
router.use((req, res, next)=>{ router.use((req, res, next)=>{
const err = new Error('Not found') const err = new Error('Not found')
err.status = 404 err.status = 404

@ -0,0 +1,20 @@
const db = require('../../db')
const sessionMiddleware = (req, res, next)=>{
(async ()=>{
let session = await db.session.validate(req.session.uuid);
if(!session) return;
// Update last active
session = await db.session.update(req.session.uuid);
// Attach updated session object to request
req.sessionObj = session;
if(session && session.user)
req.user = session.user;
})()
.then(next).catch(next);
}
module.exports = sessionMiddleware;

@ -17,7 +17,15 @@ router.post('/', parseJSON, registerValidation, async (req, res) => {
req.body.password req.body.password
) )
// TODO: Create session const session = await db.session.create(
user.uuid,
req.ip,
req.get('User Agent') || "",
req.get('Referrer') || "",
null
)
req.session.uuid = session.uuid
res.json(user) res.json(user)
}) })

@ -26,7 +26,7 @@ const Header = ({user}) => (
):( ):(
<> <>
<li><Link href="/account"><a>Account</a></Link></li> <li><Link href="/account"><a>Account</a></Link></li>
<li><Link href="/logout"><a>Log out</a></Link></li> <li><Link href="/api/auth/logout"><a>Log out</a></Link></li>
</> </>
) )
} }

@ -8,8 +8,8 @@ export default function Hero(){
return ( return (
<div className={styles.hero}> <div className={styles.hero}>
<div style={{backgroundImage: `url(${background})`}}> <div style={{backgroundImage: `url(${background})`}}>
<img class={styles.icon} src={logo}/> <img className={styles.icon} src={logo}/>
<div class={styles.content}> <div className={styles.content}>
<h2>Your Socks. Your Style.</h2> <h2>Your Socks. Your Style.</h2>
<p>Are you tired of plain (boring) white socks? We've got you covered.</p> <p>Are you tired of plain (boring) white socks? We've got you covered.</p>
<p>At Society of Socks we strive to give every design a unique personality that is sure to stand out from the crowd.</p> <p>At Society of Socks we strive to give every design a unique personality that is sure to stand out from the crowd.</p>

@ -2,5 +2,6 @@ module.exports = {
user: require('./models/user'), user: require('./models/user'),
item: require('./models/item'), item: require('./models/item'),
category: require('./models/category'), category: require('./models/category'),
user: require('./models/user') user: require('./models/user'),
session: require('./models/session')
} }

@ -4,7 +4,7 @@ module.exports = [
idProperty: 'uuid', idProperty: 'uuid',
properties: [ properties: [
'email', 'email',
'password', 'password_hash',
'email_confirmed', 'email_confirmed',
'time_registered', 'time_registered',
'time_email_confirmed' 'time_email_confirmed'
@ -44,6 +44,7 @@ module.exports = [
], ],
associations: [ associations: [
{name: 'originating_link', mapId: 'loginLinkMap', columnPrefix: 'login_link_'}, {name: 'originating_link', mapId: 'loginLinkMap', columnPrefix: 'login_link_'},
{name: 'user', mapId: 'userMap', columnPrefix: 'session_user_'},
// {name: 'cart', mapId: 'cartMap', columnPrefix: 'cart_'} // {name: 'cart', mapId: 'cartMap', columnPrefix: 'cart_'}
] ]
} }

@ -21,5 +21,33 @@ session.create = async (user_uuid, ip_address, user_agent, referer, origin_link_
debug(query); debug(query);
const {rows} = await pg.query(query) const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'sessionMap', 'session_'); return joinjs.map(rows, mappings, 'sessionMap', 'session_')[0];
}
session.validate = async (session_uuid) => {
const query = {
text: 'select * from validate_session($1)',
values: [
session_uuid
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'sessionMap', 'session_')[0];
}
session.update = async (session_uuid) => {
const query = {
text: 'select * from update_session($1)',
values: [
session_uuid
]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'sessionMap', 'session_')[0];
} }

@ -60,13 +60,13 @@ user.login = async (email, password) => {
if(!_user){ if(!_user){
// Avoid early exit timing difference // Avoid early exit timing difference
await bcrypt.hash(password, saltRounds) await bcrypt.hash(password, saltRounds)
throw new Error("User not found") return null
} }
const passwordCorrect = await bcrypt.compare(password, _user.password_hash) const passwordCorrect = await bcrypt.compare(password, _user.password_hash)
if(!passwordCorrect) if(!passwordCorrect)
throw new Error("Password incorrect") return null
return _user return _user
} }

@ -23,7 +23,20 @@ as $function$
begin begin
return query select * from v_session return query select * from v_session
where session_uuid = _session_uuid where session_uuid = _session_uuid
and session_time_last_active + session_timeout_length < now(); and session_time_last_active + session_timeout_length > now();
end; $function$;
create or replace function public.update_session(_session_uuid uuid)
returns setof public.v_session
language plpgsql
as $function$
begin
update "session"
set session_time_last_active = now()
where session_uuid = _session_uuid
and now() < (select session_time_last_active + session_timeout_length);
return query select * from validate_session(_session_uuid);
end; $function$; end; $function$;
create or replace function public.login_user_session(_user_uuid uuid, _timeout_length interval, _ip_addr varchar(50), _user_agent varchar(500), _referer varchar(500), _link uuid) create or replace function public.login_user_session(_user_uuid uuid, _timeout_length interval, _ip_addr varchar(50), _user_agent varchar(500), _referer varchar(500), _link uuid)

49
package-lock.json generated

@ -2367,11 +2367,47 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
}, },
"cookie-session": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz",
"integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==",
"requires": {
"cookies": "0.8.0",
"debug": "2.6.9",
"on-headers": "~1.0.2"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"cookie-signature": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}, },
"cookies": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
"integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
"requires": {
"depd": "~2.0.0",
"keygrip": "~1.1.0"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
}
}
},
"copy-concurrently": { "copy-concurrently": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
@ -4050,6 +4086,14 @@
"minimist": "^1.2.0" "minimist": "^1.2.0"
} }
}, },
"keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
"integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
"requires": {
"tsscmp": "1.0.6"
}
},
"kind-of": { "kind-of": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -7326,6 +7370,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}, },
"tsscmp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",

@ -16,6 +16,7 @@
"base64-async": "^2.1.3", "base64-async": "^2.1.3",
"bcrypt": "^3.0.8", "bcrypt": "^3.0.8",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"cookie-session": "^1.4.0",
"cross-env": "^7.0.0", "cross-env": "^7.0.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",

@ -1,17 +1,20 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import axios from 'axios'
import Header from '~/components/header' import Header from '~/components/header'
import Footer from '~/components/footer' import Footer from '~/components/footer'
import "../styles/layout.css" import "../styles/layout.css"
const Layout = ({ Component, pageProps }) => { Layout.getInitialProps = async ({ctx}) => {
// Retrieve user information const {data: user} = await axios.get(`/api/auth`, { headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined })
// const user = useUserSession() return {user}
}
function Layout({ Component, pageProps, user }){
return ( return (
<> <>
<Header /> <Header user={user} />
<main><Component {...pageProps} /></main> <main><Component {...pageProps} /></main>
<Footer/> <Footer/>
</> </>

@ -1,18 +1,31 @@
import React from 'react' import React from 'react'
import Link from 'next/link' import Link from 'next/link'
import Router from 'next/router'
import isEmail from 'validator/lib/isEmail' import isEmail from 'validator/lib/isEmail'
import axios from 'axios' import axios from 'axios'
import {FormController, Input, Button} from '~/components/form' import {FormController, Input, Button} from '~/components/form'
import useErrorReducer from '../hooks/errorReducer'
export default function Login(){ export default function Login(){
const [errors, dispatch] = useErrorReducer()
const submit = async (values)=>{ const submit = async (values)=>{
const {data} = await axios.post(`/api/auth`, values) try {
console.log(data) await axios.post(`/api/auth`, values)
Router.push('/')
} catch (err) {
if(!err.response || err.response.status !== 422) throw err;
dispatch({
type: 'set_errors',
errors: err.response.data.errors
})
}
} }
return ( return (
<FormController onSubmit={submit}> <FormController errors={errors} errorDispatch={dispatch} onSubmit={submit}>
<h1>Login</h1> <h1>Login</h1>
<Input label="Email" type="text" name="email" validate={value=>isEmail(value)} hint="Enter a valid email address" /> <Input label="Email" type="text" name="email" validate={value=>isEmail(value)} hint="Enter a valid email address" />
<Input label="Password" type="password" name="password" validate={value=>(value.length >= 8)} hint="Password must be at least 8 characters long" /> <Input label="Password" type="password" name="password" validate={value=>(value.length >= 8)} hint="Password must be at least 8 characters long" />

Loading…
Cancel
Save