Browse Source

got alpha home page working

master
forest 1 month ago
parent
commit
8caaa28aaf
13 changed files with 176 additions and 59 deletions
  1. +7
    -1
      Dockerfile
  2. +1
    -1
      build-docker.sh
  3. +2
    -0
      example_config.json
  4. +66
    -21
      frontend.go
  5. +8
    -6
      frontend/index-highlight.gotemplate.html
  6. +32
    -5
      frontend/index.gotemplate.html
  7. +33
    -16
      frontend/static/greenhouse.css
  8. +11
    -4
      frontend_login.go
  9. +1
    -0
      go.mod
  10. +2
    -0
      go.sum
  11. +5
    -0
      main.go
  12. +3
    -1
      readme/ALPHA.md
  13. +5
    -4
      readme/PRIVACY_POLICY.md

+ 7
- 1
Dockerfile View File

@ -5,7 +5,13 @@ ARG GO_BUILD_ARGS=
RUN mkdir /build
WORKDIR /build
COPY . .
COPY pki/go.mod pki/go.sum ./pki/
COPY go.mod go.sum ./
RUN go mod download
COPY pki ./pki
COPY *.go ./
RUN go build -v $GO_BUILD_ARGS -o /build/greenhouse .


+ 1
- 1
build-docker.sh View File

@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION="0.0.6"
VERSION="0.0.9"
rm -rf dockerbuild || true
mkdir dockerbuild


+ 2
- 0
example_config.json View File

@ -1,6 +1,8 @@
{
"FrontendPort": 8080,
"FrontendDomain": "greenhouse-alpha.server.garden",
"EnableRegistration": false,
"HomepageMarkdownURL": "https://git.sequentialread.com/forest/greenhouse/raw/branch/master/readme/ALPHA.md",
"DatabaseConnectionString": "host=localhost port=5432 user=postgres password=dev database=postgres sslmode=disable",
"DatabaseType": "postgres",
"DatabaseSchema": "public",


+ 66
- 21
frontend.go View File

@ -14,10 +14,13 @@ import (
"os"
"path/filepath"
"regexp"
"runtime/debug"
"strings"
"sync"
"time"
markdown "github.com/gomarkdown/markdown"
markdown_to_html "github.com/gomarkdown/markdown/html"
"github.com/gorilla/mux"
)
@ -38,25 +41,28 @@ type ApplicationAuthSession struct {
}
type FrontendApp struct {
Port int
TLSCertificate string
TLSKey string
Domain string
WorkingDirectory string
Router *mux.Router
EmailService *EmailService
Model *DBModel
Backend *BackendApp
Ingress *IngressService
HTMLTemplates map[string]*template.Template
PasswordHashSalt string
SessionCache map[string]*Session
SessionIdByTenantId map[int]string
SessionCacheMutex *sync.Mutex
basicURLPathRegex *regexp.Regexp
AdminTenantId int
ApplicationAuthSessions map[string]*ApplicationAuthSession
Port int
TLSCertificate string
TLSKey string
Domain string
WorkingDirectory string
HomepageMarkdownURL string
EnableRegistration bool
Router *mux.Router
EmailService *EmailService
Model *DBModel
Backend *BackendApp
Ingress *IngressService
HTMLTemplates map[string]*template.Template
PasswordHashSalt string
SessionCache map[string]*Session
SessionIdByTenantId map[int]string
SessionCacheMutex *sync.Mutex
AdminTenantId int
httpClient *http.Client
homepageHTML string
basicURLPathRegex *regexp.Regexp
applicationAuthSessions map[string]*ApplicationAuthSession
}
func initFrontend(workingDirectory string, config *Config, model *DBModel, backend *BackendApp, emailService *EmailService, ingress *IngressService) FrontendApp {
@ -67,6 +73,7 @@ func initFrontend(workingDirectory string, config *Config, model *DBModel, backe
TLSKey: config.FrontendTLSKey,
Domain: config.FrontendDomain,
AdminTenantId: config.AdminTenantId,
HomepageMarkdownURL: config.HomepageMarkdownURL,
WorkingDirectory: workingDirectory,
Router: mux.NewRouter(),
EmailService: emailService,
@ -79,11 +86,49 @@ func initFrontend(workingDirectory string, config *Config, model *DBModel, backe
SessionIdByTenantId: map[int]string{},
SessionCacheMutex: &sync.Mutex{},
basicURLPathRegex: regexp.MustCompile("(?i)[a-z0-9/?&_+-]+"),
ApplicationAuthSessions: map[string]*ApplicationAuthSession{},
applicationAuthSessions: map[string]*ApplicationAuthSession{},
homepageHTML: "<h3>Error: <code>homepageMarkdown</code> is missing</h3>",
httpClient: &http.Client{
Timeout: time.Second * 5,
},
}
// poll the HomepageMarkdownURL and update the homepageHTML
go func() {
defer (func() {
if r := recover(); r != nil {
go postTelemetry("greenhouse-cloud", "", "admin", fmt.Sprintf("HomepageMarkdown poller panic: %s", r))
fmt.Printf("HomepageMarkdown poller: panic: %+v\n", r)
debug.PrintStack()
}
})()
markdownRenderer := markdown_to_html.NewRenderer(markdown_to_html.RendererOptions{
Flags: markdown_to_html.CommonFlags | markdown_to_html.HrefTargetBlank,
})
for {
response, err := app.httpClient.Get(app.HomepageMarkdownURL)
if err == nil && response.StatusCode < 299 {
homepageMarkdownBytes, err := ioutil.ReadAll(response.Body)
if err == nil {
app.homepageHTML = string(markdown.ToHTML(homepageMarkdownBytes, nil, markdownRenderer))
}
}
time.Sleep(60)
}
}()
// serve the homepage
app.handleWithSessionNotRequired("/", func(responseWriter http.ResponseWriter, request *http.Request, session Session) {
pageContent, err := app.renderTemplateToHTML("index.html", nil)
homepageData := struct {
DynamicContent template.HTML
}{template.HTML(app.homepageHTML)}
pageContent, err := app.renderTemplateToHTML("index.html", homepageData)
if err != nil {
app.unhandledError(responseWriter, request, err)
return


+ 8
- 6
frontend/index-highlight.gotemplate.html View File

@ -1,9 +1,11 @@
<img class="image-medium float-right" src="static/images/greenhouse-border.webp"/>
<h1>the easiest way to grow IT yourself</h1>
<h1>greenhouse alpha test program</h1>
<p>
Greenhouse is a specialized cloud service designed to make self-hosting your own website, email,
web services, etc, as easy as possible.
<br/><br/>
<i>We don't ask you to compromise on privacy or security:
it's <b>your</b> server, we simply make it reliably accessible on the internet.</i> <br/>
Greenhouse is a new cloud service designed to break down barriers to entry & make
real ownership and production of <em>The Internet</em> accessible to more people.
</p>
<p>
It enables you to self-host internet services / websites on your own computer easily,
while retaining exlusive ownership of your processes, data, and your user's data as well.
</p>

+ 32
- 5
frontend/index.gotemplate.html View File

@ -1,6 +1,33 @@
<img class="image-medium float-right" src="static/images/mascot-dancing.webp"/>
<h1>greenhouse alpha test program</h1>
<p>
</p>
<img class="float-right" src="static/images/mascot-dancing.webp"/>
<!--<h1>greenhouse alpha test program</h1>
<p>
Greenhouse is a new cloud service designed to break down barriers to entry & make
real ownership and production of <em>The Internet</em> accessible to more people.
</p>
<p>
It enables you to self-host internet services / websites on your own computer easily,
while retaining exlusive ownership of your processes, data, and your user's data as well.
</p>
-->
<p>
For more in-depth information, see the project page: <a href="https://greenhouse.server.garden">greenhouse.server.garden</a>
</p>
{{.DynamicContent}}
<section class="comments">
<h3>Feedback / Discussion</h3>
<div
id="sqr-comment-container"
data-comments-url="https://comments.sequentialread.com/"
data-comments-document-id="greenhouse-alpha"
>
</div>
<script src="https://comments.sequentialread.com/static/comments.js"></script>
<script src="https://captcha.sequentialread.com/static/captcha.js"></script>
</section>

+ 33
- 16
frontend/static/greenhouse.css View File

@ -76,6 +76,10 @@ a {
font-weight: 600;
color: #66811a;
}
a:visited {
font-weight: 600;
color: #406e14;
}
img {
max-width: 100%;
}
@ -337,27 +341,40 @@ input[type=number] {
-moz-appearance: textfield;
}
/* roughly copy and pasted from the firefox user agent style sheet on linux :D */
.fake-text-input {
text-rendering: auto;
color: rgb(58, 58, 58);
display: inline-block;
text-align: start;
-webkit-appearance: textfield;
label {
margin-right: 1em;
}
section.comments {
background-color: white;
margin: 0em;
font: 400 13.3333px Arial;
padding: 7px;
border-radius: 3px;
box-shadow: inset 0 0 3px 0px rgba(0,0,0,0.1);
border: 1px solid #ccc;
min-width: 18em;
border-radius: 20px;
}
section.comments h2,
section.comments h3,
section.comments h4 {
padding-top: 20px;
padding-left: 20px;
}
section.comments a {
font-weight: 600;
color: #7333e0;
}
section.comments a:visited {
font-weight: 600;
color: #7e1cac;
}
label {
margin-right: 1em;
#sqr-comment-container {
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
}
#sqr-comment-container .sqr-comment-bottom-row {
padding-bottom: 0.4em;
}
form.vertical input,


+ 11
- 4
frontend_login.go View File

@ -107,6 +107,13 @@ func registerLoginRoutes(app *FrontendApp, emailService *EmailService) {
})
app.handleWithSessionNotRequired("/register", func(responseWriter http.ResponseWriter, request *http.Request, session Session) {
if !app.EnableRegistration {
app.setFlash(responseWriter, session, "error", "registration is currently disabled 😥\n")
http.Redirect(responseWriter, request, "/", http.StatusFound)
return
}
data := struct {
Email string
PasswordHashSalt string
@ -283,13 +290,13 @@ func registerLoginRoutes(app *FrontendApp, emailService *EmailService) {
telemetryValue := fmt.Sprintf("POST id=%s name=%s", getHashForTelemetry(app, appAuthSessionId), name)
go postTelemetry("app-auth-session", getRemoteIpFromRequest(request), "", telemetryValue)
app.ApplicationAuthSessions[appAuthSessionId] = &ApplicationAuthSession{
app.applicationAuthSessions[appAuthSessionId] = &ApplicationAuthSession{
Name: name,
}
responseWriter.Write([]byte("OK"))
} else {
telemetryValue := fmt.Sprintf("GET id=%s", getHashForTelemetry(app, appAuthSessionId))
appAuthSession, hasAppAuthSession := app.ApplicationAuthSessions[appAuthSessionId]
appAuthSession, hasAppAuthSession := app.applicationAuthSessions[appAuthSessionId]
if hasAppAuthSession {
if appAuthSession.TenantId != 0 {
telemetryValue = fmt.Sprintf("%s name=%s", telemetryValue, appAuthSession.Name)
@ -324,9 +331,9 @@ func registerLoginRoutes(app *FrontendApp, emailService *EmailService) {
telemetryValue := fmt.Sprintf("GET id=%s", getHashForTelemetry(app, appAuthSessionId))
appAuthSession, hasAppAuthSession := app.ApplicationAuthSessions[appAuthSessionId]
appAuthSession, hasAppAuthSession := app.applicationAuthSessions[appAuthSessionId]
if hasAppAuthSession && appAuthSession.TenantId == 0 {
app.ApplicationAuthSessions[appAuthSessionId].TenantId = session.TenantId
app.applicationAuthSessions[appAuthSessionId].TenantId = session.TenantId
app.buildPageFromTemplate(responseWriter, request, session, "app-connect.html", nil)
telemetryValue = fmt.Sprintf("%s success!!", telemetryValue)


+ 1
- 0
go.mod View File

@ -6,6 +6,7 @@ require (
git.sequentialread.com/forest/easypki.git v1.1.3-0.20210825184937-473ebf3609b4
git.sequentialread.com/forest/greenhouse/pki v0.0.0-20210405174447-b8a188ca6a97
git.sequentialread.com/forest/pkg-errors v0.9.2
github.com/gomarkdown/markdown v0.0.0-20210918233619-6c1113f12c4a // indirect
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.10.0
github.com/shengdoushi/base58 v1.0.0


+ 2
- 0
go.sum View File

@ -4,6 +4,8 @@ git.sequentialread.com/forest/pkg-errors v0.9.2 h1:j6pwbL6E+TmE7TD0tqRtGwuoCbCfO
git.sequentialread.com/forest/pkg-errors v0.9.2/go.mod h1:8TkJ/f8xLWFIAid20aoqgDZcCj9QQt+FU+rk415XO1w=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gomarkdown/markdown v0.0.0-20210918233619-6c1113f12c4a h1:syEwbl3pF5Y1mnIStrPwqd50vNU1AAKuAy8HFCPAgUc=
github.com/gomarkdown/markdown v0.0.0-20210918233619-6c1113f12c4a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=


+ 5
- 0
main.go View File

@ -23,6 +23,7 @@ type Config struct {
FrontendDomain string
FrontendTLSCertificate string
FrontendTLSKey string
HomepageMarkdownURL string
AdminTenantId int
DigitalOceanAPIKey string
DigitalOceanRegion string
@ -160,6 +161,7 @@ func getConfig(workingDirectory string) *Config {
config.AdminTenantId = 1
}
homepageMarkdownURL := os.Getenv("GREENHOUSE_HOMEPAGE_MARKDOWN_URL")
digitalOceanAPIKey := os.Getenv("GREENHOUSE_DIGITALOCEAN_API_KEY")
databaseConnectionString := os.Getenv("GREENHOUSE_DATABASE_CONNECTION_STRING")
databaseSchema := os.Getenv("GREENHOUSE_DATABASE_SCHEMA")
@ -174,6 +176,9 @@ func getConfig(workingDirectory string) *Config {
smtpPassword := os.Getenv("GREENHOUSE_SMTP_PASSWORD")
smtpEncryption := os.Getenv("GREENHOUSE_SMTP_ENCRYPTION")
if homepageMarkdownURL != "" {
config.HomepageMarkdownURL = homepageMarkdownURL
}
if digitalOceanAPIKey != "" {
config.DigitalOceanAPIKey = digitalOceanAPIKey
}


+ 3
- 1
readme/ALPHA.md View File

@ -22,6 +22,7 @@ GREENHOUSE AND ITS CLIENT SOFTWARE PACKAGES ARE PROVIDED “AS IS” WITHOUT WAR
## Known Issues
```
- the greenhouse web app will log you out randomly if you use it for a long time (refresh session does not work)
- change personal subdomain is somewhat buggy, requires you to restart the desktop app / re-create your tunnels
- desktop app constantly scrolling to the top of the tunnels list (so far fixed with heuristic method)
@ -31,4 +32,5 @@ GREENHOUSE AND ITS CLIENT SOFTWARE PACKAGES ARE PROVIDED “AS IS” WITHOUT WAR
- built in web server (caddy) logs are nearly impossible to read due to them being JSON-formatted
- should separate the built in web server (caddy) access logs from caddy admin/cert info log
- the background service (greenhouse-daemon) can get locked up when a threshold test fails (apply config mutex never unlocked?)
- most emojis (and characters outside the notorious BMP, Basic Multilingual Plane) dont show up in the logs in the desktop app
- most emojis (and characters outside the notorious BMP, Basic Multilingual Plane) dont show up in the logs in the desktop app
```

+ 5
- 4
readme/PRIVACY_POLICY.md View File

@ -30,7 +30,7 @@ However, we must still collect and store some data in order to provide services
## ADDITIONAL INFORMATION WE COLLECT AS A PART OF OUR ALPHA TEST PROGRAM
We also collect and store timestamped personal information detailing your use of the services (telemetry data) as a part of our alpha test program. We collect this data on solely to help us discover / debug issues with the Greenhouse service and software.
We also collect and store timestamped personal information detailing your use of the services (telemetry data) as a part of our alpha test program. We collect this data solely to help us discover / debug issues with the Greenhouse service and software.
This information includes but is not limited to:
@ -47,7 +47,7 @@ In general, we strive to only store your information on our server(s), where it
However, Greenhouse is built on top of other 3rd party cloud service providers. A minimal amount of information about our users is implicitly shared with these providers, as it is neccessary for the service to operate:
- We automatically generate a greenhouseusers.com subdomain based on your email address. For example, if your email address is `janelane86@sicksadworld.com`, your subdomain would be `janelane86.greenhouseusers.com`. This subdomain will be transmitted to [gandi.net](https://www.gandi.net/) when you register your greenhouse account, however, gandi does not get any other information associated with that domain name, such as your IP address or greenhouse internal user ID.
- We automatically generate a greenhouseusers.com subdomain based on your email address. For example, if your email address is `janelane86@sicksadworld.com`, your subdomain would be `janelane86.greenhouseusers.com` by default. This subdomain will be transmitted to [gandi.net](https://www.gandi.net/) when you register your greenhouse account, however, gandi does not get any other information associated with that domain name, such as your IP address or greenhouse internal user ID. You may change this subdomain at any time after registering your account.
- We host our [threshold](https://git.sequentialread.com/forest/threshold) network gateway software on [DigitalOcean](https://www.digitalocean.com/).
- The domain names that you configure as well as a pseudonomous internal user id will be securely transmitted to our threshold server application running on DigitalOcean's platform.
- Your server computer will connect to our threshold server running on DigitalOcean's platform.
@ -61,8 +61,9 @@ However, Greenhouse is built on top of other 3rd party cloud service providers.
Greenhouse has the right to process personal data of EU residents
a. Due to their provided consent to Greenhouse; and/or
b. In order to take steps at the request of the EU resident prior to entering into a contract with them
> a. Due to their provided consent to Greenhouse; and/or
> b. In order to take steps at the request of the data subject prior to entering into a contract with them
per Article 6 (1) (a)-(b) of the General Data Protection Regulation.


Loading…
Cancel
Save