Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
624f7046af | ||
|
|
8b3d642104 | ||
|
|
46369f84bd | ||
|
|
68f3effa8d | ||
|
|
4657ae2aec | ||
|
|
f134d57b79 | ||
|
|
9b4da98ac4 | ||
|
|
599ca83f0f | ||
|
|
b952007f3b | ||
|
|
c0e74f4756 | ||
|
|
68a8e7ef97 |
@@ -113,6 +113,7 @@ const opText = {
|
||||
edit: "edited",
|
||||
import: "imported",
|
||||
tag: "tagged",
|
||||
untag: "untagged",
|
||||
"move-in": "moved in",
|
||||
"move-out": "moved-out",
|
||||
"move-out": "moved out",
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import moment from "moment";
|
||||
|
||||
<div class="change">
|
||||
<span class="change-id color-menu">[${moment(input.data.date).format("MMM D HH:mm:SS")}] * </span>
|
||||
<span class="change-id color-menu">[${moment(input.data.date).format("MMM D HH:mm:ss")}] * </span>
|
||||
<span class="author color-text">${input.data.author} </span>
|
||||
<span class="text color-text">${state.operation} </span>
|
||||
<span for(link in state.links | status-var=loop) key=link.href class="link">
|
||||
|
||||
@@ -13,6 +13,7 @@ module.exports = class {
|
||||
removed: false,
|
||||
multipart: false,
|
||||
anchored: false,
|
||||
prevWasText: false,
|
||||
}
|
||||
|
||||
this.mounted = false;
|
||||
@@ -48,6 +49,7 @@ module.exports = class {
|
||||
|
||||
const timediff = Date.parse(input.post.time) - Date.parse(input.prev.time)
|
||||
this.state.multipart = (input.post.nick == input.prev.nick && input.prev.multipart !== false) && (Math.abs(timediff) < 60000);
|
||||
this.state.prevWasText = (input.prev.kind === "text");
|
||||
|
||||
this.updatePost(input)
|
||||
}
|
||||
@@ -79,14 +81,14 @@ module.exports = class {
|
||||
this.state.name = input.post.nick
|
||||
this.state.nameSuffix = ""
|
||||
|
||||
this.state.text = input.post.text.replace(/\x02/g, "**").replace(/\x1D/g, "_").trim();
|
||||
this.state.text = input.post.text.replace(/\x02/g, "**").replace(/\x1D/g, "*").replace(/\`/g, "\\`").trim();
|
||||
|
||||
if (this.state.text.startsWith("|")) {
|
||||
this.state.name = ""
|
||||
this.state.text = this.state.text.slice(1).trim();
|
||||
}
|
||||
|
||||
if (input.post.kind === "text" && !this.state.text.includes("\"") && !this.state.text.includes("|")) {
|
||||
if (input.post.kind === "text" && !this.state.text.includes("\"") && !this.state.text.includes("\''") && !this.state.text.includes("|")) {
|
||||
const colonIndex = this.state.text.indexOf(": ");
|
||||
if (colonIndex != -1 && colonIndex < this.state.text.indexOf(" ")) {
|
||||
this.state.text = this.state.text.replace(": ", ": \"") + "\"";
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<markdown class="post-content post-scene" source=state.text />
|
||||
</div>
|
||||
<div if(input.post.kind === "action") class="post-body color-text">
|
||||
$ const prefix = (!state.multipart ? state.shortName + state.nameSuffix + " " : "");
|
||||
$ const prefix = ((!state.multipart || state.prevWasText) ? state.shortName + state.nameSuffix + " " : "");
|
||||
<markdown class="post-content post-action" source=(prefix + state.text) />
|
||||
</div>
|
||||
<div if(input.post.kind === "text") class="post-body color-text">
|
||||
|
||||
@@ -49,9 +49,9 @@ module.exports = class {
|
||||
|
||||
const search = this.state.search.toLocaleLowerCase()
|
||||
|
||||
this.state.characters = input.characters.filter(c => !this.state.activeMap.characters[c.id]).filter(c => this.matches(c.name, search)).sort((a,b) => a.name.localeCompare(b.name))
|
||||
this.state.channels = input.channels.filter(c => !this.state.activeMap.channels[c.name]).filter(c => this.matches(c.name, search)).sort((a,b) => a.name.localeCompare(b.name))
|
||||
this.state.eventNames = input.eventNames.filter(e => !this.state.activeMap.events[e]).filter(e => this.matches(e, search)).sort((a,b) => a.localeCompare(b))
|
||||
this.state.characters = input.characters.filter(c => !this.state.activeMap.characters[c.id]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search))
|
||||
this.state.channels = input.channels.filter(c => !this.state.activeMap.channels[c.name]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search))
|
||||
this.state.eventNames = input.eventNames.filter(e => !this.state.activeMap.events[e]).filter(e => this.matches(e, search)).sort((a,b) => compareFilters(a, b, search))
|
||||
}
|
||||
|
||||
updateActive({filter, characters, channels, eventNames}) {
|
||||
@@ -126,4 +126,29 @@ module.exports = class {
|
||||
|
||||
return str.toLowerCase().indexOf(search) !== -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} a
|
||||
* @param {string} b
|
||||
* @param {string} search
|
||||
*/
|
||||
function compareFilters(a, b, search) {
|
||||
if (search != "") {
|
||||
if (search.charAt(0) != "#" && a.charAt(0) == "#" && b.charAt(0) == "#") {
|
||||
search = '#' + search
|
||||
}
|
||||
|
||||
const aStarts = a.toLocaleLowerCase().startsWith(search)
|
||||
const bStarts = b.toLocaleLowerCase().startsWith(search)
|
||||
|
||||
if (aStarts && !bStarts) {
|
||||
return -1
|
||||
} else if (bStarts && !aStarts) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return a.localeCompare(b)
|
||||
}
|
||||
@@ -24,11 +24,12 @@ module.exports = class {
|
||||
} else {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {search: null})
|
||||
}
|
||||
|
||||
} else {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {[type]: (this.state.filter[type] || []).filter(f => f !== filter).concat(filter)})
|
||||
}
|
||||
|
||||
this.state.filter = Object.assign({}, this.state.filter, {limit: 0})
|
||||
|
||||
this.updateQuery(this.state.filter)
|
||||
|
||||
this.refresh()
|
||||
@@ -67,6 +68,9 @@ module.exports = class {
|
||||
if (filter.search) {
|
||||
queries.push("search=" + encodeURIComponent(filter.search))
|
||||
}
|
||||
if (filter.limit) {
|
||||
queries.push("limit=" + encodeURIComponent(filter.limit.toString()))
|
||||
}
|
||||
|
||||
if (queries.length > 0) {
|
||||
history.replaceState("", "", `/logs/?${queries.join("&")}`)
|
||||
@@ -75,6 +79,13 @@ module.exports = class {
|
||||
}
|
||||
}
|
||||
|
||||
clearLimit() {
|
||||
this.state.filter = Object.assign({}, this.state.filter, {limit: 0})
|
||||
|
||||
this.refresh()
|
||||
this.updateQuery(this.state.filter)
|
||||
}
|
||||
|
||||
refresh() {
|
||||
logHeaderApi.list(this.state.filter).then(logs => {
|
||||
this.state.logs = logs
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<logs-menu key="menu" on-select("open") selected=(input.selected || {}) user=input.user filter=state.filter characters=input.characters />
|
||||
<filter-menu on-add("addFilter") on-remove("removeFilter") filter=state.filter characters=input.characters channels=input.channels eventNames=input.eventNames />
|
||||
<main class="with-right">
|
||||
<main class="logs-page with-right">
|
||||
<logs-table logs=state.logs on-addfilter("addFilter") />
|
||||
<if(state.filter.limit > 0)>
|
||||
<div class="show-more-button" on-click("clearLimit")>
|
||||
<span>Show All</span>
|
||||
</div>
|
||||
</if>
|
||||
</main>
|
||||
<add-log-modal enabled=(state.modal === "log.add") on-close("close") />
|
||||
<import-log-modal enabled=(state.modal === "log.import") on-close("close") />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
main.logs-page {
|
||||
div.show-more-button {
|
||||
margin: 1em 0;
|
||||
padding: 0.5em 2em;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
|
||||
color: #DA1;
|
||||
}
|
||||
|
||||
div.show-more-button:hover {
|
||||
cursor: pointer;
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="story-chapter">
|
||||
<a class="anchor" id=input.chapter.id />
|
||||
<h1 if(input.chapter.title) class="title color-primary">${input.chapter.title}</h1>
|
||||
<h1 if(input.chapter.title && !input.hideTitle) class="title color-primary">${input.chapter.title}</h1>
|
||||
<div class="metadata">
|
||||
<div class="options color-menu">
|
||||
<if-permitted user=input.user author=input.chapter.author permission="chapter.edit">
|
||||
|
||||
@@ -8,18 +8,22 @@
|
||||
<a on-click("open", "story.edit") class="color-menu">Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=state.story.author permission="story.remove">
|
||||
<a on-click("open", "story.remove") class="color-menu">Remove</a>
|
||||
<a on-click("open", "story.remove") class="color-menu">Remove</a>
|
||||
</if-permitted>
|
||||
</if>
|
||||
</div>
|
||||
<story-tags tags=state.story.tags author=state.story.author user=(!state.removed ? input.user : null) on-select("open", "story.tags")/>
|
||||
<story-tags tags=state.story.tags open=state.story.open author=state.story.author user=(!state.removed ? input.user : null) on-select("open", "story.tags")/>
|
||||
<annotation if(state.removed) level="error">
|
||||
<p>
|
||||
This story has been removed. Your browser has not refreshed the page yet,
|
||||
so you can still read it (or back it up) before leaving the page.
|
||||
</p>
|
||||
</annotation>
|
||||
<chapter for(chapter in state.story.chapters) key=chapter.id chapter=chapter user=(!state.removed ? input.user : null) on-edit("updateChapter", chapter.id) on-remove("removeChapter", chapter.id) on-move("moveChapter", chapter) />
|
||||
<chapter for(chapter in state.story.chapters) key=chapter.id
|
||||
chapter=chapter
|
||||
user=(!state.removed ? input.user : null)
|
||||
hideTitle=(state.story.chapters[0] === chapter && chapter.title === state.story.name)
|
||||
on-edit("updateChapter", chapter.id) on-remove("removeChapter", chapter.id) on-move("moveChapter", chapter) />
|
||||
</div>
|
||||
|
||||
<create-chapter-modal storyId=state.story.id enabled=(state.modal === "chapter.add") chapter=input.chapter on-close("close") on-add("addChapter") />
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<div class="story-tags">
|
||||
<a for(tag in input.tags) href=("/story/by-tag/" + tag.kind + "/" + encodeURIComponent(tag.name)) class=["tag", "color-tag-" + tag.kind.toLowerCase()]>${tag.name}</a>
|
||||
<if-permitted user=input.user author=input.author permission="story.edit">
|
||||
<a on-click("select", "edit") class="option color-menu">Manage Tags</a>
|
||||
</if-permitted>
|
||||
|
||||
<if(input.open)>
|
||||
<if-permitted user=input.user permission="member">
|
||||
<a on-click("select", "edit") class="option color-menu">Manage Tags (Open)</a>
|
||||
</if-permitted>
|
||||
</if>
|
||||
<else>
|
||||
<if-permitted user=input.user author=input.author permission="story.edit">
|
||||
<a on-click("select", "edit") class="option color-menu">Manage Tags</a>
|
||||
</if-permitted>
|
||||
</else>
|
||||
</div>
|
||||
Generated
+5
@@ -3405,6 +3405,11 @@
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"express-session": "^1.15.6",
|
||||
"graphql-query-compress": "^1.1.0",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"ip": "^1.1.5",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"lasso": "^3.2.6",
|
||||
|
||||
@@ -8,19 +8,26 @@ const {channelApi} = require("../../rpdata/api/Channel")
|
||||
const listTemplate = require("../../marko/page/logs/list.marko")
|
||||
|
||||
router.get("/", async(req, res) => {
|
||||
const filter = {limit: 0}
|
||||
const filter = {limit: 75}
|
||||
|
||||
if (req.query.characters) {
|
||||
filter.characters = req.query.characters.split(",")
|
||||
filter.limit = 0
|
||||
}
|
||||
if (req.query.channels) {
|
||||
filter.channels = req.query.channels.split(",")
|
||||
filter.limit = 0
|
||||
}
|
||||
if (req.query.events) {
|
||||
filter.events = req.query.events.split(",")
|
||||
filter.limit = 0
|
||||
}
|
||||
if (req.query.search) {
|
||||
filter.search = req.query.search
|
||||
filter.limit = 0
|
||||
}
|
||||
if (req.query.limit) {
|
||||
filter.limit = parseInt(req.query.limit)
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -9,6 +9,8 @@ const config = require("./config")
|
||||
const express = require("express")
|
||||
const proxy = require("express-http-proxy")
|
||||
const proxy2 = require("http-proxy-middleware")
|
||||
const ip = require("ip")
|
||||
const dns = require("dns")
|
||||
const lasso = require("lasso")
|
||||
const lassoMiddleware = require("lasso/middleware")
|
||||
const markoExpress = require("marko/express")
|
||||
@@ -26,7 +28,39 @@ lasso.configure(require("./marko-config"))
|
||||
|
||||
// Set trusted proxy
|
||||
if (config.trustedProxy != null) {
|
||||
app.set('trust proxy', config.trustedProxy)
|
||||
if (ip.isV4Format(config.trustedProxy) || ip.isV6Format(config.trustedProxy)) {
|
||||
app.set('trust proxy', config.trustedProxy)
|
||||
|
||||
console.log("Trusting proxy", config.trustedProxy);
|
||||
} else {
|
||||
let trusted = null
|
||||
|
||||
setInterval(() => {
|
||||
dns.resolve4(config.trustedProxy, (err, res) => {
|
||||
if (err != null) {
|
||||
if (trusted === null) {
|
||||
console.error("Failed to resolve trustedProxy hostname")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if (res.length === 0) {
|
||||
if (trusted === null) {
|
||||
console.error("No results for trustedProxy hostname")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (trusted !== res[0]) {
|
||||
console.log("Trusting proxy", res[0], `(${config.trustedProxy})`);
|
||||
app.set('trust proxy', res[0])
|
||||
|
||||
trusted = res[0]
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply middleware
|
||||
|
||||
Reference in New Issue
Block a user