Comments (5)
Here's the resurrection diff:
diff --git a/main.js b/main.js
index cc3a0da..e38d7cc 100644
--- a/main.js
+++ b/main.js
@@ -29,9 +29,13 @@ class DatasetteServer {
this.process = null;
}
async startOrRestart() {
- const datasette_bin = await this.ensureDatasetteInstalled();
+ const python_bin = await this.ensureDatasetteInstalled();
const args = [
+ "-u", // Unbuffered, to ensure process.stdin gets data
+ "-m",
+ "datasette",
"--memory",
+ "--root",
"--port",
this.port,
"--version-note",
@@ -40,12 +44,30 @@ class DatasetteServer {
if (this.process) {
this.process.kill();
}
+ const re = new RegExp('.*(http://[^/]+/-/auth-token\\?token=\\w+).*');
+ let serverStarted = false;
+ let authURL = null;
return new Promise((resolve, reject) => {
- const process = cp.spawn(datasette_bin, args);
+ const process = cp.spawn(python_bin, args, {stdio: 'pipe'});
this.process = process;
+ process.stdout.on("data", (data) => {
+ console.log("stdout", data.toString());
+ const m = re.exec(data);
+ if (m) {
+ authURL = m[1];
+ if (serverStarted) {
+ resolve(authURL);
+ }
+ }
+ });
process.stderr.on("data", (data) => {
+ console.log("stderr", data.toString());
if (/Uvicorn running/.test(data)) {
- resolve(`http://localhost:${this.port}/`);
+ console.log("Uvicorn is running");
+ serverStarted = true;
+ if (authURL) {
+ resolve(authURL);
+ }
}
});
this.process.on("error", (err) => {
@@ -76,7 +98,7 @@ class DatasetteServer {
const venv_dir = path.join(datasette_app_dir, "venv");
const datasette_binary = path.join(venv_dir, "bin", "datasette");
if (fs.existsSync(datasette_binary)) {
- return datasette_binary;
+ return path.join(venv_dir, "bin", "python3.9");
}
if (!fs.existsSync(datasette_app_dir)) {
await mkdir(datasette_app_dir);
@@ -91,7 +113,7 @@ class DatasetteServer {
"datasette-app-support>=0.1.2",
]);
await new Promise((resolve) => setTimeout(resolve, 500));
- return datasette_binary;
+ return path.join(venv_dir, "bin", "python3.9");
}
}
from datasette-app.
Documentation: https://www.electronjs.org/docs/api/cookies
It looks like cookies are per-browser-window by default, but you can establish a session and pass session: thatSession
to those browser windows when you create them.
from datasette-app.
I'm having enormous trouble fixing this. It may be a bug? electron/electron#7268
Here's my most recent attempt that didn't work:
diff --git a/main.js b/main.js
index cc3a0da..227849a 100644
--- a/main.js
+++ b/main.js
@@ -1,4 +1,11 @@
-const { app, Menu, BrowserWindow, dialog, shell } = require("electron");
+const {
+ app,
+ Menu,
+ BrowserWindow,
+ dialog,
+ session,
+ shell,
+} = require("electron");
const request = require("electron-request");
const path = require("path");
const cp = require("child_process");
@@ -11,15 +18,21 @@ const util = require("util");
const execFile = util.promisify(cp.execFile);
const mkdir = util.promisify(fs.mkdir);
-function postConfigure(mainWindow) {
- mainWindow.webContents.on("will-navigate", function (event, reqUrl) {
+function postConfigure(win) {
+ win.webContents.on("will-navigate", function (event, reqUrl) {
let requestedHost = new url.URL(reqUrl).host;
- let currentHost = new url.URL(mainWindow.webContents.getURL()).host;
+ let currentHost = new url.URL(win.webContents.getURL()).host;
if (requestedHost && requestedHost != currentHost) {
event.preventDefault();
shell.openExternal(reqUrl);
}
});
+ win.on('did-start-navigation', function() {
+ session.defaultSession.cookies.flushStore();
+ });
+ win.on('did-navigate', function() {
+ session.defaultSession.cookies.flushStore();
+ });
}
class DatasetteServer {
@@ -29,9 +42,13 @@ class DatasetteServer {
this.process = null;
}
async startOrRestart() {
- const datasette_bin = await this.ensureDatasetteInstalled();
+ const python_bin = await this.ensureDatasetteInstalled();
const args = [
+ "-u", // Unbuffered, to ensure process.stdin gets data
+ "-m",
+ "datasette",
"--memory",
+ "--root",
"--port",
this.port,
"--version-note",
@@ -40,12 +57,30 @@ class DatasetteServer {
if (this.process) {
this.process.kill();
}
+ const re = new RegExp(".*(http://[^/]+/-/auth-token\\?token=\\w+).*");
+ let serverStarted = false;
+ let authURL = null;
return new Promise((resolve, reject) => {
- const process = cp.spawn(datasette_bin, args);
+ const process = cp.spawn(python_bin, args, { stdio: "pipe" });
this.process = process;
+ process.stdout.on("data", (data) => {
+ console.log("stdout", data.toString());
+ const m = re.exec(data);
+ if (m) {
+ authURL = m[1].replace('auth-token', 'auth-token-persistent');
+ if (serverStarted) {
+ resolve(authURL);
+ }
+ }
+ });
process.stderr.on("data", (data) => {
+ console.log("stderr", data.toString());
if (/Uvicorn running/.test(data)) {
- resolve(`http://localhost:${this.port}/`);
+ console.log("Uvicorn is running");
+ serverStarted = true;
+ if (authURL) {
+ resolve(authURL);
+ }
}
});
this.process.on("error", (err) => {
@@ -76,7 +111,7 @@ class DatasetteServer {
const venv_dir = path.join(datasette_app_dir, "venv");
const datasette_binary = path.join(venv_dir, "bin", "datasette");
if (fs.existsSync(datasette_binary)) {
- return datasette_binary;
+ return path.join(venv_dir, "bin", "python3.9");
}
if (!fs.existsSync(datasette_app_dir)) {
await mkdir(datasette_app_dir);
@@ -91,7 +126,7 @@ class DatasetteServer {
"datasette-app-support>=0.1.2",
]);
await new Promise((resolve) => setTimeout(resolve, 500));
- return datasette_binary;
+ return path.join(venv_dir, "bin", "python3.9");
}
}
@@ -115,12 +150,16 @@ function windowOpts() {
let opts = {
width: 800,
height: 600,
+ webPreferences: {
+ partition: "persist:sharecookies"
+ }
};
if (BrowserWindow.getFocusedWindow()) {
const pos = BrowserWindow.getFocusedWindow().getPosition();
opts.x = pos[0] + 22;
opts.y = pos[1] + 22;
}
+ console.log(opts);
return opts;
}
@@ -130,9 +169,8 @@ function createWindow() {
let mainWindow = null;
mainWindow = new BrowserWindow({
- width: 800,
- height: 600,
- show: false,
+ ...windowOpts(),
+ ...{ show: false },
});
mainWindow.loadFile("loading.html");
mainWindow.once("ready-to-show", () => {
@@ -293,11 +331,19 @@ function createWindow() {
},
],
},
+ {
+ label: 'Debug',
+ submenu: [{
+ label: 'Open DevTools',
+ click() {
+ BrowserWindow.getFocusedWindow().webContents.openDevTools();
+ }
+ }]
+ }
]);
Menu.setApplicationMenu(menu);
}
);
- // mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
from datasette-app.
I also modified datasette-app-support
to set those cookies with an expiry date so they wouldn't be treated as session cookies - diff for that is:
diff --git a/datasette_app_support/__init__.py b/datasette_app_support/__init__.py
index 4a97927..e5c01cd 100644
--- a/datasette_app_support/__init__.py
+++ b/datasette_app_support/__init__.py
@@ -1,10 +1,11 @@
from datasette.database import Database
-from datasette.utils.asgi import Response
+from datasette.utils.asgi import Response, Forbidden
from datasette.utils import sqlite3
from datasette import hookimpl
import json
import os
import pathlib
+import secrets
@hookimpl
@@ -51,6 +52,26 @@ async def open_database_file(request, datasette):
return Response.json({"ok": True})
+async def auth_token_persistent(request, datasette):
+ token = request.args.get("token") or ""
+ if not datasette._root_token:
+ raise Forbidden("Root token has already been used")
+ if secrets.compare_digest(token, datasette._root_token):
+ datasette._root_token = None
+ response = Response.redirect(datasette.urls.instance())
+ response.set_cookie(
+ "ds_actor", datasette.sign({"a": {"id": "root"}}, "actor"),
+ expires=364 * 24 * 60 * 60
+ )
+ print(response._set_cookie_headers)
+ return response
+ else:
+ raise Forbidden("Invalid token")
+
+
@hookimpl
def register_routes():
- return [(r"^/-/open-database-file$", open_database_file)]
+ return [
+ (r"^/-/open-database-file$", open_database_file),
+ (r"^/-/auth-token-persistent$", auth_token_persistent)
+ ]
from datasette-app.
I've spent enough time on this - I'm going to assume that each BrowserWindow
cannot share cookies (or there are bugs that prevent them from doing that) and consider an alternative approach in a separate ticket.
from datasette-app.
Related Issues (20)
- Create `brew install --cask datasette-app` for macOS HOT 1
- Publish ("export") to web with authentication and metadata (minimum feature set) HOT 4
- Can't "go back" after choosing view as JSON or CSV and saving/storing queries in underlying SQLite
- Allow for load / open of data from URL where CSV (or JSON?) is compressed
- Allow handling of larger CSV files e.g. CFPB complaints data HOT 1
- Switch to Playwright for automated tests HOT 28
- Failing to render in retina resolution when running on my MacBook Pro (macOS Catalina) HOT 1
- Research faster launch times
- Mechanism for plugins that need a specific DB HOT 3
- "Loading..." hangs on launch after parsing "tag_name" in a plugin HOT 6
- npm test is failing HOT 2
- All sorts of features not working due to bad HTTP requests HOT 9
- London Fire Brigade demo breaks HOT 7
- Import CSV from URL should show errors
- New Playwright tests should exercise key features
- Ability to use templates
- The icon looks blurry in the alt-tab window
- Loading... hangs on launch with packaging.version.InvalidVersion HOT 6
- "About Datasette" button fails to open information window HOT 2
- Unable to load preload script HOT 14
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from datasette-app.