mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-24 17:35:16 +00:00
Update readme, add stack+support. Add redis env vars. Add disable registration flag
This commit is contained in:
parent
dc1a2df968
commit
feb2ac37da
@ -3,6 +3,11 @@ MYSQL_USER=
|
|||||||
MYSQL_PASS=
|
MYSQL_PASS=
|
||||||
MYSQL_DB=
|
MYSQL_DB=
|
||||||
|
|
||||||
|
REDIS_URL=
|
||||||
|
REDIS_DB=
|
||||||
|
REDIS_PREFIX="gs:"
|
||||||
|
REDIS_AUTH=""
|
||||||
|
|
||||||
JWT_SECRET=
|
JWT_SECRET=
|
||||||
JWT_EXPIRY=86400
|
JWT_EXPIRY=86400
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ build-react:
|
|||||||
expire_in: 1 day
|
expire_in: 1 day
|
||||||
paths:
|
paths:
|
||||||
- web/build
|
- web/build
|
||||||
- web/public
|
|
||||||
- .env.example
|
- .env.example
|
||||||
|
|
||||||
bundle:
|
bundle:
|
||||||
@ -41,8 +40,8 @@ bundle:
|
|||||||
before_script:
|
before_script:
|
||||||
- apk add --no-cache zip tar
|
- apk add --no-cache zip tar
|
||||||
script:
|
script:
|
||||||
- zip -r goscrobble.zip web goscrobble migrations init .env.example
|
- zip -r goscrobble.zip web/build goscrobble migrations init .env.example
|
||||||
- tar -czf goscrobble.tar.gz web goscrobble migrations init .env.example
|
- tar -czf goscrobble.tar.gz web/build goscrobble migrations init .env.example
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 6 months
|
expire_in: 6 months
|
||||||
paths:
|
paths:
|
||||||
|
12
README.md
12
README.md
@ -1,14 +1,14 @@
|
|||||||
# go-scrobble
|
# go-scrobble
|
||||||
|
|
||||||
Golang based music scrobbler. MySQL 8.0+
|
Golang based music scrobbler.
|
||||||
|
|
||||||
Currently building on Node V15.X & Go V1.16.X
|
Stack: Go 1.16+, Node 15+, React 17+, MySQL 8.0+, Redis
|
||||||
|
|
||||||
|
There are prebuilt binaries/packages available.
|
||||||
|
|
||||||
With a prebuilt binary - you will still need the migrations folder + web/build folder on prod.
|
|
||||||
|
|
||||||
Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET.
|
Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET.
|
||||||
|
|
||||||
|
|
||||||
## Setup MySQL
|
## Setup MySQL
|
||||||
create user 'goscrobble'@'%' identified by 'supersecurepass';
|
create user 'goscrobble'@'%' identified by 'supersecurepass';
|
||||||
create database goscrobble;
|
create database goscrobble;
|
||||||
@ -32,3 +32,7 @@ We need to build NPM package, and then ship web/build with the binary.
|
|||||||
cd web npm install --production && REACT_APP_API_URL=https://goscrobble.com npm run build
|
cd web npm install --production && REACT_APP_API_URL=https://goscrobble.com npm run build
|
||||||
go build -o goscrobble cmd/go-scrobble/*.go
|
go build -o goscrobble cmd/go-scrobble/*.go
|
||||||
./goscrobble
|
./goscrobble
|
||||||
|
|
||||||
|
|
||||||
|
## Support Development!
|
||||||
|
Feel free to support hosting and my coffee addiction https://liberapay.com/idanoo
|
21
docs/config.md
Normal file
21
docs/config.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
## FRONTEND VARS
|
||||||
|
REACT_APP_REGISTRATION_DISABLED=true // Disables registration
|
||||||
|
REACT_APP_API_URL=https://goscrobble.com // Sets API URL
|
||||||
|
|
||||||
|
|
||||||
|
## BACKEND VARS
|
||||||
|
MYSQL_HOST= // MySQL Server
|
||||||
|
MYSQL_USER= // MySQL User
|
||||||
|
MYSQL_PASS= // MySQL Password
|
||||||
|
MYSQL_DB= // MySQL Database
|
||||||
|
|
||||||
|
REDIS_URL= // Redis host
|
||||||
|
REDIS_DB=4 // Redis DB
|
||||||
|
REDIS_PREFIX="gs:" // Redis key prefix
|
||||||
|
REDIS_AUTH="" // Redis password
|
||||||
|
|
||||||
|
JWT_SECRET= // 32+ Char JWT secret
|
||||||
|
JWT_EXPIRY=86400 // JWT expiry
|
||||||
|
|
||||||
|
REVERSE_PROXIES=127.0.0.1 // Comma separated list of servers to ignore for IP logs
|
||||||
|
PORT=42069 // Server port
|
140
web/package-lock.json
generated
140
web/package-lock.json
generated
@ -13,8 +13,10 @@
|
|||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
"@testing-library/user-event": "^12.8.3",
|
"@testing-library/user-event": "^12.8.3",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
|
"formik": "^2.2.6",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^1.5.2",
|
"react-bootstrap": "^1.5.2",
|
||||||
|
"react-cookie": "^4.0.3",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-redux": "^7.2.3",
|
"react-redux": "^7.2.3",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
@ -22,6 +24,7 @@
|
|||||||
"react-toast": "^1.0.1",
|
"react-toast": "^1.0.1",
|
||||||
"react-toast-notifications": "^2.4.3",
|
"react-toast-notifications": "^2.4.3",
|
||||||
"reactstrap": "^8.9.0",
|
"reactstrap": "^8.9.0",
|
||||||
|
"redux-persist": "^6.0.0",
|
||||||
"web-vitals": "^1.1.1"
|
"web-vitals": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -3095,6 +3098,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
||||||
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
|
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cookie": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "7.2.7",
|
"version": "7.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
|
||||||
@ -8961,6 +8969,42 @@
|
|||||||
"node": ">= 0.12"
|
"node": ">= 0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/formik": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://opencollective.com/formik"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": "^2.1.1",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"lodash": "^4.17.14",
|
||||||
|
"lodash-es": "^4.17.14",
|
||||||
|
"react-fast-compare": "^2.0.1",
|
||||||
|
"tiny-warning": "^1.0.2",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/formik/node_modules/deepmerge": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/formik/node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
@ -16241,6 +16285,19 @@
|
|||||||
"regenerator-runtime": "^0.13.4"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-cookie": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-cmi6IpdVgTSvjqssqIEvo779Gfqc4uPGHRrKMEdHcqkmGtPmxolGfsyKj95bhdLEKqMdbX8MLBCwezlnhkHK0g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hoist-non-react-statics": "^3.0.1",
|
||||||
|
"hoist-non-react-statics": "^3.0.0",
|
||||||
|
"universal-cookie": "^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">= 16.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dev-utils": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||||
@ -16357,6 +16414,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-fast-compare": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
@ -16862,6 +16924,14 @@
|
|||||||
"symbol-observable": "^1.2.0"
|
"symbol-observable": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux-persist": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"redux": ">4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
@ -19892,6 +19962,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/universal-cookie": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cookie": "^0.3.3",
|
||||||
|
"cookie": "^0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||||
@ -24453,6 +24532,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
|
||||||
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
|
"integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
|
||||||
},
|
},
|
||||||
|
"@types/cookie": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
|
||||||
|
},
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "7.2.7",
|
"version": "7.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.7.tgz",
|
||||||
@ -29137,6 +29221,32 @@
|
|||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"formik": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==",
|
||||||
|
"requires": {
|
||||||
|
"deepmerge": "^2.1.1",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"lodash": "^4.17.14",
|
||||||
|
"lodash-es": "^4.17.14",
|
||||||
|
"react-fast-compare": "^2.0.1",
|
||||||
|
"tiny-warning": "^1.0.2",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
@ -34782,6 +34892,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-cookie": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-cmi6IpdVgTSvjqssqIEvo779Gfqc4uPGHRrKMEdHcqkmGtPmxolGfsyKj95bhdLEKqMdbX8MLBCwezlnhkHK0g==",
|
||||||
|
"requires": {
|
||||||
|
"@types/hoist-non-react-statics": "^3.0.1",
|
||||||
|
"hoist-non-react-statics": "^3.0.0",
|
||||||
|
"universal-cookie": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||||
@ -34872,6 +34992,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
||||||
},
|
},
|
||||||
|
"react-fast-compare": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
@ -35276,6 +35401,12 @@
|
|||||||
"symbol-observable": "^1.2.0"
|
"symbol-observable": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux-persist": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"redux-thunk": {
|
"redux-thunk": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
@ -37681,6 +37812,15 @@
|
|||||||
"crypto-random-string": "^1.0.0"
|
"crypto-random-string": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"universal-cookie": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/cookie": "^0.3.3",
|
||||||
|
"cookie": "^0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"universalify": {
|
"universalify": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
"@testing-library/react": "^11.2.5",
|
"@testing-library/react": "^11.2.5",
|
||||||
"@testing-library/user-event": "^12.8.3",
|
"@testing-library/user-event": "^12.8.3",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
|
"formik": "^2.2.6",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^1.5.2",
|
"react-bootstrap": "^1.5.2",
|
||||||
|
"react-cookie": "^4.0.3",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-redux": "^7.2.3",
|
"react-redux": "^7.2.3",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
@ -17,6 +19,7 @@
|
|||||||
"react-toast": "^1.0.1",
|
"react-toast": "^1.0.1",
|
||||||
"react-toast-notifications": "^2.4.3",
|
"react-toast-notifications": "^2.4.3",
|
||||||
"reactstrap": "^8.9.0",
|
"reactstrap": "^8.9.0",
|
||||||
|
"redux-persist": "^6.0.0",
|
||||||
"web-vitals": "^1.1.1"
|
"web-vitals": "^1.1.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -2,14 +2,12 @@ import './App.css';
|
|||||||
import Home from './Components/Pages/Home';
|
import Home from './Components/Pages/Home';
|
||||||
import About from './Components/Pages/About';
|
import About from './Components/Pages/About';
|
||||||
import Login from './Components/Pages/Login';
|
import Login from './Components/Pages/Login';
|
||||||
|
import Settings from './Components/Pages/Settings';
|
||||||
import Register from './Components/Pages/Register';
|
import Register from './Components/Pages/Register';
|
||||||
import Navigation from './Components/Navigation';
|
import Navigation from './Components/Navigation';
|
||||||
|
|
||||||
import { Route, Switch, HashRouter } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
import { ToastProvider } from 'react-toast-notifications';
|
|
||||||
|
|
||||||
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
@ -28,17 +26,16 @@ function mapDispatchToProps(dispatch) {
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
let exact = true
|
let exact = true
|
||||||
return (
|
return (
|
||||||
<HashRouter>
|
<div>
|
||||||
<ToastProvider autoDismiss="true" autoDismissTimeout="5000" placement="bottom-right">
|
<Navigation />
|
||||||
<Navigation />
|
<Switch>
|
||||||
<Switch>
|
<Route exact={exact} path="/" component={Home} />
|
||||||
<Route exact={exact} path="/" component={Home} />
|
<Route path="/about" component={About} />
|
||||||
<Route path="/about" component={About} />
|
<Route path="/settings" component={Settings} />
|
||||||
<Route path="/login" component={Login} />
|
<Route path="/login" component={Login} />
|
||||||
<Route path="/register" component={Register} />
|
<Route path="/register" component={Register} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</ToastProvider>
|
</div>
|
||||||
</HashRouter>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
web/src/Components/Api.js
Normal file
1
web/src/Components/Api.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// https://stackoverflow.com/questions/38397653/redux-what-is-the-correct-place-to-save-cookie-after-login-request
|
45
web/src/Components/AppProvider.js
Normal file
45
web/src/Components/AppProvider.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { persistStore } from 'redux-persist';
|
||||||
|
|
||||||
|
class AppProvider extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
store: PropTypes.object.isRequired,
|
||||||
|
children: PropTypes.node
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { rehydrated: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
const opts = {
|
||||||
|
whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
|
||||||
|
};
|
||||||
|
|
||||||
|
persistStore(this.props.store, opts, () => {
|
||||||
|
this.setState({ rehydrated: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (!this.state.rehydrated) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider store={this.props.store}>
|
||||||
|
{this.props.children}
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppProvider.propTypes = {
|
||||||
|
store: PropTypes.object.isRequired,
|
||||||
|
children: PropTypes.node
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppProvider;
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import '../../App.css';
|
import '../../App.css';
|
||||||
import './Login.css';
|
import './Login.css';
|
||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
|
import { Formik, Form, Field } from 'formik';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
|
|
||||||
function withToast(Component) {
|
function withToast(Component) {
|
||||||
@ -29,16 +29,15 @@ class Login extends React.Component {
|
|||||||
this.setState({password: event.target.value});
|
this.setState({password: event.target.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit(values) {
|
||||||
event.preventDefault();
|
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: this.state.username,
|
username: values.username,
|
||||||
password: this.state.password,
|
password: values.password,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
const apiUrl = process.env.REACT_APP_API_URL + '/api/v1/login';
|
const apiUrl = process.env.REACT_APP_API_URL + '/api/v1/login';
|
||||||
@ -47,7 +46,7 @@ class Login extends React.Component {
|
|||||||
.then((function(data) {
|
.then((function(data) {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
this.props.addToast(data.error, { appearance: 'error' });
|
this.props.addToast(data.error, { appearance: 'error' });
|
||||||
} else {
|
} else if (data.token) {
|
||||||
this.props.addToast(data.token, { appearance: 'success' });
|
this.props.addToast(data.token, { appearance: 'success' });
|
||||||
}
|
}
|
||||||
this.setState({loading: false});
|
this.setState({loading: false});
|
||||||
@ -59,30 +58,35 @@ class Login extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let trueBool = true;
|
||||||
return (
|
return (
|
||||||
<div className="pageWrapper">
|
<div className="pageWrapper">
|
||||||
<h1>
|
<h1>
|
||||||
Login
|
Login
|
||||||
</h1>
|
</h1>
|
||||||
<div className="loginBody">
|
<div className="loginBody">
|
||||||
<form onSubmit={this.handleSubmit}>
|
<Formik
|
||||||
|
initialValues={{ username: '', password: '' }}
|
||||||
|
onSubmit={async values => this.handleSubmit(values)}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
<label>
|
<label>
|
||||||
Email / Username<br/>
|
Email / Username<br/>
|
||||||
<input
|
<Field
|
||||||
|
name="username"
|
||||||
type="text"
|
type="text"
|
||||||
|
required={trueBool}
|
||||||
className="loginFields"
|
className="loginFields"
|
||||||
value={this.state.username}
|
|
||||||
onChange={this.handleUsernameChange}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<br/>
|
<br/>
|
||||||
<label>
|
<label>
|
||||||
Password<br/>
|
Password<br/>
|
||||||
<input
|
<Field
|
||||||
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
|
required={trueBool}
|
||||||
className="loginFields"
|
className="loginFields"
|
||||||
value={this.state.password}
|
|
||||||
onChange={this.handlePasswordChange}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
@ -92,7 +96,8 @@ class Login extends React.Component {
|
|||||||
className="loginButton"
|
className="loginButton"
|
||||||
disabled={this.state.loading}
|
disabled={this.state.loading}
|
||||||
>Login</Button>
|
>Login</Button>
|
||||||
</form>
|
</Form>
|
||||||
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import '../../App.css';
|
import '../../App.css';
|
||||||
import './Login.css';
|
import './Login.css';
|
||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
|
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
|
|
||||||
function withToast(Component) {
|
function withToast(Component) {
|
||||||
@ -84,64 +83,73 @@ class Register extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let trueBool = true;
|
||||||
return (
|
return (
|
||||||
<div className="pageWrapper">
|
<div className="pageWrapper">
|
||||||
<h1>
|
{
|
||||||
Register
|
// TODO: Move to DB:config REGISTRATION_DISABLED=1|0
|
||||||
</h1>
|
process.env.REACT_APP_REGISTRATION_DISABLED === "true" ?
|
||||||
<div className="loginBody">
|
<p>Registration is temporarily disabled. Please try again soon!</p>
|
||||||
<form onSubmit={this.handleSubmit}>
|
:
|
||||||
<label>
|
<div>
|
||||||
Username*<br/>
|
<h1>
|
||||||
<input
|
Register
|
||||||
type="text"
|
</h1>
|
||||||
required="true"
|
<div className="loginBody">
|
||||||
className="loginFields"
|
<form onSubmit={this.handleSubmit}>
|
||||||
value={this.state.username}
|
<label>
|
||||||
onChange={this.handleUsernameChange}
|
Username*<br/>
|
||||||
/>
|
<input
|
||||||
</label>
|
type="text"
|
||||||
<br/>
|
required={trueBool}
|
||||||
<label>
|
className="loginFields"
|
||||||
Email<br/>
|
value={this.state.username}
|
||||||
<input
|
onChange={this.handleUsernameChange}
|
||||||
type="email"
|
/>
|
||||||
className="loginFields"
|
</label>
|
||||||
value={this.state.email}
|
<br/>
|
||||||
onChange={this.handleEmailChange}
|
<label>
|
||||||
/>
|
Email<br/>
|
||||||
</label>
|
<input
|
||||||
<br/>
|
type="email"
|
||||||
<label>
|
className="loginFields"
|
||||||
Password<br/>
|
value={this.state.email}
|
||||||
<input
|
onChange={this.handleEmailChange}
|
||||||
type="password"
|
/>
|
||||||
required="true"
|
</label>
|
||||||
className="loginFields"
|
<br/>
|
||||||
value={this.state.password}
|
<label>
|
||||||
onChange={this.handlePasswordChange}
|
Password<br/>
|
||||||
/>
|
<input
|
||||||
</label>
|
type="password"
|
||||||
<br/>
|
required={trueBool}
|
||||||
<label>
|
className="loginFields"
|
||||||
Password<br/>
|
value={this.state.password}
|
||||||
<input
|
onChange={this.handlePasswordChange}
|
||||||
type="password"
|
/>
|
||||||
required="true"
|
</label>
|
||||||
className="loginFields"
|
<br/>
|
||||||
value={this.state.passwordconfirm}
|
<label>
|
||||||
onChange={this.handlePasswordConfirmChange}
|
Password<br/>
|
||||||
/>
|
<input
|
||||||
</label>
|
type="password"
|
||||||
<br/><br/>
|
required={trueBool}
|
||||||
<Button
|
className="loginFields"
|
||||||
color="primary"
|
value={this.state.passwordconfirm}
|
||||||
type="submit"
|
onChange={this.handlePasswordConfirmChange}
|
||||||
className="loginButton"
|
/>
|
||||||
disabled={this.state.loading}
|
</label>
|
||||||
>Login</Button>
|
<br/><br/>
|
||||||
</form>
|
<Button
|
||||||
</div>
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
className="loginButton"
|
||||||
|
disabled={this.state.loading}
|
||||||
|
>Login</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
0
web/src/Components/Pages/Settings.css
Normal file
0
web/src/Components/Pages/Settings.css
Normal file
36
web/src/Components/Pages/Settings.js
Normal file
36
web/src/Components/Pages/Settings.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import '../../App.css';
|
||||||
|
import './Settings.css';
|
||||||
|
|
||||||
|
import { useToasts } from 'react-toast-notifications';
|
||||||
|
|
||||||
|
function withToast(Component) {
|
||||||
|
return function WrappedComponent(props) {
|
||||||
|
const toastFuncs = useToasts()
|
||||||
|
return <Component {...props} {...toastFuncs} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Settings extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {username: '', password: '', loading: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="pageWrapper">
|
||||||
|
<h1>
|
||||||
|
Settings
|
||||||
|
</h1>
|
||||||
|
<div className="loginBody">
|
||||||
|
<p>
|
||||||
|
All the settings
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withToast(Settings);
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { HashRouter } from 'react-router-dom'
|
||||||
|
import { ToastProvider } from 'react-toast-notifications';
|
||||||
|
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { createStore } from 'redux'
|
import { createStore } from 'redux'
|
||||||
@ -14,10 +15,12 @@ const goScorbbleStore = (state = false, logIn) => {
|
|||||||
const store = createStore(goScorbbleStore);
|
const store = createStore(goScorbbleStore);
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<HashRouter>
|
||||||
<BrowserRouter>
|
<ToastProvider autoDismiss="true" autoDismissTimeout="5000" placement="bottom-right">
|
||||||
<App />
|
<Provider store={store}>
|
||||||
</BrowserRouter>
|
<App />
|
||||||
</Provider>,
|
</Provider>
|
||||||
|
</ToastProvider>
|
||||||
|
</HashRouter>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user