Implement admin file manager, lots of cleanups

This commit is contained in:
John Bintz 2020-04-14 22:19:34 -04:00
parent bc5b2ccc5d
commit 48ad2e4fb7
13 changed files with 5964 additions and 294 deletions

12
.cloudcmd.menu.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
'G - Commit all changes and re-run Hugo': async({ CloudCmd }) => {
const response = await fetch('/commit')
const responseJSON = await response.json()
if (responseJSON.ok) {
window.location.href = '/'
} else {
console.error(responseJSON.error)
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -1,3 +1,8 @@
# V0.69.0~2020-04-14
* Upgrade Hugo.
* Replace Caddy with Cloud Commander for admin file management.
# V0.20-sandstorm2 # V0.20-sandstorm2
* Upgrade Hugo. * Upgrade Hugo.

View File

@ -22,4 +22,7 @@ fi
cd /opt/app cd /opt/app
cp post-receive /var/git/.git/hooks cp post-receive /var/git/.git/hooks
# cloudcmd wants this in your home directory
ln -sf /opt/app/.cloudcmd.menu.js /var/.cloudcmd.menu.js
npm start npm start

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@ const pkgdef :Spk.PackageDefinition = (
appVersion = 4, # Increment this for every release. appVersion = 4, # Increment this for every release.
appMarketingVersion = (defaultText = "0.69.0-sandstorm"), appMarketingVersion = (defaultText = "0.69.0~2020-04-14"),
# Human-readable representation of appVersion. Should match the way you # Human-readable representation of appVersion. Should match the way you
# identify versions of your app in documentation and marketing. # identify versions of your app in documentation and marketing.
@ -123,8 +123,8 @@ const pkgdef :Spk.PackageDefinition = (
# Screenshots to use for marketing purposes. Examples below. # Screenshots to use for marketing purposes. Examples below.
# Sizes are given in device-independent pixels, so if you took these # Sizes are given in device-independent pixels, so if you took these
# screenshots on a Retina-style high DPI screen, divide each dimension by two. # screenshots on a Retina-style high DPI screen, divide each dimension by two.
(width = 1366, height = 672, png = embed "app-graphics/screenshot1.png"), (width = 758, height = 718, png = embed "app-graphics/site_setup.png"),
(width = 1366, height = 672, png = embed "app-graphics/screenshot2.png"), (width = 761, height = 861, png = embed "app-graphics/admin_interface.png"),
], ],
changeLog = (defaultText = embed "changelog.md"), changeLog = (defaultText = embed "changelog.md"),
# Documents the history of changes in Github-flavored markdown format (with the same restrictions # Documents the history of changes in Github-flavored markdown format (with the same restrictions
@ -143,7 +143,8 @@ const pkgdef :Spk.PackageDefinition = (
( sourcePath = "/", # Then search the system root directory. ( sourcePath = "/", # Then search the system root directory.
hidePaths = [ "home", "proc", "sys", hidePaths = [ "home", "proc", "sys",
"etc/hosts", "etc/host.conf", "etc/hosts", "etc/host.conf",
"etc/passwd", "etc/nsswitch.conf", "etc/resolv.conf" ] "etc/passwd", "etc/nsswitch.conf", "etc/resolv.conf",
"opt/app/.git"]
# You probably don't want the app pulling files from these places, # You probably don't want the app pulling files from these places,
# so we hide them. Note that /dev, /var, and /tmp are implicitly # so we hide them. Note that /dev, /var, and /tmp are implicitly
# hidden because Sandstorm itself provides them. # hidden because Sandstorm itself provides them.
@ -245,6 +246,7 @@ const myCommand :Spk.Manifest.Command = (
(key = "SANDSTORM", value = "1"), (key = "SANDSTORM", value = "1"),
(key = "HOME", value = "/var"), (key = "HOME", value = "/var"),
(key = "NODE_ENV", value = "production"), (key = "NODE_ENV", value = "production"),
(key = "THREAD_IT_COUNT", value = "0"),
# Export SANDSTORM=1 into the environment, so that apps running within Sandstorm # Export SANDSTORM=1 into the environment, so that apps running within Sandstorm
# can detect if $SANDSTORM="1" at runtime, switching UI and/or backend to use # can detect if $SANDSTORM="1" at runtime, switching UI and/or backend to use
# the app's Sandstorm-specific integration code. # the app's Sandstorm-specific integration code.

View File

@ -1,27 +1,12 @@
## V0 (2016-10-10) ## 0.69.0 (2020-04-14)
* Initial release.
## 0.69.0 (2020-04-10)
The "I can't stand Hugo 0.20.something anymore" release.
### Enhancements ### Enhancements
* Upgrade Hugo to 0.69.0 * Upgrade Hugo to 0.69.0.
* Remove Caddy & admin interface for now * Replace Caddy with [Cloud Commander](https://cloudcmd.io/) for
* This never quite worked right for me and direct support for web-based file management.
Hugo management was [dropped during Caddy 2 development](https://caddy.community/t/new-old-plugin-http-filebrowser/5103). * Update NodeJS to 10 and `yarn upgrade` all transitive dependencies.
Bringing back Caddy would require a bit more work than I'm able to put into
it for this release.
* Update NodeJS to 10 and `yarn upgrade` all transitive dependencies
### Workarounds ## V0 (2016-10-10)
* For some reason the `getPublicId` script causes some part of Cap'n Proto to * Initial release.
crash the grain when executed, specifically as the script is exiting.
I've tried everything I know to have it not do that, but ended up with a
terrible workaround: capture the stdout of the script to a file as it
executes and memoize the output based on the session ID parameter.
For my single-person Sandstorm server, this seems fine. I would love help
figuring this out.

View File

@ -21,4 +21,22 @@
td { td {
padding: 0 10 0 10; padding: 0 10 0 10;
} }
.button {
display: inline-block;
padding: 0.5rem;
border: solid #aaa 1px;
background-color: #eee;
color: #000;
border-radius: 3px;
margin: 0 1rem;
text-decoration: none;
cursor: pointer;
}
.button.disabled {
color: #555;
background-color: #aaa;
cursor: not-allowed;
}
</style> </style>

View File

@ -12,10 +12,11 @@
"babel-preset-stage-2": "^6.24.1", "babel-preset-stage-2": "^6.24.1",
"express": "^4.15.2", "express": "^4.15.2",
"git-http-backend": "^1.0.1", "git-http-backend": "^1.0.1",
"http-proxy": "^1.15.1",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"morgan": "^1.8.1", "morgan": "^1.8.1",
"nuxt": "^0.10.6" "nuxt": "^0.10.6",
"cloudcmd": "^14.3.10",
"socket.io": "^2.3.0"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^1.11.0" "nodemon": "^1.11.0"

View File

@ -57,12 +57,25 @@
<render-template <render-template
rpcId="gitPush" rpcId="gitPush"
:template="'git remote add origin ' + this.gitUrl + '\ngit push -fu origin master'"/> :template="'git remote add origin ' + this.gitUrl + '\ngit push -fu origin master'"/>
<h2>Admin Interface</h2> <h2>Upgrade notes from the 0.20 Sandstorm release:</h2>
<ul>
<li>
If you were using inline HTML in any Markdown files, enter the following into
your <code>config.toml</code> file:
<pre>
[markup.goldmark.renderer]
unsafe= true
</pre>
</li>
</ul>
<p> <p>
...had to be removed temporarily because Caddy 1 isn't recommended anymore and Be sure to <a href="https://gohugo.io/news/">read through the Hugo release notes</a>
<a target="_blank" rel="noopener" href="https://caddy.community/t/new-old-plugin-http-filebrowser/5103">Caddy 2 needs some work to get working well with Hugo</a>. if you notice any other odd behavior after upgrading.
<a target="_blank" rel="noopener" href="https://github.com/johnbintz/hugo-sandstorm">Want to help?</a>
</p> </p>
<h2>Admin Interface (<a href="https://cloudcmd.io/">Cloud Commander</a>)</h2>
<a :class="{disabled: !dirty}" class="button" @click="commitLocal">Commit &amp; publish local changes</a>
<a :class="{disabled: !dirty}" class="button" @click="deleteLocal">Delete local changes (git reset --hard)</a>
<iframe id="files" src="/admin" style="width: 100%; height: 800px; margin-top: 1rem"></iframe>
</div> </div>
</template> </template>
@ -77,7 +90,8 @@
domain: "", domain: "",
publicId: "", publicId: "",
isLoading: true, isLoading: true,
loadError: null loadError: null,
dirty: false
}), }),
computed: { computed: {
gitHost: () => { gitHost: () => {
@ -112,10 +126,38 @@
} finally { } finally {
this.isLoading = false this.isLoading = false
} }
setInterval(this.checkDirty, 5000)
}, },
head: { head: {
title: "Home" title: "Home"
}, },
methods: {
async commitLocal () {
if (!this.dirty) return
if (confirm('Are you sure?')) {
await fetch('/commit')
}
},
async deleteLocal () {
if (!this.dirty) return
if (confirm('Are you sure?')) {
await fetch('/reset-local')
document.getElementById('files').contentWindow.location.reload(true)
}
},
async checkDirty () {
const response = await fetch('/dirty')
const json = await response.json()
if (json.hasOwnProperty('dirty')) {
this.dirty = json.dirty
}
}
},
components: {RenderTemplate} components: {RenderTemplate}
} }

111
server.js
View File

@ -3,9 +3,8 @@ import logger from "morgan"
import fs from 'fs' import fs from 'fs'
import Express from "express" import Express from "express"
import gitBackend from "git-http-backend" import gitBackend from "git-http-backend"
// TODO: once Caddy 2 or something similar is in place, reinstate the import cloudcmd from "cloudcmd"
// admin proxy. import io from 'socket.io'
//import httpProxy from "http-proxy"
const spawn = require("child_process").spawn const spawn = require("child_process").spawn
const app = new Express() const app = new Express()
@ -78,12 +77,108 @@ app.use("/git", (req, res) => {
})).pipe(res) })).pipe(res)
}) })
//const proxy = httpProxy.createProxyServer({ const runCommand = (cmd, ...args) => {
// target: "http://127.0.0.1:8001/admin/", return new Promise((resolve, reject) => {
// changeOrigin: true const spawnCmd = spawn(cmd, args)
//})
// app.use("/admin/", (req, res) => proxy.web(req, res)) spawnCmd.stdout.on('data', (data) => {
console.log(data.toString())
})
spawnCmd.on('error', (err) => {
res.send(err)
reject(err)
})
spawnCmd.on('close', (code) => {
if (code === 0) {
resolve(true)
return true
} else {
reject(new Error(code))
}
})
})
}
app.get('/dirty', async (req, res) => {
const currentDir = process.cwd()
process.chdir('/var/git')
const spawnCmd = spawn('git', ['diff', '--exit-code'])
spawnCmd.on('close', (code) => {
res.json({ dirty: code !== 0 })
})
process.chdir(currentDir)
})
app.use('/commit', async (req, res) => {
const currentDir = process.cwd()
try {
process.chdir('/var/git')
await runCommand('git', 'add', '.')
await runCommand('git', 'commit', '-m', 'From admin')
process.chdir(currentDir)
await runCommand('/opt/app/post-receive')
res.json({ok: true})
} catch (e) {
res.send({error: e.msg})
} finally {
process.chdir(currentDir)
}
})
app.use('/reset-local', async (req, res) => {
const currentDir = process.cwd()
try {
process.chdir('/var/git')
await runCommand('git', 'reset', '--hard')
process.chdir(currentDir)
res.json({ok: true})
} catch (e) {
res.send({error: e.msg})
} finally {
process.chdir(currentDir)
}
})
const { createConfigManager, configPath } = cloudcmd
const socket = io.listen(server, { path: "/admin/socket.io"})
const cloudConfig = {
name: "Hugo admin",
root: "/var/git",
open: false,
prefix: "/admin",
console: false,
terminal: false,
oneFilePanel: true,
configDialog: false,
configAuth: false,
keysPanel: true,
}
const filePicker = {
data: { FilePicker: { key: 'key' } }
}
const cloudModules = { filePicker }
const configManager = createConfigManager({ configPath })
app.use("/admin", cloudcmd({
socket,
config: cloudConfig
}))
// Import and Set Nuxt.js options // Import and Set Nuxt.js options
let config = require("./nuxt.config.js") let config = require("./nuxt.config.js")

4440
yarn.lock

File diff suppressed because it is too large Load Diff