Compare commits

...

86 Commits

Author SHA1 Message Date
DutchEllie e932269f04
For now disable sending UUID with comment
continuous-integration/drone/push Build was killed Details
2022-06-25 12:13:40 +02:00
DutchEllie eb21ce6d8e
Add spyware module
continuous-integration/drone/push Build is passing Details
2022-06-25 11:28:37 +02:00
DutchEllie 89f5fd178f
Remove shit file
continuous-integration/drone/push Build is passing Details
2022-06-24 17:48:53 +02:00
DutchEllie 7e171abfb8
More music lol
continuous-integration/drone Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-06-22 21:58:51 +02:00
DutchEllie 95e5a173f1
Messed up desktop
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-06-22 13:49:43 +02:00
DutchEllie d1190c4e88
MOBILE
continuous-integration/drone/push Build is passing Details
2022-06-22 13:45:43 +02:00
DutchEllie d66bb5afeb
Fix mobile kinda
continuous-integration/drone/push Build is passing Details
2022-06-22 12:39:36 +02:00
DutchEllie dbfdaf38bb
Add hamburger
continuous-integration/drone/push Build is passing Details
2022-06-20 13:33:22 +02:00
DutchEllie 85d13a2905
New tags good
continuous-integration/drone/push Build is passing Details
2022-06-20 11:19:15 +02:00
DutchEllie f5fa2af536
New tags
continuous-integration/drone/push Build is passing Details
2022-06-20 11:17:16 +02:00
DutchEllie 6a2ec9f099
Mobile scaling
continuous-integration/drone/push Build is passing Details
2022-06-20 11:02:53 +02:00
DutchEllie 31abd9e138
Okay, I think I know
continuous-integration/drone/push Build is passing Details
2022-06-08 20:50:31 +02:00
DutchEllie ff7044ab9b
Finally... Music link fingering 😉
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-06-05 10:12:18 +02:00
DutchEllie acba8179fb
Add update button
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-05-31 10:22:29 +02:00
DutchEllie 67358d1616
Merge branch 'main' of dutchellie.nl:DutchEllie/proper-website-2
continuous-integration/drone/push Build is passing Details
2022-05-31 09:48:21 +02:00
DutchEllie 55e3e13459
Nah nvm
continuous-integration/drone/push Build is passing Details
2022-05-27 21:30:49 +02:00
DutchEllie 1f59549cc4
Possibly maybe....
continuous-integration/drone/push Build is passing Details
2022-05-27 20:51:53 +02:00
DutchEllie e414c98866
Possibly maybe....
continuous-integration/drone/push Build is failing Details
2022-05-27 20:50:12 +02:00
DutchEllie 029b426757
Made generating static site possible
continuous-integration/drone/push Build is passing Details
2022-05-27 20:35:53 +02:00
DutchEllie 629b642d86
Go mod tidy 2022-05-23 14:40:48 +02:00
DutchEllie 8aaecbab69
Idk what went wrong?
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-05-23 14:40:22 +02:00
DutchEllie 821f2c916e
Added very cool songs
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-05-14 22:24:14 +02:00
DutchEllie b765a88f27
Disabled CGO
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-05-14 21:45:04 +02:00
DutchEllie d489dd744c
Update the production pipeline too
continuous-integration/drone/push Build is passing Details
2022-05-14 21:22:11 +02:00
DutchEllie b06ec9b955
Forgot the volumes
continuous-integration/drone/push Build is passing Details
2022-05-14 21:12:04 +02:00
DutchEllie 42eba16ef2
Dependency issue
continuous-integration/drone/push Build is failing Details
2022-05-14 21:10:44 +02:00
DutchEllie caf15aadcb
CI update
continuous-integration/drone/push Build was killed Details
2022-05-14 21:09:48 +02:00
DutchEllie a88651e4b7
Revert APIURL changes, it's impossible
continuous-integration/drone/push Build is passing Details
2022-05-14 20:45:28 +02:00
DutchEllie 450bf3631c
Oh right, after browser
continuous-integration/drone/push Build is passing Details
2022-05-14 18:31:43 +02:00
DutchEllie 98a4b28e8b
Come on CI
continuous-integration/drone/push Build is passing Details
2022-05-14 18:25:45 +02:00
DutchEllie ba1fae4547
Moved API URL to environment 2022-05-14 18:24:19 +02:00
Quenten 734d9e4e27 [SKIP CI] Readme.md
Signed-off-by: Quenten <personal@quenten.nl>
2022-05-10 08:43:28 +00:00
Quenten e2f4db7b97 Updated Badge [SKIP CI] 2022-05-08 19:29:17 +00:00
DutchEllie be54e41135
Modified API url
continuous-integration/drone Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-05-08 17:11:45 +02:00
DutchEllie 77ccef7c68
Added Kyu's Left on Red
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-05-01 17:39:01 +02:00
DutchEllie 65f5adc604
Added ERROR to cache
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-04-25 14:29:37 +02:00
DutchEllie 14969944a8
Added error by towa
continuous-integration/drone/push Build is passing Details
2022-04-25 14:27:50 +02:00
DutchEllie 7ce4efb20e
Added a new song
continuous-integration/drone/push Build is passing Details
2022-04-20 18:07:52 +02:00
DutchEllie 035f3e21d0
Changed domain in intro page
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-04-20 15:25:47 +02:00
DutchEllie 24f12a1e37
Changed prod domain 2022-04-20 15:23:54 +02:00
DutchEllie d0e7f90e9f
Moved staging domain
continuous-integration/drone/push Build is passing Details
2022-04-20 14:52:20 +02:00
DutchEllie 4cd86be5a0
Added frame options
continuous-integration/drone/push Build is passing Details
2022-04-20 14:45:04 +02:00
DutchEllie 8d0b6a37c1
Fixed values
continuous-integration/drone/push Build is passing Details
2022-04-20 14:14:52 +02:00
DutchEllie 85685b1da3
Added CSP
continuous-integration/drone/push Build is failing Details
2022-04-20 14:03:46 +02:00
DutchEllie 111c6fd5de
Removed debug messages
continuous-integration/drone/push Build is passing Details
2022-04-19 23:06:27 +02:00
DutchEllie 2332cda5e4
Added caching song for expensive S3 storage
continuous-integration/drone/push Build is passing Details
2022-04-19 22:51:24 +02:00
DutchEllie 182fb13590
Added music player beta
continuous-integration/drone/push Build is passing Details
2022-04-19 22:30:47 +02:00
DutchEllie 4968a2a8ca
Added sass
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-04-18 22:20:36 +02:00
DutchEllie 7e4a5a5302
Added textwithtooltip component
continuous-integration/drone/push Build is passing Details
2022-04-18 22:02:51 +02:00
DutchEllie b8e8cc3a60
Added status check
continuous-integration/drone/push Build is passing Details
2022-04-18 16:02:41 +02:00
DutchEllie e001227cd4
Updated cache
continuous-integration/drone/push Build is passing Details
2022-04-18 13:51:17 +02:00
DutchEllie a92e64b7f2
Added GNU and rewrote page
continuous-integration/drone/push Build is passing Details
2022-04-18 13:49:16 +02:00
DutchEllie 7389daba09
Hidden blog nav for now
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-03-27 16:50:48 +02:00
DutchEllie cd8d116fef
Intermediate loading of comments
continuous-integration/drone/push Build is passing Details
2022-03-27 16:39:38 +02:00
DutchEllie 70c9f027b8
Added efficient comment loading
continuous-integration/drone/push Build is passing Details
2022-03-27 14:06:21 +02:00
DutchEllie 75dab086ff
Guestbook loading still broken 2022-03-25 14:02:14 +01:00
DutchEllie 4eb00e38bc
Blogposts are a thing now
continuous-integration/drone/push Build is passing Details
2022-03-25 13:51:25 +01:00
DutchEllie 5e688e3870
Change compression level
continuous-integration/drone/push Build is passing Details
2022-03-24 15:55:18 +01:00
DutchEllie 08514b50c6
Fixed bg
continuous-integration/drone/push Build is passing Details
2022-03-24 14:50:36 +01:00
DutchEllie 7717210578
Compressing handlergit add .
continuous-integration/drone/push Build is passing Details
2022-03-24 14:31:02 +01:00
DutchEllie bb163abb0a
Fixed the API url in makefile [CI SKIP] 2022-03-24 13:36:44 +01:00
DutchEllie d7c8ff80e4
Fixed EOF error
continuous-integration/drone/push Build is passing Details
2022-03-24 13:28:28 +01:00
DutchEllie acbdd113d4
Started working on blogposts
continuous-integration/drone/push Build is failing Details
2022-03-24 13:25:07 +01:00
DutchEllie 6b4c6bb447
Added entity
continuous-integration/drone/push Build is passing Details
2022-03-24 13:12:57 +01:00
DutchEllie 30d57404f9
eeeeh
continuous-integration/drone/push Build is passing Details
2022-03-18 22:45:29 +01:00
DutchEllie 6482fcd6f1
Caching
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-03-16 13:13:14 +01:00
DutchEllie 858abd350e
Fixed some layout 2022-03-16 12:49:52 +01:00
DutchEllie 44da256e10
Wait noo
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-03-15 19:38:46 +01:00
DutchEllie 715aac28b4
God I hate CSS
continuous-integration/drone/push Build is passing Details
2022-03-15 19:37:46 +01:00
DutchEllie 5c4b46b65e
Fixed
continuous-integration/drone/push Build is passing Details
2022-03-15 17:44:29 +01:00
DutchEllie a32ced531f
This would be really cool
continuous-integration/drone/push Build is passing Details
2022-03-15 17:25:42 +01:00
DutchEllie bc38196fd3
Todo 2022-03-15 16:47:49 +01:00
DutchEllie 330385a357
Some aligning
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-03-15 16:44:32 +01:00
DutchEllie cd07bef29e
Rewrote guestbook 2022-03-15 16:42:26 +01:00
DutchEllie 73442b212c
Updater
continuous-integration/drone/push Build is passing Details
2022-03-15 14:12:15 +01:00
DutchEllie 0ca691e3ae
Layout and refactoring 2022-03-15 13:22:59 +01:00
DutchEllie 03a09fc452
Moved pages 2022-03-15 13:06:52 +01:00
DutchEllie 2e381108d1
More unused code 2022-03-15 12:57:12 +01:00
DutchEllie 0b7b478cd3
Removed unused code 2022-03-15 12:56:08 +01:00
DutchEllie 81d9e315a0
Major refactor
continuous-integration/drone/push Build is passing Details
2022-03-15 12:48:47 +01:00
DutchEllie 73cf59ff86
App notes 2022-03-15 09:00:19 +01:00
DutchEllie f82f72ec1c
Added pronouns
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
2022-03-14 21:40:38 +01:00
DutchEllie 73385c9d39
Added Evillious guide website
continuous-integration/drone/push Build is passing Details
2022-03-14 21:37:22 +01:00
DutchEllie fb7c79f570
omg this layout is so goood 2022-03-14 21:10:10 +01:00
DutchEllie f46005130e
Fixed css failure 2022-03-14 21:05:03 +01:00
DutchEllie a2fbcf03ca
New frontpage design
continuous-integration/drone/push Build is passing Details
2022-03-14 21:04:30 +01:00
61 changed files with 2780 additions and 697 deletions

View File

@ -1,26 +1,54 @@
kind: pipeline
type: kubernetes
name: default
name: staging
trigger:
event:
- push
# STAGING!!!!!!!!!!!!!
# STAGING!!!!
steps:
- name: docker
- name: build-wasm
image: golang:1.17.8-alpine
volumes:
- name: build-staging
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api/testing
CGO_ENABLED: 0
commands:
- mkdir ./build/web
- GOARCH=wasm GOOS=js go build -o ./build/web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-server
image: golang:1.17.8-alpine
volumes:
- name: build-staging
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api/testing
CGO_ENABLED: 0
commands:
- go build -o ./build/app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-publish-image
image: plugins/docker
privileged: true
volumes:
- name: build-staging
path: /drone/src/build
settings:
username:
cache_from:
- "dutchellie/proper-website-2:dev"
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/proper-website-2
build_args:
- APIURL=https://api.nicecock.eu/api/testingcomment
tags:
- dev
- ${DRONE_COMMIT_SHA:0:8}
tags:
- dev-${DRONE_COMMIT_SHA:0:8}
depends_on:
- build-wasm
- build-server
- name: deploy-staging
image: pelotech/drone-helm3
settings:
@ -30,10 +58,9 @@ steps:
release: newsite-staging
skip_tls_verify: true
values_files:
# - .drone/helm/values.yaml
- .drone/helm/staging-val.yaml
values:
- "image=dutchellie/proper-website-2:${DRONE_COMMIT_SHA:0:8}"
- "image=dutchellie/proper-website-2:dev-${DRONE_COMMIT_SHA:0:8}"
kube_api_server:
from_secret: staging_api_server
kube_token:
@ -42,12 +69,18 @@ steps:
from_secret: staging_kube_certificate
kube_service_account: drone-deploy
dry_run: false
depends_on:
- build-publish-image
volumes:
- name: build-staging
temp: {}
---
kind: pipeline
type: kubernetes
name: deploy-prod
name: production
trigger:
event:
@ -55,20 +88,49 @@ trigger:
target:
- production
# PRODUCTION!!!!
steps:
- name: docker
- name: build-wasm
image: golang:1.17.8-alpine
volumes:
- name: build
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api
CGO_ENABLED: 0
commands:
- mkdir ./build/web
- GOARCH=wasm GOOS=js go build -o ./build/web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-server
image: golang:1.17.8-alpine
volumes:
- name: build
path: /drone/src/build
environment:
APIURL: https://api.quenten.nl/api
CGO_ENABLED: 0
commands:
- go build -o ./build/app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
- name: build-publish-image
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
username:
cache_from:
- "dutchellie/proper-website-2:latest"
username:
from_secret: docker_username
password:
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/proper-website-2
build_args:
- APIURL=https://api.nicecock.eu/api/comment
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
tags:
- latest-${DRONE_COMMIT_SHA:0:8}
depends_on:
- build-wasm
- build-server
- name: deploy-production
image: pelotech/drone-helm3
settings:
@ -78,10 +140,9 @@ steps:
release: newsite-production
skip_tls_verify: true
values_files:
# - .drone/helm/values.yaml
- .drone/helm/prod-val.yaml
values:
- "image=dutchellie/proper-website-2:${DRONE_COMMIT_SHA:0:8}"
- "image=dutchellie/proper-website-2:latest-${DRONE_COMMIT_SHA:0:8}"
kube_api_server:
from_secret: prod_api_server
kube_token:
@ -90,3 +151,9 @@ steps:
from_secret: prod_kube_certificate
kube_service_account: drone-deploy
dry_run: false
depends_on:
- build-publish-image
volumes:
- name: build
temp: {}

View File

@ -31,5 +31,9 @@ spec:
ports:
- name: http
containerPort: 8000
{{- if .Values.containerEnv }}
env: {{ toYaml .Values.containerEnv | nindent 12 }}
{{- end }}

View File

@ -6,6 +6,7 @@ podAnnotations: {}
containerName: newsite
image: dutchellie/proper-website-2:latest
imagePullPolicy: Always
containerEnv: []
service:
name: newsite-service

View File

@ -1,17 +1,23 @@
name: newsite-prod
containerEnv:
- name: APIURL
value: https://api.quenten.nl/api
service:
name: newsite-prod
ingress:
name: newsite-prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
external-dns.alpha.kubernetes.io/hostname: "newsite.dutchellie.nl"
external-dns.alpha.kubernetes.io/hostname: "quenten.nl"
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
proxy_hide_header X-Frame-Options ;
tls:
- hosts:
- newsite.dutchellie.nl
- quenten.nl
secretName: newsite-tls
hosts:
- host: newsite.dutchellie.nl
- host: quenten.nl
paths:
- path: /
pathType: Prefix

View File

@ -1,17 +1,23 @@
name: newsite-staging
containerEnv:
- name: APIURL
value: https://api.quenten.nl/api/testing
service:
name: newsite-staging
ingress:
name: newsite-staging
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
external-dns.alpha.kubernetes.io/hostname: "newsite.staging.dutchellie.nl"
external-dns.alpha.kubernetes.io/hostname: "staging.quenten.nl"
nginx.ingress.kubernetes.io/configuration-snippet: |
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
proxy_hide_header X-Frame-Options ;
tls:
- hosts:
- newsite.staging.dutchellie.nl
- staging.quenten.nl
secretName: newsite-staging-tls
hosts:
- host: newsite.staging.dutchellie.nl
- host: staging.quenten.nl
paths:
- path: /
pathType: Prefix

View File

@ -1,16 +1,17 @@
FROM golang:1.17.8-alpine AS builder
ARG APIURL
WORKDIR /project
ADD . /project/
RUN go mod tidy
RUN GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o web/app.wasm
RUN go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o app
#FROM golang:1.17.8-alpine AS builder
#ARG APIURL
#WORKDIR /project
#ADD . /project/
#RUN go mod tidy
#RUN GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
#RUN go build -o app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
FROM alpine:latest AS staging
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /project/web ./web/
COPY --from=builder /project/app ./
WORKDIR /root
RUN mkdir ./web
COPY ./web ./web
COPY ./build/web/app.wasm ./web/
COPY ./build/app ./
EXPOSE 8000
ENV TESTING true
CMD ["./app"]

View File

@ -1,16 +1,26 @@
APIURL_prod := https://api.nicecock.eu/api/comment
APIURL_staging := https://api.nicecock.eu/api/testingcomment
APIURL_prod := https://api.quenten.nl/api
APIURL_staging := https://api.quenten.nl/api/testing
build:
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o web/app.wasm
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o app
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
cp -r web staticsite/
build-new:
GOARCH=wasm GOOS=js go build -o web/app.wasm ./src
go build -o app ./src
cp -r web staticsite/
build-prod:
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o web/app.wasm
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o app
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
cp -r web staticsite/
run: build
./app
run-new: build-new
APIURL=${APIURL_staging} ./app
run-prod: build-prod
./app

View File

@ -1,14 +1,23 @@
# proper-website-2
[![Build Status](https://drone.nicecock.eu/api/badges/DutchEllie/proper-website-2/status.svg)](https://drone.nicecock.eu/DutchEllie/proper-website-2)
[![Build Status](https://drone.dutchellie.nl/api/badges/DutchEllie/proper-website-2/status.svg)](https://drone.dutchellie.nl/DutchEllie/proper-website-2)
A truly proper website this time™
**TODO**:
- Redo the entire CSS with a framework
- Change domain to quenten.nl and staging.quenten.nl
- Dynamically make domains for other branches
- Make a generic page component, so that you don't have to add the standard panels every time (such as navbar, leftbar, header, etc)
App notes:
- leftbar (under nav) is default always the same.
This is in a standardized "page" component.
it can be changed by specifying it when creating the "page" component, or maybe in a prerender/onnav (or both).
Structure will be:
Generic page component IS IMPLEMENTED BY "specific page" WHERE OnNav/OnPreRender specify its details WHICH IS CREATED with its own "newXPage()" function. See https://github.com/maxence-charriere/go-app/blob/master/docs/src/actions-page.go for example.
- "main content" is an array of app.UI elements.
This can also be further abstracted using "block" components (the ones with the cool color)
- THOSE BLOCK COMPONENTS' CONTENT can be set manually on creation, OR by passing HTML/MarkDown (not decided yet)
This website will be done with this:
- Backend written in Golang

62
api/spyware.go Normal file
View File

@ -0,0 +1,62 @@
package api
import (
"log"
"net/http"
"net/http/cookiejar"
"github.com/google/uuid"
"github.com/gorilla/mux"
"golang.org/x/net/publicsuffix"
)
type ApiApplication interface {
NewRouter() *mux.Router
Visit(w http.ResponseWriter, r *http.Request)
}
func NewApiApp() (ApiApplication, error) {
cookiejar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
return nil, err
}
return &apiapp{
cj: cookiejar,
}, nil
}
type apiapp struct {
router mux.Router
cj *cookiejar.Jar
}
func (a *apiapp) NewRouter() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/visit", a.Visit)
return router
}
// Called when someone visits any page of the website
// Calls for the spyware cookie and sets it if it doesn't yet exist
func (a *apiapp) Visit(w http.ResponseWriter, r *http.Request) {
log.Printf("Visit called\n")
c, err := r.Cookie("spyware")
if err != nil && err != http.ErrNoCookie {
log.Printf("Error: %s\n", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
} else if err == http.ErrNoCookie {
// Create cookie and send it
log.Printf("No cookie sent by client, sending cookie to them!\n")
c = &http.Cookie{Name: "spyware", Value: uuid.NewString(), Path: "/", MaxAge: 0}
http.SetCookie(w, c)
}
w.WriteHeader(200)
w.Write([]byte("yeet"))
log.Printf("Someone visited: %s\n", c.Value)
}

View File

@ -1,47 +0,0 @@
package components
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type AboutPage struct {
app.Compo
}
func NewAboutPage() *AboutPage {
return &AboutPage{}
}
func (a *AboutPage) Render() app.UI {
return app.Div().Body(
&header{},
&navbar{},
&aboutPanel{},
)
}
type aboutPanel struct {
app.Compo
aboutText string
}
func (a *aboutPanel) Render() app.UI {
return app.Div().Body(
app.Img().Src("/web/static/images/rin-1.gif").Styles(map[string]string{"width": "100px", "position": "absolute", "top": "10px", "right": "10px"}),
app.Raw(`<p class="content-text">I am a 21 year old computer science student, living and studying in The Netherlands. I like Docker, Kubernetes and Golang!
<br>
I made this website because I was inspired again by the amazing Neocities pages that I discovered because of my friends.
They also have their own pages (you can find them on the friends tab, do check them out!) and I just had to get a good website of my own!
<br>
I am not that great at web development, especially design, but I love trying it regardless!
<br><br>
To say a bit more about me personally, I love all things computers. From servers to embedded devices! I love the cloud and all that it brings
(except for big megacorps, but alright) and it's my goal to work for a big cloud company!
<br>
Aside from career path ambitions, ボーカロイドはすきです I love vocaloid and other Japanese music and culture!!
I also like Vtubers, especially from Hololive and it's my goal to one day finally understand them in their native language!
<br><br>
There is a lot more to say in words, but who cares about those! Have a look around my creative digital oasis and see what crazy stuff you can find!</p>`),
).Class("content")
}

View File

@ -1,23 +0,0 @@
package components
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type contentView struct {
app.Compo
panels []app.UI
}
func newContentView(panels ...app.UI) *contentView {
return &contentView{panels: panels}
}
func (c *contentView) Render() app.UI {
return app.Div().Body(
app.Range(c.panels).Slice(func(i int) app.UI {
return c.panels[i]
}),
)
}

View File

@ -1,81 +0,0 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type GalaxiesPage struct {
app.Compo
}
func NewGalaxiesPage() *GalaxiesPage {
return &GalaxiesPage{}
}
func (f *GalaxiesPage) Render() app.UI {
return app.Div().Body(
&header{},
&navbar{},
&galaxiesPanel{},
).Class("main")
}
type galaxiesPanel struct {
app.Compo
}
func (b *galaxiesPanel) Render() app.UI {
return app.Div().Body(
app.P().
Text(`Galaxies`).
Class("p-h1"),
app.P().
Class("content-text").
Text(`Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!`),
app.Div().
Body(
app.P().
Class("p-h2 m-tb10").
Text("My friends!"),
app.Ul().Body(
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://forestofunix.xyz").
Class("p-h3 m-t5").
Text("Forest of Unix"),
app.P().
Class("m-t5").
Text(`A website by Sebastiaan. A massive Linux fanboy, runs Gentoo on his
ThinkPad. Absolutely based.`),
),
),
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://nymphali.neocities.org").
Class("p-h3 m-t5").
Text("Nymphali"),
app.P().
Class("m-t5").
Text(`The website made by , whoops Nymphali. They have an awesome
minimalist website that's just lovely.`),
),
),
app.Li().Body(
app.Div().Body(
// TODO: Create a modal popup for each name!!!
app.A().Href("https://kristypixel.neocities.org").
Class("p-h3 m-t5").
Text("Kristy"),
app.P().
Class("m-t5").
Text(`Website made by Kristy. Very cute website, I love it! Keep up the
awesome work!`),
),
),
),
),
).Class("content")
}

View File

@ -1,102 +0,0 @@
package components
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type guestbookForm struct {
app.Compo
name string
message string
gbModalOpen bool
OnSubmit func(
name string,
message string,
) // Handler to implement which calls the api
}
func (g *guestbookForm) Render() app.UI {
return app.Div().Body(
app.Form().Body(
app.Input().
Type("text").
Name("name").
Placeholder("Name").
Required(true).
OnChange(g.ValueTo(&g.name)).
Value(g.name),
app.Input().
Type("text").
Name("message").
Placeholder("Message").
Required(true).
OnChange(g.ValueTo(&g.message)).
Value(g.message),
app.Input().
Type("submit").
Name("submit"),
).ID("form").
OnSubmit(func(ctx app.Context, e app.Event) {
// This was to prevent the page from reloading
e.PreventDefault()
if g.name == "" || g.message == "" {
fmt.Printf("Error: one or more field(s) are empty. For now unhandled\n")
return
}
if len(g.name) > 40 || len(g.message) > 360 {
fmt.Printf("Error: Your message is too long fucker\n")
g.gbModalOpen = true
return
}
g.OnSubmit(g.name, g.message)
g.clear()
}),
app.If(
g.gbModalOpen,
&guestbookAlertModal{
OnClose: func() {
g.gbModalOpen = false
g.Update()
},
},
),
).Class("content")
}
func (g *guestbookForm) clear() {
g.name = ""
g.message = ""
}
type guestbookAlertModal struct {
app.Compo
PreviousAttempts int
OnClose func() // For when we close the modal
}
func (g *guestbookAlertModal) Render() app.UI {
return app.Div().
Class("gb-modal").
ID("gbModal").
OnClick(func(ctx app.Context, e app.Event) {
g.OnClose()
}).
Body(
app.Div().
Class("gb-modal-content").
Body(
app.Span().Class("close").Text("X").
OnClick(func(ctx app.Context, e app.Event) {
//modal := app.Window().GetElementByID("gbModal")
//modal.Set("style", "none")
g.OnClose()
}),
app.P().Text("Your name must be <= 40 and your message must be <= 360 characters"),
),
)
}

View File

@ -1,87 +0,0 @@
package components
import (
"encoding/json"
"io"
"net/http"
"time"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
/*
What is this supposed to do:
- It should call on the API to give it a certain number of comments, in the range x to y (this has to be implemented in the api)
- When it has called that, it should display those
- Dynamic links are there to navigate the user along the pages
- Comments are shown dynamically
- This panel can be shown or hidden (maybe)
AND VERY IMPORTANT!
- If a user submits a new comment, automatically put it on the page, no reloading
*/
type guestbookPanel struct {
app.Compo
comments []entity.Comment
}
func newGuestbookPanel() *guestbookPanel {
g := &guestbookPanel{}
g.LoadComments()
return g
}
func (g *guestbookPanel) Render() app.UI {
return app.Div().Body(
app.Range(g.comments).Slice(func(i int) app.UI {
return &guestbookComment{
Comment: g.comments[i],
}
}),
).Class("content gbp")
}
func (g *guestbookPanel) LoadComments() {
// TODO: maybe you can put this in a localbrowser storage?
url := ApiURL
res, err := http.Get(url)
if err != nil {
app.Log(err)
return
}
defer res.Body.Close()
jsondata, err := io.ReadAll(res.Body)
if err != nil {
app.Log(err)
return
}
err = json.Unmarshal(jsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
}
type guestbookComment struct {
app.Compo
Comment entity.Comment
time string
}
func (c *guestbookComment) Render() app.UI {
c.time = c.Comment.PostDate.Format(time.RFC1123)
return app.Div().Body(
app.Div().Class().Body(
app.P().Text(c.Comment.Name).Class("name"),
app.P().Text(c.time).Class("date"),
).Class("comment-header"),
app.Div().Class("comment-message").Body(
app.P().Text(c.Comment.Message),
),
).Class("comment")
}

View File

@ -1,66 +0,0 @@
package components
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
var (
ApiURL string
)
type Homepage struct {
app.Compo
showGuestbook bool
page string
}
func NewHomepage() *Homepage {
return &Homepage{showGuestbook: true, page: "home"}
}
func (p *Homepage) Render() app.UI {
gbp := newGuestbookPanel()
return app.Div().Body(
&header{},
&navbar{},
&homePanel{
onShowClick: func() {
p.showGuestbook = !p.showGuestbook
},
},
&guestbookForm{
OnSubmit: func(name, message string) {
var comment entity.Comment
comment.Name = name
comment.Message = message
jsondata, err := json.Marshal(comment)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
url := ApiURL
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
if req.StatusCode == 200 {
p.Update()
}
defer req.Body.Close()
},
},
//app.If(p.showGuestbook, gbp),
gbp.Render(),
).Class("main")
}

View File

@ -1,42 +0,0 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type homePanel struct {
app.Compo
onShowClick func()
updateAvailable bool
}
func newHomePanel() *homePanel {
return &homePanel{}
}
func (p *homePanel) Render() app.UI {
return app.Div().Body(
app.P().Text("Welcome, internet surfer!").Class("p-h1"),
app.Raw(`<p class="content-text">This website is my creative outlet and a way of expressing myself.
As of now, it's probably the most impressive thing I've ever coded.
<br><br>
Please enjoy yourself and do sign the guestbook!!</p>`),
app.If(p.updateAvailable,
app.Div().Body(
app.P().
Class("content-text").
Text("An update is available! Reload to update!"),
)),
app.Div().Body(
app.P().Text("Please sign my guestbook!").Class("small"),
app.Img().Src("/web/static/images/email3.gif").Style("width", "40px").Style("position", "absolute").Style("bottom", "0px").Style("right", "0px"),
).Style("position", "absolute").Style("bottom", "5px").Style("right", "5px").
OnClick(func(ctx app.Context, e app.Event) {
e.PreventDefault()
p.onShowClick()
}),
).Class("content")
}
func (p *homePanel) OnAppUpdate(ctx app.Context) {
p.updateAvailable = true
}

View File

@ -1,25 +0,0 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type navbar struct {
app.Compo
OnClickButton func(page string)
}
func (n *navbar) Render() app.UI {
return app.Div().Body(
app.Ul().Body(
app.Li().Body(
app.A().Href("/").Text("Home"),
),
app.Li().Body(
app.A().Href("/about").Text("About"),
),
app.Li().Body(
app.A().Href("/galaxies").Text("Galaxies"),
),
),
).Class("navbar")
}

View File

@ -1,27 +0,0 @@
package components
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type updater struct {
app.Compo
updateAvailable bool
}
func (u *updater) onAppUpdate(ctx app.Context) {
u.updateAvailable = ctx.AppUpdateAvailable()
}
func (u *updater) Render() app.UI {
return app.Div().Body(
app.If(u.updateAvailable,
app.Div().Body(
app.P().Text("An update for this website is available! Please click here to reload!"),
).Styles(map[string]string{"position": "absolute", "width": "100px", "bottom": "10px", "right": "10px"}).OnClick(u.onUpdateClick),
),
)
}
func (u *updater) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}

15
entity/blogpost.go Normal file
View File

@ -0,0 +1,15 @@
package entity
import "time"
// Blogpost contains the name, date and such of a blogpost
// The actual post itself is hosted somewhere else in the form of an (html) document
// This is put in the path field
type BlogPost struct {
ID string `json:"id,omitempty" bson:"_id,omitempty"`
Title string `json:"title" bson:"title"`
Author string `json:"author" bson:"author"`
Path string `json:"path" bson:"path"`
PostDate time.Time `json:"time" bson:"time"`
}

View File

@ -9,4 +9,6 @@ type Comment struct {
Email string `json:"email,omitempty" bson:"email,omitempty"`
Message string `json:"message" bson:"message"`
PostDate time.Time `json:"time" bson:"time"`
UUID string `json:"uuid" bson:"uuid"`
}

13
go.mod
View File

@ -2,6 +2,15 @@ module dutchellie.nl/DutchEllie/proper-website-2
go 1.17
require github.com/maxence-charriere/go-app/v9 v9.3.3
require (
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/maxence-charriere/go-app/v9 v9.3.3
golang.org/x/net v0.0.0-20220622184535-263ec571b305
)
require github.com/google/uuid v1.3.0 // indirect
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
)

15
go.sum
View File

@ -1,9 +1,16 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/maxence-charriere/go-app/v9 v9.3.3 h1:vo+1oohWMfTQ0S3eg9JOjuFEy1n3bVNgeDi4eHNfMzA=
github.com/maxence-charriere/go-app/v9 v9.3.3/go.mod h1:zo0n1kh4OMKn7P+MrTUUi7QwUMU2HOfHsZ293TITtxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -13,10 +20,16 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I=
golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

99
main.go
View File

@ -1,99 +0,0 @@
package main
import (
"log"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/components"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//type application struct {
// client *mongo.Client
// database *mongo.Database
// collection *mongo.Collection
//}
const (
apiurl = "https://quenten.nl:8007/"
)
func main() {
homepage := components.NewHomepage()
aboutpage := components.NewAboutPage()
galaxiespage := components.NewGalaxiesPage()
app.Route("/", homepage)
app.Route("/about", aboutpage)
app.Route("/galaxies", galaxiespage)
// This is executed on the client side only.
// It handles client side stuff
// It exits immediately on the server side
app.RunWhenOnBrowser()
icon := &app.Icon{
Default: "/web/static/images/icon-small.png",
Large: "/web/static/images/icon.png",
}
handler := &app.Handler{
Name: "Internetica Galactica",
Icon: *icon,
BackgroundColor: "#362730",
ThemeColor: "#362730",
LoadingLabel: "Internetica Galactica",
Title: "Internetica Galactica",
Description: "A 1990's style PWA!",
Author: "Quenten",
Keywords: []string{
"Based website",
"Cool website",
"PWA",
"Programming",
"Go", "Golang",
"Webassembly", "WASM",
"DutchEllie", "Quenten",
},
Styles: []string{
"/web/static/style.css",
"/web/static/adreena.css",
"/web/static/anisha.css",
"/web/static/havakana.css",
},
CacheableResources: []string{},
}
app.GenerateStaticWebsite("./staticsite", handler)
/*
uri := "mongodb+srv://guestbook-database:5WUDzpvBKBBiiMCy@cluster0.wtt64.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
if err != nil {
fmt.Println(err)
return
}
defer func() {
if err = client.Disconnect(context.TODO()); err != nil {
panic(err)
}
}()
// Ping the primary
if err := client.Ping(context.TODO(), readpref.Primary()); err != nil {
panic(err)
}
db := client.Database("guestbook")
coll := db.Collection("comments")
apiapp := &application{
client: client,
database: db,
collection: coll,
}
*/
http.Handle("/", handler)
//http.HandleFunc("/api/comment", apiapp.Comment)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}

30
src/aboutpage.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type AboutPage struct {
app.Compo
}
func NewAboutPage() *AboutPage {
return &AboutPage{}
}
func (a *AboutPage) Render() app.UI {
return newPage().
Title("About me").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/about.html"),
)
}

86
src/block.go Normal file
View File

@ -0,0 +1,86 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type htmlBlock struct {
app.Compo
Iclass string
Isrc string // HTML document source
Iid string
// TODO: implement invisibility for other background functions
}
func newHTMLBlock() *htmlBlock {
return &htmlBlock{}
}
func (b *htmlBlock) ID(v string) *htmlBlock {
b.Iid = v
return b
}
func (b *htmlBlock) Class(v string) *htmlBlock {
b.Iclass = app.AppendClass(b.Iclass, v)
return b
}
func (b *htmlBlock) Src(v string) *htmlBlock {
b.Isrc = v
return b
}
func (b *htmlBlock) Render() app.UI {
return app.Div().
Class("block").
Class(b.Iclass).
Body(
newRemoteHTMLDoc().
Src(b.Isrc),
)
}
// ==================
// UI element block
// ==================
type uiBlock struct {
app.Compo
Iclass string
Iui []app.UI
Iid string
}
func newUIBlock() *uiBlock {
return &uiBlock{}
}
func (b *uiBlock) ID(v string) *uiBlock {
b.Iid = v
return b
}
func (b *uiBlock) Class(v string) *uiBlock {
b.Iclass = app.AppendClass(b.Iclass, v)
return b
}
func (b *uiBlock) UI(v ...app.UI) *uiBlock {
b.Iui = app.FilterUIElems(v...)
return b
}
func (b *uiBlock) Render() app.UI {
return app.Div().
Class("block").
Class(b.Iclass).
Body(
app.Range(b.Iui).Slice(func(i int) app.UI {
return b.Iui[i]
}),
)
}

74
src/bloglinks.go Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"encoding/json"
"io"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type blogLinks struct {
app.Compo
blogposts []entity.BlogPost
}
func (b *blogLinks) OnMount(ctx app.Context) {
b.LoadPosts(ctx)
}
func (b *blogLinks) Render() app.UI {
return newUIBlock().
Class("left").
Class("leftbarblock").
Class("blogpost-bar").
UI(
app.P().
Class("p-h3").
Style("margin-left", "10px").
Style("margin-top", "10px").
Style("text-decoration", "underline").
Text("Posts"),
app.Range(b.blogposts).Slice(func(i int) app.UI {
return app.P().
Class("blogpost-titles").
Style("cursor", "pointer").
Text(b.blogposts[i].Title).
OnClick(func(ctx app.Context, e app.Event) {
//e.PreventDefault()
// Calling the update-blogpost action defined in the blogpage.go file.
// There it's updated
ctx.NewAction("update-blogpost", app.T("blogpost", b.blogposts[i].Path))
})
}),
)
}
func (b *blogLinks) LoadPosts(ctx app.Context) {
// TODO: maybe you can put this in a localbrowser storage?
url := ApiURL + "/blogpost"
ctx.Async(func() {
res, err := http.Get(url)
if err != nil {
app.Log(err)
return
}
defer res.Body.Close()
jsondata, err := io.ReadAll(res.Body)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(jsondata, &b.blogposts)
if err != nil {
app.Log(err)
return
}
})
})
}

54
src/blogpage.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type BlogPage struct {
app.Compo
display bool
currentPostPath string
}
// TODO: write the backend API for this
// TODO: Find a proper way of rendering blog posts in markdown
// Backend ideas: In the DB, create an entry for each post and a link to where the html file is located!
// That way, I don't have to parse and render markdown!!
// Layout, the leftbar contains the blogpost links and the mainbar contains the post itself!
// Function: After pressing a link for a blog post, that blog post ID gets put in the state instead of the URL
func NewBlogPage() *BlogPage {
return &BlogPage{}
}
func (b *BlogPage) OnMount(ctx app.Context) {
ctx.Handle("update-blogpost", b.onUpdateBlogPost)
b.display = false
}
func (b *BlogPage) Render() app.UI {
return newPage().
Title("Blog").
LeftBar(
&blogLinks{},
).
Main(
app.If(b.display,
newHTMLBlock().
Class("right").
Class("contentblock").
Src(b.currentPostPath),
),
)
}
func (b *BlogPage) onUpdateBlogPost(ctx app.Context, a app.Action) {
fmt.Printf("Called the update-blogpost ActionHandler\n")
blogpost := a.Tags.Get("blogpost")
b.currentPostPath = blogpost
b.display = true
}

15
src/emptypage.go Normal file
View File

@ -0,0 +1,15 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type EmptyPage struct {
app.Compo
}
func NewEmptyPage() *EmptyPage {
return &EmptyPage{}
}
func (e *EmptyPage) Render() app.UI {
return app.Head().Body()
}

137
src/galaxiespage.go Normal file
View File

@ -0,0 +1,137 @@
package main
import (
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type GalaxiesPage struct {
app.Compo
gnuOver bool
mousex, mousey int
}
func NewGalaxiesPage() *GalaxiesPage {
return &GalaxiesPage{}
}
func (f *GalaxiesPage) Render() app.UI {
return newPage().
Title("Galaxies").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
/*
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/galaxies.html"),
*/
newUIBlock().
Class("right").
Class("contentblock").
UI(
app.Div().
Body(
app.P().
Class("p-h1").
Text("Galaxies"),
app.P().
Class("content-text").
Text(`Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!`),
app.Div().
Body(
app.P().
Class("p-h2").
Class("mt-20").
Class("mb-10").
Class("bold").
Text("My friends!"),
app.Ul().
Body(
app.Li().
Body(
newLinkWithStatus().
Link("https://forestofunix.xyz").
LinkText("Forest of Unix").
Text("A website made by Sebastiaan. A massive Linux fanboy, runs Gentoo on his ThinkPad. Absolutely based. His website is written in Lisp, that's why it's often offline. That was the inspiration for the online/offline status text."),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://nymphali.neocities.org").
LinkText("Nymphali").
Text("The website made by ■■■■■■, whoops Nymphali. They have an awesome minimalist website that's just lovely."),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://kristypixel.neocities.org").
LinkText("Kristypixel").
Text("Website made by Kristy. Very cute website, I love it! Keep up the awesome work!"),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://leftonred.neocities.org").
LinkText("Left on Red").
Text("Kyu made this website, he's a friend of mine as well! Still very new, but their dark mode design is very cool!"),
),
),
),
app.Div().
Body(
app.P().
Class("p-h2").
Class("mt-20").
Class("mb-10").
Class("bold").
Text("Neat webspaces"),
app.P().
Class("m-t5").
Style("margin-left", "10px").
Text("Just very neat websites I found and causes I support. Not necessarily by people I know. I just wanted to share them here!"),
app.Ul().
Body(
app.Li().
Body(
newLinkWithStatus().
Link("https://evillious.ylimegirl.com").
LinkText("Evillious Chronicles fan guide").
Text("A VERY cool website made by Ylimegirl! They wrote a whole website dedicated to Evillious Chronicles, which is a super good Japanese light novel and vocaloid series!! Definitely look it up!"),
),
app.Li().
Body(
newLinkWithStatus().
Link("https://www.gnu.org").
LinkBody(
newTextWithTooltip().
Text("The GNU Project").
Tooltip(
app.Img().
Src("/web/static/images/gnu-head-sm.png").
Width(129).
Height(122).
Alt("GNU"),
),
).
Text("The official website of the GNU project. They advocate for free/libre software. This is not to be confused with 'open source' software. I highly recommend you read about them and their efforts."),
),
),
),
),
),
)
}
/*
func (f *GalaxiesPage) onMouseOverGnu(ctx app.Context, e app.Event) {
}*/

387
src/guestbook.go Normal file
View File

@ -0,0 +1,387 @@
package main
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type guestbook struct {
app.Compo
comments []entity.Comment
name string
email string
website string
message string
lastHash [32]byte
gbModalOpen bool
gbErrorModalOpen bool
errorText string
OnSubmit func(
ctx app.Context,
name string,
email string,
website string,
message string,
uuid string,
) // Handler to implement which calls the api
}
// TODO: The comments are loaded like 2 or 3 times every time the page is loaded...
func (g *guestbook) OnMount(ctx app.Context) {
ctx.Handle("guestbook-loadcomments", g.onHandleLoadComments)
ctx.NewAction("guestbook-loadcomments")
}
/*
func (g *guestbook) OnNav(ctx app.Context) {
g.LoadComments(ctx)
}
func (g *guestbook) OnUpdate(ctx app.Context) {
g.LoadComments(ctx)
}*/
func (g guestbook) Render() app.UI {
return app.Div().Body(
app.Form().
Class("guestbook-form").
Body(
app.Div().
Class("input-groups").
Body(
app.Div().
Class("fr").
Body(
app.Div().
Class("input-group").
Class("input-group-name").
Body(
app.Label().
For("name").
Text("Name:"),
app.Input().
Type("text").
Name("name").
Class("input").
Required(true).
OnChange(g.ValueTo(&g.name)),
),
app.Div().
Class("input-group").
Class("input-group-email").
Body(
app.Label().
For("email").
Text("Email: (optional)"),
app.Input().
Type("text").
Name("email").
Class("input").
Required(false).
OnChange(g.ValueTo(&g.email)),
),
),
app.Div().
Class("input-group").
Class("input-group-website").
Body(
app.Label().
For("website").
Text("Website: (optional)"),
app.Input().
Type("text").
Name("website").
Class("input").
Required(false).
OnChange(g.ValueTo(&g.website)),
),
app.Div().
Class("input-group").
Class("input-group-message").
Body(
app.Label().
For("message").
Text("Message:"),
app.Textarea().
Name("message").
Class("input").
Rows(5).
Cols(30).
Required(true).
OnChange(g.ValueTo(&g.message)),
),
app.Div().
Class("submit-field").
Body(
app.Input().
Type("submit").
Value("Send!"),
),
),
).OnSubmit(func(ctx app.Context, e app.Event) {
// This was to prevent the page from reloading
fmt.Println("Send clicked")
e.PreventDefault()
if g.name == "" || g.message == "" {
fmt.Printf("Error: one or more field(s) are empty. For now unhandled\n")
g.gbErrorModalOpen = true
g.errorText = "One or more field(s) are empty. Fix that shit man."
return
}
fmt.Println("Nothing too empty")
if len(g.name) > 40 || len(g.message) > 360 {
fmt.Printf("Error: Your message is too long fucker\n")
g.gbModalOpen = true
return
}
fmt.Println("Got through validity")
var uuid string = ""
// c := client.Jar.Cookies(app.Window().URL())
// fmt.Println("Getting cookies")
// for _, c2 := range c {
// if c2.Name == "spyware" {
// uuid = c2.Value
// fmt.Println("Found spyware cookie")
// }
// }
// // Check if uuid is set, if it's not, then clearly the cookie does not exist
// if uuid == "" {
// uuid = "undetermined"
// g.errorText = "Failed sending message, couldn't insert UUID"
// g.gbErrorModalOpen = true
// return
// }
g.OnSubmit(ctx, g.name, g.email, g.website, g.message, uuid)
g.clear()
ctx.NewAction("guestbook-loadcomments")
//g.LoadComments(ctx)
}),
app.If(
g.gbModalOpen,
NewGuestbookAlertModal().
OnClose(func() {
g.gbModalOpen = false
g.Update()
}).
Text("Your name must be <= 40 and your message must be <= 360 characters"),
),
app.If(
g.gbErrorModalOpen,
NewGuestbookAlertModal().
OnClose(func() {
g.gbErrorModalOpen = false
g.Update()
}).
Text(fmt.Sprintf("Error placing comment: %s", g.errorText)),
),
app.Div().Body(
app.Range(g.comments).Slice(func(i int) app.UI {
return &guestbookComment{
Comment: g.comments[i],
}
},
),
),
)
}
func (g *guestbook) SmartLoadComments(ctx app.Context) {
{
// Get the comments quickly to at least render something
tmpjsondata := make([]byte, 0)
err := ctx.LocalStorage().Get("comments", &tmpjsondata)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(tmpjsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
})
g.Update()
}
var lasthash []byte
err := ctx.LocalStorage().Get("lasthash", &lasthash)
if err != nil {
app.Log(err)
return
}
if lasthash == nil {
fmt.Printf("Program thinks lasthash is empty\n")
g.LoadComments(ctx)
return
}
url := ApiURL + "/commenthash"
ctx.Async(func() {
res, err := http.Get(url)
if err != nil {
app.Log(err)
return
}
defer res.Body.Close()
hash, err := io.ReadAll(res.Body)
if err != nil {
app.Log(err)
return
}
//fmt.Printf("hash: %v\n", hash)
//fmt.Printf("lasthash: %v\n", lasthash)
// If the hash is different, aka there was an update in the comments
if !bytes.Equal(hash, lasthash) {
fmt.Printf("Hash calculation is different\n")
g.LoadComments(ctx)
return
}
// if the hash is not different, then there is no need to load new comments
jsondata := make([]byte, 0)
err = ctx.LocalStorage().Get("comments", &jsondata)
if err != nil {
app.Log(err)
return
}
//fmt.Printf("jsondata: %v\n", jsondata)
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(jsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
})
return
})
}
func (g *guestbook) LoadComments(ctx app.Context) {
// TODO: maybe you can put this in a localbrowser storage?
//fmt.Printf("Called LoadComments()\n")
url := ApiURL + "/comment"
ctx.Async(func() {
res, err := http.Get(url)
if err != nil {
app.Log(err)
return
}
defer res.Body.Close()
jsondata, err := io.ReadAll(res.Body)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
err = json.Unmarshal(jsondata, &g.comments)
if err != nil {
app.Log(err)
return
}
})
ctx.LocalStorage().Set("comments", jsondata)
// Calculating the hash
//fmt.Printf("Calculating the hash from LoadComments\n")
hash := sha256.Sum256(jsondata)
//fmt.Printf("hash fresh from calculation: %v\n", hash)
//g.lastHash = hash
err = ctx.LocalStorage().Set("lasthash", []byte(fmt.Sprintf("%x\n", hash)))
if err != nil {
app.Log(err)
return
}
})
}
func (g *guestbook) clear() {
g.name = ""
g.message = ""
g.website = ""
g.email = ""
}
func (g *guestbook) onHandleLoadComments(ctx app.Context, a app.Action) {
g.SmartLoadComments(ctx)
g.Update()
}
type guestbookAlertModal struct {
app.Compo
PreviousAttempts int
IOnClose func() // For when we close the modal
IText string
}
func NewGuestbookAlertModal() *guestbookAlertModal {
return &guestbookAlertModal{}
}
func (g *guestbookAlertModal) OnClose(v func()) *guestbookAlertModal {
g.IOnClose = v
return g
}
func (g *guestbookAlertModal) Text(v string) *guestbookAlertModal {
g.IText = v
return g
}
func (g *guestbookAlertModal) Render() app.UI {
return app.Div().
Class("gb-modal").
ID("gbModal").
OnClick(func(ctx app.Context, e app.Event) {
g.IOnClose()
}).
Body(
app.Div().
Class("gb-modal-content").
Body(
app.Span().Class("close").Text("X").
OnClick(func(ctx app.Context, e app.Event) {
//modal := app.Window().GetElementByID("gbModal")
//modal.Set("style", "none")
g.IOnClose()
}),
app.P().Text(g.IText),
),
)
}
type guestbookComment struct {
app.Compo
Comment entity.Comment
time string
}
func (c *guestbookComment) Render() app.UI {
c.time = c.Comment.PostDate.Format(time.RFC1123)
return app.Div().Body(
app.Div().Class().Body(
app.P().Text(c.Comment.Name).Class("name"),
app.P().Text(c.time).Class("date"),
).Class("comment-header"),
app.Div().Class("comment-message").Body(
app.P().Text(c.Comment.Message),
),
).Class("comment")
}

View File

@ -1,4 +1,4 @@
package components
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
@ -7,5 +7,10 @@ type header struct {
}
func (h *header) Render() app.UI {
return app.Div().Text("Internetica Galactica").Class("header")
return app.Div().
Class("header").
Body(
app.Text("Internetica Galactica"),
//&updater{},
)
}

77
src/homepage.go Normal file
View File

@ -0,0 +1,77 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"dutchellie.nl/DutchEllie/proper-website-2/entity"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
var (
ApiURL string
)
type Homepage struct {
app.Compo
}
func NewHomepage() *Homepage {
return &Homepage{}
}
func (p *Homepage) Render() app.UI {
return newPage().
Title("Homepage").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/intro.html"),
newUIBlock().
Class("right").
Class("contentblock").
UI(
&guestbook{
OnSubmit: func(ctx app.Context, name, email, website, message, uuid string) {
var comment entity.Comment
comment.Name = name
comment.Email = email
comment.Website = website
comment.Message = message
comment.UUID = uuid
jsondata, err := json.Marshal(comment)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
url := ApiURL + "/comment"
// This is not Async'ed, because otherwise you run into a race
// condition where you reload the comments before the server had time
// to process the request!
{
req, err := http.Post(url, "application/json", bytes.NewBuffer(jsondata))
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
if req.StatusCode == 200 {
p.Update()
}
defer req.Body.Close()
}
},
},
),
)
}

75
src/html-doc.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type htmlDoc struct {
app.Compo
Ihtml string
}
func newHTMLDoc() *htmlDoc {
return &htmlDoc{}
}
func (h *htmlDoc) HTML(v string) *htmlDoc {
h.Ihtml = fmt.Sprintf("<div>%s</div>", v)
return h
}
func (h *htmlDoc) Render() app.UI {
return app.Raw(h.Ihtml)
}
type remoteHTMLDoc struct {
app.Compo
Isrc string
html htmlContent
}
func newRemoteHTMLDoc() *remoteHTMLDoc {
return &remoteHTMLDoc{}
}
func (h *remoteHTMLDoc) Src(v string) *remoteHTMLDoc {
h.Isrc = v
return h
}
func (h *remoteHTMLDoc) OnMount(ctx app.Context) {
h.load(ctx)
}
func (h *remoteHTMLDoc) OnNav(ctx app.Context) {
h.load(ctx)
}
func (h *remoteHTMLDoc) load(ctx app.Context) {
src := h.Isrc
ctx.ObserveState(htmlState(src)).
While(func() bool {
return src == h.Isrc
}).
OnChange(func() {
}).
Value(&h.html)
ctx.NewAction(getHTML, app.T("path", h.Isrc))
}
func (h *remoteHTMLDoc) Render() app.UI {
return app.Div().
Body(
app.If(h.html.Status == loaded,
newHTMLDoc().
HTML(h.html.Data),
).Else(),
)
}

63
src/html.go Normal file
View File

@ -0,0 +1,63 @@
package main
import (
"errors"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
const (
getHTML = "/html/get"
)
func handleGetHTML(ctx app.Context, a app.Action) {
path := a.Tags.Get("path")
if path == "" {
app.Log(errors.New("getting html failed"))
return
}
state := htmlState(path)
var ht htmlContent
ctx.GetState(state, &ht)
switch ht.Status {
case loading, loaded:
return
}
ht.Status = loading
ht.Error = nil
ctx.SetState(state, ht)
res, err := get(ctx, path)
if err != nil {
ht.Status = loadingErr
ht.Error = err
ctx.SetState(state, ht)
return
}
ht.Status = loaded
ht.Data = string(res)
ctx.SetState(state, ht)
}
func htmlState(src string) string {
return src
}
type htmlContent struct {
Status status
Error error
Data string
}
type status int
const (
neverLoaded status = iota
loading
loadingErr
loaded
)

41
src/http.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"fmt"
"io"
"net/http"
"strings"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
func get(ctx app.Context, path string) ([]byte, error) {
url := path
if !strings.HasPrefix(url, "http") {
u := ctx.Page().URL()
u.Path = path
url = u.String()
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Printf("Error at getting html page\n")
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
// Which means either client or server error
if res.StatusCode >= 400 {
return nil, err
}
b, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
return b, nil
}

129
src/main.go Normal file
View File

@ -0,0 +1,129 @@
package main
import (
"compress/gzip"
"log"
"net/http"
"net/http/cookiejar"
"os"
"dutchellie.nl/DutchEllie/proper-website-2/api"
"github.com/gorilla/handlers"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//type application struct {
// client *mongo.Client
// database *mongo.Database
// collection *mongo.Collection
//}
var jar http.CookieJar
var client http.Client
func main() {
// Create cookiejar
var err error
jar, err = cookiejar.New(nil)
if err != nil {
log.Fatalf("Error creating cookiejar: %s\n", err.Error())
}
client = http.Client{
Jar: jar,
}
homepage := NewHomepage()
aboutpage := NewAboutPage()
galaxiespage := NewGalaxiesPage()
undertalePage := NewUndertalePage()
musicPage := NewMusicPage()
app.Route("/", homepage)
app.Route("/about", aboutpage)
app.Route("/galaxies", galaxiespage)
app.Route("/undertale", undertalePage)
app.Route("/blog", NewBlogPage())
app.Route("/music", musicPage)
app.Handle(getHTML, handleGetHTML)
// This is executed on the client side only.
// It handles client side stuff
// It exits immediately on the server side
app.RunWhenOnBrowser()
icon := &app.Icon{
Default: "/web/static/images/icon-small.png",
Large: "/web/static/images/icon.png",
}
handler := &app.Handler{
Name: "Internetica Galactica",
Icon: *icon,
BackgroundColor: "#362730",
ThemeColor: "#362730",
LoadingLabel: "Internetica Galactica",
Title: "Internetica Galactica",
Description: "A 1990's style PWA!",
Author: "Quenten",
Keywords: []string{
"Based website",
"Cool website",
"PWA",
"Programming",
"Go", "Golang",
"Webassembly", "WASM",
"DutchEllie", "Quenten",
},
Styles: []string{
"/web/static/style.css",
"/web/static/adreena.css",
"/web/static/anisha.css",
"/web/static/havakana.css",
"/web/static/form.css",
},
CacheableResources: []string{
// Images
"/web/static/images/email3.gif",
"/web/static/images/rin-len1.webp",
"/web/static/images/background_star.gif",
"/web/static/images/kanata-1.gif",
"/web/static/images/rin-1.gif",
"/web/static/images/rin-2.gif",
"/web/static/images/gnu-head-sm.png",
// Pages
"/web/blocks/pages/about.html",
"/web/blocks/pages/intro.html",
"/web/blocks/snippets/bannerpanel.html",
// Music
"https://music-website.s3.nl-ams.scw.cloud/Tokusya-Seizon%20Wonder-la-der%21%21.mp3",
"https://music-website.s3.nl-ams.scw.cloud/kegarenaki-barajuuji.mp3",
"https://music-website.s3.nl-ams.scw.cloud/error-towa.mp3",
"https://music-website.s3.nl-ams.scw.cloud/diamond-city-lights-lazulight.opus",
"https://music-website.s3.nl-ams.scw.cloud/tsunami-finana.opus",
"https://music-website.s3.nl-ams.scw.cloud/%E7%A5%9E%E3%81%A3%E3%81%BD%E3%81%84%E3%81%AA.m4a",
"https://music-website.s3.nl-ams.scw.cloud/Servant%20of%20Evil%20with%20English%20Sub%20-%20%E6%82%AA%E3%83%8E%E5%8F%AC%E4%BD%BF%20-%20Kagamine%20Len%20-%20HQ.m4a",
"https://music-website.s3.nl-ams.scw.cloud/%E3%83%94%E3%83%8E%E3%82%AD%E3%82%AA%E3%83%94%E3%83%BC%20-%20%E3%81%8D%E3%81%BF%E3%82%82%E6%82%AA%E3%81%84%E4%BA%BA%E3%81%A7%E3%82%88%E3%81%8B%E3%81%A3%E3%81%9F%20feat.%20%E5%88%9D%E9%9F%B3%E3%83%9F%E3%82%AF%20_%20I%27m%20glad%20you%27re%20evil%20too.m4a",
},
}
app.GenerateStaticWebsite("./staticsite", handler)
compressed := handlers.CompressHandlerLevel(handler, gzip.BestSpeed)
// Create spyware module
spywareapi, err := api.NewApiApp()
if err != nil {
log.Fatal(err)
}
http.Handle("/", compressed)
http.HandleFunc("/api/visit", spywareapi.Visit)
// router.HandleFunc("/api/visit", spywareapi.Visit)
if os.Getenv("GEN_STATIC_SITE") == "true" {
return
}
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}

110
src/menu.go Normal file
View File

@ -0,0 +1,110 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type menu struct {
app.Compo
Iclass string
updateAvailable bool
IpaneWidth int
OnClickButton func(page string)
}
func newMenu() *menu {
return &menu{}
}
func (m *menu) PaneWidth(px int) *menu {
if px > 0 {
m.IpaneWidth = px
}
return m
}
func (m *menu) Class(v string) *menu {
m.Iclass = app.AppendClass(m.Iclass, v)
return m
}
func (m *menu) OnAppUpdate(ctx app.Context) {
m.updateAvailable = ctx.AppUpdateAvailable()
}
func (m *menu) Render() app.UI {
return app.Div().
Class("block").
// Class("leftbarblock-nop").
Class("navbar").
Body(
app.Ul().Body(
newMenuLink().
Link("/").
Text("Home"),
newMenuLink().
Link("/about").
Text("About"),
newMenuLink().
Link("/galaxies").
Text("Galaxies"),
newMenuLink().
Link("/music").
Text("Music"),
// Disabled for now since there are none anyway
app.Li().
Body(
app.A().Href("/blog").Text("Blog"),
).Style("display", "none"),
),
app.If(m.updateAvailable,
app.Div().Body(
app.Img().
Src("/web/static/images/hot1.gif").
Class("update-img"),
app.Span().
Text("Update available! Click here to update!").
Class("update-text"),
).
Class("update-div").
OnClick(m.onUpdateClick),
),
)
}
func (m *menu) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}
type menuLink struct {
app.Compo
IText string
ILink string
}
func newMenuLink() *menuLink {
return &menuLink{}
}
func (m *menuLink) Text(v string) *menuLink {
m.IText = v
return m
}
func (m *menuLink) Link(v string) *menuLink {
m.ILink = v
return m
}
func (m *menuLink) Render() app.UI {
return app.A().
Class("menuitem-link").
Href(m.ILink).
Body(app.Div().
Class("menuitem").
Body(app.Span().
Class("menuitem-text").
Text(m.IText)),
)
}

213
src/misc.go Normal file
View File

@ -0,0 +1,213 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
//#################################
//## ###
//## Link with status component ###
//## ###
//#################################
type linkWithStatus struct {
app.Compo
Ilink string
IText string
ILinkText string
ILinkBody app.UI
status bool
}
func newLinkWithStatus() *linkWithStatus {
return &linkWithStatus{}
}
func (f *linkWithStatus) Link(s string) *linkWithStatus {
f.Ilink = s
return f
}
func (f *linkWithStatus) LinkBody(v app.UI) *linkWithStatus {
f.ILinkBody = v
return f
}
func (f *linkWithStatus) LinkText(s string) *linkWithStatus {
return f.LinkBody(app.Text(s))
}
func (f *linkWithStatus) Text(s string) *linkWithStatus {
f.IText = s
return f
}
func (f *linkWithStatus) checkStatus(ctx app.Context) {
ctx.Async(func() {
data := struct {
Url string `json:"url"`
}{
Url: f.Ilink,
}
jsondata, err := json.Marshal(data)
if err != nil {
app.Log(err)
return
}
req, err := http.NewRequest("POST", ApiURL+"/checkonline", bytes.NewBuffer(jsondata))
if err != nil {
app.Log(err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
app.Log(err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
app.Log(err)
return
}
jsonresp := struct {
Status bool `json:"status"`
}{}
err = json.Unmarshal(body, &jsonresp)
if err != nil {
app.Log(err)
return
}
ctx.Dispatch(func(ctx app.Context) {
f.status = jsonresp.Status
})
})
}
func (f *linkWithStatus) OnNav(ctx app.Context) {
f.checkStatus(ctx)
}
func (f *linkWithStatus) Render() app.UI {
return app.Div().
Body(
app.Div().
Style("display", "flex").
Style("gap", "20px").
Body(
app.A().
Href(f.Ilink).
Class("p-h3").
Class("m-t5").
Body(f.ILinkBody),
app.If(f.status,
app.P().
Style("color", "green").
Style("width", "fit-content").
Style("margin", "5px 0px 0px 0px").
Text("Online"),
).Else(
app.P().
Style("color", "red").
Style("width", "fit-content").
Style("margin", "5px 0px 0px 0px").
Text("Offline"),
),
),
app.P().
Class("m-t5").
Text(f.IText),
)
}
//#################################
//## ###
//## Text with tooltip ###
//## ###
//#################################
type textWithTooltip struct {
app.Compo
ITooltip app.UI
IClass string
ITextClass string
IText string
activated bool
mousex, mousey int
}
func newTextWithTooltip() *textWithTooltip {
return &textWithTooltip{}
}
func (f *textWithTooltip) Class(v string) *textWithTooltip {
f.IClass = app.AppendClass(f.IClass, v)
return f
}
func (f *textWithTooltip) TextClass(v string) *textWithTooltip {
f.ITextClass = app.AppendClass(f.ITextClass, v)
return f
}
func (f *textWithTooltip) Text(v string) *textWithTooltip {
f.IText = v
return f
}
func (f *textWithTooltip) Tooltip(v app.UI) *textWithTooltip {
f.ITooltip = v
return f
}
func (f *textWithTooltip) Render() app.UI {
return app.Div().
Class(f.IClass).
Body(
app.Span().
Class(f.ITextClass).
Text(f.IText).
OnMouseOver(func(ctx app.Context, e app.Event) {
f.activated = true
}).
OnMouseMove(func(ctx app.Context, e app.Event) {
f.mousex, f.mousey = app.Window().CursorPosition()
}).
OnMouseOut(func(ctx app.Context, e app.Event) {
f.activated = false
}),
app.If(f.activated,
app.Div().
Style("position", "fixed").
Style("overflow", "hidden").
Style("top", fmt.Sprintf("%dpx", f.mousey+20)).
Style("left", fmt.Sprintf("%dpx", f.mousex+20)).
Body(
f.ITooltip,
),
),
)
}
//#################################
//## ###
//## Tooltip ###
//## ###
//#################################
type toolTip struct {
app.Compo
}

View File

@ -1,4 +1,4 @@
package components
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"

51
src/music.go Normal file
View File

@ -0,0 +1,51 @@
// Idea for this page:
// - Make a navbar on the top for different genres and switch the pages content when clicked
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type MusicPage struct {
app.Compo
}
func NewMusicPage() *MusicPage {
return &MusicPage{}
}
func (f *MusicPage) Render() app.UI {
return newPage().
Title("Music!").
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
// Genre navbar above this
newUIBlock().
Class("right").
Class("contentblock").
UI(
app.Div().
Body(
app.P().
Class("m-t5").
Text(`I am quite picky with my music most of the time. I rarely enjoy an entire album of an artist and most artists for me have only a couple amazing songs.
My tastes in music are almost exclusively Japanese songs. Vocaloid is how I began and nowadays I listen to all sorts of Japanese music.
Here are some of the songs, artists and albums I like the most.`),
app.P().
Class("p-h3").
Style("color", "red").
Text("Warning! Player feature still in beta. Stuff can break and design is most certainly not final at all!"),
app.P().
Text("Just click one of the songs to play it."),
app.P().
Class("p-h2").
Text("Songs"),
newMusicPlayer(),
),
),
)
}

165
src/musicplayer.go Normal file
View File

@ -0,0 +1,165 @@
package main
import (
"fmt"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
const (
songRepoURL string = "" // URL where the music files are stored. Made a bit like the ApiURL
)
type song struct {
ITitle string
IID string
IURL string
}
func newSong() *song {
return &song{}
}
func (f *song) Title(v string) *song {
f.ITitle = v
return f
}
func (f *song) URL(v string) *song {
f.IURL = v
return f
}
func (f *song) ID(v string) *song {
f.IID = v
return f
}
type musicPlayer struct {
app.Compo
songs map[string](*song)
currentlySelectedSong string
}
func newMusicPlayer() *musicPlayer {
return &musicPlayer{
songs: make(map[string]*song),
}
}
// On... handlers
func (f *musicPlayer) OnMount(ctx app.Context) {
ctx.Handle("switchSong", f.handleSwitchSong)
// Statically create all the music.
// I am not making a database for this shit lmao
// Also do not forget to at least set the currentlySelectedSong to the first one added, or at least make it point somewhere
// f.songs["ievan-polka"] = newSong().
// Title("Ievan Polka - Hatsune Miku").
// URL("https://files.catbox.moe/lh229f.mp3").
// ID("ievan-polka")
f.songs["god-ish"] = newSong().
Title("God-ish (神っぽいな) feat. Hatsune Miku - PinocchioP").
URL("https://music-website.s3.nl-ams.scw.cloud/%E7%A5%9E%E3%81%A3%E3%81%BD%E3%81%84%E3%81%AA.m4a").
ID("god-ish")
f.songs["servant-of-evil"] = newSong().
Title("Servant of Evil (悪ノ召使) feat. Kagamine Rin - mothy / AkunoP").
URL("https://music-website.s3.nl-ams.scw.cloud/Servant%20of%20Evil%20with%20English%20Sub%20-%20%E6%82%AA%E3%83%8E%E5%8F%AC%E4%BD%BF%20-%20Kagamine%20Len%20-%20HQ.m4a").
ID("servant-of-evil")
f.songs["im-glad-youre-evil-too"] = newSong().
Title("I'm glad you're evil too (feat. Hatsune Miku) - PinocchioP").
URL("https://music-website.s3.nl-ams.scw.cloud/%E3%83%94%E3%83%8E%E3%82%AD%E3%82%AA%E3%83%94%E3%83%BC%20-%20%E3%81%8D%E3%81%BF%E3%82%82%E6%82%AA%E3%81%84%E4%BA%BA%E3%81%A7%E3%82%88%E3%81%8B%E3%81%A3%E3%81%9F%20feat.%20%E5%88%9D%E9%9F%B3%E3%83%9F%E3%82%AF%20_%20I%27m%20glad%20you%27re%20evil%20too.m4a").
ID("im-glad-youre-evil-too")
f.songs["tokusya-seizon"] = newSong().
Title("Tokusya-Seizon Wonder-la-der!! - Amane Kanata").
URL("https://music-website.s3.nl-ams.scw.cloud/Tokusya-Seizon%20Wonder-la-der%21%21.mp3").
ID("tokusya-seizon")
f.songs["kegarenaki-barajuuji"] = newSong().
Title("Kegarenaki Barajuuji - Ariabl'eyeS").
URL("https://music-website.s3.nl-ams.scw.cloud/kegarenaki-barajuuji.mp3").
ID("kegarenaki-barajuuji")
f.songs["error-towa"] = newSong().
Title("-ERROR (Cover) - Tokoyami Towa").
URL("https://music-website.s3.nl-ams.scw.cloud/error-towa.mp3").
ID("error-towa")
f.songs["diamond-city-lights"] = newSong().
Title("Diamond City Lights - LazuLight").
URL("https://music-website.s3.nl-ams.scw.cloud/diamond-city-lights-lazulight.opus").
ID("diamond-city-lights")
f.songs["tsunami-finana"] = newSong().
Title("TSUNAMI - Finana Ryugu").
URL("https://music-website.s3.nl-ams.scw.cloud/tsunami-finana.opus").
ID("tsunami-finana")
}
// Action handlers
// Call with a value called "title" to switch to the right song
func (f *musicPlayer) handleSwitchSong(ctx app.Context, a app.Action) {
title, ok := a.Value.(string)
if !ok {
app.Log("Error calling handleSwitchSong function. Title value was not found")
return
}
v, ok := f.songs[title]
if !ok {
app.Log("Error getting song. Song with title does not exist")
return
}
f.currentlySelectedSong = v.IID
f.Update()
}
func (f *musicPlayer) Render() app.UI {
// Don't forget to handle the possibility of no songs having been added and the currentlySelectedSong to be empty
if f.currentlySelectedSong == "" {
return app.Div().
Body(
app.Range(f.songs).Map(func(s string) app.UI {
return app.Div().
Style("border", "solid 1px red").
Style("width", "fit-content").
Body(
app.Span().
Style("text-decoration", "underline").
Style("width", "fit-content").
Class("finger-hover").
Text(f.songs[s].ITitle).
OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue("switchSong", f.songs[s].IID)
}),
)
}),
)
}
return app.Div().Body(
app.P().
Class("p-h3").
Text(fmt.Sprintf("Currently playing: %s", f.songs[f.currentlySelectedSong].ITitle)),
app.Audio().
Src(f.songs[f.currentlySelectedSong].IURL).
Controls(true).
AutoPlay(true),
// Lots of buttons of songs
app.Div().
Body(
app.Range(f.songs).Map(func(s string) app.UI {
return app.Div().
Style("border", "solid 1px red").
Style("width", "fit-content").
Body(
app.Span().
Style("text-decoration", "underline").
Style("width", "fit-content").
Class("finger-hover").
Text(f.songs[s].ITitle).
OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue("switchSong", f.songs[s].IID)
}),
)
}),
),
)
}

32
src/navbar.go Normal file
View File

@ -0,0 +1,32 @@
package main
import (
"dutchellie.nl/DutchEllie/proper-website-2/ui"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type navbar struct {
app.Compo
updateAvailable bool
OnClickButton func(page string)
}
func (n *navbar) OnAppUpdate(ctx app.Context) {
n.updateAvailable = ctx.AppUpdateAvailable()
}
func (n *navbar) Render() app.UI {
return ui.Menu().
PaneWidth(250).
Menu(
newMenu(),
).
HamburgerMenu(
newMenu(),
)
}
func (n *navbar) onUpdateClick(ctx app.Context, e app.Event) {
ctx.Reload()
}

127
src/page.go Normal file
View File

@ -0,0 +1,127 @@
package main
import (
"fmt"
"net/url"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
// Page is a generic page. By default it has a header, navbar and a default leftbar
type page struct {
app.Compo
Ititle string
/*Description
Blah blah
etc*/
IbackgroundClass string
Ibackground []app.UI
IleftBar []app.UI
Imain []app.UI
hideRightContent bool
// TODO: Possibly add "updateavailable" here, so it shows up on every page
}
func newPage() *page {
return &page{
hideRightContent: false,
}
}
func (p *page) Background(v ...app.UI) *page {
p.Ibackground = app.FilterUIElems(v...)
return p
}
func (p *page) BackgroundClass(t string) *page {
p.IbackgroundClass = app.AppendClass(p.IbackgroundClass, t)
return p
}
func (p *page) Title(t string) *page {
p.Ititle = t
return p
}
func (p *page) LeftBar(v ...app.UI) *page {
p.IleftBar = app.FilterUIElems(v...)
return p
}
func (p *page) Main(v ...app.UI) *page {
p.Imain = app.FilterUIElems(v...)
return p
}
func (p *page) OnMount(ctx app.Context) {
ctx.Handle("right-hide", p.hideRight)
ctx.Handle("right-show", p.showRight)
// Send a visit request to the spyware API to track people
ctx.Async(func() {
resp, err := client.Get("/api/visit")
if err != nil {
app.Logf("Error while creating vist request %s\n", err.Error())
}
defer resp.Body.Close()
cook, err := resp.Request.Cookie("spyware")
if err != nil {
app.Logf("Error reading cookie from request: %s\n", err)
}
fmt.Printf("cook: %v\n", cook)
ctx.Dispatch(func(ctx app.Context) {
fmt.Printf("jar: %v\n", jar)
c := client.Jar.Cookies(&url.URL{Host: app.Window().URL().Hostname()})
fmt.Printf("c: %v\n", c)
})
})
}
func (p *page) Render() app.UI {
if p.IbackgroundClass == "" {
p.IbackgroundClass = app.AppendClass(p.IbackgroundClass, "background")
}
return app.Div().
Class("main").
Body(
// Header and navbar
&header{},
app.Div().
Class("left").
Body(
&navbar{},
app.Range(p.IleftBar).Slice(func(i int) app.UI {
return p.IleftBar[i]
}),
),
app.Div().
Style("display", visible(p.hideRightContent)).
Class("right").
ID("right").
Body(
app.Range(p.Imain).Slice(func(i int) app.UI {
return p.Imain[i]
}),
),
)
}
func visible(v bool) string {
if v {
return "none"
}
return "block"
}
func (p *page) hideRight(ctx app.Context, a app.Action) {
p.hideRightContent = true
}
func (p *page) showRight(ctx app.Context, a app.Action) {
p.hideRightContent = false
}

32
src/undertale.go Normal file
View File

@ -0,0 +1,32 @@
package main
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type UndertalePage struct {
app.Compo
}
// TODO: Autoplay Megalovania
func NewUndertalePage() *UndertalePage {
return &UndertalePage{}
}
func (u *UndertalePage) Render() app.UI {
return newPage().
Title("Undertale").
BackgroundClass("undertale-bg").
Background().
LeftBar(
newHTMLBlock().
Class("left").
Class("leftbarblock a").
Src("/web/blocks/snippets/bannerpanel.html"),
).
Main(
newHTMLBlock().
Class("right").
Class("contentblock").
Src("/web/blocks/pages/undertale.html"),
)
}

View File

@ -4,31 +4,40 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="static/style.css">
<link rel="stylesheet" href="../web/static/style.css">
<link rel="stylesheet" href="../web/static/form.css">
<link rel="stylesheet" href="static/anisha.css?family=anisha">
<link rel="stylesheet" href="static/adreena.css?family=adreena">
<link rel="stylesheet" href="static/havakana.css?family=havakana">
<title>Index</title>
</head>
<body>
<div class="header">
Internetica Galactica
</div>
<div class="main">
<div class="navbar">
<ul>
<li><a href="base.html">Home</a></li>
</ul>
</div>
<div class="content">
Dit is eigenlijk 1 van die dingen die steeds kan veranderen
Deze doet dat
</div>
<div class="content">
<p class="p-h1">My friends!</p>
<p>These are some of the websites of my friends!</p>
</div>
<div class="block right content">
<form action="" class="guestbook-form">
<div class="input-groups">
<div class="fr">
<div class="input-group input-group-name">
<label for="name">Name:</label>
<input type="text" name="name" class="input">
</div>
<div class="input-group input-group-email">
<label for="email">Email:</label>
<input type="text" name="email" class="input">
</div>
</div>
<div class="input-group input-group-website">
<label for="website">Website:</label>
<input type="text" name="website" class="input">
</div>
<div class="input-group input-group-message">
<label for="message">Message:</label>
<textarea name="message" cols="30" rows="5" class="input"></textarea>
</div>
</div>
<div class="submit-field">
<input type="submit" value="Send!">
</div>
</form>
</div>
</body>
</html>

176
ui/menu.go Normal file
View File

@ -0,0 +1,176 @@
package ui
import (
"strconv"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type IMenu interface {
app.UI
ID(v string) IMenu
Class(v string) IMenu
PaneWidth(px int) IMenu
HamburgerButton(v app.UI) IMenu
HamburgerMenu(v ...app.UI) IMenu
Menu(v ...app.UI) IMenu
}
func Menu() IMenu {
return &menu{}
}
type menu struct {
app.Compo
Iid string
Iclass string
Ipanewidth int
IhamburgerButton app.UI
IhamburgerMenu []app.UI
Imenu []app.UI
hideMenu bool
showHamburgerMenu bool
width int
}
func (m *menu) ID(v string) IMenu {
m.Iid = v
return m
}
func (m *menu) Class(v string) IMenu {
m.Iclass = app.AppendClass(m.Iclass, v)
return m
}
func (m *menu) PaneWidth(px int) IMenu {
if px > 0 {
m.Ipanewidth = px
}
return m
}
func (m *menu) HamburgerButton(v app.UI) IMenu {
b := app.FilterUIElems(v)
if len(b) != 0 {
m.IhamburgerButton = b[0]
}
return m
}
func (m *menu) HamburgerMenu(v ...app.UI) IMenu {
m.IhamburgerMenu = app.FilterUIElems(v...)
return m
}
func (m *menu) Menu(v ...app.UI) IMenu {
m.Imenu = app.FilterUIElems(v...)
return m
}
func (m *menu) OnPreRender(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnMount(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnResize(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) OnUpdate(ctx app.Context) {
m.refresh(ctx)
}
func (m *menu) Render() app.UI {
visible := func(v bool) string {
if v {
return "block"
}
return "none"
}
return app.Div().
ID(m.Iid).
Class(m.Iclass).
Style("height", "100%").
Body(
app.Div().
//Style("display", "flex").
Style("display", visible(!m.hideMenu)).
Style("width", "100%").
Style("height", "100%").
Style("overflow", "hidden").
Body(
app.Div().
Style("position", "relative").
Style("display", visible(!m.hideMenu)).
Style("flex-shrink", "0").
Style("flex-basis", pxToString(m.Ipanewidth)).
Style("overflow", "hidden").
Body(m.Imenu...),
),
app.Div().
Style("display", visible(m.hideMenu && len(m.IhamburgerMenu) != 0)).
Style("position", "absolute").
Style("top", "0").
Style("left", "0").
Style("cursor", "pointer").
OnClick(m.onHamburgerButtonClick).
Body(
app.If(m.IhamburgerButton == nil,
app.Div().
Class("goapp-shell-hamburger-button-default").
Text("☰"),
),
),
app.Div().
Style("display", visible(m.hideMenu && m.showHamburgerMenu)).
Style("position", "relative").
// Style("top", "0").
// Style("left", "0").
// Style("right", "0").
Style("width", "100%").
Style("height", "100%").
Style("overflow", "hidden").
OnClick(m.hideHamburgerMenu).
Body(m.IhamburgerMenu...),
)
}
func (m *menu) refresh(ctx app.Context) {
w, _ := app.Window().Size()
hideMenu := true
if w >= 914 {
hideMenu = false
}
if hideMenu != m.hideMenu ||
w != m.width {
m.hideMenu = hideMenu
m.width = w
ctx.Defer(func(app.Context) {
m.ResizeContent()
})
}
}
func pxToString(px int) string {
return strconv.Itoa(px) + "px"
}
func (m *menu) onHamburgerButtonClick(ctx app.Context, e app.Event) {
m.showHamburgerMenu = true
ctx.NewAction("right-hide")
}
func (m *menu) hideHamburgerMenu(ctx app.Context, e app.Event) {
m.showHamburgerMenu = false
ctx.NewAction("right-show")
}

View File

@ -0,0 +1,6 @@
<p class="p-h1">Test blogpost</p>
<p>This is some text under the title</p>
<p class="p-h2">Header 2</p>
<p class="p-h3">Header 3</p>
<p>Regular text here</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minus, omnis nesciunt beatae dolorem laudantium quidem? Eos fugiat doloribus, ipsam, suscipit repudiandae culpa laboriosam accusamus alias atque esse rerum, tenetur illum.</p>

View File

@ -0,0 +1,22 @@
<img src="/web/static/images/rin-1.gif" style="width:100px;position:absolute;top:10px;right:10px;">
<p class="content-text">I am a 21 year old computer science student (they/them, he/him, she/her), living and studying in
The Netherlands. I like Docker, Kubernetes and Golang!
<br>
I made this website because I was inspired again by the amazing Neocities pages that I discovered because of my
friends.
They also have their own pages (you can find them on the Galaxies page, do check them out!) and I just had to get a
good website of my own!
<br>
I am not that great at web development, especially design, but I love trying it regardless!
<br><br>
To say a bit more about me personally, I love all things computers. From servers to embedded devices! I love the
cloud and all that it brings
(except for big megacorps, but alright) and it's my goal to work for a big cloud company!
<br>
Aside from career path ambitions,ボーカロイドはすきです! I love vocaloid and other Japanese music and culture!!
I also like Vtubers, especially from Hololive and it's my goal to one day finally understand them in their native
language!
<br><br>
There is a lot more to say in words, but who cares about those! Have a look around my creative digital oasis and see
what crazy stuff you can find!
</p>

View File

@ -0,0 +1,65 @@
<p class="p-h1">
Galaxies
</p>
<p class="content-text">
Here you can find some really really really cool pages that I found on the internet.
Some of these are blogs or even blogposts I found, but the ones on top are special!
They're the websites of friends of mine! Please visit them, because they worked really hard
on their websites as well!
</p>
<div>
<p class="p-h2 mt-20 mb-10 bold">My friends!</p>
<ul>
<li>
<div><a href="https://forestofunix.xyz" class="p-h3 m-t5">Forest of Unix</a>
<p class="m-t5">A website by Sebastiaan. A massive Linux fanboy, runs Gentoo on his
ThinkPad. Absolutely based.</p>
</div>
</li>
<li>
<div><a href="https://nymphali.neocities.org" class="p-h3 m-t5">Nymphali</a>
<p class="m-t5">The website made by ■■■■■■, whoops Nymphali. They have an awesome
minimalist website that's just lovely.</p>
</div>
</li>
<li>
<div><a class="p-h3 m-t5" href="https://kristypixel.neocities.org">Kristy</a>
<p class="m-t5">Website made by Kristy. Very cute website, I love it! Keep up the
awesome work!</p>
</div>
</li>
</ul>
</div>
<div>
<p class="p-h2 mt-20 mb-10 bold">Neat webspaces</p>
<p class="m-t5" style="margin-left:10px;">Just very neat websites I found. Not necessarily by people I know.
I just thought it would be nice to share them here!</p>
<ul>
<li>
<div><a href="https://evillious.ylimegirl.com/" class="p-h3 m-t5">Evillious Chronicles fan guide</a>
<p class="m-t5">A VERY cool website made by Ylimegirl! They wrote a whole
website dedicated to Evillious Chronicles, which is a super
good Japanese light novel and vocaloid series!! Definitely look it up!</p>
</div>
</li>
<li>
<div>
<a href="https://www.gnu.org/" class="p-h3 m-t5">The GNU Project</a>
<div style="display:flex; gap:5px;">
<div style="flex:70%">
<p class="m-t5">
The official website of the GNU project.
They advocate for free/libre software.
This is not to be confused with 'open source' software.
I highly recommend you read about them and their efforts.
</p>
</div>
<div style="flex:30%">
<img src="/web/static/images/gnu-head-sm.png" alt="GNU" width="129" height="122" >
</div>
</div>
</div>
</li>
</ul>
</div>

View File

@ -0,0 +1,28 @@
<p class="p-h1">Welcome, internet surfer!</p>
<div style="position:absolute; top:10px; right:5px;">
<p class="small">Please sign my guestbook</p>
<img src="/web/static/images/email3.gif" alt="" style="width:40px; position:absolute; bottom:0px; right:0px;">
</div>
<img src="/web/static/images/rin-len1.webp" alt="" height="230" style="float:right; margin-bottom: 10px;">
<p class="content-text">
Welcome to my webspace! Whether you stumbled across this page by accident
or were linked here, you're more than welcome! This is my personal project that I like
to work on! I was inspired by a couple friends of mine, please do check their webspaces
out as well under "Galaxies" on the left side there!
If you like this page, there is a lot more, so have a look around! You can also leave a
nice message for me in the guestbook! There is no registration (unlike the rest of the "modern"
internet) so nothing of that sort!
That said, this website is my creative outlet and a way to introduce myself, so be kind please!
Also its code is entirely open-source and can be found
<a href="https://dutchellie.nl/DutchEllie/proper-website-2">on my personal Gitea instance</a> so if you like that
sort
of stuff, be my guest it's cool!
</p>
<br>
<p class="content-text">
<img src="/web/static/images/rin-2.gif" alt="Kagamine Rin drawing" style="float:left; width:100px; margin-right: 20px;">
There is a lot of stuff I want to add to this website! In fact, there is also a "staging" website, which might
contain
new features! It can be found at <a href="https://staging.quenten.nl">staging.quenten.nl</a>.
Don't worry about the invalid SSL certificate, that's normal!
</p>

View File

@ -0,0 +1 @@
<p>Test</p>

View File

@ -0,0 +1,8 @@
<div style="display: flex; gap:5px; padding:5px" >
<div style="flex:50%;">
<img src="/web/static/images/kanata-1.gif" alt="Amane Kanata excited!!" style="width:100%" >
</div>
<div style="flex:50%;">
<img src="/web/static/images/kanata-1.gif" alt="Amane Kanata excited!!" style="width:100%" >
</div>
</div>

View File

46
web/static/form.css Normal file
View File

@ -0,0 +1,46 @@
.input-groups {
display:flex;
flex-wrap:wrap;
}
.input-group {
padding: 6px;
}
.input-group-name {
width: 50%;
float:left;
}
.input-group-email {
width: 50%;
float:right;
padding-right: 16px;
}
.input-group-website {
width: 100%;
padding-right: 16px;
}
.input-group-message {
width: 100%;
padding-right: 16px;
}
.submit-field {
text-align: right;
padding-right: 10px;
padding-bottom: 10px;
width: 100%;
}
.input {
width: 100%;
}
.fr {
width: 100%;
display: flex;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
web/static/images/hot1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
web/static/images/rin-2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,9 +1,64 @@
html {
background-image: url(images/background_star.gif);
overflow-y: scroll;
/* overflow-y: scroll; */
margin: 0;
height: 99%;
}
body {
margin: 0;
height: 99%;
background-image: url(images/background_star.gif);
}
.background {
background-image: url(images/background_star.gif);
width: 100%;
height: 100vh;
position: absolute;
}
.undertale-bg {
background-image: url(images/ut-bg.webp);
width: 100%;
position: sticky;
}
.header {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
margin-bottom: 5px;
background-color: rgb(54, 39, 48);
font-size: 5em;
font-family: anisha;
text-align: center;
height: 100%;
}
.update-div {
font-size: 0.8rem;
display: flex;
}
.update-div:hover {
cursor: pointer;
}
.finger-hover:hover {
cursor: pointer;
}
.update-text {
align-self: center;
flex: 70%;
}
.update-img {
flex: 30%;
}
.main {
margin-top: 5px;
width: 900px;
position: relative;
margin-left: auto;
@ -11,23 +66,30 @@ body {
color:aliceblue;
font-family: havakana;
font-size: 1.1em;
}
.header {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
font-size: 5em;
font-family: anisha;
text-align: center;
}
.main {
margin-top: 5px;
box-sizing: border-box;
}
.navbar {
text-decoration: none;
list-style: none;
}
.navbar a, a:link, a:visited{
text-decoration: none;
color:#fce6ff
}
.left {
float:left;
max-width: 256px;
}
.right {
float:right;
max-width: 614px;
}
.leftbar {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
@ -35,13 +97,33 @@ body {
position: relative;
float:left;
width: 250px;
text-decoration: none;
list-style: none;
padding: 5px 0px;
}
.navbar a, a:link, a:visited{
text-decoration: none;
color:rgb(252, 230, 255)
.block {
border: 3px solid;
border-radius: 4px;
border-color: rgb(252, 230, 255);
background-color: rgb(54, 39, 48);
margin-bottom: 5px;
position: relative;
}
.leftbarblock-nop {
float:left;
width: 250px;
}
.leftbarblock {
float:left;
width: 250px;
padding: 5px 0px;
}
.contentblock {
float:right;
width: 614px;
padding: 10px;
}
.content {
@ -56,13 +138,28 @@ body {
padding: 10px;
}
.no-border {
border: none;
}
.content-text {
max-width: 80%;
max-width: 75%;
width: auto;
margin-left: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
.blogpost-bar {
}
.blogpost-titles {
margin-left: 10px;
text-decoration: none;
color: rgb(252, 230, 255);
}
/*
.content p {
max-width: 80%;
@ -71,10 +168,11 @@ body {
margin-bottom: 5px;
}*/
.gbp {
.guestbook-form-div {
margin: 0px 0px 10px 0px;
}
.comment {
border: 2px solid;
border-radius: 6px;
@ -200,6 +298,33 @@ div.comment-message p{
margin-bottom: 5px;
}
.update-message {
font-family: havakana;
margin-top: 0px;
}
.pulsing {
animation-name: pulsing;
animation-duration: 0.4s;
animation-timing-function: ease-out;
animation-direction: alternate;
animation-iteration-count: infinite;
animation-play-state: running;
}
@keyframes pulsing {
0% {
transform: scale(.7);
}
50% {
}
100% {
transform: scale(1.5);
}
}
/*Margin top 5px*/
.m-t5 {
margin-top: 5px;
@ -211,6 +336,94 @@ div.comment-message p{
margin-bottom: 10px;
}
.mt-20 {
margin-top: 20px;
}
.mb-10 {
margin-bottom: 10px;
}
.bold {
font-weight: 700;
}
.fit {
width: fit-content;
}
}
.invisible {
visibility: hidden;
}
@media only screen and (max-width: 914px) {
.header {
font-size: 10vw;
height: fit-content;
flex: 0 1 auto;
}
.main {
display: flex;
flex-flow: column;
padding-left: 5px;
padding-right: 5px;
width: 100%;
height: 100%;
}
.block {
box-sizing: border-box;
}
.right {
width: 100%;
max-width: none;
z-index: 0;
}
.leftbarblock {
display: none;
}
.left {
z-index: 1;
max-width: unset;
width: 100%;
flex: 1 1 auto;
}
.navbar {
height: 100%;
display: flexbox;
flex-direction: column;
}
.navbar ul {
padding-inline-end: 40px;
}
.menuitem {
padding: 3.5mm 10px;
margin-bottom: 10px;
min-width: 65px;
flex: 1 1 auto;
border: 0px none;
border-radius: 6px;
background-color: #4974a5;
text-align: center;
vertical-align: middle;
}
.menuitem-text {
}
.menuitem-link {
list-style-type: none;
}
.menuitem-link a {
text-align: center;
}
}