У меня возникли проблемы с развертыванием моего первого приложения MERN + Redux на Heroku, я перепробовал почти все, и я не знаю, что вызывает эту ошибку в консоли Chrome. В Edge ошибка вместо этого Uncaught ReferenceError: SharedArrayBuffer is not defined
.
Я не знаю, это мой серверный код или моя клиентская сторона React. Я поместил несколько отладочных console.logs, но они не сработали при развертывании, и все, что я получаю, это пустой экран на моей веб-странице Heroku. Проект отлично работает локально. Любая помощь приветствуется, и вот ссылка на проект: https://github.com/eninodel/JamShare/tree/master
Структура папок
.
├── Frontend
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src
│ │ ├── actions
│ │ │ ├── PlayingActions.js
│ │ │ ├── PostsActions.js
│ │ │ ├── TokenActions.js
│ │ │ └── UserActions.js
│ │ ├── frontend
│ │ │ ├── components
│ │ │ │ ├── Common
│ │ │ │ │ ├── DefaultImage.js
│ │ │ │ │ ├── MainNav.js
│ │ │ │ │ ├── MobileNavBar.js
│ │ │ │ │ ├── NavBar.js
│ │ │ │ │ └── Player.js
│ │ │ │ ├── CreatePost
│ │ │ │ │ ├── CreatePost.js
│ │ │ │ │ ├── DisplaySong.js
│ │ │ │ │ ├── SearchBar.js
│ │ │ │ │ └── SearchResult.js
│ │ │ │ ├── Homepage
│ │ │ │ │ └── Homepage.js
│ │ │ │ ├── Login
│ │ │ │ │ └── Login.js
│ │ │ │ ├── Posts
│ │ │ │ │ ├── Comments.js
│ │ │ │ │ ├── CreateComment.js
│ │ │ │ │ ├── DisplayPosts.js
│ │ │ │ │ └── Post.js
│ │ │ │ ├── Profile
│ │ │ │ │ └── ProfilePage.js
│ │ │ │ ├── TopPosts
│ │ │ │ │ └── TopPosts.js
│ │ │ │ ├── useAuth.js
│ │ │ │ └── useComponentVisible.js
│ │ │ ├── resources
│ │ │ │ ├── concert.jpg
│ │ │ │ ├── createpost.jpg
│ │ │ │ ├── loginPic.jpg
│ │ │ │ └── toptracks.jpg
│ │ │ ├── App.js
│ │ │ ├── index.css
│ │ │ └── Router.js
│ │ ├── reducers
│ │ │ ├── index.js
│ │ │ ├── Playing.js
│ │ │ ├── TokenReducer.js
│ │ │ ├── UpdatePosts.js
│ │ │ └── UserReducer.js
│ │ └── index.js
│ ├── package-lock.json
│ └── package.json
├── node_modules
├── .gitignore
├── index.js
├── package-lock.json
├── package.json
├── process.env
└── Procfile
/package.json
{
"name": "jamshareserver",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"heroku-postbuild": "cd Frontend && npm install --only=dev && npm install && npm run build"
},
"engines": {
"node": "14.17.0"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"http": "0.0.1-security",
"mongodb": "^4.0.1",
"nodemon": "^2.0.12",
"request": "^2.88.2",
"spotify-web-api-node": "^5.0.2"
}
}
/index.js
const express = require("express");
const spotifyWebAPI = require("spotify-web-api-node");
const cors = require("cors");
const bodyParser = require("body-parser");
const SpotifyWebApi = require("spotify-web-api-node");
const { MongoClient, ObjectId } = require("mongodb");
const path = require("path");
// require("dotenv").config({ path: "./process.env" });
require("dotenv").config();
const port = process.env.PORT || 8000;
const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
async function connect() {
try {
await client.connect();
} catch (err) {
console.log(err);
}
}
async function getRecentPosts() {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.find({})
.sort({ _id: -1 })
.limit(8);
const results = await cursor.toArray();
return results;
} catch (err) {
console.log(err);
}
}
async function updateLikes(_id, likes) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.updateOne({ _id: ObjectId(_id) }, { $set: { likes: likes } });
return cursor;
} catch (err) {
console.log(err);
}
}
async function createPost(
user,
userId,
userProfilePic,
body,
uri,
date,
image,
name
) {
try {
await connect();
const cursor = await client.db("JamShare").collection("Posts").insertOne({
body,
uri,
userProfilePic,
likes: [],
comments: [],
user,
userId,
date,
image,
name,
});
return cursor;
} catch (err) {
console.log(err);
}
}
async function deleteComment(commentId) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.updateMany(
{},
{ $pull: { comments: { commentId: ObjectId(commentId) } } }
);
return cursor;
} catch (err) {
console.log(err);
}
}
async function addComment(_id, user, userId, body) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.updateOne(
{ _id: ObjectId(_id) },
{ $push: { comments: { user, userId, body, commentId: ObjectId() } } },
{ upsert: true }
);
return cursor;
} catch (err) {
console.log(err);
}
}
async function getPost(_id) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.find({ _id: ObjectId(_id) });
const results = await cursor.toArray();
return results;
} catch (err) {
console.log(err);
}
}
async function getUser(userId) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Profiles")
.find({ userId: userId });
const result = cursor.toArray();
return result;
} catch (err) {
console.log(err);
}
}
async function addUser(userId, user, userProfilePic, bio) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Profiles")
.updateOne(
{ userId: userId },
{ $set: { userId, user, userProfilePic, bio } },
{ upsert: true }
);
return cursor;
} catch (err) {
console.log(err);
}
}
async function getUserPosts(userId) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.find({ userId: userId })
.sort({ _id: -1 });
const results = await cursor.toArray();
return results;
} catch (err) {
console.log(err);
}
}
async function deletePost(_id) {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.deleteMany({ _id: ObjectId(_id) });
return cursor;
} catch (err) {
console.log(err);
}
}
async function getTopPosts() {
try {
await connect();
const cursor = await client
.db("JamShare")
.collection("Posts")
.aggregate([
{
$project: {
body: 1,
uri: 1,
userProfilePic: 1,
comments: 1,
user: 1,
userId: 1,
date: 1,
likes: 1,
image: 1,
name: 1,
likes_count: { $size: "$likes" },
},
},
{ $sort: { likes_count: -1 } },
]);
const results = await cursor.toArray();
return results;
} catch (err) {
console.log(err);
}
}
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, "Frontend", "build")));
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "Frontend", "build", "index.html"));
});
app.get("/topPosts", (req, res) => {
getTopPosts()
.then((posts) => {
console.log(posts);
res.json({ data: posts });
})
.catch((err) => res.sendStatus(400));
});
app.get("/getUserPosts", (req, res) => {
getUserPosts(req.query.userId)
.then((posts) => res.json({ data: posts }))
.catch((err) => res.sendStatus(400));
});
app.get("/lookUpUser", (req, res) => {
getUser(req.query.userId)
.then((user) => {
const actualUser = user[0];
if (!actualUser) {
res.json({
data: {},
});
} else {
res.json({
data: {
user: actualUser.user,
userId: actualUser.userId,
bio: actualUser.bio,
userProfilePic: actualUser.userProfilePic,
},
});
}
})
.catch((err) => res.sendStatus(400));
});
app.get("/lookUpPost", (req, res) => {
getPost(req.query._id)
.then((post) => {
const actualPost = post[0];
res.json({
data: {
body: actualPost.body,
comments: actualPost.comments,
},
});
})
.catch((err) => res.sendStatus(400));
});
app.get("/recentposts", (req, res) => {
getRecentPosts()
.then((posts) => {
res.json({
data: posts,
});
})
.catch((err) => {
console.log(err);
});
});
app.post("/deletePost", (req, res) => {
deletePost(req.body._id)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/addUser", (req, res) => {
addUser(req.body.userId, req.body.user, req.body.userProfilePic, req.body.bio)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/addComment", (req, res) => {
addComment(req.body._id, req.body.user, req.body.userId, req.body.body)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/deleteComment", (req, res) => {
deleteComment(req.body.commentId)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/updateLikes", (req, res) => {
updateLikes(req.body._id, req.body.newLikes)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/createPost", (req, res) => {
createPost(
req.body.user,
req.body.userId,
req.body.userProfilePic,
req.body.body,
req.body.uri,
req.body.date,
req.body.image,
req.body.name
)
.then((data) => res.sendStatus(200))
.catch((err) => res.sendStatus(400));
});
app.post("/refresh", (req, res) => {
const refreshToken = req.body.refreshToken;
const spotifyAPI = new SpotifyWebApi({
redirectUri: "https://sharejams.herokuapp.com/",
clientId: "dfe1eb532747437b9b7d84a113a3933f",
clientSecret: process.env.CLIENT_SECRET,
refreshToken,
});
spotifyAPI
.refreshAccessToken()
.then((data) => {
res.json({
accessToken: data.body.access_token,
expiresIn: data.body.expires_in,
});
})
.catch((err) => {
res.sendStatus(400);
});
});
app.post("/login", (req, res) => {
const code = req.body.code;
const spotifyAPI = new spotifyWebAPI({
redirectUri: "https://sharejams.herokuapp.com/",
clientId: "dfe1eb532747437b9b7d84a113a3933f",
clientSecret: process.env.CLIENT_SECRET,
});
spotifyAPI
.authorizationCodeGrant(code)
.then((data) => {
res.json({
accessToken: data.body.access_token,
refreshToken: data.body.refresh_token,
expiresIn: data.body.expires_in,
});
})
.catch((err) => {
res.sendStatus(400);
});
});
app.listen(port, () => {
console.log(port);
});
/Procfile
web:npm start
/Frontend/package.json
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"express": "^4.17.1",
"http": "0.0.1-security",
"js-cookie": "^3.0.0",
"js-cookies": "^1.0.4",
"mongodb": "^4.0.1",
"node": "^16.5.0",
"query-string": "^7.0.1",
"react": "^17.0.2",
"react-alert": "^7.0.3",
"react-alert-template-basic": "^1.0.2",
"react-alert-template-mui": "^1.0.7",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-spotify-web-playback": "^0.8.2",
"react-uuid": "^1.0.2",
"redux": "^4.1.0",
"spotify-web-api-node": "^5.0.2",
"uuid": "^8.3.2",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:8000",
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
/Frontend/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link
rel="manifest"
href="%PUBLIC_URL%/manifest.json"
crossorigin="use-credentials"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Jams</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
/Frontend/public/manifest.json
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
/Frontend/public/robots.txt
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
/Внешний интерфейс/src/index.js
import React from "react";
import ReactDom from "react-dom";
import App from "./frontend/App.js";
import "./frontend/index.css";
import { createStore } from "redux";
import { Provider } from "react-redux";
import allReducers from "./reducers/index";
const store = createStore(
allReducers
// window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
ReactDom.render(
<Provider store={store}>
<App />
</Provider>
document.getElementById("root")
);
/Frontend/src/frontend/App.js
import { useEffect, React } from "react";
import UseAuth from "./components/useAuth";
import Player from "./components/Common/Player";
import Router from "./Router";
import SpotifyWebApi from "spotify-web-api-node";
import { setUserId, setProduct } from "../actions/UserActions";
import { useDispatch, useSelector } from "react-redux";
import { positions, Provider } from "react-alert";
import AlertMUITemplate from "react-alert-template-mui";
import Cookies from "js-cookie";
import defaultImgAddress from "./components/Common/DefaultImage";
import axios from "axios";
let code = new URLSearchParams(window.location.search).get("code");
const spotifyAPI = new SpotifyWebApi({
clientId: "dfe1eb532747437b9b7d84a113a3933f",
});
const options = {
timeout: 15000,
position: positions.TOP_CENTER,
offset: "500px",
containerStyle: {
zIndex: 1000,
margin: 0,
padding: 0,
},
};
function App() {
console.log("here in app");
const dispatch = useDispatch();
let accessToken = UseAuth(code);
console.log("app2");
if (!accessToken) {
console.log("getting cookies");
accessToken = Cookies.get("accessToken");
}
console.log("getting playing");
let playing = useSelector((state) => state.PlayingReducer.playing);
useEffect(() => {
if (!accessToken || accessToken === "guest") return;
console.log("made it into useeffect");
spotifyAPI.setAccessToken(accessToken);
spotifyAPI
.getMe()
.then((res) => {
dispatch(setProduct(res.body.product));
if (res.body.id) dispatch(setUserId(res.body.id));
let userProfilePic = null;
if (res.body.images.length !== 0) {
userProfilePic = res.body.images[0].url;
} else {
userProfilePic = defaultImgAddress;
}
return {
userId: res.body.id,
user: res.body.display_name,
userProfilePic: userProfilePic,
};
})
.then((data) => {
const getURL = "/lookUpUser?userId=" + data.userId;
axios
.get(getURL)
.then((res) => {
let bio = "";
if (res.data.data) {
bio = res.data.data.bio;
if (
data.userProfilePic === defaultImgAddress &&
res.data.data.userProfilePic &&
res.data.data.userProfilePic !== defaultImgAddress
) {
// keep original image
return {
...data,
userProfilePic: res.data.data.userProfilePic,
bio: bio,
};
}
}
return { ...data, bio: bio };
})
.then((data) => {
axios
.post("/addUser", {
userId: data.userId,
user: data.user,
userProfilePic: data.userProfilePic,
bio: data.bio,
})
.then((data) => {
// console.log(data);
})
.catch((err) => console.log(err));
})
.catch((err) => console.log(err));
})
.catch((err) => console.log(err));
}, [accessToken]);
return (
<Provider template={AlertMUITemplate} {...options}>
<Router />
{playing.length > 0 && <Player />}
</Provider>
);
}
export default App;
/Frontend/src/frontend/Router.js
import React from "react";
import Homepage from "./components/Homepage/Homepage";
import CreatePost from "./components/CreatePost/CreatePost";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Login from "./components/Login/Login";
import ProfilePage from "./components/Profile/ProfilePage";
import TopPosts from "./components/TopPosts/TopPosts";
import Cookies from "js-cookie";
function Router() {
const accessToken = Cookies.get("accessToken");
if (accessToken) {
return (
<BrowserRouter>
<Switch>
<Route exact path="/">
<Homepage />
</Route>
<Route path="/create">
<CreatePost />
</Route>
<Route path="/profile/:userId" children={<ProfilePage />}></Route>
<Route path="/top">
<TopPosts />
</Route>
</Switch>
</BrowserRouter>
);
} else {
return <Login />;
}
}
export default Router;
/Frontend/src/frontend/components/useAuth.js
import { useState, useEffect } from "react";
import axios from "axios";
import Cookies from "js-cookie";
export default function UseAuth(code) {
const [accessToken, setAccessToken] = useState();
const [refreshToken, setRefreshToken] = useState();
const [expiresIn, setExpiresIn] = useState();
useEffect(() => {
// gets the token
if (!code) return null;
if (code === "guest") {
Cookies.set("accessToken", "guest", { expires: 1 / 24 });
setAccessToken("guest");
setRefreshToken("guest");
setExpiresIn(3600);
return;
}
axios
.post("/login", { code })
.then((res) => {
Cookies.set("accessToken", res.data.accessToken, { expires: 1 / 24 });
setAccessToken(res.data.accessToken);
setRefreshToken(res.data.refreshToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, [code]);
useEffect(() => {
// refreshes token
if (!refreshToken || !expiresIn || accessToken === "guest") return;
const timeout = setInterval(() => {
axios
.post("/refresh", {
refreshToken,
})
.then((res) => {
Cookies.set("accessToken", res.data.accessToken, { expires: 1 / 24 });
setAccessToken(res.data.accessToken);
setExpiresIn(res.data.expiresIn);
});
}, (expiresIn - 60) * 1000);
return () => clearInterval(timeout);
}, [refreshToken, expiresIn]);
return accessToken;
}
/Frontend/src/frontend/components/Login/Login.js
import React from "react";
const AUTH_URL =
"https://accounts.spotify.com/authorize?client_id=dfe1eb532747437b9b7d84a113a3933f&response_type=code&redirect_uri=https://sharejams.herokuapp.com&scope=streaming%20user-read-email%20user-read-private%20user-library-read%20user-library-modify%20user-read-playback-state%20user-modify-playback-state";
function Login() {
return (
<div className="login">
<h1>Jam Share</h1>
<div className="loginButtonContainer">
<button onClick={() => (window.location.href = AUTH_URL)}>
Login with Spotify
</button>
<a href="/?code=guest">
Continue without logging in
<br />
(Limited experience)
</a>
</div>
</div>
);
}
export default Login;
Пожалуйста, разместите свой код прямо в вопросе, не нужно добавлять дополнительные URL-адреса, которые могут стать недействительными в будущем.