added the database

This commit is contained in:
notnull 2019-04-08 01:39:22 -04:00
parent 9b77e8c168
commit cab4dbfda3
20 changed files with 5128 additions and 105 deletions

20
server/.eslintrc Normal file
View File

@ -0,0 +1,20 @@
{
"extends": ["eslint:recommended"],
"parserOptions": {
"ecmaVersion": 8
},
"env": {
"es6": true,
"node": true
},
"rules": {
"quotes": ["warn", "single"],
"semi": ["warn", "never"],
"indent": ["warn", 2],
"no-unused-vars": ["warn"],
"no-console": 0
}
}

7
server/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# dependencies
/node_modules
# production
/build
npm-debug.log*

6
server/README.md Normal file
View File

@ -0,0 +1,6 @@
# Server
a minimal working example for the backend of a crud app built with node, express, postgres, sequelize
# TODO
- [ ] get rid of proxy

30
server/api/comments.js Executable file
View File

@ -0,0 +1,30 @@
const router = require('express').Router()
const { Comment } = require('../db/models')
module.exports = router
router.get('/', async (req, res, next) => {
try {
const comments = await Comment.findAll()
res.status(201).send(comments)
} catch (err) {
next(err)
}
})
router.get('/:id', async (req, res, next) => {
try {
const comment = await Comment.findByPk(req.params.id)
res.json(comment)
} catch (err) {
next(err)
}
})
router.post('/', async (req, res, next) => {
try {
const comment = await Comment.create(req.body)
res.status(201).json(comment)
} catch (err) {
next(err)
}
})

20
server/api/index.js Executable file
View File

@ -0,0 +1,20 @@
const router = require('express').Router()
module.exports = router
const ascii = require('../ascii')
router.use('/comments', require('./comments'))
router.get('/', async (req, res, next) => {
try {
res.json({ ascii })
} catch (err) {
console.log(err)
next()
}
})
router.use((req, res, next) => {
const error = new Error('Not Found!!!!!!!')
error.status = 404
next(error)
})

26
server/ascii.js Normal file
View File

@ -0,0 +1,26 @@
const ascii = String.raw`
. .
* . . . . *
. . . . . .
o . .
. . . .
0 . anarchy
. . , planet , ,
. \ . .
. . \ ,
. o . . .
. . . \ , .
#\##\# . .
# #O##\### .
. . #*# #\##\### .
. ##*# #\##\## .
. . ##*# #o##\# .
. *# #\# . .
\ . .
____^/\___^--____/\____O______________/\/\---/\___________--
/\^ ^ ^ ^ ^^ ^ '\ ^ ^
-- - -- - - --- __ ^
-- __ ___-- ^ ^`
module.exports = ascii

25
server/db/db.js Executable file
View File

@ -0,0 +1,25 @@
const Sequelize = require('sequelize')
const pkg = require('../package.json')
const databaseName = pkg.name + (process.env.NODE_ENV === 'test' ? '-test' : '')
const createDB = () => {
const db = new Sequelize(
process.env.DATABASE_URL || `postgres://localhost:5432/${databaseName}`,
{
logging: false,
operatorsAliases: false
}
)
return db
}
const db = createDB()
module.exports = db
// This is a global Mocha hook used for resource cleanup.
// Otherwise, Mocha v4+ does not exit after tests.
if (process.env.NODE_ENV === 'test') {
after('close database connection', () => db.close())
}

1248
server/db/desert.txt Normal file

File diff suppressed because it is too large Load Diff

6
server/db/index.js Executable file
View File

@ -0,0 +1,6 @@
const db = require('./db')
// register models
require('./models')
module.exports = db

15
server/db/models/comment.js Executable file
View File

@ -0,0 +1,15 @@
const Sequelize = require('sequelize')
const db = require('../db')
const Comment = db.define('comments', {
secs: {
type: Sequelize.FLOAT,
allowNull: false,
},
text: {
type: Sequelize.TEXT,
allowNull: false,
},
})
module.exports = Comment

3
server/db/models/index.js Executable file
View File

@ -0,0 +1,3 @@
const Comment = require('./comment')
module.exports = { Comment }

26
server/db/seed.js Executable file
View File

@ -0,0 +1,26 @@
const db = require('../db')
const { Comment } = require('./models')
const comment = {
secs: 82,
text: 'this is a great song!',
}
async function runSeed() {
await db.sync({ force: true })
console.log('db synced!')
console.log('seeding...')
try {
await Comment.create(comment)
console.log('seeded successfully')
} catch (err) {
console.error(err)
process.exitCode = 1
} finally {
console.log('closing db connection')
await db.close()
console.log('db connection closed')
}
}
runSeed()

5
server/db/setupProxy.js Normal file
View File

@ -0,0 +1,5 @@
const proxy = require('http-proxy-middleware')
module.exports = app => {
app.use(proxy('/api/*', { target: 'http://localhost:1337/' }))
}

36
server/index.js Executable file
View File

@ -0,0 +1,36 @@
const express = require('express')
const path = require('path')
const app = express()
const morgan = require('morgan')
const ascii = require('./ascii')
//const proxy = require('http-proxy-middleware')
const port = process.env.PORT || 1337
app.use(morgan('tiny'))
// body parsing middleware
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(require('body-parser').text())
app.use('/api', require('./api'))
if (process.env.NODE_ENV === 'production') {
// Express will serve up production assets
app.use(express.static(path.join(__dirname, 'dist')))
}
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '..', 'public', 'index.html'))
})
// error handling endware
app.use((err, req, res, next) => {
console.error(err)
console.error(err.stack)
res.status(err.status || 500).send(err.message || 'Internal server error.')
next()
})
app.listen(port, () => {
console.log('\n' + ascii + '\n')
console.log(`Doin' haxor stuff on port ${port}`)
})

3502
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
server/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "server",
"version": "1.0.0",
"description": "a minimalist serverside template",
"main": "index.js",
"dependencies": {
"axios": "^0.18.0",
"concurrently": "^4.0.1",
"express": "^4.16.4",
"http-proxy-middleware": "^0.19.0",
"morgan": "^1.9.1",
"nodemon": "^1.18.9",
"pg": "^7.5.0",
"sequelize": "^4.39.1"
},
"scripts": {
"seed": "node db/seed.js",
"start": "nodemon index.js"
},
"repository": {
"type": "git",
"url": "git+https://irc.anarchyplanet.org/git/notnull/server.git"
},
"author": "notnull",
"license": "ISC",
"bugs": {
"url": "https://irc.anarchyplanet.org/git/notnull/server/issues"
},
"homepage": "irc.anarchyplanet.org"
}

View File

@ -1,33 +1,33 @@
import React from 'react'
import {Tooltip, Overlay} from 'react-bootstrap'
import { Tooltip, Overlay } from 'react-bootstrap'
class Example extends React.Component {
class Comment extends React.Component {
constructor() {
super()
this.attachRef = target => this.setState({ target })
this.state = {
show: false,
}
this.toggleShow=this.toggleShow.bind(this)
this.toggleShow = this.toggleShow.bind(this)
}
toggleShow() {
this.setState({show: !this.state.show})
this.setState({ show: !this.state.show })
}
render() {
const { show, target } = this.state
return (
<div
className = "comment_icon"
className="comment_icon"
ref={this.attachRef}
style={{position: 'absolute', top: 180, left: this.props.commentLoc + '%'}}
onClick={this.toggleShow} >
<Overlay target={target} show={show} placement="bottom" >
<Tooltip>
{this.props.text}
</Tooltip>
style={{ position: 'absolute', top: 180, left: this.props.commentLoc - 1 + '%' }}
onMouseEnter={this.toggleShow}
onMouseLeave={this.toggleShow}
>
<Overlay target={target} show={show} placement="bottom">
<Tooltip>{this.props.text}</Tooltip>
</Overlay>
</div>
)
}
}
export default Example
export default Comment

View File

@ -1,22 +1,28 @@
import React, { Component } from 'react'
import Waveform from 'react-audio-waveform'
import {connect} from 'react-redux'
import { connect } from 'react-redux'
import ReactAudioPlayer from 'react-audio-player'
import CommentPopup from './CommentPopup'
import Comment from './Comment'
import {fetchAllComments, addComment} from '../../store'
import {Button, Container, Row, Col} from 'react-bootstrap'
import { fetchAllComments, addComment } from '../../store'
import { Media, Form, Button, Container, Row, Col } from 'react-bootstrap'
const config = require('./audio/loneDigger.json')
const parseTime = secs => {
var hours = Math.floor(secs / 3600)
var minutes = Math.floor((secs - (hours * 3600)) / 60)
var seconds = secs - (hours * 3600) - (minutes * 60)
var hours = Math.floor(secs / 3600)
var minutes = Math.floor((secs - hours * 3600) / 60)
var seconds = secs - hours * 3600 - minutes * 60
if (hours < 10) {hours = '0'+hours}
if (minutes < 10) {minutes = '0'+minutes}
if (seconds < 10) {seconds = '0'+seconds}
return minutes+':'+seconds
if (hours < 10) {
hours = '0' + hours
}
if (minutes < 10) {
minutes = '0' + minutes
}
if (seconds < 10) {
seconds = '0' + seconds
}
return minutes + ':' + seconds
}
class Player extends Component {
@ -25,26 +31,24 @@ class Player extends Component {
this.state = {
loading: true,
playerSize: {},
duration: 3*60+49,
songSecs: 0,
duration: 3 * 60 + 49,
songPos: 0,
commentText: '',
commentSecs: 0,
commentLoc: 0,
commentText: '',
showPopup: false,
showComments: true,
comments: [{id: 1, commentSecs: 132}]
comments: [],
}
this.getPlayerSize = this.getPlayerSize.bind(this)
this.submitComment = this.submitComment.bind(this)
this.openCommentPopup = this.openCommentPopup.bind(this)
this.updateCommentText = this.updateCommentText.bind(this)
this.handleChange = this.handleChange.bind(this)
this.updateSongPos = this.updateSongPos.bind(this)
this.commentID = 3
}
async fetchData() {
await this.props.fetchComments()
await this.setState({loading: false})
await this.setState({ loading: false })
}
componentDidMount() {
@ -52,90 +56,92 @@ class Player extends Component {
}
getPlayerSize(e) {
if(e){
this.setState({playerSize: e.getBoundingClientRect()})
if (e) {
this.setState({ playerSize: e.getBoundingClientRect() })
return e.getBoundingClientRect()
}
}
submitComment(e) {
e.preventDefault()
console.log(this.state.commentText)
this.props.postComment({id: this.commentID, commentSecs: this.state.commentSecs, text: this.state.commentText})
this.commentID ++
this.setState({showPopup: false, showComments: true})
}
openCommentPopup() {
this.props.postComment({
secs: this.state.songPos,
text: this.state.commentText,
})
this.setState({ commentText: '' })
console.log(this.state)
this.setState({ showPopup: true, showComments: false})
this.rap.audioEl.pause()
}
updateCommentText(e) {
handleChange(e) {
e.preventDefault()
this.setState({commentText : e.target.value})
this.setState({ [e.target.name]: e.target.value })
}
render() {
updateSongPos(secs) {
this.setState({ songPos: secs })
console.log((this.rap.audioEl.currentTime = secs))
}
render() {
return (
<Container className="App mt-5">
<header className="App-header">
<h1 className="App-title">Player</h1>
</header>
<div ref = {this.getPlayerSize} className = "mb-5">
<Row>
<Col>
<Waveform
ref = {el => {this.waveform = el}}
peaks={config.data}
height={200}
pos={this.state.songSecs}
duration={this.state.duration}
onClick={this.openCommentPopup}
color="#676767"
progressGradientColors={[[0, '#33cccc'], [1, '#aaa']]}
/>
<div ref={this.getPlayerSize} style={{ position: 'relative' }} className="mb-5">
<Waveform
ref={el => {
this.waveform = el
}}
barWidth={1}
peaks={config.data}
height={200}
pos={this.state.songPos}
duration={this.state.duration}
onClick={this.updateSongPos}
color="#676767"
progressGradientColors={[[0, '#33cccc'], [1, '#aaa']]}
/>
{this.state.showComments
?
this.props.comments.map(comment =>
<Comment
parseTime={parseTime}
key = {comment.id}
commentSecs = {comment.commentSecs}
commentLoc = {(comment.commentSecs / this.state.duration) * 100}
text={comment.text}
/>
)
: null}
</Col>
</Row>
{this.props.comments.map(comment => (
<Comment
parseTime={parseTime}
key={comment.id}
commentSecs={comment.secs}
commentLoc={(comment.secs / this.state.duration) * 100}
text={comment.text}
/>
))}
</div>
<Media>
<img width={36} height={36} className="mr-1" src="/img/anarchybang.jpg" />
<Media.Body>
<Form onSubmit={this.submitComment}>
<Form.Group controlId="comment">
<Form.Control
name="commentText"
type="text"
value={this.state.commentText}
onChange={this.handleChange}
placeholder="Leave a comment"
/>
</Form.Group>
</Form>
</Media.Body>
</Media>
<Row>
<Col>
<ReactAudioPlayer
src="/audio/01. Lone Digger.mp3"
//autoPlay
controls
listenInterval={100}
ref={(element) => { this.rap = element }}
onListen={()=>this.setState({songSecs:this.rap.audioEl.currentTime})}
ref={element => {
this.rap = element
}}
onListen={() => this.setState({ songSecs: this.rap.audioEl.currentTime })}
/>
</Col>
<Col>
<Button variant="dark" onClick={() => this.openCommentPopup(this.state.pos)}>Add Comment</Button>
</Col>
</Row>
{this.state.showPopup ?
<CommentPopup
commentSecs = {parseTime(this.state.songSecs)}
commentText={this.state.commentText}
submitComment={this.submitComment}
updateCommentText={this.updateCommentText}
togglePopup={this.togglePopup}
/>
: null
}
</Container>
)
}
@ -145,7 +151,10 @@ const mapState = state => state
const mapDispatch = dispatch => {
return {
fetchComments: () => dispatch(fetchAllComments()),
postComment: comment => dispatch(addComment(comment))
postComment: comment => dispatch(addComment(comment)),
}
}
export default connect(mapState, mapDispatch)(Player)
export default connect(
mapState,
mapDispatch,
)(Player)

View File

@ -1,4 +1,4 @@
//import axios from 'axios'
import axios from 'axios'
// ACTION TYPES
const GOT_ALL_COMMENTS = 'GOT_ALL_COMMENTS'
@ -8,37 +8,44 @@ const initialComments = []
// ACTION CREATORS
export const gotAllComments = comments => ({
type: GOT_ALL_COMMENTS,
comments
comments,
})
export const addComment = comment => ({
export const addedComment = comment => ({
type: ADD_COMMENT,
comment
comment,
})
// THUNK CREATORS
const comments = [{id: 1, commentSecs: 132, text: 'This is a comment.'}, {id: 2, commentSecs: 32, text: 'Yooo'}]
export const fetchAllComments = () => async dispatch => {
try {
//const res = await axios.get('https://irc.anarchyplanet.org/ircbang/api/v2/comments')
//const comments = res.data
const res = await axios.get('/api/comments')
const comments = res.data
dispatch(gotAllComments(comments))
} catch (err) {
console.error(err)
}
}
export const addComment = comment => async dispatch => {
try {
const res = await axios.post('/api/comments', comment)
dispatch(addedComment(res.data))
} catch (err) {
console.error(err)
}
}
// REDUCER
const commentReducer = (comments = initialComments, action) => {
switch (action.type) {
case GOT_ALL_COMMENTS:
return action.comments
case ADD_COMMENT:
return comments.concat(action.comment)
default:
return comments
case GOT_ALL_COMMENTS:
return action.comments
case ADD_COMMENT:
return comments.concat(action.comment)
default:
return comments
}
}

View File

@ -16,7 +16,7 @@ module.exports = {
extensions: ['*', '.js', '.jsx'],
},
output: {
path: __dirname + '/dist',
path: __dirname + '/dist',
filename: 'bundle.js',
},
plugins: [new webpack.HotModuleReplacementPlugin()],
@ -27,12 +27,14 @@ module.exports = {
historyApiFallback: true,
allowedHosts: [
'localhost',
'127.0.0.1',
'irc.anarchyplanet.org',
'services.anarchyplanet.org',
'gam4soeb3t5f56fs.onion',
'opsqtua3b5dpg6zy.onion',
],
proxy: {
'/api': 'http://localhost:31337',
'/api': 'http://localhost:1337',
},
},
}