Merge pull request #685 from Xaekai/custom.path

Customize channel path
This commit is contained in:
Calvin Montgomery 2017-06-16 21:22:11 -07:00 committed by GitHub
commit 0f5193c700
18 changed files with 71 additions and 75 deletions

View file

@ -138,8 +138,6 @@ mail:
# 5. Click "Server key"
# 6. Under "APIs & auth" click "YouTube Data API" and then click "Enable API"
youtube-v3-key: ''
# Minutes between saving channel state to disk
channel-save-interval: 5
# Limit for the number of channels a user can register
max-channels-per-user: 5
# Limit for the number of accounts an IP address can register
@ -147,6 +145,24 @@ max-accounts-per-ip: 5
# Minimum number of seconds between guest logins from the same IP
guest-login-delay: 60
# Allows you to customize the path divider. The /r/ in http://localhost/r/yourchannel
# Acceptable characters are a-z A-Z 0-9 _ and -
channel-path: 'r'
# Allows you to blacklist certain channels. Users will be automatically kicked
# upon trying to join one.
channel-blacklist: []
# Minutes between saving channel state to disk
channel-save-interval: 5
# Determines channel data storage mechanism.
# Defaults to 'file', in which channel data is JSON stringified and saved to a file
# in the `chandump/` folder. This is the legacy behavior of CyTube.
# The other possible option is 'database', in which case each key-value pair of
# channel data is stored as a row in the `channel_data` database table.
# To migrate legacy chandump files to the database, shut down CyTube (to prevent
# concurrent updates), then run `node lib/channel-storage/migrate.js`.
channel-storage:
type: 'file'
# Configure statistics tracking
stats:
# Interval (in milliseconds) between data points - default 1h
@ -208,10 +224,6 @@ playlist:
# The server must be invoked with node --expose-gc index.js for this to have any effect.
aggressive-gc: false
# Allows you to blacklist certain channels. Users will be automatically kicked
# upon trying to join one.
channel-blacklist: []
# If you have ffmpeg installed, you can query metadata from raw files, allowing
# server-synched raw file playback. This requires the following:
# * ffmpeg must be installed on the server
@ -231,16 +243,6 @@ setuid:
# how long to wait in ms before changing uid/gid
timeout: 15
# Determines channel data storage mechanism.
# Defaults to 'file', in which channel data is JSON stringified and saved to a file
# in the `chandump/` folder. This is the legacy behavior of CyTube.
# The other possible option is 'database', in which case each key-value pair of
# channel data is stored as a row in the `channel_data` database table.
# To migrate legacy chandump files to the database, shut down CyTube (to prevent
# concurrent updates), then run `node lib/channel-storage/migrate.js`.
channel-storage:
type: 'file'
# Allows for external services to access the system commandline
# Useful for setups where stdin isn't available such as when using PM2
service-socket:

View file

@ -21,4 +21,4 @@ example, for cytu.be I use:
npm run generate-userscript CyTube http://cytu.be/r/* https://cytu.be/r/*
```
This will generate `www/js/cytube-google-drive.user.js`.
This will generate `www/js/cytube-google-drive.user.js`. If you've changed the channel path, be sure to take that into account.

View file

@ -61,6 +61,7 @@ function initPasswordResetCleanup(Server) {
}
function initChannelDumper(Server) {
const chanPath = Config.get('channel-path');
var CHANNEL_SAVE_INTERVAL = parseInt(Config.get("channel-save-interval"))
* 60000;
setInterval(function () {
@ -70,9 +71,9 @@ function initChannelDumper(Server) {
return Promise.delay(wait).then(() => {
if (!chan.dead && chan.users && chan.users.length > 0) {
return chan.saveState().tap(() => {
LOGGER.info(`Saved /r/${chan.name}`);
LOGGER.info(`Saved /${chanPath}/${chan.name}`);
}).catch(err => {
LOGGER.error(`Failed to save /r/${chan.name}: ${err.stack}`);
LOGGER.error(`Failed to save /${chanPath}/${chan.name}: ${err.stack}`);
});
}
}).catch(error => {

View file

@ -114,6 +114,8 @@ function fixOldChandump(data) {
}
function migrate(src, dest, opts) {
const chanPath = Config.get('channel-path');
return src.listChannels().then(names => {
return Promise.reduce(names, (_, name) => {
// A long time ago there was a bug where CyTube would save a different
@ -143,11 +145,11 @@ function migrate(src, dest, opts) {
});
return dest.save(name, data);
}).then(() => {
console.log(`Migrated /r/${name}`);
console.log(`Migrated /${chanPath}/${name}`);
}).catch(ChannelNotFoundError, err => {
console.log(`Skipping /r/${name} (not present in the database)`);
console.log(`Skipping /${chanPath}/${name} (not present in the database)`);
}).catch(err => {
console.error(`Failed to migrate /r/${name}: ${err.stack}`);
console.error(`Failed to migrate /${chanPath}/${name}: ${err.stack}`);
});
}, 0);
});

View file

@ -70,7 +70,12 @@ var defaults = {
"from-name": "CyTube Services"
},
"youtube-v3-key": "",
"channel-blacklist": [],
"channel-path": "r",
"channel-save-interval": 5,
"channel-storage": {
type: "file"
},
"max-channels-per-user": 5,
"max-accounts-per-ip": 5,
"guest-login-delay": 60,
@ -102,7 +107,6 @@ var defaults = {
"max-items": 4000,
"update-interval": 5
},
"channel-blacklist": [],
ffmpeg: {
enabled: false,
"ffprobe-exec": "ffprobe"
@ -114,9 +118,6 @@ var defaults = {
"user": "nobody",
"timeout": 15
},
"channel-storage": {
type: "file"
},
"service-socket": {
enabled: false,
socket: "service.sock"
@ -390,6 +391,12 @@ function preprocessConfig(cfg) {
});
cfg["channel-blacklist"] = tbl;
/* Check channel path */
if(!/^[-\w]+$/.test(cfg["channel-blacklist"])){
LOGGER.error("Channel paths may only use the same characters as usernames and channel names.");
process.exit(78); // sysexits.h for bad config
}
if (cfg["link-domain-blacklist"].length > 0) {
cfg["link-domain-blacklist-regex"] = new RegExp(
cfg["link-domain-blacklist"].join("|").replace(/\./g, "\\."), "gi");

View file

@ -62,6 +62,7 @@ var Server = function () {
self.announcement = null;
self.infogetter = null;
self.servers = {};
self.chanPath = Config.get('channel-path');
// backend init
var initModule;
@ -264,7 +265,7 @@ Server.prototype.unloadChannel = function (chan, options) {
if (!options.skipSave) {
chan.saveState().catch(error => {
LOGGER.error(`Failed to save /r/${chan.name} for unload: ${error.stack}`);
LOGGER.error(`Failed to save /${this.chanPath}/${chan.name} for unload: ${error.stack}`);
});
}
@ -354,9 +355,9 @@ Server.prototype.shutdown = function () {
Promise.map(this.channels, channel => {
try {
return channel.saveState().tap(() => {
LOGGER.info(`Saved /r/${channel.name}`);
LOGGER.info(`Saved /${this.chanPath}/${channel.name}`);
}).catch(err => {
LOGGER.error(`Failed to save /r/${channel.name}: ${err.stack}`);
LOGGER.error(`Failed to save /${this.chanPath}/${channel.name}: ${err.stack}`);
});
} catch (error) {
LOGGER.error(`Failed to save channel: ${error.stack}`);
@ -391,7 +392,7 @@ Server.prototype.handlePartitionMapChange = function () {
});
this.unloadChannel(channel, { skipSave: true });
}).catch(error => {
LOGGER.error(`Failed to unload /r/${channel.name} for ` +
LOGGER.error(`Failed to unload /${this.chanPath}/${channel.name} for ` +
`partition map flip: ${error.stack}`);
});
}

View file

@ -16,7 +16,8 @@ function merge(locals, res) {
loginDomain: Config.get("https.enabled") ? Config.get("https.full-address")
: Config.get("http.full-address"),
csrfToken: typeof res.req.csrfToken === 'function' ? res.req.csrfToken() : '',
baseUrl: getBaseUrl(res)
baseUrl: getBaseUrl(res),
channelPath: Config.get("channel-path"),
};
if (typeof locals !== "object") {
return _locals;

View file

@ -4,8 +4,8 @@ import { sendPug } from '../pug';
import * as HTTPStatus from '../httpstatus';
import { HTTPError } from '../../errors';
export default function initialize(app, ioConfig) {
app.get('/r/:channel', (req, res) => {
export default function initialize(app, ioConfig, chanPath) {
app.get(`/${chanPath}/:channel`, (req, res) => {
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` +
'channel name.', { status: HTTPStatus.NOT_FOUND });

View file

@ -132,6 +132,8 @@ module.exports = {
* Initializes webserver callbacks
*/
init: function (app, webConfig, ioConfig, clusterClient, channelIndex, session) {
const chanPath = Config.get('channel-path');
app.use((req, res, next) => {
counters.add("http:request", 1);
next();
@ -147,7 +149,7 @@ module.exports = {
}
app.use(cookieParser(webConfig.getCookieSecret()));
app.use(csrf.init(webConfig.getCookieDomain()));
app.use('/r/:channel', require('./middleware/ipsessioncookie').ipSessionCookieMiddleware);
app.use(`/${chanPath}/:channel`, require('./middleware/ipsessioncookie').ipSessionCookieMiddleware);
initializeLog(app);
require('./middleware/authorize')(app, session);
@ -176,7 +178,7 @@ module.exports = {
LOGGER.info('Enabled express-minify for CSS and JS');
}
require('./routes/channel')(app, ioConfig);
require('./routes/channel')(app, ioConfig, chanPath);
require('./routes/index')(app, channelIndex, webConfig.getMaxIndexEntries());
app.get('/sioconfig(.json)?', handleLegacySocketConfig);
require('./routes/socketconfig')(app, clusterClient);

View file

@ -44,7 +44,7 @@ html(lang="en")
input(type="hidden", name="name", value=c.name)
button.btn.btn-xs.btn-danger(type="submit") Delete
span.glyphicon.glyphicon-trash
a(href="/r/"+c.name, style="margin-left: 5px")= c.name
a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name
.col-lg-6.col-md-6
h3 Register a new channel
if newChannelError
@ -57,7 +57,7 @@ html(lang="en")
.form-group
label.control-label(for="channelname") Channel URL
.input-group
span.input-group-addon #{baseUrl}/r/
span.input-group-addon #{baseUrl}/#{channelPath}/
input#channelname.form-control(type="text", name="name", maxlength="30", onkeyup="checkChannel()")
p#validate_channel.text-danger.pull-right
button#register.btn.btn-primary.btn-block(type="submit") Register

View file

@ -11,7 +11,7 @@ html(lang="en")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
- var cname = "/r/" + channelName
- var cname = `/${channelPath}/${channelName}`
ul.nav.navbar-nav
+navdefaultlinks(cname)
li: a(href="javascript:void(0)", onclick="javascript:showUserOptions()") Options

View file

@ -86,7 +86,7 @@ mixin adminoptions
#cs-adminoptions.tab-pane
h4 Admin-Only Settings
form.form-horizontal(action="javascript:void(0)")
- var defname = "CyTube - /r/" + channelName
- var defname = `CyTube - /${channelPath}/${channelName}`
+textbox-auto("cs-pagetitle", "Page title", defname)
+textbox-auto("cs-password", "Password", "leave blank to disable")
+textbox-auto("cs-externalcss", "External CSS", "Stylesheet URL")

View file

@ -11,8 +11,16 @@ mixin head()
link(href="/css/cytube.css", rel="stylesheet")
link(id="usertheme", href=DEFAULT_THEME, rel="stylesheet")
script(type="text/javascript").
var DEFAULT_THEME = '#{DEFAULT_THEME}';
if channelName
script(type="text/javascript").
var DEFAULT_THEME = '#{DEFAULT_THEME}';
var CHANNELPATH = '#{channelPath}';
var CHANNELNAME = '#{channelName}';
else
script(type="text/javascript").
var DEFAULT_THEME = '#{DEFAULT_THEME}';
var CHANNELPATH = '#{channelPath}';
script(src="/js/theme.js")
//[if lt IE 9]
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>

View file

@ -25,7 +25,7 @@ html(lang="en")
tbody
each chan in channels
tr
td: a(href="/r/"+chan.name) #{chan.pagetitle} (#{chan.name})
td: a(href=`/${channelPath}/${chan.name}`) #{chan.pagetitle} (#{chan.name})
td= chan.usercount
td= chan.mediatitle
.col-lg-3.col-md-3
@ -37,6 +37,6 @@ html(lang="en")
script(type="text/javascript").
$("#channelname").keydown(function (ev) {
if (ev.keyCode === 13) {
location.href = "/r/" + $("#channelname").val();
location.href = "/#{channelPath}/" + $("#channelname").val();
}
});

View file

@ -453,8 +453,8 @@ console.log(channels[0]);
channels.forEach(function (c) {
var tr = $("<tr/>").appendTo(tbl);
var name = $("<td/>").appendTo(tr);
$("<a/>").attr("href", "/r/" + c.name)
.text(c.pagetitle + " (/r/" + c.name + ")")
$("<a/>").attr("href", `/${CHANNELPATH}/${c.name}`)
.text(c.pagetitle + ` (/${CHANNELPATH}/${c.name})`)
.appendTo(name);
var usercount = $("<td/>").text(c.usercount).appendTo(tr);
count += c.usercount;
@ -475,7 +475,7 @@ console.log(channels[0]);
.attr("title", "Unload")
.appendTo(controlInner)
.click(function () {
if (confirm("Are you sure you want to unload /r/" + c.name + "?")) {
if (confirm(`Are you sure you want to unload /${CHANNELPATH}/${c.name}?`)) {
socket.emit("acp-force-unload", {
name: c.name
});

View file

@ -425,23 +425,6 @@ Callbacks = {
if (!CLIENT.guest) {
socket.emit("initUserPLCallbacks");
if ($("#loginform").length === 0) {
return;
}
var logoutform = $("<p/>").attr("id", "logoutform")
.addClass("navbar-text pull-right")
.insertAfter($("#loginform"));
$("<span/>").attr("id", "welcome").text("Welcome, " + CLIENT.name)
.appendTo(logoutform);
$("<span/>").html("&nbsp;&middot;&nbsp;").appendTo(logoutform);
var domain = $("#loginform").attr("action").replace("/login", "");
$("<a/>").attr("id", "logout")
.attr("href", domain + "/logout?redirect=/r/" + CHANNEL.name)
.text("Logout")
.appendTo(logoutform);
$("#loginform").remove();
}
}
},

View file

@ -19,7 +19,7 @@ var CHANNEL = {
css: "",
js: "",
motd: "",
name: false,
name: CHANNELNAME,
usercount: 0,
emotes: []
};

View file

@ -615,17 +615,6 @@ $("#shuffleplaylist").click(function() {
}
});
/* load channel */
var loc = document.location+"";
var m = loc.match(/\/r\/([a-zA-Z0-9-_]+)/);
if(m) {
CHANNEL.name = m[1];
if (CHANNEL.name.indexOf("#") !== -1) {
CHANNEL.name = CHANNEL.name.substring(0, CHANNEL.name.indexOf("#"));
}
}
/* channel ranks stuff */
function chanrankSubmit(rank) {
var name = $("#cs-chanranks-name").val();