add page, model, controller, and policy for publish keys
parent
7a18f7bed8
commit
b2166960dd
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
if (bookExists) {
|
||||
throw new HttpError(400, 'Version already exists')
|
||||
} else {
|
||||
result = await Book.create(body)
|
||||
result = await Book.create(body).fetch()
|
||||
}
|
||||
|
||||
req.file('opds').upload(sails.config.skipperConfig, async function (err, uploaded) {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
const HttpError = require('../errors/HttpError')
|
||||
|
||||
module.exports = {
|
||||
create: async function (req, res) {
|
||||
try {
|
||||
const url = req.param('url')
|
||||
if (!url.length) throw new Error('URL cannot be blank')
|
||||
|
||||
const created = await PublishKey.create({
|
||||
user: req.user.id,
|
||||
url
|
||||
}).fetch()
|
||||
return res.json(created)
|
||||
} catch (e) {
|
||||
return (new HttpError(500, e.message)).send(res)
|
||||
}
|
||||
},
|
||||
list: async function (req, res) {
|
||||
try {
|
||||
const keys = await PublishKey.find()
|
||||
return res.json(keys)
|
||||
} catch (e) {
|
||||
return (new HttpError(500, e.message)).send(res)
|
||||
}
|
||||
},
|
||||
refresh: async function (req, res) {
|
||||
try {
|
||||
const id = req.param('id')
|
||||
const key = await PublishKey.update({ id, user: req.user.id }, {}).fetch()
|
||||
return res.json(key)
|
||||
} catch (e) {
|
||||
return (new HttpError(500, e.message)).send(res)
|
||||
}
|
||||
},
|
||||
delete: async function (req, res) {
|
||||
try {
|
||||
const id = req.param('id')
|
||||
await PublishKey.destroyOne({ id })
|
||||
return res.status(204).send()
|
||||
} catch (e) {
|
||||
return (new HttpError(500, e.message)).send(res)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
const crypto = require('crypto')
|
||||
|
||||
function generateToken({ bytes, base }) {
|
||||
return new Promise((res, rej) => {
|
||||
crypto.randomBytes(bytes, (err, buf) => {
|
||||
if (err) rej(err)
|
||||
else res(buf.toString(base || 'base64'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
attributes: {
|
||||
id: {
|
||||
type: 'number',
|
||||
unique: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
user: {
|
||||
model: 'User',
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
secret: {
|
||||
type: 'string',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
beforeCreate: async function (key, next) {
|
||||
key.key = await generateToken({ bytes: 12 })
|
||||
key.secret = await generateToken({ bytes: 48 })
|
||||
next()
|
||||
},
|
||||
beforeUpdate: async function (key, next) {
|
||||
key.secret = await generateToken({ bytes: 48 })
|
||||
next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = async function (req, res, next) {
|
||||
const key = req.param('key') || req.header('x-roe-publish-key')
|
||||
const secret = req.param('secret') || req.header('x-roe-publish-secret')
|
||||
console.log(key)
|
||||
console.log(secret)
|
||||
|
||||
if (await PublishKey.findOne({ key, secret })) {
|
||||
return next()
|
||||
}
|
||||
|
||||
res.status(403).json({ error: 'Invalid publishing key.' })
|
||||
}
|
|
@ -9,6 +9,8 @@ const ACTIONS = {
|
|||
delete_url: 'delete_url',
|
||||
list_url: 'list_url',
|
||||
set_editing: 'set_editing',
|
||||
add_publisher: 'add_publisher',
|
||||
delete_publisher: 'delete_publisher',
|
||||
error: 'error'
|
||||
}
|
||||
|
||||
|
@ -29,6 +31,11 @@ export const addUrl = url => ({
|
|||
data: url
|
||||
})
|
||||
|
||||
export const addPublisher = url => ({
|
||||
type: ACTIONS.add_publisher,
|
||||
data: url
|
||||
})
|
||||
|
||||
export const setEditing = id => ({
|
||||
type: ACTIONS.set_editing,
|
||||
data: id
|
||||
|
@ -116,3 +123,43 @@ export const setUrl = (value) => async (dispatch, getState) => {
|
|||
dispatch(setWorking(false))
|
||||
}
|
||||
}
|
||||
|
||||
export const createNewPublisher = (url) => async (dispatch, getState) => {
|
||||
dispatch(setWorking(true))
|
||||
try {
|
||||
const { data } = await Ajax.post({
|
||||
url: '/api/keys',
|
||||
data: {
|
||||
url
|
||||
}
|
||||
})
|
||||
dispatch(addPublisher(data))
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.error,
|
||||
data: e
|
||||
})
|
||||
} finally {
|
||||
dispatch(setWorking(false))
|
||||
}
|
||||
}
|
||||
|
||||
export const removePublisher = id => async (dispatch, getState) => {
|
||||
dispatch(setWorking(true))
|
||||
try {
|
||||
await Ajax.delete({
|
||||
url: '/api/keys/' + id
|
||||
})
|
||||
dispatch({
|
||||
type: ACTIONS.delete_publisher,
|
||||
data: id
|
||||
})
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.error,
|
||||
data: e
|
||||
})
|
||||
} finally {
|
||||
dispatch(setWorking(false))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import IconButton from '../components/IconButton'
|
||||
import UnderlineInput from '../components/UnderlineInput'
|
||||
import { removePublisher } from '../actions'
|
||||
import '../../styles/shared/listitem.scss'
|
||||
|
||||
const uriRegex = /(.+:\/\/)?(.+\.)*(.+\.).{1,}(:\d+)?(.+)?/i
|
||||
|
||||
class PublisherListItem extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<li className='uri-list-item flex-container'>
|
||||
<div className='stack flex flex-container flex-vertical'>
|
||||
<span className='label'>Website URL</span>
|
||||
<span className='value'>{this.props.item.url}</span>
|
||||
</div>
|
||||
<div className='stack flex flex-container flex-vertical'>
|
||||
<span className='label'>Key</span>
|
||||
<input className='value' value={this.props.item.key} readOnly={true} />
|
||||
</div>
|
||||
<div className='stack flex flex-container flex-vertical'>
|
||||
<span className='label'>Secret</span>
|
||||
<input className='value' value={this.props.item.secret} readOnly={true} />
|
||||
</div>
|
||||
<IconButton icon='delete' onClick={() => this.props.dispatch(removePublisher(this.props.item.id))} />
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default PublisherListItem
|
|
@ -3,8 +3,8 @@
|
|||
import React from 'react'
|
||||
import IconButton from '../components/IconButton'
|
||||
import UnderlineInput from '../components/UnderlineInput'
|
||||
import '../../styles/shared/urilistitem.scss'
|
||||
import { changeUrlField, setUrl, removeUrl, setEditing } from '../actions/targets'
|
||||
import '../../styles/shared/listitem.scss'
|
||||
import { changeUrlField, setUrl, removeUrl, setEditing } from '../actions'
|
||||
|
||||
const uriRegex = /(.+:\/\/)?(.+\.)*(.+\.).{1,}(:\d+)?(.+)?/i
|
||||
const isbnRegex = /^(97(8|9))?\d{9}(\d|X)$/
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
'use strict'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { BrowserRouter as Router, Route, NavLink, Switch, Redirect } from 'react-router-dom'
|
||||
import Progress from './components/Progress'
|
||||
import UnderlineInput from './components/UnderlineInput'
|
||||
import UriListItem from './containers/UriListItem'
|
||||
import reducer from './reducers'
|
||||
import { fetchUrls, createNewUrl, setEditing, createNewPublisher } from './actions'
|
||||
import '../styles/index.scss'
|
||||
|
||||
class App extends React.Component {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
error: '',
|
||||
user: {
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
urls: [],
|
||||
publishers: [],
|
||||
newPublisherUrl: '',
|
||||
editingUrl: null,
|
||||
working: false
|
||||
}
|
||||
|
||||
this.dispatch = this.dispatch.bind(this)
|
||||
this.getRegisteredUris = this.getRegisteredUris.bind(this)
|
||||
this.getRegisteredPublishers = this.getRegisteredPublishers.bind(this)
|
||||
this.setPublisherUrl = this.setPublisherUrl.bind(this)
|
||||
}
|
||||
dispatch (action) {
|
||||
if (!action) throw new Error('dispatch: missing action')
|
||||
if (action instanceof Function) {
|
||||
action(this.dispatch, () => this.state)
|
||||
} else {
|
||||
const changes = reducer(this.state, action)
|
||||
if (!changes || !Object.keys(changes).length) return
|
||||
this.setState({
|
||||
...changes
|
||||
})
|
||||
}
|
||||
}
|
||||
componentDidMount () {
|
||||
// this.dispatch(fetchUrls())
|
||||
}
|
||||
setPublisherUrl (e) {
|
||||
this.setState({
|
||||
newPublisherUrl: e.target.value
|
||||
})
|
||||
}
|
||||
getRegisteredUris () {
|
||||
return this.state.urls.map((item, i) => {
|
||||
return (<UriListItem
|
||||
key={i}
|
||||
dispatch={this.dispatch}
|
||||
item={item}
|
||||
editing={this.state.editingUrl === item.id} />)
|
||||
})
|
||||
}
|
||||
getRegisteredPublishers () {
|
||||
return this.state.publishers.map((item, i) => {
|
||||
return (<PublisherListItem
|
||||
key={i}
|
||||
dispatch={this.dispatch}
|
||||
item={item} />)
|
||||
})
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<Router>
|
||||
<div className='root-container flex-container' onClick={() => this.dispatch(setEditing(null))}>
|
||||
<aside className='nav nav-left'>
|
||||
<header>
|
||||
<h1>River of Ebooks</h1>
|
||||
</header>
|
||||
<ul>
|
||||
<li><NavLink to='/keys'>Publishing keys</NavLink></li>
|
||||
<li><NavLink to='/targets'>Push URIs</NavLink></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<section className={'content flex' + (this.state.working ? ' working' : '')}>
|
||||
<Progress bound />
|
||||
{this.state.error && <div className='error-box'>{this.state.error}</div>}
|
||||
<Switch>
|
||||
<Route path='/keys' exact children={props => (
|
||||
<div>
|
||||
<header className='flex-container'>
|
||||
<div className='flex'>
|
||||
<h1>Publishing keys</h1>
|
||||
<h2>If you own a publishing site, generate a publishing key for it here.</h2>
|
||||
</div>
|
||||
</header>
|
||||
<div className='creator flex-container'>
|
||||
<UnderlineInput
|
||||
className='flex'
|
||||
placeholder='Site URL'
|
||||
value={this.state.newPublisherUrl}
|
||||
onChange={this.setPublisherUrl} />
|
||||
<button className='btn' onClick={() => this.dispatch(createNewPublisher(this.state.newPublisherUrl))}>Create keys</button>
|
||||
</div>
|
||||
<ul className='list'>
|
||||
{this.getRegisteredPublishers()}
|
||||
</ul>
|
||||
</div>
|
||||
)} />
|
||||
|
||||
<Route path='/targets' exact children={props => (
|
||||
<div>
|
||||
<header className='flex-container'>
|
||||
<div className='flex'>
|
||||
<h1>Push URIs</h1>
|
||||
<h2>Newly published books will be sent to these addresses.</h2>
|
||||
</div>
|
||||
<button className='btn' onClick={() => this.dispatch(createNewUrl())}>New address</button>
|
||||
</header>
|
||||
<ul className='list'>
|
||||
{this.getRegisteredUris()}
|
||||
</ul>
|
||||
</div>
|
||||
)} />
|
||||
|
||||
<Route path='/' render={() => <Redirect to='/keys' />} />
|
||||
</Switch>
|
||||
</section>
|
||||
</div>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
import Actions from '../actions/targets'
|
||||
import Actions from '../actions'
|
||||
|
||||
const reducer = (state = {}, action) => {
|
||||
const { type, data } = action
|
||||
|
@ -34,6 +34,16 @@ const reducer = (state = {}, action) => {
|
|||
return {
|
||||
editingUrl: data
|
||||
}
|
||||
case Actions.add_publisher:
|
||||
return {
|
||||
publishers: state.publishers.concat(data),
|
||||
error: ''
|
||||
}
|
||||
case Actions.delete_publisher:
|
||||
return {
|
||||
publishers: state.publishers.filter(x => x.id !== data),
|
||||
error: ''
|
||||
}
|
||||
case Actions.error:
|
||||
return {
|
||||
error: data.message
|
|
@ -1,79 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Progress from './components/Progress'
|
||||
import UriListItem from './containers/UriListItem'
|
||||
import reducer from './reducers/targets'
|
||||
import { fetchUrls, createNewUrl, setEditing } from './actions/targets'
|
||||
import '../styles/targets.scss'
|
||||
|
||||
class App extends React.Component {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
error: '',
|
||||
user: {
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
urls: [],
|
||||
editingUrl: null,
|
||||
working: false
|
||||
}
|
||||
|
||||
this.dispatch = this.dispatch.bind(this)
|
||||
this.getRegisteredUris = this.getRegisteredUris.bind(this)
|
||||
}
|
||||
dispatch (action) {
|
||||
if (!action) throw new Error('dispatch: missing action')
|
||||
if (action instanceof Function) {
|
||||
action(this.dispatch, () => this.state)
|
||||
} else {
|
||||
const changes = reducer(this.state, action)
|
||||
if (!changes || !Object.keys(changes).length) return
|
||||
this.setState({
|
||||
...changes
|
||||
})
|
||||
}
|
||||
}
|
||||
componentDidMount () {
|
||||
this.dispatch(fetchUrls())
|
||||
}
|
||||
getRegisteredUris () {
|
||||
return this.state.urls.map((item, i) => {
|
||||
return (<UriListItem
|
||||
key={i}
|
||||
dispatch={this.dispatch}
|
||||
item={item}
|
||||
editing={this.state.editingUrl === item.id} />)
|
||||
})
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<div className='root-container flex-container' onClick={() => this.dispatch(setEditing(null))}>
|
||||
<aside className='nav nav-left'>
|
||||
<header>
|
||||
<h1>RoE</h1>
|
||||
</header>
|
||||
</aside>
|
||||
<section className={'content flex' + (this.state.working ? ' working' : '')}>
|
||||
<Progress bound />
|
||||
{this.state.error && <div className='error-box'>{this.state.error}</div>}
|
||||
<header className='flex-container'>
|
||||
<div className='flex'>
|
||||
<h1>Push URIs</h1>
|
||||
<h2>Newly published books will be sent to these addresses.</h2>
|
||||
</div>
|
||||
<button className='btn' onClick={() => this.dispatch(createNewUrl())}>New address</button>
|
||||
</header>
|
||||
<ul className='list'>
|
||||
{this.getRegisteredUris()}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
|
@ -0,0 +1,56 @@
|
|||
@import 'lib/default';
|
||||
@import 'shared/twopanels';
|
||||
|
||||
.content {
|
||||
padding: 14px 0 42px 0;
|
||||
position: relative;
|
||||
|
||||
.error-box {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background: $red;
|
||||
color: white;
|
||||
padding: 0 14px;
|
||||
margin: -14px 0 8px 0;
|
||||
}
|
||||
& > div {
|
||||
|
||||
& > header {
|
||||
padding: 0 14px;
|
||||
|
||||
h1 {
|
||||
text-shadow: 1px 1px 2px $black-3;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
margin-top: 4px;
|
||||
color: $text-dark-2;
|
||||
text-shadow: 1px 1px 2px $black-4;
|
||||
}
|
||||
}
|
||||
.creator {
|
||||
padding: 0 14px;
|
||||
line-height: 60px;
|
||||
max-width: 500px;
|
||||
|
||||
.btn {
|
||||
margin: 12px 0 12px 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list {
|
||||
margin: 20px 14px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
// overflow: hidden;
|
||||
}
|
||||
&.working {
|
||||
& > .progress {
|
||||
top: 0;
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,12 @@ $black-3: rgba(0,0,0,.38);
|
|||
$black-4: rgba(0,0,0,.12);
|
||||
$black-5: rgba(0,0,0,.07);
|
||||
|
||||
$white-1: white;
|
||||
$white-2: rgba(255,255,255,.75);
|
||||
$white-3: rgba(255,255,255,.35);
|
||||
$white-4: rgba(255,255,255,.10);
|
||||
$white-5: rgba(255,255,255,.03);
|
||||
|
||||
$auth-width: 450px;
|
||||
|
||||
$background-1: #f2f2f2;
|
||||
|
@ -21,8 +27,8 @@ $background-2: white;
|
|||
|
||||
$text-dark-1: $black-1;
|
||||
$text-dark-2: $black-2;
|
||||
$text-light-1: white;
|
||||
$text-light-2: rgba(255,255,255,.75);
|
||||
$text-light-1: $white-1;
|
||||
$text-light-2: $white-2;
|
||||
|
||||
$accent-1: #731212;
|
||||
$accent-2: #9a834d;
|
||||
|
|
|
@ -4,4 +4,41 @@
|
|||
background: $accent-1;
|
||||
color: $text-light-1;
|
||||
box-shadow: $shadow-1;
|
||||
|
||||
header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
padding: 0 14px;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-bottom: 1px solid $white-4;
|
||||
|
||||
&:hover a {
|
||||
background: $white-5;
|
||||
}
|
||||
&:last-of-type {
|
||||
border-bottom: none
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
text-decoration: none !important;
|
||||
color: $white-2;
|
||||
|
||||
&.active {
|
||||
background: $white-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
@import 'lib/default';
|
||||
@import 'shared/twopanels';
|
||||
|
||||
.nav {
|
||||
header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
padding: 0 14px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding: 14px 0 42px 0;
|
||||
position: relative;
|
||||
|
||||
.error-box {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background: $red;
|
||||
color: white;
|
||||
padding: 0 14px;
|
||||
margin: -14px 0 8px 0;
|
||||
}
|
||||
& > header {
|
||||
padding: 0 14px;
|
||||
|
||||
h1 {
|
||||
text-shadow: 1px 1px 2px $black-3;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
margin-top: 4px;
|
||||
color: $text-dark-2;
|
||||
text-shadow: 1px 1px 2px $black-4;
|
||||
}
|
||||
}
|
||||
.list {
|
||||
margin: 20px 14px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
// overflow: hidden;
|
||||
}
|
||||
&.working {
|
||||
& > .progress {
|
||||
top: 0;
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>RoE - Push Targets</title>
|
||||
<title>River of Ebooks</title>
|
||||
<% for (item of htmlWebpackPlugin.options.links) {
|
||||
if (typeof item === 'string' || item instanceof String) { item = { href: item, rel: 'stylesheet' } } %>
|
||||
<link<% for (key in item) { %> <%= key %>="<%= item[key] %>"<% } %> /><%
|
|
@ -33,5 +33,14 @@ module.exports.policies = {
|
|||
|
||||
TargetController: {
|
||||
'*': [ 'sessionAuth' ]
|
||||
},
|
||||
|
||||
PublishKeyController: {
|
||||
'*': [ 'sessionAuth' ]
|
||||
},
|
||||
|
||||
BooksController: {
|
||||
'*': true,
|
||||
publish: [ 'keyAuth' ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,9 +69,14 @@ module.exports.routes = {
|
|||
'GET /api/me': 'UserController.me',
|
||||
|
||||
'POST /api/targets': 'TargetController.create',
|
||||
'GET /api/targets': 'TargetController.list',
|
||||
'PATCH /api/targets/:id': 'TargetController.edit',
|
||||
'DELETE /api/targets/:id': 'TargetController.delete',
|
||||
'GET /api/targets': 'TargetController.list'
|
||||
|
||||
'POST /api/keys': 'PublishKeyController.create',
|
||||
'GET /api/keys': 'PublishKeyController.list',
|
||||
'PATCH /api/keys/:id': 'PublishKeyController.refresh',
|
||||
'DELETE /api/keys/:id': 'PublishKeyController.delete'
|
||||
|
||||
// ╦ ╦╔═╗╔╗ ╦ ╦╔═╗╔═╗╦╔═╔═╗
|
||||
// ║║║║╣ ╠╩╗╠═╣║ ║║ ║╠╩╗╚═╗
|
||||
|
|
40
package.json
40
package.json
|
@ -4,6 +4,25 @@
|
|||
"version": "0.0.0",
|
||||
"description": "a Sails application",
|
||||
"keywords": [],
|
||||
"scripts": {
|
||||
"start": "npm-run-all --parallel open:client lift",
|
||||
"start:debug": "npm-run-all --parallel open:client debug",
|
||||
"start:prod": "npm-run-all --parallel build:prod lift",
|
||||
"open:client": "webpack-dev-server --mode development",
|
||||
"build": "npm run build:prod",
|
||||
"build:dev": "webpack --mode development",
|
||||
"build:prod": "webpack --mode production",
|
||||
"clean": "rimraf .tmp && mkdirp .tmp/public",
|
||||
"lift": "sails lift",
|
||||
"forever": "sudo ./node_modules/.bin/pm2 start ecosystem.config.js --env production",
|
||||
"stop": "sudo ./node_modules/.bin/pm2 delete roe-base",
|
||||
"test": "npm run lint && npm run custom-tests && echo 'Done.'",
|
||||
"lint": "standard && echo '✔ Your .js files look good.'",
|
||||
"debug": "node --inspect app.js",
|
||||
"custom-tests": "echo 'Nothing yet'",
|
||||
"db:migrate": "knex migrate:latest",
|
||||
"db:rollback": "knex migrate:rollback"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sailshq/connect-redis": "^3.2.1",
|
||||
"@sailshq/lodash": "^3.10.3",
|
||||
|
@ -42,6 +61,7 @@
|
|||
"mocha": "^5.2.0",
|
||||
"node-sass": "^4.9.4",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"standard": "^12.0.1",
|
||||
|
@ -50,25 +70,6 @@
|
|||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm-run-all --parallel open:client lift",
|
||||
"start:debug": "npm-run-all --parallel open:client debug",
|
||||
"start:prod": "npm-run-all --parallel build:prod lift",
|
||||
"open:client": "webpack-dev-server --mode development",
|
||||
"build": "npm run build:prod",
|
||||
"build:dev": "webpack --mode development",
|
||||
"build:prod": "webpack --mode production",
|
||||
"clean": "rimraf .tmp && mkdirp .tmp/public",
|
||||
"lift": "sails lift",
|
||||
"forever": "sudo ./node_modules/.bin/pm2 start ecosystem.config.js --env production",
|
||||
"stop": "sudo ./node_modules/.bin/pm2 delete roe-base",
|
||||
"test": "npm run lint && npm run custom-tests && echo 'Done.'",
|
||||
"lint": "standard && echo '✔ Your .js files look good.'",
|
||||
"debug": "node --inspect app.js",
|
||||
"custom-tests": "echo 'Nothing yet'",
|
||||
"db:migrate": "knex migrate:latest",
|
||||
"db:rollback": "knex migrate:rollback"
|
||||
},
|
||||
"main": "app.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -86,6 +87,7 @@
|
|||
"Book",
|
||||
"Passport",
|
||||
"TargetUrl",
|
||||
"PublishKey",
|
||||
"_"
|
||||
],
|
||||
"env": [
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,7 @@ module.exports = (env, argv) => {
|
|||
mode: mode || 'development',
|
||||
entry: {
|
||||
login: './assets/js/login.js',
|
||||
targets: './assets/js/targets.js'
|
||||
index: './assets/js/index.js'
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, '/.tmp/public'),
|
||||
|
@ -41,10 +41,10 @@ module.exports = (env, argv) => {
|
|||
chunks: ['login']
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'assets/templates/targets.html',
|
||||
links: mode === 'production' ? [{ rel: 'stylesheet', type: 'text/css', href: 'targets.css' }] : [],
|
||||
filename: path.join(__dirname, '/.tmp/public/targets.html'),
|
||||
chunks: ['targets']
|
||||
template: 'assets/templates/index.html',
|
||||
links: mode === 'production' ? [{ rel: 'stylesheet', type: 'text/css', href: 'index.css' }] : [],
|
||||
filename: path.join(__dirname, '/.tmp/public/index.html'),
|
||||
chunks: ['index']
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].css'
|
||||
|
@ -52,6 +52,11 @@ module.exports = (env, argv) => {
|
|||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(mode)
|
||||
})
|
||||
]
|
||||
],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
disableHostCheck: true,
|
||||
port: 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue