Compare commits
88 commits
Author | SHA1 | Date | |
---|---|---|---|
|
363c079433 | ||
|
8ca76a6de1 | ||
|
99c7b087d7 | ||
|
67e198db2f | ||
|
50805ad4ea | ||
|
b410eb8c94 | ||
|
efc38529c7 | ||
|
dfa6db6276 | ||
|
5215bfbc4e | ||
|
2b4a379638 | ||
|
9df4d29dda | ||
|
68b6f484e4 | ||
|
7584d0c7e3 | ||
|
7940c61b17 | ||
|
3d36f136f1 | ||
|
5ed72b0e14 | ||
|
922a53cc30 | ||
|
d11ba6225e | ||
|
a8678f5d1f | ||
|
423c0c9311 | ||
|
3d23635bf5 | ||
|
707e144eff | ||
|
ad87e2b8a7 | ||
|
01ae90fc15 | ||
|
1e6cb2a7c6 | ||
|
2e7d4c5112 | ||
|
15e4a97e6a | ||
|
5dc89f0a91 | ||
|
0974e6d6b0 | ||
|
64d7afd278 | ||
|
648639e6c6 | ||
|
8ded28beb3 | ||
|
b1a9d8ee74 | ||
|
9e76553f0a | ||
|
f4a50d0c85 | ||
|
23953a32e8 | ||
|
aa4a43a1a5 | ||
|
eba7fd596f | ||
|
9130b6f609 | ||
|
94f5f6dc69 | ||
|
269a2c57a7 | ||
|
1fd0ec1fc2 | ||
|
53e0d2a0db | ||
|
aebaa4f954 | ||
|
5b2108796c | ||
|
200903827a | ||
|
993b1d31e9 | ||
|
8891f298ae | ||
|
9bd779775a | ||
|
322b8a147d | ||
|
424827ea15 | ||
|
15e7456a88 | ||
|
d4e38db1ae | ||
|
d3f4c0aefb | ||
|
ee004405a6 | ||
|
6313c7eeff | ||
|
ee1b5c68bc | ||
|
962d07a6c9 | ||
|
a27f6e2b21 | ||
|
848077b057 | ||
|
b9b4333b55 | ||
|
ac3e9d1e67 | ||
|
bd0c3fd4c3 | ||
|
85cc743f90 | ||
|
0e357b7b56 | ||
|
2311e5685f | ||
|
a66511caaf | ||
|
39095c16a9 | ||
|
3d4ad3d827 | ||
|
6992b87033 | ||
|
75818e47fe | ||
|
4cfb85fed3 | ||
|
8a6cd2db13 | ||
|
dee61b5b6f | ||
|
82ab3b47b1 | ||
|
73667d4d9a | ||
|
0b4eab9bb6 | ||
|
68d2f303c1 | ||
|
b98fffc386 | ||
|
adbafa4194 | ||
|
1b069ba2a1 | ||
|
5f3f5c678a | ||
|
5ab338cfa5 | ||
|
ec9f78de16 | ||
|
b2624026bb | ||
|
b2e35c7e13 | ||
|
bf26e7d8a9 | ||
|
8279b92a46 |
46 changed files with 941 additions and 809 deletions
|
@ -1,4 +1,3 @@
|
|||
sudo: true
|
||||
dist: xenial
|
||||
#addons:
|
||||
# apt:
|
||||
|
@ -9,7 +8,7 @@ dist: xenial
|
|||
# - mysql-client
|
||||
language: node_js
|
||||
node_js:
|
||||
- "lts/*"
|
||||
- "lts/dubnium"
|
||||
cache:
|
||||
directories:
|
||||
- "node_modules"
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2019 LBRY Inc.
|
||||
Copyright (c) 2017-2020 LBRY Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
spee.ch provides a user-friendly, custom-designed, image and video hosting site backed by a decentralized network and
|
||||
blockchain ([LBRY](https://lbry.tech/)). Via just a small set of config files, you can spin your an entire spee.ch site back up including assets.
|
||||
|
||||
**Please note: the spee.ch code base and setup instructions are no longer actively maintained now that we have lbry.tv. Proceed at your own caution. Setup will require dev ops skills.**
|
||||
|
||||
![App GIF](https://spee.ch/e/speechgif.gif)
|
||||
|
||||
For a completely open, unrestricted example of a spee.ch site, check out https://www.spee.ch.
|
||||
|
@ -105,7 +107,7 @@ $ npm run start
|
|||
|
||||
#### Customize your app
|
||||
|
||||
Check out the [customization guide](https://github.com/lbryio/spee.ch/blob/readme-update/customize.md) to change your app's appearance and components
|
||||
Check out the [customization guide](https://github.com/lbryio/spee.ch/blob/master/customize.md) to change your app's appearance and components
|
||||
|
||||
#### (optional) add custom components and update the styles
|
||||
|
||||
|
|
|
@ -188,8 +188,8 @@ inquirer
|
|||
.post('http://localhost:5279', {
|
||||
method: 'channel_new',
|
||||
params: {
|
||||
channel_name: thumbnailChannelDefault,
|
||||
amount: channelBid,
|
||||
name: thumbnailChannelDefault,
|
||||
bid: channelBid,
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
|
@ -197,7 +197,6 @@ inquirer
|
|||
if (response.data.error) {
|
||||
throw new Error(response.data.error.message);
|
||||
}
|
||||
|
||||
thumbnailChannel = thumbnailChannelDefault;
|
||||
thumbnailChannelId = response.data.result.claim_id;
|
||||
siteConfig['publishing']['thumbnailChannel'] = thumbnailChannel;
|
||||
|
@ -237,7 +236,9 @@ inquirer
|
|||
'\nIt\'s a good idea to BACK UP YOUR MASTER PASSWORD \nin "/site/private/authConfig.json" so that you don\'t lose \ncontrol of your channel.'
|
||||
);
|
||||
|
||||
console.log('\nNext step: run "npm run start" to build and start your server!');
|
||||
console.log(
|
||||
'\nNext step: run "npm run build" (or "npm run dev") to compiles, and "npm run start" to start your server!'
|
||||
);
|
||||
console.log(
|
||||
'If you want to change any settings, you can edit the files in the "/site" folder.'
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Request from '../utils/request';
|
||||
|
||||
export function getLongClaimId (host, name, modifier) {
|
||||
export function getLongClaimId(host, name, modifier) {
|
||||
let body = {};
|
||||
// create request params
|
||||
if (modifier) {
|
||||
|
@ -13,40 +13,40 @@ export function getLongClaimId (host, name, modifier) {
|
|||
}
|
||||
body['claimName'] = name;
|
||||
const params = {
|
||||
method : 'POST',
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body : JSON.stringify(body),
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
// create url
|
||||
const url = `${host}/api/claim/long-id`;
|
||||
// return the request promise
|
||||
return Request(url, params);
|
||||
};
|
||||
}
|
||||
|
||||
export function getShortId (host, name, claimId) {
|
||||
export function getShortId(host, name, claimId) {
|
||||
const url = `${host}/api/claim/short-id/${claimId}/${name}`;
|
||||
return Request(url);
|
||||
};
|
||||
}
|
||||
|
||||
export function getClaimData (host, name, claimId) {
|
||||
export function getClaimData(host, name, claimId) {
|
||||
const url = `${host}/api/claim/data/${name}/${claimId}`;
|
||||
return Request(url);
|
||||
};
|
||||
}
|
||||
|
||||
export function checkClaimAvailability (claim) {
|
||||
export function checkClaimAvailability(claim) {
|
||||
const url = `/api/claim/availability/${claim}`;
|
||||
return Request(url);
|
||||
}
|
||||
|
||||
export function getClaimViews (claimId) {
|
||||
export function getClaimViews(claimId) {
|
||||
const url = `/api/claim/views/${claimId}`;
|
||||
return Request(url);
|
||||
}
|
||||
|
||||
export function doAbandonClaim (claimId) {
|
||||
export function doAbandonClaim(outpoint) {
|
||||
const params = {
|
||||
method : 'POST',
|
||||
body : JSON.stringify({claimId}),
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ outpoint }),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
|
|
|
@ -16,7 +16,7 @@ import EditPage from '@pages/EditPage';
|
|||
const App = () => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path='/' component={HomePage} />
|
||||
<Route exact path='/' component={AboutPage} />
|
||||
<Route exact path='/about' component={AboutPage} />
|
||||
<Route exact path='/tos' component={TosPage} />
|
||||
<Route exact path='/faq' component={FaqPage} />
|
||||
|
|
|
@ -22,14 +22,29 @@ export const makePublishRequestChannel = (fd, isUpdate) => {
|
|||
xhr.upload.addEventListener('load', onLoad);
|
||||
// set state change handler
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
const response = JSON.parse(xhr.response);
|
||||
if ((xhr.status === 200) && response.success) {
|
||||
emitter({success: response});
|
||||
emitter(END);
|
||||
} else {
|
||||
emitter({error: new Error(response.message)});
|
||||
emitter(END);
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
switch (xhr.status) {
|
||||
case 413:
|
||||
emitter({error: new Error("Unfortunately it appears this web server " +
|
||||
"has been misconfigured, please inform the service administrators " +
|
||||
"that they must set their nginx/apache request size maximums higher " +
|
||||
"than their file size limits.")});
|
||||
emitter(END);
|
||||
break;
|
||||
case 200:
|
||||
var response = JSON.parse(xhr.response);
|
||||
if (response.success) {
|
||||
emitter({success: response});
|
||||
emitter(END);
|
||||
} else {
|
||||
emitter({error: new Error(response.message)});
|
||||
emitter(END);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
emitter({error: new Error("Received an unexpected response from " +
|
||||
"server: " + xhr.status)});
|
||||
emitter(END);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,32 +1,13 @@
|
|||
import React from 'react';
|
||||
import Row from '@components/Row';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
const AboutSpeechDetails = () => {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<p className={'text--large'}>
|
||||
<Link className={'link--primary'} to='/tos'>Terms of Service</Link>
|
||||
<br />
|
||||
<Link className={'link--primary'} to='/faq'>Frequently Asked Questions</Link>
|
||||
</p>
|
||||
</Row>
|
||||
<Row>
|
||||
<p className={'text--large'}>
|
||||
Spee.ch is a media-hosting site that reads from and publishes content to the <a className='link--primary' href='https://lbry.com'>LBRY</a> blockchain.
|
||||
</p>
|
||||
<p className={'text--large'}>
|
||||
Spee.ch is a hosting service, but with the added benefit that it stores your content on a decentralized network of computers -- the <a className='link--primary' href='https://lbry.com/get'>LBRY</a> network. This means that your images are stored in multiple locations without a single point of failure.
|
||||
</p>
|
||||
</Row>
|
||||
<Row>
|
||||
<h3>Contribute</h3>
|
||||
<p className={'text--large'}>
|
||||
If you have an idea for your own spee.ch-like site on top of LBRY, fork our <a className='link--primary' href='https://github.com/lbryio/spee.ch'>github repo</a> and go to town!
|
||||
</p>
|
||||
<p className={'text--large'}>
|
||||
If you want to improve spee.ch, join our <a className='link--primary' href='https://chat.lbry.com'>discord channel</a> or solve one of our <a className='link--primary' href='https://github.com/lbryio/spee.ch/issues'>github issues</a>.
|
||||
Spee.ch's journey may be on hold, but LBRY is still on mission. We'd like to thank all of our testers and early adopters for helping us explore this use case.
|
||||
We're really excited about <a className='link--primary' href='https://lbry.tv' target='_blank'>lbry.tv</a> and can't wait to see you over there for a fully featured experience.
|
||||
</p>
|
||||
</Row>
|
||||
</div>
|
||||
|
|
|
@ -5,14 +5,13 @@ const AboutSpeechOverview = () => {
|
|||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<p className={'text--extra-large'}>Spee.ch is an open-source project. Please contribute to the existing site, or fork it and make your own.</p>
|
||||
<p className={'text--extra-large'}>Lbry is no longer supporting Spee.ch. However, we're excited to show you <a className='link--primary' href='https://lbry.tv' target='_blank'>lbry.tv</a>!</p>
|
||||
</Row>
|
||||
<Row>
|
||||
<div className={'text--large'}>
|
||||
<a className='link--primary' target='_blank' href='https://twitter.com/spee_ch'>TWITTER</a><br/>
|
||||
<a className='link--primary' target='_blank' href='https://github.com/lbryio/spee.ch'>GITHUB</a><br/>
|
||||
<a className='link--primary' target='_blank' href='https://twitter.com/lbry'>TWITTER</a><br/>
|
||||
<a className='link--primary' target='_blank' href='https://github.com/lbryio/'>GITHUB</a><br/>
|
||||
<a className='link--primary' target='_blank' href='https://discord.gg/YjYbwhS'>DISCORD CHANNEL</a><br/>
|
||||
<a className='link--primary' target='_blank' href='https://github.com/lbryio/spee.ch/blob/master/README.md'>DOCUMENTATION</a><br/>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,6 @@ import Img from 'react-image';
|
|||
const AssetPreview = ({ defaultThumbnail, claimData }) => {
|
||||
const {name, fileExt, contentType, thumbnail, title, blocked, transactionTime = 0} = claimData;
|
||||
const showUrl = createCanonicalLink({asset: {...claimData}});
|
||||
console.log(transactionTime)
|
||||
const embedUrl = `${showUrl}.${fileExt}`;
|
||||
const ago = Date.now() / 1000 - transactionTime;
|
||||
const dayInSeconds = 60 * 60 * 24;
|
||||
|
|
|
@ -194,7 +194,7 @@ class AssetInfo extends React.Component {
|
|||
<a
|
||||
className={'link--primary'}
|
||||
target='_blank'
|
||||
href='https://lbry.com/dmca'
|
||||
href={`https://lbry.com/dmca/${claimId}`}
|
||||
>
|
||||
Report
|
||||
</a>
|
||||
|
|
|
@ -33,14 +33,14 @@ class NavigationLinks extends React.Component {
|
|||
const { channelName, showPublish, closedRegistration } = this.props;
|
||||
return (
|
||||
<div className='navigation-links'>
|
||||
{showPublish && <NavLink
|
||||
className='nav-bar-link link--nav'
|
||||
activeClassName='link--nav-active'
|
||||
to='/'
|
||||
exact
|
||||
>
|
||||
Publish
|
||||
</NavLink>}
|
||||
{/*{showPublish && <NavLink*/}
|
||||
{/* className='nav-bar-link link--nav'*/}
|
||||
{/* activeClassName='link--nav-active'*/}
|
||||
{/* to='/'*/}
|
||||
{/* exact*/}
|
||||
{/*>*/}
|
||||
{/* Publish*/}
|
||||
{/*</NavLink>}*/}
|
||||
<NavLink
|
||||
className='nav-bar-link link--nav'
|
||||
activeClassName='link--nav-active'
|
||||
|
@ -48,24 +48,24 @@ class NavigationLinks extends React.Component {
|
|||
>
|
||||
About
|
||||
</NavLink>
|
||||
{ channelName ? (
|
||||
<NavBarChannelOptionsDropdown
|
||||
channelName={this.props.channelName}
|
||||
handleSelection={this.handleSelection}
|
||||
defaultSelection={this.props.channelName}
|
||||
VIEW={VIEW}
|
||||
LOGOUT={LOGOUT}
|
||||
/>
|
||||
) : !closedRegistration && (
|
||||
<NavLink
|
||||
id='nav-bar-login-link'
|
||||
className='nav-bar-link link--nav'
|
||||
activeClassName='link--nav-active'
|
||||
to='/login'
|
||||
>
|
||||
Channel
|
||||
</NavLink>
|
||||
)}
|
||||
{/*{ channelName ? (*/}
|
||||
{/* <NavBarChannelOptionsDropdown*/}
|
||||
{/* channelName={this.props.channelName}*/}
|
||||
{/* handleSelection={this.handleSelection}*/}
|
||||
{/* defaultSelection={this.props.channelName}*/}
|
||||
{/* VIEW={VIEW}*/}
|
||||
{/* LOGOUT={LOGOUT}*/}
|
||||
{/* />*/}
|
||||
{/*) : !closedRegistration && (*/}
|
||||
{/* <NavLink*/}
|
||||
{/* id='nav-bar-login-link'*/}
|
||||
{/* className='nav-bar-link link--nav'*/}
|
||||
{/* activeClassName='link--nav-active'*/}
|
||||
{/* to='/login'*/}
|
||||
{/* >*/}
|
||||
{/* Channel*/}
|
||||
{/* </NavLink>*/}
|
||||
{/*)}*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ class PublishDisabledMessage extends React.Component {
|
|||
<div className={'publish-disabled-message'}>
|
||||
<div className={'message'}>
|
||||
<p className={'text--secondary'}>Publishing is currently disabled.</p>
|
||||
<p className={'text--secondary'}>
|
||||
Try <a className='link--primary' href='https://lbry.tv' target='_blank'>lbry.tv</a>
|
||||
</p>
|
||||
<p className={'text--secondary'}>{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {connect} from 'react-redux';
|
||||
import {updateMetadata} from '../../actions/publish';
|
||||
import { connect } from 'react-redux';
|
||||
import { updateMetadata } from '../../actions/publish';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ publish }) => {
|
||||
|
@ -16,4 +16,7 @@ const mapDispatchToProps = dispatch => {
|
|||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(View);
|
||||
|
|
|
@ -3,7 +3,7 @@ import ErrorPage from '@pages/ErrorPage';
|
|||
import ShowAssetLite from '@pages/ShowAssetLite';
|
||||
import ShowAssetDetails from '@pages/ShowAssetDetails';
|
||||
import ShowChannel from '@pages/ShowChannel';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withRouter, Redirect } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
CHANNEL,
|
||||
|
@ -15,15 +15,24 @@ import {
|
|||
class ContentPageWrapper extends React.Component {
|
||||
componentDidMount () {
|
||||
const { onHandleShowPageUri, match, homeChannel } = this.props;
|
||||
onHandleShowPageUri(homeChannel ? { claim: homeChannel } : match.params);
|
||||
//onHandleShowPageUri(homeChannel ? { claim: homeChannel } : match.params);
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.match.params !== this.props.match.params) {
|
||||
this.props.onHandleShowPageUri(nextProps.match.params);
|
||||
//this.props.onHandleShowPageUri(nextProps.match.params);
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const { error, requestType } = this.props;
|
||||
const { error, requestType, match } = this.props;
|
||||
const { params } = match;
|
||||
const { claim, identifier } = params;
|
||||
if (identifier && claim) {
|
||||
return <Redirect to={`https://lbry.tv/${identifier}/${claim}`} />;
|
||||
} else if (identifier) {
|
||||
// return <Redirect to={`https://lbry.tv/${identifier}/`} />
|
||||
} else {
|
||||
return <Redirect to={`https://lbry.tv/${claim}`} />;
|
||||
}
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage error={error} />
|
||||
|
@ -31,13 +40,13 @@ class ContentPageWrapper extends React.Component {
|
|||
}
|
||||
switch (requestType) {
|
||||
case CHANNEL:
|
||||
return <ShowChannel />;
|
||||
// return <ShowChannel />;
|
||||
case ASSET_LITE:
|
||||
return <ShowAssetLite />;
|
||||
// return <ShowAssetLite />;
|
||||
case ASSET_DETAILS:
|
||||
return <ShowAssetDetails />;
|
||||
// return <ShowAssetDetails />;
|
||||
case SPECIAL_ASSET:
|
||||
return <ShowChannel />;
|
||||
// return <ShowChannel />;
|
||||
default:
|
||||
return <p>loading...</p>;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ const initialState = {
|
|||
description: '',
|
||||
license: '',
|
||||
licenseUrl: '',
|
||||
nsfw: false,
|
||||
tags: [],
|
||||
},
|
||||
isUpdate: false,
|
||||
hasChanged: false,
|
||||
|
|
|
@ -5,17 +5,19 @@ import { updatePublishStatus, clearFile } from '../actions/publish';
|
|||
import { removeAsset } from '../actions/show';
|
||||
import { doAbandonClaim } from '../api/assetApi';
|
||||
|
||||
function * abandonClaim (action) {
|
||||
function* abandonClaim(action) {
|
||||
const { claimData, history } = action.data;
|
||||
const { claimId } = claimData;
|
||||
const { outpoint } = claimData;
|
||||
|
||||
const confirm = window.confirm('Are you sure you want to abandon this claim? This action cannot be undone.');
|
||||
const confirm = window.confirm(
|
||||
'Are you sure you want to abandon this claim? This action cannot be undone.'
|
||||
);
|
||||
if (!confirm) return;
|
||||
|
||||
yield put(updatePublishStatus(publishStates.ABANDONING, 'Your claim is being abandoned...'));
|
||||
|
||||
try {
|
||||
yield call(doAbandonClaim, claimId);
|
||||
yield call(doAbandonClaim, outpoint);
|
||||
} catch (error) {
|
||||
return console.log('abandon error:', error.message);
|
||||
}
|
||||
|
@ -25,6 +27,6 @@ function * abandonClaim (action) {
|
|||
return history.push('/');
|
||||
}
|
||||
|
||||
export function * watchAbandonClaim () {
|
||||
export function* watchAbandonClaim() {
|
||||
yield takeLatest(actions.ABANDON_CLAIM, abandonClaim);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,11 +9,22 @@ import { selectShowState, selectAsset } from '../selectors/show';
|
|||
import { validateChannelSelection, validateNoPublishErrors } from '../utils/validate';
|
||||
import { createPublishMetadata, createPublishFormData, createThumbnailUrl } from '../utils/publish';
|
||||
import { makePublishRequestChannel } from '../channels/publish';
|
||||
|
||||
function * publishFile (action) {
|
||||
// yep
|
||||
function* publishFile(action) {
|
||||
const { history } = action.data;
|
||||
const publishState = yield select(selectPublishState);
|
||||
const { publishInChannel, selectedChannel, file, claim, metadata, thumbnailChannel, thumbnailChannelId, thumbnail, isUpdate, error: publishToolErrors } = publishState;
|
||||
const {
|
||||
publishInChannel,
|
||||
selectedChannel,
|
||||
file,
|
||||
claim,
|
||||
metadata,
|
||||
thumbnailChannel,
|
||||
thumbnailChannelId,
|
||||
thumbnail,
|
||||
isUpdate,
|
||||
error: publishToolErrors,
|
||||
} = publishState;
|
||||
const { loggedInChannel } = yield select(selectChannelState);
|
||||
const { host } = yield select(selectSiteState);
|
||||
|
||||
|
@ -39,7 +50,7 @@ function * publishFile (action) {
|
|||
// create metadata
|
||||
publishMetadata = createPublishMetadata(
|
||||
isUpdate ? asset.name : claim,
|
||||
isUpdate ? {type: asset.claimData.contentType} : file,
|
||||
isUpdate ? { type: asset.claimData.contentType } : file,
|
||||
metadata,
|
||||
publishInChannel,
|
||||
selectedChannel
|
||||
|
@ -49,7 +60,12 @@ function * publishFile (action) {
|
|||
}
|
||||
if (thumbnail) {
|
||||
// add thumbnail to publish metadata
|
||||
publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host);
|
||||
publishMetadata['thumbnail'] = createThumbnailUrl(
|
||||
thumbnailChannel,
|
||||
thumbnailChannelId,
|
||||
claim,
|
||||
host
|
||||
);
|
||||
}
|
||||
// create form data for main publish
|
||||
publishFormData = createPublishFormData(file, thumbnail, publishMetadata);
|
||||
|
@ -57,7 +73,7 @@ function * publishFile (action) {
|
|||
publishChannel = yield call(makePublishRequestChannel, publishFormData, isUpdate);
|
||||
|
||||
while (true) {
|
||||
const {loadStart, progress, load, success, error: publishError} = yield take(publishChannel);
|
||||
const { loadStart, progress, load, success, error: publishError } = yield take(publishChannel);
|
||||
if (publishError) {
|
||||
return yield put(updatePublishStatus(publishStates.FAILED, publishError.message));
|
||||
}
|
||||
|
@ -67,7 +83,7 @@ function * publishFile (action) {
|
|||
yield put({
|
||||
type: 'ASSET_UPDATE_CLAIMDATA',
|
||||
data: {
|
||||
id : `a#${success.data.name}#${success.data.claimId}`,
|
||||
id: `a#${success.data.name}#${success.data.claimId}`,
|
||||
claimData: success.data.claimData,
|
||||
},
|
||||
});
|
||||
|
@ -91,6 +107,6 @@ function * publishFile (action) {
|
|||
}
|
||||
}
|
||||
|
||||
export function * watchPublishStart () {
|
||||
export function* watchPublishStart() {
|
||||
yield takeLatest(actions.PUBLISH_START, publishFile);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ export const createPublishMetadata = (
|
|||
publishInChannel,
|
||||
selectedChannel
|
||||
) => {
|
||||
// this metadata stuff needs to be removed...
|
||||
let metadata = {
|
||||
name: claim,
|
||||
title,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Configure your own spee.ch
|
||||
|
||||
_note: this guide assumes you have done the [quickstart](https://github.com/lbryio/spee.ch/blob/readme-update/README.md) or [fullstart](https://github.com/lbryio/spee.ch/blob/readme-update/fullstart.md) guide and have a working spee.ch server_
|
||||
_note: this guide assumes you have done the [quickstart](https://github.com/lbryio/spee.ch/blob/master/README.md) or [fullstart](https://github.com/lbryio/spee.ch/blob/master/fullstart.md) guide and have a working spee.ch server_
|
||||
|
||||
## Custom Components
|
||||
The components used by spee.ch are taken from the `client/` folder, but you can override those components by defining your own in the `site/custom/` folder.
|
||||
|
|
|
@ -51,50 +51,50 @@ PUBLISHING:
|
|||
}
|
||||
|
||||
SERVING:
|
||||
|
||||
"dynamicFileSizing": {
|
||||
"enabled": false, - if you choose to allow your instance to serve transform images
|
||||
"maxDimension": 2000 - the maximum size you allow transform to scale
|
||||
},
|
||||
"markdownSettings": {
|
||||
"skipHtmlMain": true, - false: render html, in a somewhat unpredictable way~
|
||||
"escapeHtmlMain": true, - true: rather than render html, escape it and print it visibly
|
||||
"skipHtmlDescriptions": true, - as above, for descriptions
|
||||
"escapeHtmlDescriptions": true, - as above, for descriptions
|
||||
"allowedTypesMain": [], - markdown rendered as main content
|
||||
"allowedTypesDescriptions": [], - markdown rendered in description in content details
|
||||
"allowedTypesExample": [ - here are examples of allowed types
|
||||
"see react-markdown docs", `https://github.com/rexxars/react-markdown`
|
||||
"root",
|
||||
"text",
|
||||
"break",
|
||||
"paragraph",
|
||||
"emphasis",
|
||||
"strong",
|
||||
"thematicBreak",
|
||||
"blockquote",
|
||||
"delete",
|
||||
"link",
|
||||
"image", - you may not have a lot of control over how these are rendered
|
||||
"linkReference",
|
||||
"imageReference",
|
||||
"table",
|
||||
"tableHead",
|
||||
"tableBody",
|
||||
"tableRow",
|
||||
"tableCell",
|
||||
"list",
|
||||
"listItem",
|
||||
"heading",
|
||||
"inlineCode",
|
||||
"code",
|
||||
"html", - potentially DANGEROUS, intended for `serveOnlyApproved = true` environments, includes iframes, divs.
|
||||
"parsedHtml"
|
||||
],
|
||||
},
|
||||
"customFileExtensions": { - suggest a file extension for experimental content types you may be publishing
|
||||
"application/example-type": "example"
|
||||
}
|
||||
|
||||
"dynamicFileSizing": {
|
||||
"enabled": false, - if you choose to allow your instance to serve transform images
|
||||
"maxDimension": 2000 - the maximum size you allow transform to scale
|
||||
},
|
||||
"markdownSettings": {
|
||||
"skipHtmlMain": true, - false: render html, in a somewhat unpredictable way~
|
||||
"escapeHtmlMain": true, - true: rather than render html, escape it and print it visibly
|
||||
"skipHtmlDescriptions": true, - as above, for descriptions
|
||||
"escapeHtmlDescriptions": true, - as above, for descriptions
|
||||
"allowedTypesMain": [], - markdown rendered as main content
|
||||
"allowedTypesDescriptions": [], - markdown rendered in description in content details
|
||||
"allowedTypesExample": [ - here are examples of allowed types
|
||||
"see react-markdown docs", `https://github.com/rexxars/react-markdown`
|
||||
"root",
|
||||
"text",
|
||||
"break",
|
||||
"paragraph",
|
||||
"emphasis",
|
||||
"strong",
|
||||
"thematicBreak",
|
||||
"blockquote",
|
||||
"delete",
|
||||
"link",
|
||||
"image", - you may not have a lot of control over how these are rendered
|
||||
"linkReference",
|
||||
"imageReference",
|
||||
"table",
|
||||
"tableHead",
|
||||
"tableBody",
|
||||
"tableRow",
|
||||
"tableCell",
|
||||
"list",
|
||||
"listItem",
|
||||
"heading",
|
||||
"inlineCode",
|
||||
"code",
|
||||
"html", - potentially DANGEROUS, intended for `serveOnlyApproved = true` environments, includes iframes, divs.
|
||||
"parsedHtml"
|
||||
],
|
||||
},
|
||||
"customFileExtensions": { - suggest a file extension for experimental content types you may be publishing
|
||||
"application/example-type": "example"
|
||||
}
|
||||
|
||||
STARTUP:
|
||||
|
||||
|
|
958
package-lock.json
generated
958
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -36,11 +36,12 @@
|
|||
"@fortawesome/fontawesome-svg-core": "^1.2.8",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.5.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.3",
|
||||
"axios": "^0.18.0",
|
||||
"axios": "^0.18.1",
|
||||
"bcrypt": "^3.0.3",
|
||||
"body-parser": "^1.18.3",
|
||||
"connect-multiparty": "^2.2.0",
|
||||
"cookie-session": "^2.0.0-beta.3",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.16.4",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"express-http-context": "^1.2.0",
|
||||
|
@ -52,7 +53,7 @@
|
|||
"inquirer": "^5.2.0",
|
||||
"ip": "^1.1.5",
|
||||
"isbot": "^2.2.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash": "^4.17.13",
|
||||
"make-dir": "^1.3.0",
|
||||
"mime-types": "^2.1.21",
|
||||
"module-alias": "^2.1.0",
|
||||
|
|
|
@ -82,6 +82,7 @@ export default (db, table, sequelize) => ({
|
|||
};
|
||||
const selectWhere = {
|
||||
...whereClause,
|
||||
claim_type: 1,
|
||||
publisher_id: channelClaimId,
|
||||
};
|
||||
return await table
|
||||
|
@ -103,6 +104,7 @@ export default (db, table, sequelize) => ({
|
|||
.findAll({
|
||||
where: {
|
||||
name: claimName,
|
||||
claim_type: 1,
|
||||
publisher_id: channelClaimId,
|
||||
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] },
|
||||
},
|
||||
|
@ -201,6 +203,10 @@ export default (db, table, sequelize) => ({
|
|||
}
|
||||
|
||||
return claimArray[0];
|
||||
})
|
||||
.catch(error => {
|
||||
logger.verbose(`resolveClaim failed: ${error}`)
|
||||
reject(error);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -210,6 +216,7 @@ export default (db, table, sequelize) => ({
|
|||
.findAll({
|
||||
where: {
|
||||
name: claimName,
|
||||
claim_type: 1,
|
||||
publisher_id: channelId,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -9,28 +9,28 @@ const authenticateUser = require('../publish/authentication.js');
|
|||
*/
|
||||
|
||||
const claimAbandon = async (req, res) => {
|
||||
const {claimId} = req.body;
|
||||
const {user} = req;
|
||||
const { outpoint } = req.body;
|
||||
const { user } = req;
|
||||
try {
|
||||
const [channel, claim] = await Promise.all([
|
||||
authenticateUser(user.channelName, null, null, user),
|
||||
db.Claim.findOne({where: {claimId}}),
|
||||
db.Claim.findOne({ where: { outpoint } }),
|
||||
]);
|
||||
|
||||
if (!claim) throw new Error('That channel does not exist');
|
||||
if (!channel.channelName) throw new Error('You don\'t own this channel');
|
||||
if (!channel.channelName) throw new Error("You don't own this channel");
|
||||
|
||||
await abandonClaim({claimId});
|
||||
const file = await db.File.findOne({where: {claimId}});
|
||||
await abandonClaim({ outpoint });
|
||||
const file = await db.File.findOne({ where: { outpoint } });
|
||||
await Promise.all([
|
||||
deleteFile(file.filePath),
|
||||
db.File.destroy({where: {claimId}}),
|
||||
db.Claim.destroy({where: {claimId}}),
|
||||
db.File.destroy({ where: { outpoint } }),
|
||||
db.Claim.destroy({ where: { outpoint } }),
|
||||
]);
|
||||
logger.debug(`Claim abandoned: ${claimId}`);
|
||||
logger.debug(`Claim abandoned: ${outpoint}`);
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: `Claim with id ${claimId} abandonded`,
|
||||
message: `Claim with outpoint ${outpoint} abandonded`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('abandon claim error:', error);
|
||||
|
|
|
@ -21,7 +21,7 @@ const claimGet = async ({ ip, originalUrl, params, headers }, res) => {
|
|||
try {
|
||||
let claimInfo = await chainquery.claim.queries.resolveClaim(name, claimId).catch(() => {});
|
||||
if (claimInfo) {
|
||||
logger.info('claim/get: claim resolved in chainquery');
|
||||
logger.debug(`claim/get: claim resolved in chainquery`);
|
||||
}
|
||||
if (!claimInfo) {
|
||||
claimInfo = await db.Claim.resolveClaim(name, claimId);
|
||||
|
@ -30,15 +30,15 @@ const claimGet = async ({ ip, originalUrl, params, headers }, res) => {
|
|||
throw new Error('claim/get: resolveClaim: No matching uri found in Claim table');
|
||||
}
|
||||
if (headers && headers['user-agent'] && isBot(headers['user-agent'])) {
|
||||
let lbrynetResolveResult = await resolveUri(`${name}#${claimId}`);
|
||||
const { message, completed } = lbrynetResolveResult;
|
||||
logger.info(`Bot GetClaim: claimId: ${claimId}`);
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message,
|
||||
message: 'bot',
|
||||
completed: false,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
logger.info(`GetClaim: ${claimId} UA: ${headers['user-agent']}`);
|
||||
let lbrynetResult = await getClaim(`${name}#${claimId}`);
|
||||
if (!lbrynetResult) {
|
||||
throw new Error(`claim/get: getClaim Unable to Get ${name}#${claimId}`);
|
||||
|
|
|
@ -33,25 +33,26 @@ const createPublishParams = (
|
|||
name,
|
||||
file_path: filePath,
|
||||
bid: publishing.fileClaimBidAmount,
|
||||
metadata: {
|
||||
description,
|
||||
title,
|
||||
author: details.title,
|
||||
language: 'en',
|
||||
license,
|
||||
licenseUrl,
|
||||
nsfw,
|
||||
},
|
||||
description,
|
||||
title,
|
||||
author: details.title,
|
||||
languages: ['en'],
|
||||
license,
|
||||
license_url: licenseUrl,
|
||||
tags: [],
|
||||
claim_address: publishing.primaryClaimAddress,
|
||||
};
|
||||
// add thumbnail to channel if video
|
||||
if (thumbnail) {
|
||||
publishParams['metadata']['thumbnail'] = thumbnail;
|
||||
publishParams['thumbnail_url'] = thumbnail;
|
||||
}
|
||||
if (nsfw) {
|
||||
publishParams.tags = ['mature'];
|
||||
}
|
||||
// add channel details if publishing to a channel
|
||||
if (channelName && channelClaimId) {
|
||||
publishParams['channel_name'] = channelName;
|
||||
publishParams['channel_id'] = channelClaimId;
|
||||
publishParams['channel_name'] = channelName;
|
||||
}
|
||||
// log params
|
||||
logger.debug('publish params:', publishParams);
|
||||
|
|
|
@ -7,19 +7,25 @@ const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, lic
|
|||
}
|
||||
logger.debug(`Creating Thumbnail Publish Parameters`);
|
||||
// create the publish params
|
||||
|
||||
if (license === null || license.trim() === '') {
|
||||
license = ''; // default to empty string
|
||||
}
|
||||
// provide default for licenseUrl
|
||||
if (licenseUrl === null || licenseUrl.trim() === '') {
|
||||
licenseUrl = ''; // default to empty string
|
||||
}
|
||||
|
||||
return {
|
||||
name: `${claimName}-thumb`,
|
||||
file_path: thumbnailFilePath,
|
||||
bid: publishing.fileClaimBidAmount,
|
||||
metadata: {
|
||||
title: `${claimName} thumbnail`,
|
||||
description: `a thumbnail for ${claimName}`,
|
||||
author: details.title,
|
||||
language: 'en',
|
||||
license,
|
||||
licenseUrl,
|
||||
nsfw,
|
||||
},
|
||||
title: `${claimName} thumbnail`,
|
||||
description: `a thumbnail for ${claimName}`,
|
||||
author: details.title,
|
||||
languages: ['en'],
|
||||
license,
|
||||
license_url: licenseUrl,
|
||||
claim_address: publishing.primaryClaimAddress,
|
||||
channel_name: publishing.thumbnailChannel,
|
||||
channel_id: publishing.thumbnailChannelId,
|
||||
|
|
|
@ -141,12 +141,12 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
return publish(publishParams, fileName, fileType, filePath);
|
||||
})
|
||||
.then(publishResults => {
|
||||
logger.info('Publish success >', publishResults);
|
||||
logger.debug('Publish success >', publishResults);
|
||||
claimData = publishResults;
|
||||
({ claimId } = claimData);
|
||||
|
||||
if (channelName) {
|
||||
logger.info(`api/claim/publish: claimData.certificateId ${claimData.certificateId}`);
|
||||
logger.debug(`api/claim/publish: claimData.certificateId ${claimData.certificateId}`);
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(
|
||||
claimData.certificateId,
|
||||
channelName
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
const logger = require('winston');
|
||||
const db = require('../../../../models');
|
||||
const { publishClaim } = require('../../../../lbrynet');
|
||||
const { createFileRecordDataAfterPublish } = require('../../../../models/utils/createFileRecordData.js');
|
||||
const { createClaimRecordDataAfterPublish } = require('../../../../models/utils/createClaimRecordData.js');
|
||||
const db = require('server/models');
|
||||
const { publishClaim } = require('server/lbrynet');
|
||||
const { createFileRecordDataAfterPublish } = require('server/models/utils/createFileRecordData.js');
|
||||
const {
|
||||
createClaimRecordDataAfterPublish,
|
||||
} = require('server/models/utils/createClaimRecordData.js');
|
||||
const deleteFile = require('./deleteFile.js');
|
||||
|
||||
const publish = async (publishParams, fileName, fileType) => {
|
||||
|
@ -10,58 +12,76 @@ const publish = async (publishParams, fileName, fileType) => {
|
|||
let channel;
|
||||
let fileRecord;
|
||||
let newFile = Boolean(publishParams.file_path);
|
||||
let publishResultsOutput;
|
||||
|
||||
try {
|
||||
publishResults = await publishClaim(publishParams);
|
||||
logger.info(`Successfully published ${publishParams.name} ${fileName}`, publishResults);
|
||||
const outpoint = `${publishResults.output.txid}:${publishResults.output.nout}`;
|
||||
// get the channel information
|
||||
if (publishParams.channel_name) {
|
||||
logger.debug(`this claim was published in channel: ${publishParams.channel_name}`);
|
||||
channel = await db.Channel.findOne({
|
||||
where: {
|
||||
channelName: publishParams.channel_name,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
channel = null;
|
||||
}
|
||||
const certificateId = channel ? channel.channelClaimId : null;
|
||||
const channelName = channel ? channel.channelName : null;
|
||||
publishResultsOutput = publishResults && publishResults.outputs && publishResults.outputs[0];
|
||||
|
||||
const claimRecord = await createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults);
|
||||
const {claimId} = claimRecord;
|
||||
const upsertCriteria = {name: publishParams.name, claimId};
|
||||
logger.verbose(`Successfully published ${publishParams.name} ${fileName}`, publishResults);
|
||||
const outpoint = `${publishResultsOutput.txid}:${publishResultsOutput.nout}`;
|
||||
// get the channel information
|
||||
// if (publishParams.channel_name) {
|
||||
// logger.debug(`this claim was published in channel: ${publishParams.channel_name}`);
|
||||
// channel = await db.Channel.findOne({
|
||||
// where: {
|
||||
// channelName: publishParams.channel_name,
|
||||
// },
|
||||
// });
|
||||
// } else {
|
||||
// channel = null;
|
||||
// }
|
||||
|
||||
const certificateId = publishResultsOutput.signing_channel
|
||||
? publishResultsOutput.signing_channel.claim_id
|
||||
: null;
|
||||
const channelName = publishResultsOutput.signing_channel
|
||||
? publishResultsOutput.signing_channel.name
|
||||
: null;
|
||||
|
||||
const claimRecord = await createClaimRecordDataAfterPublish(
|
||||
certificateId,
|
||||
channelName,
|
||||
fileName,
|
||||
fileType,
|
||||
publishParams,
|
||||
publishResultsOutput
|
||||
);
|
||||
const { claimId } = claimRecord;
|
||||
const upsertCriteria = { name: publishParams.name, claimId };
|
||||
if (newFile) {
|
||||
// this is the problem
|
||||
//
|
||||
fileRecord = await createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults);
|
||||
fileRecord = await createFileRecordDataAfterPublish(
|
||||
fileName,
|
||||
fileType,
|
||||
publishParams,
|
||||
publishResultsOutput
|
||||
);
|
||||
} else {
|
||||
fileRecord = await db.File.findOne({where: {claimId}}).then(result => result.dataValues);
|
||||
fileRecord = await db.File.findOne({ where: { claimId } }).then(result => result.dataValues);
|
||||
}
|
||||
|
||||
const [file, claim] = await Promise.all([
|
||||
db.upsert(db.File, fileRecord, upsertCriteria, 'File'),
|
||||
db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'),
|
||||
]);
|
||||
logger.info(`File and Claim records successfully created (${publishParams.name})`);
|
||||
logger.debug(`File and Claim records successfully created (${publishParams.name})`);
|
||||
|
||||
await Promise.all([
|
||||
file.setClaim(claim),
|
||||
claim.setFile(file),
|
||||
]);
|
||||
logger.info(`File and Claim records successfully associated (${publishParams.name})`);
|
||||
await Promise.all([file.setClaim(claim), claim.setFile(file)]);
|
||||
logger.debug(`File and Claim records successfully associated (${publishParams.name})`);
|
||||
|
||||
return Object.assign({}, claimRecord, {outpoint});
|
||||
return Object.assign({}, claimRecord, { outpoint });
|
||||
} catch (err) {
|
||||
// parse daemon response when err is a string
|
||||
// this needs work
|
||||
logger.info('publish/publish err:', err);
|
||||
logger.error('publish/publish err:', err);
|
||||
const error = typeof err === 'string' ? JSON.parse(err) : err;
|
||||
if (publishParams.file_path) {
|
||||
await deleteFile(publishParams.file_path);
|
||||
}
|
||||
const message = error.error && error.error.message ? error.error.message : 'Unknown publish error';
|
||||
const message =
|
||||
error.error && error.error.message ? error.error.message : 'Unknown publish error';
|
||||
return {
|
||||
error: true,
|
||||
message,
|
||||
|
|
|
@ -38,7 +38,7 @@ const rando = () => {
|
|||
|
||||
const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) => {
|
||||
// logging
|
||||
logger.info('Claim update request:', {
|
||||
logger.debug('Claim update request:', {
|
||||
ip,
|
||||
headers,
|
||||
body,
|
||||
|
@ -138,7 +138,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
nsfw: claimRecord.nsfw,
|
||||
license: claimRecord.license,
|
||||
licenseUrl: claimRecord.license_url,
|
||||
language: 'en',
|
||||
languages: ['en'],
|
||||
author: details.title,
|
||||
},
|
||||
updateMetadata({ title, description, nsfw, license, licenseUrl })
|
||||
|
@ -149,9 +149,19 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
claim_address: primaryClaimAddress,
|
||||
channel_name: channelName,
|
||||
channel_id: channelId,
|
||||
metadata,
|
||||
title,
|
||||
description,
|
||||
author: details.title,
|
||||
languages: ['en'],
|
||||
license: license || '',
|
||||
license_url: licenseUrl || '',
|
||||
tags: [],
|
||||
};
|
||||
|
||||
if (nsfw) {
|
||||
publishParams.tags = ['mature'];
|
||||
}
|
||||
|
||||
if (files.file) {
|
||||
if (thumbnailUpdate) {
|
||||
// publish new thumbnail
|
||||
|
@ -185,14 +195,14 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
|
||||
if (channelName) {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(
|
||||
result.certificateId,
|
||||
publishResult.certificateId,
|
||||
channelName
|
||||
);
|
||||
} else {
|
||||
return chainquery.claim.queries
|
||||
.getShortClaimIdFromLongClaimId(result.claimId, name, result)
|
||||
.getShortClaimIdFromLongClaimId(publishResult.claimId, name, publishResult)
|
||||
.catch(() => {
|
||||
return result.claimId.slice(0, 1);
|
||||
return publishResult.claimId.slice(0, 1);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@ const chainquery = require('chainquery').default;
|
|||
const fileAvailability = ({ ip, originalUrl, params }, res) => {
|
||||
const name = params.name;
|
||||
const claimId = params.claimId;
|
||||
logger.debug(`fileAvailability params: name:${name} claimId:${claimId}`);
|
||||
logger.verbose(`fileAvailability params: name:${name} claimId:${claimId}`);
|
||||
// TODO: we probably eventually want to check the publishCache for the claimId too,
|
||||
// and shop the outpoint to file_list.
|
||||
return chainquery.claim.queries
|
||||
|
|
|
@ -64,18 +64,19 @@ const getClaimIdAndServeAsset = (
|
|||
claimDataValues.outpoint ||
|
||||
`${claimDataValues.transaction_hash_id}:${claimDataValues.vout}`;
|
||||
logger.debug('Outpoint:', outpoint);
|
||||
return db.Blocked.isNotBlocked(outpoint).then(() => {
|
||||
return db.Blocked.isNotBlocked(outpoint)
|
||||
// .then(() => {
|
||||
// If content was found, is approved, and not blocked - log a view.
|
||||
if (headers && headers['user-agent'] && /LBRY/.test(headers['user-agent']) === false) {
|
||||
db.Views.create({
|
||||
time: Date.now(),
|
||||
isChannel: false,
|
||||
claimId: claimDataValues.claim_id || claimDataValues.claimId,
|
||||
publisherId: claimDataValues.publisher_id || claimDataValues.certificateId,
|
||||
ip,
|
||||
});
|
||||
}
|
||||
});
|
||||
// if (headers && headers['user-agent'] && /LBRY/.test(headers['user-agent']) === false) {
|
||||
// db.Views.create({
|
||||
// time: Date.now(),
|
||||
// isChannel: false,
|
||||
// claimId: claimDataValues.claim_id || claimDataValues.claimId,
|
||||
// publisherId: claimDataValues.publisher_id || claimDataValues.certificateId,
|
||||
// ip,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
})
|
||||
.then(() => {
|
||||
return db.File.findOne({
|
||||
|
|
|
@ -20,10 +20,7 @@ const { setupBlockList } = require('./utils/blockList');
|
|||
const speechPassport = require('./speechPassport');
|
||||
const processTrending = require('./utils/processTrending');
|
||||
|
||||
const {
|
||||
logMetricsMiddleware,
|
||||
setRouteDataInContextMiddleware,
|
||||
} = require('./middleware/logMetricsMiddleware');
|
||||
const { setRouteDataInContextMiddleware } = require('./middleware/httpContextMiddleware');
|
||||
|
||||
const {
|
||||
details: { port: PORT, blockListEndpoint },
|
||||
|
@ -145,7 +142,7 @@ function Server() {
|
|||
|
||||
app[routeMethod](
|
||||
routePath,
|
||||
logMetricsMiddleware,
|
||||
// logMetricsMiddleware,
|
||||
setRouteDataInContextMiddleware(routePath, routeData),
|
||||
...controllers
|
||||
);
|
||||
|
|
|
@ -73,13 +73,14 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
},
|
||||
async abandonClaim({ claimId }) {
|
||||
logger.debug(`lbryApi >> Abandon claim "${claimId}"`);
|
||||
async abandonClaim({ outpoint }) {
|
||||
logger.debug(`lbryApi >> Abandon claim "${outpoint}"`);
|
||||
const gaStartTime = Date.now();
|
||||
const [txid, nout] = outpoint.split(':');
|
||||
try {
|
||||
const abandon = await axios.post(lbrynetUri, {
|
||||
method: 'claim_abandon',
|
||||
params: { claim_id: claimId },
|
||||
method: 'stream_abandon',
|
||||
params: { txid: txid, nout: Number(nout) },
|
||||
});
|
||||
sendGATimingEvent('lbrynet', 'abandonClaim', 'ABANDON_CLAIM', gaStartTime, Date.now());
|
||||
return abandon.data;
|
||||
|
@ -172,10 +173,10 @@ module.exports = {
|
|||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(lbrynetUri, {
|
||||
method: 'channel_new',
|
||||
method: 'channel_create',
|
||||
params: {
|
||||
channel_name: name,
|
||||
amount: publishing.channelClaimBidAmount,
|
||||
name: name,
|
||||
bid: publishing.channelClaimBidAmount,
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
|
|
|
@ -5,11 +5,13 @@ const {
|
|||
publishing: { publishingChannelWhitelist },
|
||||
} = require('@config/siteConfig');
|
||||
const ipBanFile = './site/config/ipBan.txt';
|
||||
const ipWhitelist = './site/config/ipWhitelist.txt';
|
||||
const forbiddenMessage =
|
||||
'<h1>Forbidden</h1>If you are seeing this by mistake, please contact us using <a href="https://chat.lbry.com/">https://chat.lbry.com/</a>';
|
||||
|
||||
const maxPublishesInTenMinutes = 20;
|
||||
let ipCounts = {};
|
||||
let blockedAddresses = [];
|
||||
let whitelistedAddresses = [];
|
||||
|
||||
if (fs.existsSync(ipBanFile)) {
|
||||
const lineReader = require('readline').createInterface({
|
||||
|
@ -23,9 +25,28 @@ if (fs.existsSync(ipBanFile)) {
|
|||
});
|
||||
}
|
||||
|
||||
// If a file called ipWhitelist.txt exists
|
||||
// Please comment above each whitelisted IP why/who/when etc
|
||||
// # Jim because he's awesome - January 2018
|
||||
if (fs.existsSync(ipWhitelist)) {
|
||||
const lineReader = require('readline').createInterface({
|
||||
input: require('fs').createReadStream(ipWhitelist),
|
||||
});
|
||||
|
||||
lineReader.on('line', line => {
|
||||
if (line && line !== '' && line[0] !== '#') {
|
||||
whitelistedAddresses.push(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const autoblockPublishMiddleware = (req, res, next) => {
|
||||
let ip = (req.headers['x-forwarded-for'] || req.connection.remoteAddress).split(/,\s?/)[0];
|
||||
|
||||
if (whitelistedAddresses.indexOf(ip) !== -1) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
if (blockedAddresses.indexOf(ip) !== -1) {
|
||||
res.status(403).send(forbiddenMessage);
|
||||
res.end();
|
||||
|
@ -44,7 +65,7 @@ const autoblockPublishMiddleware = (req, res, next) => {
|
|||
}
|
||||
}, 600000 /* 10 minute retainer */);
|
||||
|
||||
if (count === 10) {
|
||||
if (count === maxPublishesInTenMinutes) {
|
||||
logger.error(`Banning IP: ${ip}`);
|
||||
blockedAddresses.push(ip);
|
||||
res.status(403).send(forbiddenMessage);
|
||||
|
|
13
server/middleware/httpContextMiddleware.js
Normal file
13
server/middleware/httpContextMiddleware.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const httpContext = require('express-http-context');
|
||||
|
||||
function setRouteDataInContextMiddleware(routePath, routeData) {
|
||||
return function(req, res, next) {
|
||||
httpContext.set('routePath', routePath);
|
||||
httpContext.set('routeData', routeData);
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setRouteDataInContextMiddleware,
|
||||
};
|
|
@ -388,7 +388,9 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
return new Promise((resolve, reject) => {
|
||||
this.fetchClaim(name, claimId)
|
||||
.then(claim => {
|
||||
logger.info('resolveClaim claims:', claim);
|
||||
logger.debug(
|
||||
`resolveClaim: ${name}, ${claimId}, -> certificateId: ${claim && claim.certificateId}`
|
||||
);
|
||||
if (
|
||||
serveOnlyApproved &&
|
||||
!isApprovedChannel({ longId: claim.certificateId }, approvedChannels)
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
const db = require('../index.js');
|
||||
|
||||
const createClaimRecordDataAfterPublish = (certificateId, channelName, fileName, fileType, publishParams, publishResults) => {
|
||||
const createClaimRecordDataAfterPublish = (
|
||||
certificateId,
|
||||
channelName,
|
||||
fileName,
|
||||
fileType,
|
||||
publishParams,
|
||||
publishResultsOutput
|
||||
) => {
|
||||
const {
|
||||
name,
|
||||
metadata: {
|
||||
title,
|
||||
description,
|
||||
thumbnail,
|
||||
nsfw,
|
||||
},
|
||||
title,
|
||||
description,
|
||||
thumbnail,
|
||||
nsfw,
|
||||
claim_address: address,
|
||||
bid: amount,
|
||||
} = publishParams;
|
||||
|
||||
const {
|
||||
claim_id: claimId,
|
||||
txid,
|
||||
nout,
|
||||
} = publishResults;
|
||||
const { claim_id: claimId, txid, nout } = publishResultsOutput;
|
||||
|
||||
return db.Claim.getCurrentHeight()
|
||||
.then(height => {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
title,
|
||||
description,
|
||||
address,
|
||||
thumbnail,
|
||||
outpoint : `${txid}:${nout}`,
|
||||
height,
|
||||
contentType: fileType,
|
||||
nsfw,
|
||||
amount,
|
||||
certificateId,
|
||||
channelName,
|
||||
};
|
||||
});
|
||||
return db.Claim.getCurrentHeight().then(height => {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
title,
|
||||
description,
|
||||
address,
|
||||
thumbnail,
|
||||
outpoint: `${txid}:${nout}`,
|
||||
height,
|
||||
contentType: fileType,
|
||||
nsfw,
|
||||
amount,
|
||||
certificateId,
|
||||
channelName,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
const getMediaDimensions = require('../../utils/getMediaDimensions.js');
|
||||
|
||||
async function createFileRecordDataAfterGet (resolveResult, getResult) {
|
||||
const {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
contentType: fileType,
|
||||
} = resolveResult;
|
||||
async function createFileRecordDataAfterGet(resolveResult, getResult) {
|
||||
const { name, claimId, outpoint, contentType: fileType } = resolveResult;
|
||||
|
||||
const {
|
||||
file_name: fileName,
|
||||
download_path: filePath,
|
||||
} = getResult;
|
||||
const { file_name: fileName, download_path: filePath } = getResult;
|
||||
|
||||
const {
|
||||
height: fileHeight,
|
||||
width: fileWidth,
|
||||
} = await getMediaDimensions(fileType, filePath);
|
||||
const { height: fileHeight, width: fileWidth } = await getMediaDimensions(fileType, filePath);
|
||||
|
||||
return {
|
||||
name,
|
||||
|
@ -30,22 +19,17 @@ async function createFileRecordDataAfterGet (resolveResult, getResult) {
|
|||
};
|
||||
}
|
||||
|
||||
async function createFileRecordDataAfterPublish (fileName, fileType, publishParams, publishResults) {
|
||||
const {
|
||||
name,
|
||||
file_path: filePath,
|
||||
} = publishParams;
|
||||
async function createFileRecordDataAfterPublish(
|
||||
fileName,
|
||||
fileType,
|
||||
publishParams,
|
||||
publishResultsOutput
|
||||
) {
|
||||
const { name, file_path: filePath } = publishParams;
|
||||
|
||||
const {
|
||||
claim_id: claimId,
|
||||
txid,
|
||||
nout,
|
||||
} = publishResults;
|
||||
const { claim_id: claimId, txid, nout } = publishResultsOutput;
|
||||
|
||||
const {
|
||||
height: fileHeight,
|
||||
width: fileWidth,
|
||||
} = await getMediaDimensions(fileType, filePath);
|
||||
const { height: fileHeight, width: fileWidth } = await getMediaDimensions(fileType, filePath);
|
||||
|
||||
return {
|
||||
name,
|
||||
|
|
|
@ -8,6 +8,7 @@ import createSagaMiddleware from 'redux-saga';
|
|||
import { call } from 'redux-saga/effects';
|
||||
import Helmet from 'react-helmet';
|
||||
import * as httpContext from 'express-http-context';
|
||||
import logger from 'winston';
|
||||
|
||||
import Reducers from '@reducers';
|
||||
import GAListener from '@components/GAListener';
|
||||
|
@ -92,6 +93,7 @@ export default (req, res) => {
|
|||
const preloadedState = store.getState();
|
||||
|
||||
// send the rendered page back to the client
|
||||
|
||||
res.send(renderFullPage(helmet, html, preloadedState));
|
||||
};
|
||||
|
||||
|
@ -121,8 +123,8 @@ export default (req, res) => {
|
|||
}
|
||||
|
||||
if (canonicalUrl && canonicalUrl !== req.originalUrl) {
|
||||
console.log(`redirecting ${req.originalUrl} to ${canonicalUrl}`);
|
||||
res.redirect(canonicalUrl);
|
||||
logger.verbose(`redirecting ${req.originalUrl} to ${canonicalUrl}`);
|
||||
return res.redirect(canonicalUrl);
|
||||
}
|
||||
|
||||
return renderPage(store);
|
||||
|
|
|
@ -25,6 +25,7 @@ const publishingConfig = require('../../controllers/api/config/site/publishing')
|
|||
const getTorList = require('../../controllers/api/tor');
|
||||
const getBlockedList = require('../../controllers/api/blocked');
|
||||
const getOEmbedData = require('../../controllers/api/oEmbed');
|
||||
const cors = require('cors');
|
||||
|
||||
export default {
|
||||
// homepage routes
|
||||
|
@ -43,10 +44,10 @@ export default {
|
|||
'/api/claim/data/:claimName/:claimId' : { controller: [ torCheckMiddleware, claimData ] },
|
||||
'/api/claim/get/:name/:claimId' : { controller: [ torCheckMiddleware, claimGet ] },
|
||||
'/api/claim/list/:name' : { controller: [ torCheckMiddleware, claimList ] },
|
||||
'/api/claim/long-id' : { method: 'post', controller: [ torCheckMiddleware, claimLongId ] }, // note: should be a 'get'
|
||||
'/api/claim/publish' : { method: 'post', controller: [ torCheckMiddleware, autoblockPublishMiddleware, multipartMiddleware, autoblockPublishBodyMiddleware, claimPublish ] },
|
||||
'/api/claim/update' : { method: 'post', controller: [ torCheckMiddleware, multipartMiddleware, claimUpdate ] },
|
||||
'/api/claim/abandon' : { method: 'post', controller: [ torCheckMiddleware, multipartMiddleware, claimAbandon ] },
|
||||
'/api/claim/long-id' : { method: 'post', controller: [ cors(), torCheckMiddleware, claimLongId ] }, // note: should be a 'get'
|
||||
'/api/claim/publish' : { method: 'post', controller: [ cors(), torCheckMiddleware, autoblockPublishMiddleware, multipartMiddleware, autoblockPublishBodyMiddleware, claimPublish ] },
|
||||
'/api/claim/update' : { method: 'post', controller: [ cors(), torCheckMiddleware, multipartMiddleware, claimUpdate ] },
|
||||
'/api/claim/abandon' : { method: 'post', controller: [ cors(), torCheckMiddleware, multipartMiddleware, claimAbandon ] },
|
||||
'/api/claim/resolve/:name/:claimId' : { controller: [ torCheckMiddleware, claimResolve ] },
|
||||
'/api/claim/short-id/:longId/:name' : { controller: [ torCheckMiddleware, claimShortId ] },
|
||||
'/api/claim/views/:claimId' : { controller: [ torCheckMiddleware, claimViews ] },
|
||||
|
@ -55,7 +56,7 @@ export default {
|
|||
// user routes
|
||||
'/api/user/password/' : { method: 'put', controller: [ torCheckMiddleware, userPassword ] },
|
||||
// configs
|
||||
'/api/config/site/publishing' : { controller: [ torCheckMiddleware, publishingConfig ] },
|
||||
'/api/config/site/publishing' : { controller: [ cors(), torCheckMiddleware, publishingConfig ] },
|
||||
// tor
|
||||
'/api/tor' : { controller: [ torCheckMiddleware, getTorList ] },
|
||||
// blocked
|
||||
|
|
|
@ -2,6 +2,6 @@ module.exports = {
|
|||
...require('./pages').default,
|
||||
...require('./api').default,
|
||||
...require('./auth').default,
|
||||
...require('./assets').default,
|
||||
// ...require('./assets').default,
|
||||
...require('./fallback').default,
|
||||
};
|
||||
|
|
|
@ -2,7 +2,9 @@ const PassportLocalStrategy = require('passport-local').Strategy;
|
|||
const { createChannel } = require('../../lbrynet');
|
||||
const logger = require('winston');
|
||||
const db = require('../../models');
|
||||
const { publishing: { closedRegistration } } = require('@config/siteConfig');
|
||||
const {
|
||||
publishing: { closedRegistration },
|
||||
} = require('@config/siteConfig');
|
||||
|
||||
module.exports = new PassportLocalStrategy(
|
||||
{
|
||||
|
@ -28,19 +30,23 @@ module.exports = new PassportLocalStrategy(
|
|||
logger.verbose('userData >', userData);
|
||||
// create user record
|
||||
const channelData = {
|
||||
channelName : `@${username}`,
|
||||
channelClaimId: tx.claim_id,
|
||||
channelName: `@${username}`,
|
||||
channelClaimId: tx.outputs[0].claim_id,
|
||||
};
|
||||
logger.verbose('channelData >', channelData);
|
||||
// create certificate record
|
||||
const certificateData = {
|
||||
claimId: tx.claim_id,
|
||||
name : `@${username}`,
|
||||
claimId: tx.outputs[0].claim_id,
|
||||
name: `@${username}`,
|
||||
// address,
|
||||
};
|
||||
logger.verbose('certificateData >', certificateData);
|
||||
// save user and certificate to db
|
||||
return Promise.all([db.User.create(userData), db.Channel.create(channelData), db.Certificate.create(certificateData)]);
|
||||
return Promise.all([
|
||||
db.User.create(userData),
|
||||
db.Channel.create(channelData),
|
||||
db.Certificate.create(certificateData),
|
||||
]);
|
||||
})
|
||||
.then(([newUser, newChannel, newCertificate]) => {
|
||||
logger.verbose('user and certificate successfully created');
|
||||
|
@ -54,7 +60,10 @@ module.exports = new PassportLocalStrategy(
|
|||
})
|
||||
.then(() => {
|
||||
logger.verbose('user and certificate successfully associated');
|
||||
return db.Certificate.getShortChannelIdFromLongChannelId(userInfo.channelClaimId, userInfo.channelName);
|
||||
return db.Certificate.getShortChannelIdFromLongChannelId(
|
||||
userInfo.channelClaimId,
|
||||
userInfo.channelName
|
||||
);
|
||||
})
|
||||
.then(shortChannelId => {
|
||||
userInfo['shortChannelId'] = shortChannelId;
|
||||
|
|
|
@ -13,8 +13,10 @@ const awaitFileSize = (outpoint, size, interval, timeout) => {
|
|||
function checkFileList() {
|
||||
logger.debug('checkFileList');
|
||||
return getFileListFileByOutpoint(outpoint).then(result => {
|
||||
logger.debug('File List Result', result);
|
||||
if (result[0]['completed'] === true || result[0]['written_bytes'] > size) {
|
||||
const { items: fileInfos } = result;
|
||||
const fileInfo = fileInfos[0];
|
||||
logger.debug('File List Result', fileInfo);
|
||||
if (fileInfo.completed === true || fileInfo.written_bytes > size) {
|
||||
logger.debug('FILE READY');
|
||||
return 'ready';
|
||||
} else if (timeout !== 0 && Date.now() - start > timeout) {
|
||||
|
|
|
@ -3,7 +3,7 @@ const logger = require('winston');
|
|||
const config = require('@config/loggerConfig');
|
||||
const { logLevel } = config;
|
||||
|
||||
function configureLogging () {
|
||||
function configureLogging() {
|
||||
logger.info('configuring winston logger...');
|
||||
if (!config) {
|
||||
return logger.warn('No logger config found');
|
||||
|
@ -14,12 +14,12 @@ function configureLogging () {
|
|||
// configure the winston logger
|
||||
logger.configure({
|
||||
transports: [
|
||||
new (logger.transports.Console)({
|
||||
level : logLevel || 'debug',
|
||||
timestamp : false,
|
||||
colorize : true,
|
||||
prettyPrint : true,
|
||||
handleExceptions : true,
|
||||
new logger.transports.Console({
|
||||
level: logLevel || 'debug',
|
||||
timestamp: true,
|
||||
colorize: true,
|
||||
prettyPrint: true,
|
||||
handleExceptions: true,
|
||||
humanReadableUnhandledException: true,
|
||||
}),
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue