Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e81dccef6 | |||
| adeb8c4267 | |||
| eb6c3a40d7 | |||
| 4d4a0b5401 | |||
| 490860159e |
@@ -39,7 +39,7 @@ docker-compose up --build
|
|||||||
To set up this template in development mode:
|
To set up this template in development mode:
|
||||||
|
|
||||||
1. Ensure mariadb is running in your development environment
|
1. Ensure mariadb is running in your development environment
|
||||||
2. Run `mariadb sql/create_database.sql` as the root user
|
2. Run `mariadb tools/create_database.sql` as the root user
|
||||||
3. Run `npm install`
|
3. Run `npm install`
|
||||||
4. Run `cp .envdev .env` and enter your details into the `.env` file
|
4. Run `cp .envdev .env` and enter your details into the `.env` file
|
||||||
5. Execute `npm run dev`
|
5. Execute `npm run dev`
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ const Account = props => {
|
|||||||
|
|
||||||
//grab the user's info
|
//grab the user's info
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/account`, {
|
authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/account`)
|
||||||
method: 'GET'
|
|
||||||
})
|
|
||||||
.then(blob => blob.json())
|
.then(blob => blob.json())
|
||||||
.then(json => contactRef.current.checked = json.contact)
|
.then(json => contactRef.current.checked = json.contact)
|
||||||
.catch(e => console.error(e))
|
.catch(e => console.error(e))
|
||||||
|
|||||||
@@ -31,15 +31,14 @@ const Login = props => {
|
|||||||
async evt => {
|
async evt => {
|
||||||
//on submit
|
//on submit
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
const [err, newTokens] = await handleSubmit(emailRef.current.value, passwordRef.current.value);
|
const [err, accessToken] = await handleSubmit(emailRef.current.value, passwordRef.current.value);
|
||||||
if (err) {
|
if (err) {
|
||||||
alert(err);
|
alert(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
//save auth tokens and redirect
|
//save auth tokens and redirect
|
||||||
if (newTokens) {
|
if (accessToken) {
|
||||||
authTokens.setAccessToken(newTokens.accessToken);
|
authTokens.setAccessToken(accessToken);
|
||||||
authTokens.setRefreshToken(newTokens.refreshToken);
|
|
||||||
|
|
||||||
props.history.push('/');
|
props.history.push('/');
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,8 @@ const handleSubmit = async (email, password) => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
})
|
}),
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|
||||||
//handle errors
|
//handle errors
|
||||||
@@ -88,8 +88,8 @@ const handleSubmit = async (email, password) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//return the new auth tokens
|
//return the new auth tokens
|
||||||
const newTokens = await result.json();
|
const accessToken = await result.text();
|
||||||
return [null, newTokens];
|
return [null, accessToken];
|
||||||
};
|
};
|
||||||
|
|
||||||
//returns an error message, or null on success
|
//returns an error message, or null on success
|
||||||
|
|||||||
@@ -51,13 +51,7 @@ const handleSubmit = async (password, authTokens) => {
|
|||||||
|
|
||||||
//force a logout
|
//force a logout
|
||||||
const result2 = await authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/logout`, {
|
const result2 = await authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/logout`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE'
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: authTokens.refreshToken
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result2.ok) {
|
if (!result2.ok) {
|
||||||
@@ -65,7 +59,6 @@ const handleSubmit = async (password, authTokens) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
authTokens.setAccessToken('');
|
authTokens.setAccessToken('');
|
||||||
authTokens.setRefreshToken('');
|
|
||||||
|
|
||||||
return [null];
|
return [null];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,13 +12,7 @@ const Logout = () => {
|
|||||||
{ /* Logout logs you out of the server too */ }
|
{ /* Logout logs you out of the server too */ }
|
||||||
<Link to='/' onClick={async () => {
|
<Link to='/' onClick={async () => {
|
||||||
const result = await authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/logout`, { //NOTE: this gets overwritten as a bugfix
|
const result = await authTokens.tokenFetch(`${process.env.AUTH_URI}/auth/logout`, { //NOTE: this gets overwritten as a bugfix
|
||||||
method: 'DELETE',
|
method: 'DELETE'
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: authTokens.refreshToken
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//any problems?
|
//any problems?
|
||||||
@@ -26,7 +20,6 @@ const Logout = () => {
|
|||||||
console.error(await result.text());
|
console.error(await result.text());
|
||||||
} else {
|
} else {
|
||||||
authTokens.setAccessToken('');
|
authTokens.setAccessToken('');
|
||||||
authTokens.setRefreshToken('');
|
|
||||||
}
|
}
|
||||||
}}>Logout</Link>
|
}}>Logout</Link>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -18,12 +18,7 @@ const NewsEditor = props => {
|
|||||||
|
|
||||||
//run once
|
//run once
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
const result = await fetch(`${process.env.NEWS_URI}/news/metadata?limit=999`, {
|
const result = await fetch(`${process.env.NEWS_URI}/news/metadata?limit=999`);
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
const err = `${result.status}: ${await result.text()}`;
|
const err = `${result.status}: ${await result.text()}`;
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ const NewsFeed = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//this... um...
|
//this... um...
|
||||||
fetch(`${process.env.NEWS_URI}/news`, {
|
fetch(`${process.env.NEWS_URI}/news`, {
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
signal: aborter.current.signal //oh dear
|
signal: aborter.current.signal //oh dear
|
||||||
})
|
})
|
||||||
.then(blob => blob.json())
|
.then(blob => blob.json())
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const Static = props => {
|
const Static = props => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className='page central'>
|
||||||
<header>
|
<header>
|
||||||
<h1 className='text centered'>Credits</h1>
|
<h1 className='text centered'>Credits</h1>
|
||||||
</header>
|
</header>
|
||||||
<h2>MERN-template</h2>
|
<h2 className='text centered'>MERN-template</h2>
|
||||||
<p>The <a href='https://github.com/krgamestudios/MERN-template'>MERN-template</a> developed by Kayne Ruse, KR Game Studios</p>
|
<p>The <a href='https://github.com/krgamestudios/MERN-template'>MERN-template</a> developed by Kayne Ruse, KR Game Studios</p>
|
||||||
</>
|
|
||||||
|
<Link className='text centered' to='/'>Return Home</Link>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Static;
|
export default Static;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const Static = props => {
|
const Static = props => {
|
||||||
return (
|
return (
|
||||||
<header>
|
<div className='page central'>
|
||||||
<h1 className="text centered">Privacy Policy</h1>
|
<header>
|
||||||
</header>
|
<h1 className="text centered">Privacy Policy</h1>
|
||||||
|
|
||||||
|
<Link className='text centered' to='/'>Return Home</Link>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Static;
|
export default Static;
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,16 @@ export const TokenContext = createContext();
|
|||||||
const TokenProvider = props => {
|
const TokenProvider = props => {
|
||||||
//state to be used
|
//state to be used
|
||||||
const [accessToken, setAccessToken] = useState('');
|
const [accessToken, setAccessToken] = useState('');
|
||||||
const [refreshToken, setRefreshToken] = useState('');
|
|
||||||
|
|
||||||
//make the access and refresh tokens persist between reloads
|
//make the access token persist between reloads
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAccessToken(localStorage.getItem("accessToken") || '');
|
setAccessToken(localStorage.getItem("accessToken") || '');
|
||||||
setRefreshToken(localStorage.getItem("refreshToken") || '');
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
//update the stored copies
|
//update the stored copies
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem("accessToken", accessToken);
|
localStorage.setItem("accessToken", accessToken);
|
||||||
localStorage.setItem("refreshToken", refreshToken);
|
}, [accessToken]);
|
||||||
}, [accessToken, refreshToken]);
|
|
||||||
|
|
||||||
//wrap the default fetch function
|
//wrap the default fetch function
|
||||||
const tokenFetch = async (url, options) => {
|
const tokenFetch = async (url, options) => {
|
||||||
@@ -36,24 +33,16 @@ const TokenProvider = props => {
|
|||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${bearer}`
|
'Authorization': `Bearer ${bearer}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
credentials: 'include'
|
||||||
token: refreshToken
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//ping the auth server for a new token
|
//ping the auth server for a new access token
|
||||||
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
credentials: 'include'
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: refreshToken
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//any errors, throw them
|
//any errors, throw them
|
||||||
@@ -65,7 +54,6 @@ const TokenProvider = props => {
|
|||||||
const newAuth = await response.json();
|
const newAuth = await response.json();
|
||||||
|
|
||||||
setAccessToken(newAuth.accessToken);
|
setAccessToken(newAuth.accessToken);
|
||||||
setRefreshToken(newAuth.refreshToken);
|
|
||||||
bearer = newAuth.accessToken;
|
bearer = newAuth.accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +63,8 @@ const TokenProvider = props => {
|
|||||||
headers: {
|
headers: {
|
||||||
...(options || { headers: {} }).headers,
|
...(options || { headers: {} }).headers,
|
||||||
'Authorization': `Bearer ${bearer}`
|
'Authorization': `Bearer ${bearer}`
|
||||||
}
|
},
|
||||||
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,12 +77,7 @@ const TokenProvider = props => {
|
|||||||
//ping the auth server for a new token
|
//ping the auth server for a new token
|
||||||
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
credentials: 'include'
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: refreshToken
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//any errors, throw them
|
//any errors, throw them
|
||||||
@@ -105,7 +89,6 @@ const TokenProvider = props => {
|
|||||||
const newAuth = await response.json();
|
const newAuth = await response.json();
|
||||||
|
|
||||||
setAccessToken(newAuth.accessToken);
|
setAccessToken(newAuth.accessToken);
|
||||||
setRefreshToken(newAuth.refreshToken);
|
|
||||||
|
|
||||||
//finally
|
//finally
|
||||||
return cb(newAuth.accessToken);
|
return cb(newAuth.accessToken);
|
||||||
@@ -115,7 +98,7 @@ const TokenProvider = props => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TokenContext.Provider value={{ accessToken, refreshToken, setAccessToken, setRefreshToken, tokenFetch, tokenCallback, getPayload: () => decode(accessToken) }}>
|
<TokenContext.Provider value={{ accessToken, setAccessToken, tokenFetch, tokenCallback, getPayload: () => decode(accessToken) }}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</TokenContext.Provider>
|
</TokenContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-2
@@ -70,7 +70,7 @@ See https://github.com/krgamestudios/MERN-template/wiki for help.
|
|||||||
//auth configuration
|
//auth configuration
|
||||||
const authName = await question('Auth Name', 'auth');
|
const authName = await question('Auth Name', 'auth');
|
||||||
const authWebAddress = await question('Auth Web Address', `${authName}.${projectWebAddress}`);
|
const authWebAddress = await question('Auth Web Address', `${authName}.${projectWebAddress}`);
|
||||||
const authPostValidationHook = await question('Auth Post Validation Hook', '');
|
const authPostValidationHookArray = await question('Auth Post Validation Hook Array', '');
|
||||||
const authResetAddress = await question('Auth Reset Addr', `${projectWebAddress}/reset`);
|
const authResetAddress = await question('Auth Reset Addr', `${projectWebAddress}/reset`);
|
||||||
const authDBUser = await question('Auth DB Username', authName);
|
const authDBUser = await question('Auth DB Username', authName);
|
||||||
const authDBPass = await question('Auth DB Password', 'charizard');
|
const authDBPass = await question('Auth DB Password', 'charizard');
|
||||||
@@ -189,7 +189,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- WEB_PROTOCOL=https
|
- WEB_PROTOCOL=https
|
||||||
- WEB_ADDRESS=${authWebAddress}
|
- WEB_ADDRESS=${authWebAddress}
|
||||||
- HOOK_POST_VALIDATION=${authPostValidationHook}
|
- HOOK_POST_VALIDATION_ARRAY=${authPostValidationHookArray}
|
||||||
- WEB_RESET_ADDRESS=${authResetAddress}
|
- WEB_RESET_ADDRESS=${authResetAddress}
|
||||||
- WEB_PORT=${authPort}
|
- WEB_PORT=${authPort}
|
||||||
- DB_HOSTNAME=database
|
- DB_HOSTNAME=database
|
||||||
|
|||||||
Generated
+1662
-3065
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mern-template",
|
"name": "mern-template",
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"description": "A website template using the MERN stack.",
|
"description": "A website template using the MERN stack.",
|
||||||
"main": "server/server.js",
|
"main": "server/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user