@@ -12,6 +12,7 @@ prebuild.run({
|
||||
"./marko/page/data/channels.marko",
|
||||
"./marko/page/data/characters.marko",
|
||||
"./marko/page/data/changes.marko",
|
||||
"./marko/page/data/files.marko",
|
||||
"./marko/page/story-content/view.marko",
|
||||
"./marko/page/logs-content/view.marko",
|
||||
]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<tr class="compact-list-item color-highlight-dark">
|
||||
<tr class=["compact-list-item", input.highlight ? "color-highlight-primary" : "color-highlight-dark"]>
|
||||
<include(input.renderBody)/>
|
||||
</tr>
|
||||
@@ -2,4 +2,22 @@ tr.compact-list-item {
|
||||
user-select: none;
|
||||
outline: 0.05px dotted;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tr.compact-list-item.color-highlight-primary {
|
||||
a {
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
label {
|
||||
color: #CCC !important;
|
||||
}
|
||||
|
||||
div.label {
|
||||
color: #AA8 !important;
|
||||
}
|
||||
|
||||
div.value.color-text {
|
||||
color: #FFF !important;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
table.compact-list {
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 3em;
|
||||
|
||||
width: 90%;
|
||||
max-width: 100ch;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
file: null,
|
||||
name: "",
|
||||
public: false,
|
||||
|
||||
realFile: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
if (key === "file") {
|
||||
this.state.values = Object.assign({}, this.state.values, {
|
||||
[key]: ev.target.value,
|
||||
realFile: ev.target.files[0],
|
||||
name: ev.target.files[0].name,
|
||||
})
|
||||
} else {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
input.file = input.realFile
|
||||
delete input.realFile
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.uploadFile(input).then((res) => {
|
||||
this.emit("added", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<label>File</label>
|
||||
<input key="eventName" type="file" placeholder="(None)" on-change("change", "file") value=state.values.file />
|
||||
|
||||
<label>Name</label>
|
||||
<input key="name" autofocus placeholder="(Required)" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
<label>Public</label>
|
||||
<toggle value=state.values.public on="On" off="Off"
|
||||
onDesc="Other members may see this file on this page."
|
||||
offDesc="Only you may see this file on this page."
|
||||
on-change("change", "public") />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -99,6 +99,19 @@ module.exports = class {
|
||||
break
|
||||
}
|
||||
|
||||
case "File": {
|
||||
const key = data.keys.find(k => k.id !== "*")
|
||||
const fileObj = data.objects.find(o => o.type === "File") || {name: key.id}
|
||||
|
||||
this.state.operation += " file"
|
||||
this.state.links = [{
|
||||
text: fileObj.name,
|
||||
href: `/data/files#${key.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Post": {
|
||||
const logKey = data.keys.find(k => k.model === "Log")
|
||||
const postKeys = data.keys.filter(k => k.model === "Post")
|
||||
@@ -124,6 +137,7 @@ module.exports = class {
|
||||
const opText = {
|
||||
add: "added",
|
||||
remove: "removed",
|
||||
upload: "uploaded",
|
||||
move: "moved",
|
||||
edit: "edited",
|
||||
import: "imported",
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
</main>
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-added("channelAdded") on-close("close") />
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-close("close") />
|
||||
|
||||
@@ -31,6 +31,15 @@ module.exports = class {
|
||||
}
|
||||
|
||||
characterAdded(character) {
|
||||
const index = this.state.characters.findIndex(c => c.id === character.id)
|
||||
if (index !== -1) {
|
||||
const characters = this.state.characters.slice()
|
||||
characters[index] = character
|
||||
this.state.characters = characters
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.state.characters = this.state.characters.concat(character)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
</main>
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-added("characterAdded") on-close("close") />
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-close("close") />
|
||||
|
||||
@@ -3,14 +3,18 @@
|
||||
<menu-link key="characters" selected=input.selected.characters icon="C" href="/data/characters/">Characters</menu-link>
|
||||
<menu-link key="channels" selected=input.selected.channels icon="#" href="/data/channels/">Channels</menu-link>
|
||||
<menu-link key="history" selected=input.selected.changes icon="H" href="/data/changes/">History</menu-link>
|
||||
<if-permitted user=input.user permission=["member", "character.add", "channel.add"]>
|
||||
<menu-link key="files" selected=input.selected.files icon="F" href="/data/files/">Files</menu-link>
|
||||
<if-permitted user=input.user permission=["member", "character.add", "channel.add", "file.add"]>
|
||||
<menu-gap />
|
||||
<menu-header>Add</menu-header>
|
||||
<if-permitted user=input.user permission=["character.add", "member"]>
|
||||
<menu-link dark key="characters_add" icon="+" on-click("emit", "open", "character.add")>Character</menu-link>
|
||||
<if-permitted key="characters_add" user=input.user permission=["character.add", "member"]>
|
||||
<menu-link dark icon="+" on-click("emit", "open", "character.add")>Character</menu-link>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user permission="channel.add">
|
||||
<menu-link dark key="channels_add" icon="+" on-click("emit", "open", "channel.add")>Channel</menu-link>
|
||||
<if-permitted key="channels_add" user=input.user permission="channel.add">
|
||||
<menu-link dark icon="+" on-click("emit", "open", "channel.add")>Channel</menu-link>
|
||||
</if-permitted>
|
||||
<if-permitted key="files_add" user=input.user permission=["file.add", "member"]>
|
||||
<menu-link dark icon="+" on-click("emit", "open", "file.add")>File</menu-link>
|
||||
</if-permitted>
|
||||
</if-permitted>
|
||||
</menu>
|
||||
@@ -0,0 +1,59 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
id: "",
|
||||
name: "",
|
||||
public: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.values = {
|
||||
id: input.file.id,
|
||||
name: input.file.name,
|
||||
public: input.file.public,
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
input.file = input.realFile
|
||||
delete input.realFile
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.editFile(input).then((res) => {
|
||||
this.emit("edited", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<label>Name</label>
|
||||
<input key="name" autofocus placeholder="(Required)" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
<label>Public</label>
|
||||
<toggle value=state.values.public on="On" off="Off"
|
||||
onDesc="Other members may see this file on this page."
|
||||
offDesc="Only you may see this file on this page."
|
||||
on-change("change", "public") />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = class {
|
||||
isHighlighted(id) {
|
||||
if (typeof(window) !== "undefined") {
|
||||
return window.location.hash.endsWith(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<compact-list>
|
||||
<compact-list-item for(file in input.files) key=file.id id=file.id highlight=component.isHighlighted(file.id)>
|
||||
<file data=file
|
||||
user=input.user
|
||||
on-removed("emit", "removed", file)
|
||||
on-edited("emit", "edited", file)
|
||||
/>
|
||||
</compact-list-item>
|
||||
</compact-list>
|
||||
@@ -0,0 +1,38 @@
|
||||
const moment = require("moment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
modal: null,
|
||||
deleted: false,
|
||||
date: moment(input.data.time).format("MMM D, YYYY"),
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (input && input.data) {
|
||||
this.state.date = moment(input.data.time).format("MMM D, YYYY")
|
||||
}
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
|
||||
removed() {
|
||||
this.state.deleted = true
|
||||
this.emit("removed")
|
||||
}
|
||||
|
||||
sizeString(bytes) {
|
||||
const order = Math.floor(Math.log2(bytes) / 10)
|
||||
const unit = ["B", "KB", "MB", "GB", "TB", "PB"][order]
|
||||
const number = bytes / Math.pow(2, (order)*10)
|
||||
|
||||
return `${number.toFixed(2)} ${unit}`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<if (!state.deleted)>
|
||||
<a class="anchor" id=input.data.id />
|
||||
<compact-list-property primary long label="Name" value=input.data.name />
|
||||
<compact-list-property short label="Date" value=state.date />
|
||||
<compact-list-property short label="Type" value=input.data.mimeType />
|
||||
<compact-list-property short label="Author" value=input.data.author />
|
||||
<compact-list-property short label="Size" value=component.sizeString(input.data.size) />
|
||||
<compact-list-options long>
|
||||
<a key="link" class="color-menu" on-click("open", "link")>Link</a>
|
||||
<if(input.data.kind === "upload")>
|
||||
<if-permitted user=input.user author=input.data.author permission="file.edit">
|
||||
<a key="edit" on-click("open", "edit", input.data)>Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=input.data.author permission="file.remove">
|
||||
<a key="remove" on-click("open", "remove", input.data)>Remove</a>
|
||||
</if-permitted>
|
||||
</if>
|
||||
</compact-list-options>
|
||||
<link-file-modal enabled=(state.modal === "link") file=input.data on-close("close") />
|
||||
<edit-file-modal enabled=(state.modal === "edit") file=input.data on-edited("emit", "edited") on-close("close") />
|
||||
<remove-file-modal enabled=(state.modal === "remove") file=input.data on-removed("removed") on-close("close") />
|
||||
</if>
|
||||
@@ -0,0 +1,47 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
publics: input.publics.sort(byDate),
|
||||
privates: input.privates.sort(byDate),
|
||||
modal: null,
|
||||
}
|
||||
}
|
||||
|
||||
onMount() {
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
|
||||
fileRemoved({id}) {
|
||||
this.state.publics = this.state.publics.filter(f => f.id !== id)
|
||||
this.state.privates = this.state.privates.filter(f => f.id !== id)
|
||||
}
|
||||
|
||||
fileEdited(file, patch) {
|
||||
this.fileRemoved({id: file.id})
|
||||
this.fileAdded(Object.assign({}, file, patch))
|
||||
|
||||
console.log(Object.assign({}, file, patch))
|
||||
}
|
||||
|
||||
fileAdded(file) {
|
||||
if (file.public) {
|
||||
this.state.publics = [].concat(...this.state.publics).concat(file).sort(byDate)
|
||||
} else {
|
||||
this.state.privates = [].concat(...this.state.privates).concat(file).sort(byDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function byDate(a, b) {
|
||||
const ad = new Date(a.time)
|
||||
const bd = new Date(b.time)
|
||||
|
||||
return ad.getTime() - bd.getTime()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<data-menu categories=input.categories selected=(input.selected || {}) user=input.user on-open("open") />
|
||||
<main class="data-file-page">
|
||||
<h1 class="file-list-headers color-primary">Your Private Files</h1>
|
||||
<file-list user=input.user files=state.privates on-removed("fileRemoved") on-edited("fileEdited") on-nicks("fileNicksChanged") />
|
||||
|
||||
<h1 class="file-list-headers color-primary">Public Files</h1>
|
||||
<file-list user=input.user files=state.publics on-removed("fileRemoved") on-edited("fileEdited") on-nicks("fileNicksChanged") />
|
||||
</main>
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-close("close") />
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-added("fileAdded") on-close("close") />
|
||||
@@ -0,0 +1,10 @@
|
||||
h1.file-list-headers {
|
||||
margin: 0;
|
||||
font-size: 1.5em;
|
||||
margin-top: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main.data-file-page {
|
||||
margin-bottom: 4em;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
isImage: (input.file && input.file.mimeType.startsWith("image/"))
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.isImage = (input.file && input.file.mimeType.startsWith("image/"))
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<modal class="modal color-text nolabel link-file-modal" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Links for ${input.file.name}</h1>
|
||||
|
||||
<p>
|
||||
Below are links and copy-pastable syntax for sharing the file. They can be used for linking to it
|
||||
in your stories and wiki pages.
|
||||
</p>
|
||||
|
||||
<p class="color-danger">
|
||||
This is the link to the actual file on the CDN, not a share of it. You must delete the file if you want
|
||||
to un-share it.
|
||||
</p>
|
||||
|
||||
<if(state.isImage)>
|
||||
<p class="color-danger">
|
||||
Images uploaded here cannot be inlined on the wiki. For that you have to upload it there.
|
||||
</p>
|
||||
</if>
|
||||
|
||||
<label>URL</label>
|
||||
<input key="url" disabled placeholder="(None)" on-change("change", "eventName") value=input.file.url />
|
||||
|
||||
<label>Markdown link</label>
|
||||
<input key="mdLink" disabled placeholder="(None)" on-change("change", "locationName") value=`[Link text](${input.file.url})` />
|
||||
|
||||
<label>Wiki link</label>
|
||||
<input key="wikiLink" disabled placeholder="(None)" on-change("change", "locationName") value=`[${input.file.url} Link text]` />
|
||||
|
||||
<if(state.isImage)>
|
||||
<label>Markdown image</label>
|
||||
<input key="mdImage" disabled placeholder="(None)" on-change("change", "locationName") value=`` />
|
||||
|
||||
<label>Image Preview</label>
|
||||
<img src=input.file.url alt="File preview"/>
|
||||
</if>
|
||||
</modal>
|
||||
@@ -0,0 +1,19 @@
|
||||
div.link-file-modal {
|
||||
user-select: text;
|
||||
|
||||
p {
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 0.25em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
user-select: all !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
id: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.values = {
|
||||
id: input.file.id,
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
doIt() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.removeFile(input).then((res) => {
|
||||
this.emit("removed", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Delete File ${input.file.name}</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<p class="color-danger">
|
||||
It might take up to an hour for it to be removed from CDN caches if it's been recently loaded.
|
||||
</p>
|
||||
|
||||
<button disabled=state.loading on-click("doIt")>Remove</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,6 @@
|
||||
<include("../layout", {title: "Data", site: "data"})>
|
||||
<@body>
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<files-page user=input.user privates=input.privates publics=input.publics selected=input.selected />
|
||||
</@body>
|
||||
</include>
|
||||
Generated
+162
-38
@@ -23,6 +23,95 @@
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"lasso": "3.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"events": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
||||
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA=="
|
||||
},
|
||||
"lasso": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/lasso/-/lasso-3.2.6.tgz",
|
||||
"integrity": "sha512-Ln4i6XepzXJ8e/+PTxsB+vdZHXbN0wIzepT6Ygwvxtl37Qm4HWLy2VnkVbSXG2gV2XJmC/JP0acg0ShPYKa/nA==",
|
||||
"requires": {
|
||||
"app-root-dir": "^1.0.2",
|
||||
"assert": "^1.4.1",
|
||||
"babel-code-frame": "^6.26.0",
|
||||
"browser-refresh-client": "^1.1.4",
|
||||
"buffer": "^5.1.0",
|
||||
"clean-css": "^4.1.11",
|
||||
"clone": "^2.1.1",
|
||||
"complain": "^1.2.0",
|
||||
"escodegen": "^1.10.0",
|
||||
"esprima": "^4.0.0",
|
||||
"estraverse": "^4.2.0",
|
||||
"events": "^3.0.0",
|
||||
"glob": "^7.1.2",
|
||||
"image-size": "^0.6.3",
|
||||
"lasso-caching-fs": "^1.0.2",
|
||||
"lasso-loader": "^3.0.2",
|
||||
"lasso-modules-client": "^2.0.5",
|
||||
"lasso-package-root": "^1.0.1",
|
||||
"lasso-resolve-from": "^1.2.0",
|
||||
"marko": "^4.10.0",
|
||||
"mime": "^2.3.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"path-browserify": "1.0.0",
|
||||
"pify": "^3.0.0",
|
||||
"process": "^0.11.10",
|
||||
"property-handlers": "^1.1.1",
|
||||
"raptor-async": "^1.1.3",
|
||||
"raptor-cache": "^2.0.4",
|
||||
"raptor-css-parser": "^1.1.5",
|
||||
"raptor-detect": "^1.0.1",
|
||||
"raptor-logging": "^1.1.3",
|
||||
"raptor-objects": "^1.0.2",
|
||||
"raptor-polyfill": "^1.0.2",
|
||||
"raptor-promises": "^1.0.3",
|
||||
"raptor-regexp": "^1.0.1",
|
||||
"raptor-strings": "^1.0.2",
|
||||
"raptor-util": "^3.2.0",
|
||||
"resolve-from": "^4.0.0",
|
||||
"semver": "^5.5.0",
|
||||
"send": "^0.16.2",
|
||||
"stream-browserify": "^2.0.1",
|
||||
"string_decoder": "^1.1.1",
|
||||
"strip-json-comments": "^2.0.1",
|
||||
"through": "^2.3.8",
|
||||
"uglify-js": "^3.4.0",
|
||||
"url": "^0.11.0",
|
||||
"util": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
|
||||
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
|
||||
},
|
||||
"path-browserify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.0.tgz",
|
||||
"integrity": "sha512-Hkavx/nY4/plImrZPHRk2CL9vpOymZLgEbMNX1U0bjcBL7QN9wODxyx0yaMZURSQaUtSEvDrfAvxa9oPb0at9g=="
|
||||
},
|
||||
"raptor-util": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/raptor-util/-/raptor-util-3.2.0.tgz",
|
||||
"integrity": "sha1-I7DIA8jxrIocrmfZpjiLSRYcl1g="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||
"integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
@@ -1293,6 +1382,11 @@
|
||||
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@@ -1435,9 +1529,9 @@
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
|
||||
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
|
||||
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
|
||||
},
|
||||
"complain": {
|
||||
"version": "1.3.0",
|
||||
@@ -3011,9 +3105,9 @@
|
||||
"integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
|
||||
},
|
||||
"htmljs-parser": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-2.4.0.tgz",
|
||||
"integrity": "sha512-Vmc4hiSt5uqGc44JaBNuzzs5MPExjY+w5z1gWl+KaGxvdGIZ6LfDn+vHfJXT+SJ6E3RG8uMCr+0HyDEWqGN5iA==",
|
||||
"version": "2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-2.6.8.tgz",
|
||||
"integrity": "sha512-il0NrLbNyt29nZuciLbrRKRMBi8VVVjVpv/x37z9qyubQscsoMzcsw3Fp35QvPR9p2eurQ/TBKrfdZ+joDX/rw==",
|
||||
"requires": {
|
||||
"char-props": "^0.1.5",
|
||||
"complain": "^1.0.0"
|
||||
@@ -3767,9 +3861,9 @@
|
||||
}
|
||||
},
|
||||
"lasso": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/lasso/-/lasso-3.2.6.tgz",
|
||||
"integrity": "sha512-Ln4i6XepzXJ8e/+PTxsB+vdZHXbN0wIzepT6Ygwvxtl37Qm4HWLy2VnkVbSXG2gV2XJmC/JP0acg0ShPYKa/nA==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/lasso/-/lasso-3.3.1.tgz",
|
||||
"integrity": "sha512-hhqKMN/4Q5vbaE3As7NchFpjIJjWm1T27wKK/P0//QWxkTLgkTuYCof0qn69VyE8L7ubzSQ60kM2fT5s3Kbm2Q==",
|
||||
"requires": {
|
||||
"app-root-dir": "^1.0.2",
|
||||
"assert": "^1.4.1",
|
||||
@@ -3814,8 +3908,8 @@
|
||||
"stream-browserify": "^2.0.1",
|
||||
"string_decoder": "^1.1.1",
|
||||
"strip-json-comments": "^2.0.1",
|
||||
"terser": "^3.17.0",
|
||||
"through": "^2.3.8",
|
||||
"uglify-js": "^3.4.0",
|
||||
"url": "^0.11.0",
|
||||
"util": "^0.11.0"
|
||||
},
|
||||
@@ -3826,9 +3920,9 @@
|
||||
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA=="
|
||||
},
|
||||
"mime": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz",
|
||||
"integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg=="
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
|
||||
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
|
||||
},
|
||||
"path-browserify": {
|
||||
"version": "1.0.0",
|
||||
@@ -3846,9 +3940,9 @@
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.0.tgz",
|
||||
"integrity": "sha512-5n12uMzKCjvB2HPFHnbQSjaqAa98L5iIXmHrZCLavuZVe0qe/SJGbDGWlpaHk5lnBkWRDO+dRu1/PgmUYKPPTw==",
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||
"integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
@@ -3919,9 +4013,9 @@
|
||||
}
|
||||
},
|
||||
"lasso-marko": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/lasso-marko/-/lasso-marko-2.4.4.tgz",
|
||||
"integrity": "sha512-7Q0wNg0rQhdjezbUBmRw/W3dSa9NAqmm99oZhlVOss++OsCwLmPF7r870qMA5a3IUIX+ziOrZbCVHdWQdD/+XA==",
|
||||
"version": "2.4.7",
|
||||
"resolved": "https://registry.npmjs.org/lasso-marko/-/lasso-marko-2.4.7.tgz",
|
||||
"integrity": "sha512-fC882KkHB56zKHS/C2xicoDkDmWSNnglWhvgvSO8Lzm4XHNcSoLhZlxBkrcU+0eiGtT/jrC8/Yam4hDdYYuCzQ==",
|
||||
"requires": {
|
||||
"callbackify": "^1.1.0"
|
||||
}
|
||||
@@ -4089,15 +4183,16 @@
|
||||
"integrity": "sha1-1k1xPuzsVc5M/esyF1DswJniwtw="
|
||||
},
|
||||
"marko": {
|
||||
"version": "4.13.8",
|
||||
"resolved": "https://registry.npmjs.org/marko/-/marko-4.13.8.tgz",
|
||||
"integrity": "sha512-APfb/5KgDswm2fdxZBknxubEgNmXchmGZ1aCV1Brb8eGqHzd8I1xZzykvj1/mDU/bzhsAIYDV2yyC9Os7Ue9AQ==",
|
||||
"version": "4.18.13",
|
||||
"resolved": "https://registry.npmjs.org/marko/-/marko-4.18.13.tgz",
|
||||
"integrity": "sha512-1sFI4yfbOXxY/48lgunZgpoiOd86ZGuO42bshBiZqlkmcumlznnHknbBPKtq+CLq+LTXL2apW/XlUSWqAe09Rw==",
|
||||
"requires": {
|
||||
"app-module-path": "^2.2.0",
|
||||
"argly": "^1.0.0",
|
||||
"browser-refresh-client": "^1.0.0",
|
||||
"camelcase": "^5.0.0",
|
||||
"char-props": "~0.1.5",
|
||||
"complain": "^1.3.0",
|
||||
"complain": "^1.6.0",
|
||||
"deresolve": "^1.1.2",
|
||||
"escodegen": "^1.8.1",
|
||||
"esprima": "^4.0.0",
|
||||
@@ -4105,7 +4200,7 @@
|
||||
"events": "^1.0.2",
|
||||
"events-light": "^1.0.0",
|
||||
"he": "^1.1.0",
|
||||
"htmljs-parser": "^2.3.2",
|
||||
"htmljs-parser": "^2.6.7",
|
||||
"lasso-caching-fs": "^1.0.1",
|
||||
"lasso-modules-client": "^2.0.4",
|
||||
"lasso-package-root": "^1.0.1",
|
||||
@@ -4123,9 +4218,17 @@
|
||||
"simple-sha1": "^2.1.0",
|
||||
"strip-json-comments": "^2.0.1",
|
||||
"try-require": "^1.2.1",
|
||||
"warp10": "^1.0.0"
|
||||
"warp10": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"complain": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/complain/-/complain-1.6.0.tgz",
|
||||
"integrity": "sha512-9oBfSEfxveaNmo2eSp/vEPkaBVxUhiJTZVgGYayzBchSAXQM6CK1PAQeV5ICShnSgfT+biYzrN7egKwwX+HkCw==",
|
||||
"requires": {
|
||||
"error-stack-parser": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"raptor-util": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/raptor-util/-/raptor-util-3.2.0.tgz",
|
||||
@@ -5234,9 +5337,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.16.2",
|
||||
@@ -5340,9 +5443,9 @@
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-sha1": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-sha1/-/simple-sha1-2.1.1.tgz",
|
||||
"integrity": "sha512-pFMPd+I/lQkpf4wFUeS/sED5IqdIG1lUlrQviBMV4u4mz8BRAcB5fvUx5Ckfg3kBigEglAjHg7E9k/yy2KlCqA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-sha1/-/simple-sha1-2.1.2.tgz",
|
||||
"integrity": "sha512-TQl9rm4rdKAVmhO++sXAb8TNN0D6JAD5iyI1mqEPNpxUzTRrtm4aOG1pDf/5W/qCFihiaoK6uuL9rvQz1x1VKw==",
|
||||
"requires": {
|
||||
"rusha": "^0.8.1"
|
||||
}
|
||||
@@ -5714,6 +5817,27 @@
|
||||
"acorn-node": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "3.17.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz",
|
||||
"integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==",
|
||||
"requires": {
|
||||
"commander": "^2.19.0",
|
||||
"source-map": "~0.6.1",
|
||||
"source-map-support": "~0.5.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map-support": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
|
||||
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
@@ -5865,11 +5989,11 @@
|
||||
"integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.8",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.8.tgz",
|
||||
"integrity": "sha512-WatYTD84gP/867bELqI2F/2xC9PQBETn/L+7RGq9MQOA/7yFBNvY1UwXqvtILeE6n0ITwBXxp34M0/o70dzj6A==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
|
||||
"integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
|
||||
"requires": {
|
||||
"commander": "~2.17.1",
|
||||
"commander": "~2.20.0",
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
@@ -6060,9 +6184,9 @@
|
||||
"integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw=="
|
||||
},
|
||||
"warp10": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/warp10/-/warp10-1.3.6.tgz",
|
||||
"integrity": "sha512-7cijX+NprqPV+UGUZXZw1I15JFHPqoy65tNVvP6cL43Vlanpcm8hBYoQTuDYUHa5x90Bct4gHhRtqqOOkLhQkw=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/warp10/-/warp10-2.0.1.tgz",
|
||||
"integrity": "sha512-nS1KG2RCVTWepfSitZKzL+HYiqqMJag06KtOJF/LwklrJWeJYRWHjMYpd6GXMt09ot+HIEj+Y4dmQNnNNE0Bjg=="
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "2.0.4",
|
||||
|
||||
+3
-3
@@ -30,15 +30,15 @@
|
||||
"irc-caret-notation": "^1.0.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"lasso": "^3.2.6",
|
||||
"lasso": "^3.3.1",
|
||||
"lasso-babel-transform": "^1.0.2",
|
||||
"lasso-less": "^3.0.1",
|
||||
"lasso-marko": "^2.4.4",
|
||||
"lasso-marko": "^2.4.7",
|
||||
"less": "^3.8.1",
|
||||
"markdown-it": "^8.4.2",
|
||||
"markdown-it-container": "^2.0.0",
|
||||
"markdown-it-regexp": "^0.4.0",
|
||||
"marko": "^4.13.8",
|
||||
"marko": "^4.18.13",
|
||||
"moment": "^2.22.2",
|
||||
"moment-timezone": "^0.5.23",
|
||||
"passport": "^0.4.0",
|
||||
|
||||
@@ -7,7 +7,7 @@ const changesTemplate = require("../../marko/page/data/changes.marko")
|
||||
|
||||
router.get("/", async(req, res) => {
|
||||
res.markoAsync(changesTemplate, {
|
||||
changes: changesApi.list({limit: 2000}),
|
||||
changes: changesApi.list({limit: 1000}),
|
||||
selected: {changes: true},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const express = require("express")
|
||||
const router = express.Router()
|
||||
|
||||
const {generateToken} = require("../graphql")
|
||||
const {fileApi} = require("../../rpdata/api/File")
|
||||
|
||||
const filesTemplate = require("../../marko/page/data/files.marko")
|
||||
|
||||
router.get("/", async(req, res) => {
|
||||
let privates = []
|
||||
if (res.locals.user) {
|
||||
privates = fileApi.list({public: false}, {token: generateToken(res.locals.user.name)})
|
||||
}
|
||||
|
||||
res.markoAsync(filesTemplate, {
|
||||
privates: privates,
|
||||
publics: fileApi.list(),
|
||||
selected: {files: true},
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
+6
-12
@@ -3,16 +3,10 @@ const express = require("express")
|
||||
const jwt = require("jsonwebtoken")
|
||||
|
||||
const config = require("../config")
|
||||
const { query } = require("../rpdata/client")
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.post("/", (req, res) => {
|
||||
if (!req.header("Content-Type").startsWith("application/json")) {
|
||||
res.status(400).json({errors: [{message: "Incorrect input type, expected application/json"}]})
|
||||
return
|
||||
}
|
||||
|
||||
const user = res.locals.user
|
||||
const permissions = (req.header("X-Permissions") || "").split(",").filter(t => t != "" && t != "undefined" && t != "null")
|
||||
let authorization = req.header("Authorization")
|
||||
@@ -20,17 +14,17 @@ router.post("/", (req, res) => {
|
||||
authorization = ""
|
||||
}
|
||||
|
||||
if (!authorization && permissions.length > 0 && user.loggedIn) {
|
||||
if (!authorization && user.loggedIn) {
|
||||
authorization = `Bearer ${generateToken(user.name, permissions)}`
|
||||
}
|
||||
|
||||
fetch(config.graphqlEndpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": req.header("Content-Type"),
|
||||
"Authorization": authorization,
|
||||
},
|
||||
body: JSON.stringify(req.body),
|
||||
body: req,
|
||||
credentials: "include",
|
||||
}).then(fetchRes => {
|
||||
res.setHeader("Content-Type", fetchRes.headers.get("Content-Type"))
|
||||
@@ -51,8 +45,8 @@ router.use("/", proxy(config.graphqlEndpoint, {ws: true}))
|
||||
* @param {string} user
|
||||
* @param {string[]} permissions
|
||||
*/
|
||||
function generateToken(user, permissions) {
|
||||
return jwt.sign({user, permissions, exp: Math.floor((Date.now() / 1000) + 1200)}, config.backend.secret, {header: {kid: config.backend.kid}})
|
||||
function generateToken(user) {
|
||||
return jwt.sign({user, exp: Math.floor((Date.now() / 1000) + 1200)}, config.backend.secret, {header: {kid: config.backend.kid}})
|
||||
}
|
||||
|
||||
module.exports = router
|
||||
module.exports = {router, generateToken}
|
||||
@@ -48,6 +48,11 @@ class ChangesAPI {
|
||||
objects {
|
||||
type: __typename
|
||||
|
||||
...on File {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
...on Character {
|
||||
id
|
||||
name
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
const {query, queryForm} = require("../client")
|
||||
|
||||
class File {
|
||||
/**
|
||||
* @param {{id:string,kind:string,time:Date|string|number,public:boolean,name:string,mimeType:string,size:number,author:string,url:string}} data
|
||||
*/
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.kind = data.kind
|
||||
this.time = new Date(data.time)
|
||||
this.public = data.public
|
||||
this.name = data.name
|
||||
this.mimeType = data.mimeType
|
||||
this.size = data.size
|
||||
this.author = data.author
|
||||
this.url = data.url
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FileAPI {
|
||||
/**
|
||||
* Call `characters(filter)` query
|
||||
*
|
||||
* @param {{public:boolean,mimeTypes:string[]}} filter
|
||||
* @returns {Promise<File[]>}
|
||||
*/
|
||||
list(filter = {public: true}, opts = {}) {
|
||||
return query(`
|
||||
query ListFiles($filter: FilesFilter!) {
|
||||
files(filter:$filter) {
|
||||
id
|
||||
kind
|
||||
time
|
||||
public
|
||||
name
|
||||
mimeType
|
||||
size
|
||||
author
|
||||
url
|
||||
}
|
||||
}
|
||||
`, {filter}, opts).then(({files}) => {
|
||||
return files.map(d => new File(d))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file
|
||||
*
|
||||
* @param {{public:boolean, file:File, name:string}} input
|
||||
*/
|
||||
uploadFile(input) {
|
||||
return queryForm(`
|
||||
mutation UploadFile($input: FileUploadInput!) {
|
||||
uploadFile(input:$input) {
|
||||
id
|
||||
kind
|
||||
time
|
||||
public
|
||||
name
|
||||
mimeType
|
||||
size
|
||||
author
|
||||
url
|
||||
}
|
||||
}
|
||||
`, {input}).then(({uploadFile}) => {
|
||||
return new File(uploadFile)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a file
|
||||
*
|
||||
* @param {{id: string, public:boolean, name:string}} input
|
||||
*/
|
||||
editFile(input) {
|
||||
return queryForm(`
|
||||
mutation EditFile($input: FileEditInput!) {
|
||||
editFile(input:$input) {
|
||||
id
|
||||
public
|
||||
name
|
||||
}
|
||||
}
|
||||
`, {input}).then(({editFile}) => {
|
||||
return editFile
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a file
|
||||
*
|
||||
* @param {{id: string, public:boolean, name:string}} input
|
||||
*/
|
||||
removeFile(input) {
|
||||
return queryForm(`
|
||||
mutation RemoveFIle($input: FileRemoveInput!) {
|
||||
removeFile(input:$input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`, {input}).then(({removeFile}) => {
|
||||
return removeFile
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { File, fileApi: new FileAPI() }
|
||||
+64
-3
@@ -10,13 +10,13 @@ const compressQuery = require("graphql-query-compress")
|
||||
* @param {{[x:string]: any}} variables
|
||||
* @param {{operationName:string, token: string, permissions: string[]}} options
|
||||
*/
|
||||
function query(query, variables = {}, options = {}) {
|
||||
function query(query, variables = {}, options = {}) {
|
||||
return fetch(config.graphqlEndpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": options.token ? `Bearer ${options.token}` : null,
|
||||
"X-Permissions": options.permissions ? options.permissions.join(",") : null,
|
||||
//"X-Permissions": options.permissions ? options.permissions.join(",") : null,
|
||||
},
|
||||
body: JSON.stringify({query: query.length > 256 ? compressQuery(query || "") : query, variables, operationName: options.operationName}),
|
||||
credentials: "include",
|
||||
@@ -31,6 +31,67 @@ function query(query, variables = {}, options = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {string} query GQL Query
|
||||
* @param {Object} variables Variables
|
||||
* @param {{token?:string,permissions?:string[]}} options
|
||||
*/
|
||||
function queryForm(query, variables = {}, options = {}) {
|
||||
const map = {}
|
||||
const files = []
|
||||
|
||||
function findFiles(prefix, data) {
|
||||
for (const key in data) {
|
||||
if (!data.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
const value = data[key]
|
||||
|
||||
if (value instanceof File) {
|
||||
map[files.length] = [`${prefix}.${key}`]
|
||||
files.push(value)
|
||||
} else if (typeof(value) === 'object') {
|
||||
data[key] = findFiles(`${prefix}.${key}`, Object.assign({}, value))
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
const operations = {
|
||||
query: query,
|
||||
variables: findFiles("variables", Object.assign({}, variables)),
|
||||
}
|
||||
|
||||
const form = new FormData()
|
||||
form.append("operations", JSON.stringify(operations))
|
||||
form.append("map", JSON.stringify(map))
|
||||
for (let i = 0; i < files.length; ++i) {
|
||||
form.append(i.toString(), files[i])
|
||||
}
|
||||
|
||||
console.log(form)
|
||||
|
||||
return fetch(config.graphqlEndpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": options.token ? `Bearer ${options.token}` : null,
|
||||
"X-Permissions": options.permissions ? options.permissions.join(",") : null,
|
||||
},
|
||||
body: form,
|
||||
credentials: "include",
|
||||
}).then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
if (json.errors != null && json.errors.length > 0) {
|
||||
return Promise.reject(json.errors)
|
||||
}
|
||||
|
||||
return json.data
|
||||
})
|
||||
}
|
||||
|
||||
class Subscription extends EventEmitter {
|
||||
constructor(query, variables) {
|
||||
super()
|
||||
@@ -115,4 +176,4 @@ class Subscription extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { query, Subscription }
|
||||
module.exports = { query, queryForm, Subscription }
|
||||
@@ -69,6 +69,12 @@ app.use(session)
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
app.use(require("./middleware/locals"))
|
||||
|
||||
// Proxy (does not want all middleware)
|
||||
app.use("/graphql", require("./routes/graphql").router)
|
||||
app.use("/playground", proxy(config.playgroundEndpoint))
|
||||
|
||||
// The rest of the middleware
|
||||
app.use(bodyParser.json({limit: "1mb"}))
|
||||
app.use(bodyParser.text({limit: "256kb"}))
|
||||
|
||||
@@ -79,10 +85,6 @@ app.use(lassoMiddleware.serveStatic())
|
||||
// Authentication
|
||||
app.use("/auth", require("./routes/auth"))
|
||||
|
||||
// API Proxy
|
||||
app.use("/graphql", require("./routes/graphql"))
|
||||
app.use("/playground", proxy(config.playgroundEndpoint))
|
||||
|
||||
// Global css doesn't work with prebuild.
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
app.use("/hax/global.css", require("./routes/globalcss"))
|
||||
@@ -108,6 +110,7 @@ app.use("/data/", require("./routes/data"))
|
||||
app.use("/data/characters/", require("./routes/data/characters"))
|
||||
app.use("/data/channels/", require("./routes/data/channels"))
|
||||
app.use("/data/changes/", require("./routes/data/changes"))
|
||||
app.use("/data/files/", require("./routes/data/files"))
|
||||
|
||||
// Entry point
|
||||
app.get("/", function(req, res) {
|
||||
|
||||
Reference in New Issue
Block a user