1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-01-26 18:48:15 +02:00

Merge pull request #52 from mattermost/single-user-token

Single user token
This commit is contained in:
Chen-I Lim 2021-02-11 10:58:53 -08:00 committed by GitHub
commit ac142e4a98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 250 additions and 139 deletions

96
.vscode/launch.json vendored
View File

@ -11,20 +11,36 @@
"mode": "debug",
"program": "${workspaceFolder}/server/main",
"cwd": "${workspaceFolder}"
},
{
"name": "Go: Test Current File",
"type": "go",
"request": "launch",
"mode": "test",
"remotePath": "",
"port": 8888,
"host": "127.0.0.1",
"program": "${file}",
"env": {},
"args": [],
"showLog": true
},
},
{
"name": "Go: Launch Windows App",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/win",
"cwd": "${workspaceFolder}/win/temp",
},
{
"name": "Go: Launch Linux App",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/linux",
"cwd": "${workspaceFolder}/linux/dist/focalboard-app",
},
{
"name": "Go: Test Current File",
"type": "go",
"request": "launch",
"mode": "test",
"remotePath": "",
"port": 8888,
"host": "127.0.0.1",
"program": "${file}",
"env": {},
"args": [],
"showLog": true
},
{
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
@ -34,30 +50,30 @@
],
"type": "pwa-node"
},
{
"type": "node",
"request": "launch",
"name": "Jest: run all tests",
"program": "${workspaceRoot}/webapp/node_modules/jest/bin/jest.js",
"cwd": "${workspaceRoot}/webapp",
"args": [
"--verbose",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Jest: run current file",
"program": "${workspaceRoot}/webapp/node_modules/jest/bin/jest.js",
"cwd": "${workspaceRoot}/webapp",
"args": [
"${fileBasename}",
"--verbose",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
]
{
"type": "node",
"request": "launch",
"name": "Jest: run all tests",
"program": "${workspaceRoot}/webapp/node_modules/jest/bin/jest.js",
"cwd": "${workspaceRoot}/webapp",
"args": [
"--verbose",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Jest: run current file",
"program": "${workspaceRoot}/webapp/node_modules/jest/bin/jest.js",
"cwd": "${workspaceRoot}/webapp",
"args": [
"${fileBasename}",
"--verbose",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
]
}

View File

@ -56,24 +56,6 @@ server-linux-package: server-linux webapp
cd package && tar -czvf ../dist/focalboard-server-linux-amd64.tar.gz ${PACKAGE_FOLDER}
rm -rf package
server-single-user:
$(eval LDFLAGS += -X "github.com/mattermost/focalboard/server/model.Edition=dev")
cd server; go build -ldflags '$(LDFLAGS)' -o ../bin/focalboard-server ./main --single-user
server-mac-single-user:
mkdir -p bin/mac
$(eval LDFLAGS += -X "github.com/mattermost/focalboard/server/model.Edition=mac")
cd server; env GOOS=darwin GOARCH=amd64 go build -ldflags '$(LDFLAGS)' -o ../bin/mac/focalboard-server ./main --single-user
server-linux-single-user:
mkdir -p bin/linux
$(eval LDFLAGS += -X "github.com/mattermost/focalboard/server/model.Edition=linux")
cd server; env GOOS=linux GOARCH=amd64 go build -ldflags '$(LDFLAGS)' -o ../bin/linux/focalboard-server ./main --single-user
server-win-single-user:
$(eval LDFLAGS += -X "github.com/mattermost/focalboard/server/model.Edition=win")
cd server; env GOOS=windows GOARCH=amd64 go build -ldflags '$(LDFLAGS)' -o ../bin/focalboard-server.exe ./main --single-user
generate:
cd server; go get -modfile=go.tools.mod github.com/golang/mock/mockgen
cd server; go get -modfile=go.tools.mod github.com/jteeuwen/go-bindata
@ -81,9 +63,9 @@ generate:
server-lint:
@if ! [ -x "$$(command -v golangci-lint)" ]; then \
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
exit 1; \
fi; \
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
exit 1; \
fi; \
cd server; golangci-lint run -p format -p unused -p complexity -p bugs -p performance -E asciicheck -E depguard -E dogsled -E dupl -E funlen -E gochecknoglobals -E gochecknoinits -E goconst -E gocritic -E godot -E godox -E goerr113 -E goheader -E golint -E gomnd -E gomodguard -E goprintffuncname -E gosimple -E interfacer -E lll -E misspell -E nlreturn -E nolintlint -E stylecheck -E unconvert -E whitespace -E wsl --skip-dirs services/store/sqlstore/migrations/ ./...
server-test:

View File

@ -6,6 +6,6 @@
"useSSL": false,
"webpath": "./pack",
"filespath": "./files",
"telemetry": true,
"localOnly": true
"telemetry": true,
"localOnly": true
}

View File

@ -8,12 +8,12 @@
"useSSL": false,
"webpath": "./webapp/pack",
"filespath": "./files",
"telemetry": true,
"webhook_update": [],
"secret": "this-is-a-secret-string",
"session_expire_time": 2592000,
"session_refresh_time": 18000,
"localOnly": false,
"enableLocalMode": true,
"localModeSocketLocation": "/var/tmp/focalboard_local.socket"
"telemetry": true,
"webhook_update": [],
"secret": "this-is-a-secret-string",
"session_expire_time": 2592000,
"session_refresh_time": 18000,
"localOnly": false,
"enableLocalMode": true,
"localModeSocketLocation": "/var/tmp/focalboard_local.socket"
}

View File

@ -2,4 +2,7 @@ module github.com/mattermost/focalboard/linux
go 1.15
require github.com/webview/webview v0.0.0-20200724072439-e0c01595b361
require (
github.com/google/uuid v1.2.0
github.com/webview/webview v0.0.0-20200724072439-e0c01595b361
)

View File

@ -1,2 +1,4 @@
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/webview/webview v0.0.0-20200724072439-e0c01595b361 h1:e0+/fQY5l9NdCwPsEg9S8AgE5lFhZ/6UX+b2KkpIBFg=
github.com/webview/webview v0.0.0-20200724072439-e0c01595b361/go.mod h1:rpXAuuHgyEJb6kXcXldlkOjU6y4x+YcASKKXJNUhh0Y=

View File

@ -2,19 +2,25 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strconv"
"github.com/google/uuid"
"github.com/webview/webview"
)
var sessionToken string = "su-" + uuid.New().String()
func runServer(ctx context.Context) {
cmd := exec.CommandContext(ctx, "./focalboard-server", "--monitorpid", strconv.FormatInt(int64(os.Getpid()), 10), "--single-user")
cmd := exec.CommandContext(ctx, "./focalboard-server", "--monitorpid", strconv.FormatInt(int64(os.Getpid()), 10), "-single-user")
cmd.Env = []string{fmt.Sprintf("FOCALBOARD_SINGLE_USER_TOKEN=%s", sessionToken)}
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Println("Failed to start server")
log.Fatal(err)
}
log.Printf("Just ran subprocess %d, exiting\n", cmd.Process.Pid)
@ -29,6 +35,10 @@ func main() {
w.SetTitle("Focalboard")
w.SetSize(1024, 768, webview.HintNone)
script := fmt.Sprintf("localStorage.setItem('sessionId', '%s');", sessionToken)
w.Init(script)
w.Navigate("http://localhost:8088")
w.Run()
cancel()

View File

@ -9,6 +9,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private var serverProcess: Process?
var serverPort = 8088
var sessionToken: String = ""
func applicationDidFinishLaunching(_ aNotification: Notification) {
copyResources()
@ -68,7 +69,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
private func generateSessionToken() -> String {
let bytesCount = 16
var randomNumber = ""
var randomBytes = [UInt8](repeating: 0, count: bytesCount)
let status = SecRandomCopyBytes(kSecRandomDefault, bytesCount, &randomBytes)
if status != errSecSuccess {
fatalError("SecRandomCopyBytes ERROR: \(status)")
}
randomNumber = randomBytes.map({String(format: "%02hhx", $0)}).joined(separator: "")
return "su-" + randomNumber
}
private func startServer() {
sessionToken = generateSessionToken()
let cwdUrl = webFolder()
let executablePath = Bundle.main.path(forResource: "resources/bin/focalboard-server", ofType: "")
@ -76,7 +93,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSLog("pid: \(pid)")
let serverProcess = Process()
serverProcess.currentDirectoryPath = cwdUrl.path
serverProcess.arguments = ["-monitorpid", "\(pid)", "-port", "\(serverPort)", "--single-user"]
serverProcess.arguments = ["-monitorpid", "\(pid)", "-port", "\(serverPort)", "-single-user"]
serverProcess.environment = ["FOCALBOARD_SINGLE_USER_TOKEN": sessionToken]
serverProcess.launchPath = executablePath
serverProcess.launch()
self.serverProcess = serverProcess

View File

@ -17,9 +17,9 @@ class ViewController:
webView.navigationDelegate = self
webView.uiDelegate = self
webView.isHidden = true
clearWebViewCache()
loadHomepage()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(onServerStarted), name: AppDelegate.serverStartedNotification, object: nil)
@ -40,10 +40,22 @@ class ViewController:
@objc func onServerStarted() {
NSLog("onServerStarted")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.updateSessionToken()
self.loadHomepage()
}
}
private func updateSessionToken() {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
let script = WKUserScript(
source: "localStorage.setItem('sessionId', '\(appDelegate.sessionToken)');",
injectionTime: .atDocumentStart,
forMainFrameOnly: true
)
webView.configuration.userContentController.removeAllUserScripts()
webView.configuration.userContentController.addUserScript(script)
}
private func loadHomepage() {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
let port = appDelegate.serverPort
@ -143,8 +155,14 @@ class ViewController:
NSLog("webView didFinish navigation: \(webView.url?.absoluteString ?? "")")
// Disable right-click menu
webView.evaluateJavaScript("document.body.setAttribute('oncontextmenu', 'event.preventDefault();');", completionHandler: nil)
webView.isHidden = false
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webView.isHidden = false
}
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if let frame = navigationAction.targetFrame,
frame.isMainFrame {

View File

@ -27,12 +27,15 @@ const (
// REST APIs
type API struct {
appBuilder func() *app.App
singleUser bool
appBuilder func() *app.App
singleUserToken string
}
func NewAPI(appBuilder func() *app.App, singleUser bool) *API {
return &API{appBuilder: appBuilder, singleUser: singleUser}
func NewAPI(appBuilder func() *app.App, singleUserToken string) *API {
return &API{
appBuilder: appBuilder,
singleUserToken: singleUserToken,
}
}
func (a *API) app() *app.App {

View File

@ -78,6 +78,12 @@ func isValidPassword(password string) error {
}
func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
@ -111,6 +117,12 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
@ -164,6 +176,12 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
return
}
vars := mux.Vars(r)
userID := vars["userID"]
@ -198,12 +216,19 @@ func (a *API) sessionRequired(handler func(w http.ResponseWriter, r *http.Reques
func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request), required bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf(`Single User: %v`, a.singleUser)
if a.singleUser {
token, _ := auth.ParseAuthTokenFromRequest(r)
log.Printf(`Single User: %v`, len(a.singleUserToken) > 0)
if len(a.singleUserToken) > 0 {
if required && (token != a.singleUserToken) {
errorResponse(w, http.StatusUnauthorized, nil, nil)
return
}
now := time.Now().Unix()
session := &model.Session{
ID: "single-user",
Token: "single-user",
Token: token,
UserID: "single-user",
CreateAt: now,
UpdateAt: now,
@ -213,11 +238,10 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
return
}
token, _ := auth.ParseAuthTokenFromRequest(r)
session, err := a.app().GetSession(token)
if err != nil {
if required {
errorResponse(w, http.StatusUnauthorized, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusUnauthorized, nil, err)
return
}

View File

@ -20,7 +20,8 @@ func TestGetParentID(t *testing.T) {
cfg := config.Configuration{}
store := mockstore.NewMockStore(ctrl)
auth := auth.New(&cfg, store)
wsserver := ws.NewServer(auth, true)
sessionToken := "TESTTOKEN"
wsserver := ws.NewServer(auth, sessionToken)
webhook := webhook.NewClient(&cfg)
app := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook)

View File

@ -63,10 +63,11 @@ type Client struct {
HttpHeader map[string]string
}
func NewClient(url string) *Client {
func NewClient(url string, sessionToken string) *Client {
url = strings.TrimRight(url, "/")
headers := map[string]string{
"X-Requested-With": "XMLHttpRequest",
"Authorization": "Bearer " + sessionToken,
}
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, headers}
}

View File

@ -27,13 +27,14 @@ func getTestConfig() *config.Configuration {
}
func SetupTestHelper() *TestHelper {
sessionToken := "TESTTOKEN"
th := &TestHelper{}
srv, err := server.New(getTestConfig(), true)
srv, err := server.New(getTestConfig(), sessionToken)
if err != nil {
panic(err)
}
th.Server = srv
th.Client = client.NewClient(srv.Config().ServerRoot)
th.Client = client.NewClient(srv.Config().ServerRoot, sessionToken)
return th
}

View File

@ -71,6 +71,16 @@ func main() {
singleUser = *pSingleUser
}
singleUserToken := ""
if singleUser {
singleUserToken = os.Getenv("FOCALBOARD_SINGLE_USER_TOKEN")
if len(singleUserToken) < 1 {
log.Fatal("The FOCALBOARD_SINGLE_USER_TOKEN environment variable must be set for single user mode ")
return
}
log.Printf("Single user mode")
}
if pMonitorPid != nil && *pMonitorPid > 0 {
monitorPid(*pMonitorPid)
}
@ -81,7 +91,7 @@ func main() {
config.Port = *pPort
}
server, err := server.New(config, singleUser)
server, err := server.New(config, singleUserToken)
if err != nil {
log.Fatal("server.New ERROR: ", err)
}

View File

@ -49,7 +49,7 @@ type Server struct {
localModeServer *http.Server
}
func New(cfg *config.Configuration, singleUser bool) (*Server, error) {
func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
logger, err := zap.NewProduction()
if err != nil {
return nil, err
@ -63,7 +63,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) {
auth := auth.New(cfg, store)
wsServer := ws.NewServer(auth, singleUser)
wsServer := ws.NewServer(auth, singleUserToken)
filesBackendSettings := model.FileSettings{}
filesBackendSettings.SetDefaults(false)
@ -78,7 +78,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) {
webhookClient := webhook.NewClient(cfg)
appBuilder := func() *app.App { return app.New(cfg, store, auth, wsServer, filesBackend, webhookClient) }
api := api.NewAPI(appBuilder, singleUser)
api := api.NewAPI(appBuilder, singleUserToken)
// Local router for admin APIs
localRouter := mux.NewRouter()
@ -157,7 +157,7 @@ func New(cfg *config.Configuration, singleUser bool) (*Server, error) {
"port": cfg.Port == config.DefaultPort,
"useSSL": cfg.UseSSL,
"dbType": cfg.DBType,
"single_user": singleUser,
"single_user": len(singleUserToken) > 0,
}
})
telemetryService.RegisterTracker("activity", func() map[string]interface{} {

View File

@ -18,11 +18,11 @@ type IsValidSessionToken func(token string) bool
// Server is a WebSocket server.
type Server struct {
upgrader websocket.Upgrader
listeners map[string][]*websocket.Conn
mu sync.RWMutex
auth *auth.Auth
singleUser bool
upgrader websocket.Upgrader
listeners map[string][]*websocket.Conn
mu sync.RWMutex
auth *auth.Auth
singleUserToken string
}
// UpdateMsg is sent on block updates
@ -50,7 +50,7 @@ type websocketSession struct {
}
// NewServer creates a new Server.
func NewServer(auth *auth.Auth, singleUser bool) *Server {
func NewServer(auth *auth.Auth, singleUserToken string) *Server {
return &Server{
listeners: make(map[string][]*websocket.Conn),
upgrader: websocket.Upgrader{
@ -58,8 +58,8 @@ func NewServer(auth *auth.Auth, singleUser bool) *Server {
return true
},
},
auth: auth,
singleUser: singleUser,
auth: auth,
singleUserToken: singleUserToken,
}
}
@ -91,7 +91,7 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
wsSession := websocketSession{
client: client,
isAuthenticated: ws.singleUser,
isAuthenticated: false,
}
// Simple message handling loop
@ -134,8 +134,8 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
}
func (ws *Server) isValidSessionToken(token string) bool {
if ws.singleUser {
return true
if len(ws.singleUserToken) > 0 {
return token == ws.singleUserToken
}
session, err := ws.auth.GetSession(token)
@ -160,10 +160,6 @@ func (ws *Server) authenticateListener(wsSession *websocketSession, token string
}
func (ws *Server) checkAuthentication(wsSession *websocketSession, command *WebsocketCommand) bool {
if ws.singleUser {
return true
}
if wsSession.isAuthenticated {
return true
}

View File

@ -6,6 +6,6 @@
"useSSL": false,
"webpath": "../pack",
"filespath": "../../files",
"telemetry": false,
"webhook_update": []
"telemetry": false,
"webhook_update": []
}

View File

@ -10,6 +10,11 @@ describe('Create and delete board / card', () => {
const boardTitle = `Test Board (${timestamp})`;
const cardTitle = `Test Card (${timestamp})`;
beforeEach(() => {
localStorage.setItem('sessionId', 'TESTTOKEN');
cy.expect(localStorage.getItem('sessionId')).to.eq('TESTTOKEN');
});
it('Can create and delete a board and card', () => {
cy.visit('/');
cy.contains('+ Add Board').click({force: true});

View File

@ -11,7 +11,7 @@
"check": "eslint --ext .tsx,.ts . --quiet --cache",
"fix": "eslint --ext .tsx,.ts . --quiet --fix --cache",
"i18n-extract": "formatjs extract src/**/*.tsx src/**/*.ts --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json",
"runserver-test": "cd cypress && ../../bin/focalboard-server --single-user",
"runserver-test": "cd cypress && FOCALBOARD_SINGLE_USER_TOKEN=TESTTOKEN ../../bin/focalboard-server -single-user",
"cypress:ci": "start-server-and-test runserver-test http://localhost:8088 cypress:run",
"cypress:run": "cypress run",
"cypress:run:chrome": "cypress run --browser chrome",

View File

@ -11,16 +11,17 @@ import {Utils} from './utils'
//
class OctoClient {
readonly serverUrl: string
token?: string
readonly readToken?: string
get token(): string {
return localStorage.getItem('sessionId') || ''
}
get readToken(): string {
const queryString = new URLSearchParams(window.location.search)
const readToken = queryString.get('r') || ''
return readToken
}
constructor(
serverUrl?: string,
token?: string,
readToken?: string) {
constructor(serverUrl?: string) {
this.serverUrl = serverUrl || window.location.origin
this.token = token
this.readToken = readToken
Utils.log(`OctoClient serverUrl: ${this.serverUrl}`)
}
@ -46,9 +47,8 @@ class OctoClient {
}
const responseJson = (await this.getJson(response)) as {token?: string}
this.token = responseJson.token || ''
if (this.token) {
localStorage.setItem('sessionId', this.token)
if (responseJson.token) {
localStorage.setItem('sessionId', responseJson.token)
return true
}
return false
@ -330,12 +330,6 @@ class OctoClient {
}
}
function getReadToken(): string {
const queryString = new URLSearchParams(window.location.search)
const readToken = queryString.get('r') || ''
return readToken
}
const client = new OctoClient(undefined, localStorage.getItem('sessionId') || '', getReadToken())
const client = new OctoClient()
export default client

View File

@ -4,5 +4,6 @@ go 1.15
require (
github.com/gonutz/w32 v1.0.0
github.com/google/uuid v1.2.0
github.com/zserge/lorca v0.1.10-0.20200301195127-a3e43396a47e
)

View File

@ -1,5 +1,7 @@
github.com/gonutz/w32 v1.0.0 h1:3t1z6ZfkFvirjFYBx9pHeHBuKoN/VBVk9yHb/m2Ll/k=
github.com/gonutz/w32 v1.0.0/go.mod h1:Rc/YP5K9gv0FW4p6X9qL3E7Y56lfMflEol1fLElfMW4=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/zserge/lorca v0.1.9 h1:vbDdkqdp2/rmeg8GlyCewY2X8Z+b0s7BqWyIQL/gakc=
github.com/zserge/lorca v0.1.9/go.mod h1:bVmnIbIRlOcoV285KIRSe4bUABKi7R7384Ycuum6e4A=
github.com/zserge/lorca v0.1.10-0.20200301195127-a3e43396a47e h1:RqKGfaG8v1WBC6JX5vhG7GocwY1lENlMiraQibyGRsY=

View File

@ -2,26 +2,32 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"runtime"
"github.com/gonutz/w32"
"github.com/google/uuid"
"github.com/zserge/lorca"
)
var sessionToken string = "su-" + uuid.New().String()
func runServer(ctx context.Context) *exec.Cmd {
// cmd := exec.CommandContext(ctx, "focalboard-server.exe", "--monitorpid", strconv.FormatInt(int64(os.Getpid()), 10), "--single-user")
cmd := exec.CommandContext(ctx, "focalboard-server.exe", "--single-user")
// cmd := exec.CommandContext(ctx, "focalboard-server.exe", "--monitorpid", strconv.FormatInt(int64(os.Getpid()), 10), "-single-user")
cmd := exec.CommandContext(ctx, "focalboard-server.exe", "-single-user")
// cmd := exec.CommandContext(ctx, "cmd.exe", "/C", "start", "./bin/focalboard-server.exe", "--monitorpid", strconv.FormatInt(int64(os.Getpid()), 10))
// cmd := exec.CommandContext(ctx, "cmd.exe", "/C", "start", "./bin/focalboard-server.exe")
// cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Env = []string{fmt.Sprintf("FOCALBOARD_SINGLE_USER_TOKEN=%s", sessionToken)}
cmd.Stdout = os.Stdout
go func() {
err := cmd.Run()
if err != nil {
log.Println("Failed to start server")
log.Fatal(err)
}
log.Printf("Just ran subprocess %d, exiting\n", cmd.Process.Pid)
@ -49,20 +55,38 @@ func main() {
ctx, cancel := context.WithCancel(context.Background())
cmd := runServer(ctx)
ui, err := lorca.New("http://localhost:8088", "", 1024, 768)
defer func() {
log.Println("Cleanup")
cancel()
if err := cmd.Process.Kill(); err != nil {
log.Fatal("failed to kill server process: ", err)
}
if r := recover(); r != nil {
log.Fatal("ERROR: ", r)
}
}()
ui, err := lorca.New("", "", 1024, 768)
if err != nil {
log.Fatal(err)
log.Panic(err)
}
defer ui.Close()
if err := ui.Load("http://localhost:8088"); err != nil {
log.Panic(err)
}
script := fmt.Sprintf("localStorage.setItem('sessionId', '%s');", sessionToken)
value := ui.Eval(script)
if err := value.Err(); err != nil {
log.Panic(err)
}
// defer ui.Close()
log.Printf("Started")
<-ui.Done()
log.Printf("App Closed")
cancel()
if err := cmd.Process.Kill(); err != nil {
log.Fatal("failed to kill process: ", err)
}
log.Println("App Closed")
}
func hideConsole() {