147 lines
3.7 KiB
JavaScript
147 lines
3.7 KiB
JavaScript
const { ipcMain, app, BrowserWindow } = require("electron")
|
|
const gemini = require("gemini-server")
|
|
const fs = require("fs")
|
|
const url = require("url")
|
|
const path = require("path")
|
|
|
|
const sigils_dir = "sigils"
|
|
const gemini_port = 1965
|
|
|
|
const electron_options = {
|
|
show: false,
|
|
webPreferences: {
|
|
sandbox: true,
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
enableRemoteModule: false,
|
|
webgl: false,
|
|
disableDialogs: true,
|
|
preload: path.join(__dirname, "preload.js"),
|
|
},
|
|
}
|
|
|
|
const server_options = {
|
|
cert: fs.readFileSync('tls/cert.pem'),
|
|
key: fs.readFileSync('tls/key.pem')
|
|
}
|
|
|
|
const server = gemini(server_options)
|
|
|
|
|
|
async function handle_request(request, response) {
|
|
if (!request.query) {
|
|
response.data("Please supply a url in the query string.")
|
|
return
|
|
}
|
|
const target_url = decodeURIComponent(request.query)
|
|
const win = new BrowserWindow(electron_options)
|
|
|
|
const load_timeout_ms = 2000
|
|
const exec_timeout_ms = 2000
|
|
|
|
async function timeout(timeout_ms, reason = "") {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(
|
|
reject,
|
|
timeout_ms,
|
|
reason + " timed out after " + timeout_ms + " miliseconds"
|
|
)
|
|
})
|
|
}
|
|
|
|
var error
|
|
var current_url
|
|
var page_sigil_name
|
|
var page_sigil
|
|
var data = ""
|
|
var available_sigils = fs.readdirSync(sigils_dir)
|
|
|
|
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
|
|
return callback(false)
|
|
})
|
|
|
|
win.webContents.setWindowOpenHandler(() => {
|
|
console.log("Blocked a popup request")
|
|
return {action: 'deny'}
|
|
})
|
|
|
|
console.log(`Got request for "${target_url}"`)
|
|
|
|
await Promise.race([
|
|
win.loadURL(target_url)
|
|
.catch((err) => { // there was an error loading the page
|
|
if (err.errno !== -3) {
|
|
error = err.message
|
|
} }),
|
|
timeout(load_timeout_ms)
|
|
]).catch(() => { // loading timed out
|
|
win.webContents.stop()
|
|
})
|
|
|
|
|
|
try {
|
|
current_url = new URL(win.webContents.getURL())
|
|
} catch (err) {
|
|
error = err.code
|
|
response.data(error)
|
|
win.destroy()
|
|
return
|
|
}
|
|
|
|
for (let sigil_name of available_sigils) {
|
|
if (current_url.host === sigil_name) {
|
|
page_sigil_name = sigil_name
|
|
console.log(`Using sigil "${sigil_name}"`)
|
|
break
|
|
} else if (
|
|
sigil_name[0] === "*"
|
|
&& current_url.host.endsWith(sigil_name.replace("*.", ""))
|
|
){
|
|
page_sigil_name = sigil_name
|
|
console.log(`Using sigil "${sigil_name}"`)
|
|
break
|
|
}
|
|
}
|
|
|
|
if (! page_sigil_name) {
|
|
page_sigil = fs.readFileSync(sigils_dir + "/default")
|
|
} else {
|
|
page_sigil = fs.readFileSync(sigils_dir + "/" + page_sigil_name)
|
|
}
|
|
|
|
ipcMain.on("page-gemtext", (event, arg) => {
|
|
data += arg
|
|
})
|
|
|
|
await Promise.race([
|
|
win.webContents.executeJavaScript(page_sigil).catch(
|
|
(err) => error = err
|
|
),
|
|
|
|
timeout(exec_timeout_ms, "Script execution")
|
|
]).catch((err) => error = err)
|
|
|
|
ipcMain.removeAllListeners()
|
|
|
|
win.destroy()
|
|
|
|
if (error) {
|
|
response.data(error)
|
|
console.log(`Error handling request for "${target_url}": ${error}`)
|
|
} else {
|
|
response.data(data, mimeType="text/gemini")
|
|
console.log(`Handled request for "${target_url}"`)
|
|
}
|
|
}
|
|
|
|
app.on('window-all-closed', () => {
|
|
;
|
|
})
|
|
|
|
app.whenReady().then(() => {
|
|
server.on("*", handle_request)
|
|
server.listen(() => {
|
|
console.log(`listening on localhost:${gemini_port}`)
|
|
}, gemini_port)
|
|
});
|