Skip to content
This repository has been archived by the owner on Oct 15, 2023. It is now read-only.

Commit

Permalink
v0.6.3: LatestRelease <- Development (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
lmbek authored Jul 4, 2023
2 parents 171b8ab + 7ba7f68 commit f69855f
Show file tree
Hide file tree
Showing 38 changed files with 857 additions and 109 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/Run-Go-Tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

jobs:
build:
runs-on: windows-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

Expand All @@ -24,5 +24,5 @@ jobs:
go-version: 1.19

- name: Run Go Tests
run: go test -v ./tests/./...
run: go test -v ./backend/tests/./...

58 changes: 21 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# go-local-web-gui (Local Go Chrome Framework)
# gobek (Local GUI / Go Chrome Framework)

go-local-web (GOLW) is a simple framework made for developing localhosted software that can reuse chrome/chromium or embed chromium (in future releases). Both available in deployment for the applications.
gobek is a simple framework made for developing localhosted software that can reuse chrome/chromium or embed chromium (in future releases). Both available in deployment for the applications.

This framework uses Chrome (Windows) or Chromium (Linux) as frontend by opening them with cmd/terminal and hosting a localhost webserver, while opening chrome/chromium with --app and --user-directory arguments. The frontend can be changed by the user in runtime, while the backend needs to be compiled/build. The API can be decoupled in future versions, so every part of the application is changeable - Sustainable development. Frontends is easy to change. Alternatives to this is embedding a chromium or webview framework into the project, which will require more space. I chose to depend on Chrome/Chromium, as they are market leaders and html/css/javascript technology frontrunners.

Expand All @@ -10,18 +10,24 @@ I am currently working on this project, it will be updated and maintained. I con

This project is used by Beksoft ApS for projects such as:
* BekCMS
* KeyFiles
* PingPong Game made in Three.js
* Several local webbased software projects

Write to me at [email protected] if you want to have your project listed

## Contributors
Lars Morten Bek (https://github.com/lmbek)
Ida Marcher Jensen (https://github.com/notHooman996)

## Requirements to developers
Go 1.20+
Chrome (Windows) or Chromium (Linux)
macOS/DARWIN NOT SUPPORTED YET
other NOT SUPPORTED YET

## Requirements for users
Chrome (Windows) or Chromium (Linux)
macOS/DARWIN NOT SUPPORTED YET

## How to use (download example project)
The best way to start using the project is to download the example project at:
Expand All @@ -38,66 +44,44 @@ Example: how to add framework to main.go
package main

import (
"api"
"fmt"
"github.com/lmbek/gobek/fileserver"
"github.com/lmbek/gobek/launcher"
"os"
)

// For windows we need a organisation name and project name
var organisationName = "NewOrganisationName" // put in organisation name
var projectName = "NewProjectName" // put in project name
// For windows, we need an organisation name and project name
var organisationName = "NewOrganisationName"
var projectName = "NewProjectName"

var frontendPath = "./frontend" // this should be set to where frontend files is (frontend folder: html, css, javascript...)
// Set frontend path (where we have all the: html, css, javascript...)
var frontendPath = "./frontend"

var chromeLauncher = launcher.ChromeLauncher{
Location: os.Getenv("programfiles") + "\\Google\\Chrome\\Application\\chrome.exe",
FrontendInstallLocation: os.Getenv("localappdata") + "\\Google\\Chrome\\InstalledApps\\" + "DefaultOrganisationName" + "\\" + "DefaultProjectName",
FrontendInstallLocation: os.Getenv("localappdata") + "\\Google\\Chrome\\InstalledApps\\" + organisationName + "\\" + projectName,
}

var chromiumLauncher = launcher.DefaultChromiumLauncher // default chrome or chromium launcher settings can be used like this

/*
// Otherwise they can also be customized like this

var chromiumLauncher = launcher.ChromiumLauncher{
Location: "/var/lib/snapd/desktop/applications/chromium_chromium.desktop", // TODO: check if better location or can be customised
Domain: "localhost",
}
*/

func main() {
// api example is temporary not avaiable before release of gobek 0.7.0
//api.Init()
/*
var once sync.Once
once.Do(func() {
http.HandleFunc("/", fileserver.ServeFileServer)
http.HandleFunc("/api/", api.ServeAPIUseGZip)
})
err := launcher.Start(frontendPath, chromeLauncher, chromiumLauncher) // serves "/" as fileserver.ServeFileServer. If you want to manage "/", then use launcher.StartCustom() instead
if err != nil {
fmt.Println(err)
}
*/
//api.Init() // need to be added by you (example on gobek-example)
err := launcher.StartDefault(frontendPath, chromeLauncher, chromiumLauncher)
if err != nil {
fmt.Println(err)
}
}

</pre>

Please do note however that using the main.go from the gobek-example project is recommended
## How to test
<code>go test ./backend/tests/...</code>
<code>go test ./tests/...</code>

## How to run
<code>go run main.go</code>
make your own main.go by following https://github.com/lmbek/gobek-example

## How to apply manifest and logo to executible
Use something like goversioninfo: https://github.com/josephspurrier/goversioninfo

## How to build
<code>go build -ldflags -H=windowsgui -o NewProjectName.exe</code>

## How to make setup file and update functionality
Coming later
57 changes: 24 additions & 33 deletions backend/api/api.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package api

import (
"api/types"
"compress/gzip"
"encoding/json"
"example"
"fmt"
"io"
"net/http"
"strings"
)

func Init() {
http.HandleFunc("/api/", ServeAPIUseGZip)
}

func ServeAPIUseGZip(response http.ResponseWriter, request *http.Request) {
if !strings.Contains(request.Header.Get("Accept-Encoding"), "gzip") {
ServeAPI(response, request)
Expand All @@ -25,48 +29,35 @@ func ServeAPIUseGZip(response http.ResponseWriter, request *http.Request) {
func ServeAPI(response http.ResponseWriter, request *http.Request) {
// Redirect if not trailing slash
// if the url contains / in the end, then we will ignore it for the API listener (return same result no matter if there is / or not
urlPath := request.URL.Path
if hasTrailingSlash(urlPath) == false {
addTrailingSlashRedirect(urlPath, response, request)
}
//urlPath := request.URL.Path
// if hasTrailingSlash(urlPath) == false {
//addTrailingSlashRedirect(urlPath, response, request)
// }
//response.Header().Add("Cache-Control", "max-age=31536000, immutable")

// setting cache to no cache
response.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate")

// set response header as json
response.Header().Set("Content-Type", "application/json")
//response.Header().Add("Cache-Control", "max-age=31536000, immutable")

// response
var jsonResponse any = JSONMessageResponse{false, "Not set"}

// api commands (URLs)
switch request.URL.Path {
case "/api/example/list/":
list, err := example.List(true)
if err != nil {
// error getting data, sending error
jsonResponse = JSONMessageResponse{false, err.Error()}
} else {
// successfully sending data
jsonResponse = JSONDataResponse{true, list}
}
break
case "/api/example/list2/":
list, err := example.List(true)
if err != nil {
// error getting data, sending error
jsonResponse = JSONMessageResponse{false, err.Error()}
} else {
// successfully sending data
jsonResponse = JSONDataResponse{true, list}
var jsonResponse any = types.JSONMessageResponse{false, "Not set"}

// API endpoints
data, err := HandleEndpoint(request.URL.Path, response, request)
if err != nil {
if err.Error() == "not json response" {
return
}
break
default:
// if not part of api
jsonResponse = JSONMessageResponse{false, "Part of the api does not exist"}
break
jsonResponse = types.JSONMessageResponse{Success: false, Message: err.Error()}
} else {
jsonResponse = types.JSONDataResponse{Success: true, Data: data}
}

// Marshal JSON
jsonFormattedResponse := jsonMarshalled(jsonResponse)

// Send Response
sendResponse(response, jsonFormattedResponse)
}
Expand Down
30 changes: 30 additions & 0 deletions backend/api/download/download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package download

import (
"errors"
"fmt"
"net/http"
"os"
)

func Start(response http.ResponseWriter, request *http.Request) (any, error) {
fmt.Printf("%v started download of %v \n", request.RemoteAddr, "./data/test.txt")
file, err := os.Open("./data/test.txt")
if err != nil {
// Handle the error if file cannot be opened
return "", errors.New("Could not open ./data/test.txt")
}
defer file.Close()

// Get file information
fileInfo, err := file.Stat()
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return nil, errors.New("Could not get file information")
}
response.Header().Set("Content-Type", "application/octet-stream")
response.Header().Set("Content-Disposition", "attachment; filename=test.txt")
http.ServeContent(response, request, "test.txt", fileInfo.ModTime(), file)

return nil, errors.New("not json response")
}
56 changes: 56 additions & 0 deletions backend/api/endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package api

import (
"api/download"
"api/example"
"api/functions"
"api/html"
"api/links"
"api/native"
"api/places"
"api/png"
"api/user"
"api/users"
"errors"
"net/http"
)

// path: /api/*

func HandleEndpoint(endpoint string, response http.ResponseWriter, request *http.Request) (any, error) {
// (Example) start with: /api/user/995/name
reducedEndpoint := functions.ReducedFirstLayerEndpoint(endpoint) // (Example) we get: user/995/name/something
currentEndpoint := functions.CurrentLayer(reducedEndpoint) // (Example) we get: user

switch currentEndpoint {
case "native":
return native.HandleEndpoint(reducedEndpoint)
case "example":
return example.List(true)
case "links":
return links.Get()
case "user":
return user.HandleEndpoint(reducedEndpoint)
case "users":
return users.Get()
case "places":
return places.Get()
case "download", "png", "html":
return ModifyResponse(currentEndpoint, response, request)
}

return nil, errors.New("invalid endpoint")
}

func ModifyResponse(endpoint string, response http.ResponseWriter, request *http.Request) (any, error) {
switch endpoint {
case "download":
return download.Start(response, request)
case "png":
return png.Get(response, request)
case "html":
return html.Get(response, request)
}

return nil, errors.New("invalid endpoint")
}
1 change: 0 additions & 1 deletion backend/api/example/go.mod

This file was deleted.

2 changes: 1 addition & 1 deletion backend/api/example/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

// List Do something here like reading data from a file or database and return results
func List(isAllowed bool) ([]string, error) {
func List(isAllowed bool) (any, error) {
list := []string{"this", "is", "some", "data"}
if isAllowed {
return list, nil
Expand Down
30 changes: 30 additions & 0 deletions backend/api/functions/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package functions

import "strings"

func CurrentLayer(endpoint string) string {
// /api/personal//user/995/name

splitted := strings.Split(endpoint, "/")
// ["", "api", "user", "995", "name"]
if len(splitted) >= 1 {
return splitted[0] // /personal//user/995/name
}
return ""
}

func ReducedEndpoint(endpoint string) string {
splitted := strings.Split(endpoint, "/")
if len(splitted) >= 1 {
return strings.Join(splitted[1:], "/")
}
return ""
}

func ReducedFirstLayerEndpoint(endpoint string) string {
splitted := strings.Split(endpoint, "/")
if len(splitted) >= 2 {
return strings.Join(splitted[2:], "/")
}
return ""
}
26 changes: 26 additions & 0 deletions backend/api/html/html.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package html

import (
"errors"
"net/http"
"os"
)

func Get(response http.ResponseWriter, request *http.Request) (any, error) {
file, err := os.Open("./data/test.html")
if err != nil {
// Handle the error if file cannot be opened
return "", errors.New("Could not open ./data/test.html")
}
defer file.Close()

// Get file information
fileInfo, err := file.Stat()
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
return nil, errors.New("Could not get file information")
}
response.Header().Set("Content-Type", "text/html")
http.ServeContent(response, request, "test.html", fileInfo.ModTime(), file)
return nil, errors.New("not json response")
}
14 changes: 14 additions & 0 deletions backend/api/links/links.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package links

import (
"errors"
)

func Get() (any, error) {
if true {
myData := []string{"https://facebook.dk", "https://google.dk", "https://linkedin.com"}
return myData, nil
}

return nil, errors.New("database could not connect")
}
Loading

0 comments on commit f69855f

Please sign in to comment.