Compare commits
91 Commits
eb920c80fc
...
main
Author | SHA1 | Date | |
---|---|---|---|
083f560e1a | |||
54825d5a0e | |||
18f445502b | |||
8730911a5d | |||
4b32e554dc | |||
e932269f04 | |||
eb21ce6d8e | |||
89f5fd178f | |||
7e171abfb8 | |||
95e5a173f1 | |||
d1190c4e88 | |||
d66bb5afeb | |||
dbfdaf38bb | |||
85d13a2905 | |||
f5fa2af536 | |||
6a2ec9f099 | |||
31abd9e138 | |||
ff7044ab9b | |||
acba8179fb | |||
67358d1616 | |||
55e3e13459 | |||
1f59549cc4 | |||
e414c98866 | |||
029b426757 | |||
629b642d86 | |||
8aaecbab69 | |||
821f2c916e | |||
b765a88f27 | |||
d489dd744c | |||
b06ec9b955 | |||
42eba16ef2 | |||
caf15aadcb | |||
a88651e4b7 | |||
450bf3631c | |||
98a4b28e8b | |||
ba1fae4547 | |||
734d9e4e27 | |||
e2f4db7b97 | |||
be54e41135 | |||
77ccef7c68 | |||
65f5adc604 | |||
14969944a8 | |||
7ce4efb20e | |||
035f3e21d0 | |||
24f12a1e37 | |||
d0e7f90e9f | |||
4cd86be5a0 | |||
8d0b6a37c1 | |||
85685b1da3 | |||
111c6fd5de | |||
2332cda5e4 | |||
182fb13590 | |||
4968a2a8ca | |||
7e4a5a5302 | |||
b8e8cc3a60 | |||
e001227cd4 | |||
a92e64b7f2 | |||
7389daba09 | |||
cd8d116fef | |||
70c9f027b8 | |||
75dab086ff | |||
4eb00e38bc | |||
5e688e3870 | |||
08514b50c6 | |||
7717210578 | |||
bb163abb0a | |||
d7c8ff80e4 | |||
acbdd113d4 | |||
6b4c6bb447 | |||
30d57404f9 | |||
6482fcd6f1 | |||
858abd350e | |||
44da256e10 | |||
715aac28b4 | |||
5c4b46b65e | |||
a32ced531f | |||
bc38196fd3 | |||
330385a357 | |||
cd07bef29e | |||
73442b212c | |||
0ca691e3ae | |||
03a09fc452 | |||
2e381108d1 | |||
0b7b478cd3 | |||
81d9e315a0 | |||
73cf59ff86 | |||
f82f72ec1c | |||
73385c9d39 | |||
fb7c79f570 | |||
f46005130e | |||
a2fbcf03ca |
.drone.yml
.drone/helm
.gitignoreDockerfileMakefileREADME.mdapi
components
aboutpage.gocontent-view.gogalaxiespage.goguestbookform.goguestbookpanel.gohomepage.gohomepanel.gonavbar.goupdater.go
entity
go.modgo.summain.gosrc
aboutpage.goblock.gobloglinks.goblogpage.goemptypage.gogalaxiespage.goguestbook.goheader.gohomepage.gohtml-doc.gohtml.gohttp.gomain.gomenu.gomisc.gomodal.gomusic.gomusicplayer.gonavbar.gopage.goundertale.go
test-website
ui
web
313
.drone.yml
313
.drone.yml
@ -1,74 +1,140 @@
|
|||||||
|
#kind: pipeline
|
||||||
|
#type: kubernetes
|
||||||
|
#name: staging
|
||||||
|
#
|
||||||
|
#trigger:
|
||||||
|
# event:
|
||||||
|
# - push
|
||||||
|
# branch:
|
||||||
|
# - main
|
||||||
|
#
|
||||||
|
## STAGING!!!!
|
||||||
|
#steps:
|
||||||
|
#- 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:
|
||||||
|
# cache_from:
|
||||||
|
# - "dutchellie/proper-website-2:dev"
|
||||||
|
# username:
|
||||||
|
# from_secret: docker_username
|
||||||
|
# password:
|
||||||
|
# from_secret: docker_password
|
||||||
|
# dockerfile: Dockerfile
|
||||||
|
# repo: dutchellie/proper-website-2
|
||||||
|
# tags:
|
||||||
|
# - dev-${DRONE_COMMIT_SHA:0:8}
|
||||||
|
# depends_on:
|
||||||
|
# - build-wasm
|
||||||
|
# - build-server
|
||||||
|
#- name: deploy-staging
|
||||||
|
# image: pelotech/drone-helm3
|
||||||
|
# settings:
|
||||||
|
# mode: upgrade
|
||||||
|
# chart: .drone/helm/chart
|
||||||
|
# namespace: drone-staging
|
||||||
|
# release: newsite-staging
|
||||||
|
# skip_tls_verify: true
|
||||||
|
# values_files:
|
||||||
|
# - .drone/helm/staging-val.yaml
|
||||||
|
# values:
|
||||||
|
# - "image=dutchellie/proper-website-2:dev-${DRONE_COMMIT_SHA:0:8}"
|
||||||
|
# - "baseURL=staging.quenten.nl"
|
||||||
|
# - "name=newsite-staging"
|
||||||
|
# kube_api_server:
|
||||||
|
# from_secret: staging_api_server
|
||||||
|
# kube_token:
|
||||||
|
# from_secret: staging_kube_token
|
||||||
|
# kube_certificate:
|
||||||
|
# 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
|
kind: pipeline
|
||||||
type: kubernetes
|
type: kubernetes
|
||||||
name: default
|
name: production
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- push
|
- push
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
|
||||||
# STAGING!!!!!!!!!!!!!
|
# PRODUCTION!!!!
|
||||||
steps:
|
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
|
image: plugins/docker
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
path: /drone/src/build
|
||||||
settings:
|
settings:
|
||||||
username:
|
cache_from:
|
||||||
|
- "dutchellie/proper-website-2:latest"
|
||||||
|
username:
|
||||||
from_secret: docker_username
|
from_secret: docker_username
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
|
dockerfile: Dockerfile
|
||||||
repo: dutchellie/proper-website-2
|
repo: dutchellie/proper-website-2
|
||||||
build_args:
|
tags:
|
||||||
- APIURL=https://api.nicecock.eu/api/testingcomment
|
- latest-${DRONE_COMMIT_SHA:0:8}
|
||||||
tags:
|
depends_on:
|
||||||
- dev
|
- build-wasm
|
||||||
- ${DRONE_COMMIT_SHA:0:8}
|
- build-server
|
||||||
- name: deploy-staging
|
|
||||||
image: pelotech/drone-helm3
|
|
||||||
settings:
|
|
||||||
mode: upgrade
|
|
||||||
chart: .drone/helm/chart
|
|
||||||
namespace: drone-staging
|
|
||||||
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}"
|
|
||||||
kube_api_server:
|
|
||||||
from_secret: staging_api_server
|
|
||||||
kube_token:
|
|
||||||
from_secret: staging_kube_token
|
|
||||||
kube_certificate:
|
|
||||||
from_secret: staging_kube_certificate
|
|
||||||
kube_service_account: drone-deploy
|
|
||||||
dry_run: false
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
kind: pipeline
|
|
||||||
type: kubernetes
|
|
||||||
name: deploy-prod
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- promote
|
|
||||||
target:
|
|
||||||
- production
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: docker
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: dutchellie/proper-website-2
|
|
||||||
build_args:
|
|
||||||
- APIURL=https://api.nicecock.eu/api/comment
|
|
||||||
tags:
|
|
||||||
- latest
|
|
||||||
- ${DRONE_COMMIT_SHA:0:8}
|
|
||||||
- name: deploy-production
|
- name: deploy-production
|
||||||
image: pelotech/drone-helm3
|
image: pelotech/drone-helm3
|
||||||
settings:
|
settings:
|
||||||
@ -78,10 +144,11 @@ steps:
|
|||||||
release: newsite-production
|
release: newsite-production
|
||||||
skip_tls_verify: true
|
skip_tls_verify: true
|
||||||
values_files:
|
values_files:
|
||||||
# - .drone/helm/values.yaml
|
|
||||||
- .drone/helm/prod-val.yaml
|
- .drone/helm/prod-val.yaml
|
||||||
values:
|
values:
|
||||||
- "image=dutchellie/proper-website-2:${DRONE_COMMIT_SHA:0:8}"
|
- "image=dutchellie/proper-website-2:latest-${DRONE_COMMIT_SHA:0:8}"
|
||||||
|
- "baseURL=old.quenten.nl"
|
||||||
|
- "name=newsite-prod"
|
||||||
kube_api_server:
|
kube_api_server:
|
||||||
from_secret: prod_api_server
|
from_secret: prod_api_server
|
||||||
kube_token:
|
kube_token:
|
||||||
@ -90,3 +157,131 @@ steps:
|
|||||||
from_secret: prod_kube_certificate
|
from_secret: prod_kube_certificate
|
||||||
kube_service_account: drone-deploy
|
kube_service_account: drone-deploy
|
||||||
dry_run: false
|
dry_run: false
|
||||||
|
depends_on:
|
||||||
|
- build-publish-image
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: feature-branch
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
branch:
|
||||||
|
exclude:
|
||||||
|
- main
|
||||||
|
|
||||||
|
# FEATURE DEPLOY
|
||||||
|
steps:
|
||||||
|
- name: build-wasm
|
||||||
|
image: golang:1.17.8-alpine
|
||||||
|
volumes:
|
||||||
|
- name: build-feature
|
||||||
|
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-feature
|
||||||
|
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-feature
|
||||||
|
path: /drone/src/build
|
||||||
|
settings:
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
repo: dutchellie/proper-website-2
|
||||||
|
tags:
|
||||||
|
- feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}
|
||||||
|
depends_on:
|
||||||
|
- build-wasm
|
||||||
|
- build-server
|
||||||
|
- name: deploy-staging
|
||||||
|
image: pelotech/drone-helm3
|
||||||
|
settings:
|
||||||
|
mode: upgrade
|
||||||
|
chart: .drone/helm/chart
|
||||||
|
namespace: drone-staging
|
||||||
|
release: feature-${DRONE_BRANCH}
|
||||||
|
skip_tls_verify: true
|
||||||
|
values_files:
|
||||||
|
- .drone/helm/staging-val.yaml
|
||||||
|
values:
|
||||||
|
- "image=dutchellie/proper-website-2:feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}"
|
||||||
|
- "baseURL=${DRONE_BRANCH}.quenten.nl"
|
||||||
|
- "name=${DRONE_BRANCH}-deployment"
|
||||||
|
kube_api_server:
|
||||||
|
from_secret: staging_api_server
|
||||||
|
kube_token:
|
||||||
|
from_secret: staging_kube_token
|
||||||
|
kube_certificate:
|
||||||
|
from_secret: staging_kube_certificate
|
||||||
|
kube_service_account: drone-deploy
|
||||||
|
dry_run: false
|
||||||
|
depends_on:
|
||||||
|
- build-publish-image
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: build-feature
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: remove-feature-branch
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- promote
|
||||||
|
target:
|
||||||
|
- destroy
|
||||||
|
branch:
|
||||||
|
exclude:
|
||||||
|
- main
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: destroy
|
||||||
|
image: pelotech/drone-helm3
|
||||||
|
settings:
|
||||||
|
mode: uninstall
|
||||||
|
chart: .drone/helm/chart
|
||||||
|
namespace: drone-staging
|
||||||
|
release: feature-${DRONE_BRANCH}
|
||||||
|
skip_tls_verify: true
|
||||||
|
values_files:
|
||||||
|
- .drone/helm/staging-val.yaml
|
||||||
|
values:
|
||||||
|
- "image=dutchellie/proper-website-2:feature-${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:8}"
|
||||||
|
- "baseURL=${DRONE_BRANCH}.quenten.nl"
|
||||||
|
- "name=${DRONE_BRANCH}-deployment"
|
||||||
|
kube_api_server:
|
||||||
|
from_secret: staging_api_server
|
||||||
|
kube_token:
|
||||||
|
from_secret: staging_kube_token
|
||||||
|
kube_certificate:
|
||||||
|
from_secret: staging_kube_certificate
|
||||||
|
kube_service_account: drone-deploy
|
||||||
|
dry_run: false
|
@ -1,3 +1,3 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: newsite
|
name: newsite
|
||||||
version: v0.0.1
|
version: v0.0.2
|
@ -31,5 +31,9 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8000
|
containerPort: 8000
|
||||||
|
{{- if .Values.containerEnv }}
|
||||||
|
env: {{ toYaml .Values.containerEnv | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Values.ingress.name }}
|
name: {{ tpl .Values.ingress.name . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- tpl (. | toYaml) $ | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
@ -14,14 +14,14 @@ spec:
|
|||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ tpl . $ | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
secretName: {{ .secretName }}
|
secretName: {{ tpl (.secretName | toYaml) $ }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ tpl .host $ | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
{{- range .paths }}
|
{{- range .paths }}
|
||||||
@ -29,7 +29,7 @@ spec:
|
|||||||
pathType: {{ .pathType }}
|
pathType: {{ .pathType }}
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ $.Values.service.name }}
|
name: {{ tpl $.Values.service.name $ }}
|
||||||
port:
|
port:
|
||||||
number: 8000
|
number: 8000
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Values.service.name }}
|
name: {{ tpl .Values.service.name . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
{{- with .Values.service.annotations }}
|
{{- with .Values.service.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
|
@ -6,6 +6,7 @@ podAnnotations: {}
|
|||||||
containerName: newsite
|
containerName: newsite
|
||||||
image: dutchellie/proper-website-2:latest
|
image: dutchellie/proper-website-2:latest
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
containerEnv: []
|
||||||
|
|
||||||
service:
|
service:
|
||||||
name: newsite-service
|
name: newsite-service
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
|
baseURL: old.quenten.nl
|
||||||
name: newsite-prod
|
name: newsite-prod
|
||||||
|
containerEnv:
|
||||||
|
- name: APIURL
|
||||||
|
value: https://api.quenten.nl/api
|
||||||
service:
|
service:
|
||||||
name: newsite-prod
|
name: "{{ .Values.name }}"
|
||||||
ingress:
|
ingress:
|
||||||
name: newsite-prod
|
name: "{{ .Values.name }}"
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
external-dns.alpha.kubernetes.io/hostname: "newsite.dutchellie.nl"
|
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
|
||||||
|
proxy_hide_header X-Frame-Options ;
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- newsite.dutchellie.nl
|
- "{{ .Values.baseURL }}"
|
||||||
secretName: newsite-tls
|
secretName: "{{ .Values.name }}-tls"
|
||||||
hosts:
|
hosts:
|
||||||
- host: newsite.dutchellie.nl
|
- host: "{{ .Values.baseURL }}"
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
@ -1,17 +1,24 @@
|
|||||||
|
baseURL: staging.quenten.nl
|
||||||
name: newsite-staging
|
name: newsite-staging
|
||||||
|
containerEnv:
|
||||||
|
- name: APIURL
|
||||||
|
value: https://api.quenten.nl/api/testing
|
||||||
service:
|
service:
|
||||||
name: newsite-staging
|
name: "{{ .Values.name }}"
|
||||||
ingress:
|
ingress:
|
||||||
name: newsite-staging
|
name: "{{ .Values.name }}"
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-staging
|
cert-manager.io/cluster-issuer: "letsencrypt-staging"
|
||||||
external-dns.alpha.kubernetes.io/hostname: "newsite.staging.dutchellie.nl"
|
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
|
||||||
|
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
|
||||||
|
proxy_hide_header X-Frame-Options ;
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- newsite.staging.dutchellie.nl
|
- "{{ .Values.baseURL }}"
|
||||||
secretName: newsite-staging-tls
|
secretName: "{{ .Values.name }}-tls"
|
||||||
hosts:
|
hosts:
|
||||||
- host: newsite.staging.dutchellie.nl
|
- host: "{{ .Values.baseURL }}"
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.vscode
|
.vscode
|
||||||
app
|
app
|
||||||
web/*.wasm
|
web/*.wasm
|
||||||
staticsite
|
staticsite
|
||||||
|
node_modules
|
25
Dockerfile
25
Dockerfile
@ -1,16 +1,17 @@
|
|||||||
FROM golang:1.17.8-alpine AS builder
|
#FROM golang:1.17.8-alpine AS builder
|
||||||
ARG APIURL
|
#ARG APIURL
|
||||||
WORKDIR /project
|
#WORKDIR /project
|
||||||
ADD . /project/
|
#ADD . /project/
|
||||||
RUN go mod tidy
|
#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 GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
|
||||||
RUN go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o app
|
#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
|
RUN apk --no-cache add ca-certificates
|
||||||
WORKDIR /root/
|
WORKDIR /root
|
||||||
COPY --from=builder /project/web ./web/
|
RUN mkdir ./web
|
||||||
COPY --from=builder /project/app ./
|
COPY ./web ./web
|
||||||
|
COPY ./build/web/app.wasm ./web/
|
||||||
|
COPY ./build/app ./
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
ENV TESTING true
|
|
||||||
CMD ["./app"]
|
CMD ["./app"]
|
22
Makefile
22
Makefile
@ -1,16 +1,26 @@
|
|||||||
APIURL_prod := https://api.nicecock.eu/api/comment
|
APIURL_prod := https://api.quenten.nl/api
|
||||||
APIURL_staging := https://api.nicecock.eu/api/testingcomment
|
APIURL_staging := https://api.quenten.nl/api/testing
|
||||||
|
|
||||||
build:
|
build:
|
||||||
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o web/app.wasm
|
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
|
||||||
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o app
|
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:
|
build-prod:
|
||||||
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o web/app.wasm
|
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
|
||||||
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o app
|
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
|
||||||
|
cp -r web staticsite/
|
||||||
|
|
||||||
run: build
|
run: build
|
||||||
./app
|
./app
|
||||||
|
|
||||||
|
run-new: build-new
|
||||||
|
APIURL=${APIURL_staging} ./app
|
||||||
|
|
||||||
run-prod: build-prod
|
run-prod: build-prod
|
||||||
./app
|
./app
|
13
README.md
13
README.md
@ -1,14 +1,23 @@
|
|||||||
# proper-website-2
|
# proper-website-2
|
||||||
|
|
||||||
[](https://drone.nicecock.eu/DutchEllie/proper-website-2)
|
[](https://drone.dutchellie.nl/DutchEllie/proper-website-2)
|
||||||
|
|
||||||
A truly proper website this time™
|
A truly proper website this time™
|
||||||
|
|
||||||
**TODO**:
|
**TODO**:
|
||||||
- Redo the entire CSS with a framework
|
|
||||||
- Change domain to quenten.nl and staging.quenten.nl
|
- Change domain to quenten.nl and staging.quenten.nl
|
||||||
- Dynamically make domains for other branches
|
- 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:
|
This website will be done with this:
|
||||||
- Backend written in Golang
|
- Backend written in Golang
|
||||||
|
62
api/spyware.go
Normal file
62
api/spyware.go
Normal 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)
|
||||||
|
|
||||||
|
}
|
@ -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")
|
|
||||||
}
|
|
@ -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]
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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
15
entity/blogpost.go
Normal 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"`
|
||||||
|
}
|
@ -9,4 +9,6 @@ type Comment struct {
|
|||||||
Email string `json:"email,omitempty" bson:"email,omitempty"`
|
Email string `json:"email,omitempty" bson:"email,omitempty"`
|
||||||
Message string `json:"message" bson:"message"`
|
Message string `json:"message" bson:"message"`
|
||||||
PostDate time.Time `json:"time" bson:"time"`
|
PostDate time.Time `json:"time" bson:"time"`
|
||||||
|
|
||||||
|
UUID string `json:"uuid" bson:"uuid"`
|
||||||
}
|
}
|
||||||
|
13
go.mod
13
go.mod
@ -2,6 +2,15 @@ module dutchellie.nl/DutchEllie/proper-website-2
|
|||||||
|
|
||||||
go 1.17
|
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
15
go.sum
@ -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.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/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.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 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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 h1:vo+1oohWMfTQ0S3eg9JOjuFEy1n3bVNgeDi4eHNfMzA=
|
||||||
github.com/maxence-charriere/go-app/v9 v9.3.3/go.mod h1:zo0n1kh4OMKn7P+MrTUUi7QwUMU2HOfHsZ293TITtxI=
|
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=
|
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=
|
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/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-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-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-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-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.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=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
99
main.go
99
main.go
@ -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
30
src/aboutpage.go
Normal 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
86
src/block.go
Normal 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
74
src/bloglinks.go
Normal 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
54
src/blogpage.go
Normal 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
15
src/emptypage.go
Normal 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
137
src/galaxiespage.go
Normal 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
387
src/guestbook.go
Normal 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")
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package components
|
package main
|
||||||
|
|
||||||
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
|
|
||||||
@ -7,5 +7,10 @@ type header struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *header) Render() app.UI {
|
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
77
src/homepage.go
Normal 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
75
src/html-doc.go
Normal 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
63
src/html.go
Normal 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
41
src/http.go
Normal 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
129
src/main.go
Normal 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
110
src/menu.go
Normal 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
213
src/misc.go
Normal 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
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package components
|
package main
|
||||||
|
|
||||||
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
|
|
51
src/music.go
Normal file
51
src/music.go
Normal 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
165
src/musicplayer.go
Normal 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
32
src/navbar.go
Normal 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
127
src/page.go
Normal 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
32
src/undertale.go
Normal 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"),
|
||||||
|
)
|
||||||
|
}
|
@ -4,31 +4,40 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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/anisha.css?family=anisha">
|
||||||
<link rel="stylesheet" href="static/adreena.css?family=adreena">
|
<link rel="stylesheet" href="static/adreena.css?family=adreena">
|
||||||
<link rel="stylesheet" href="static/havakana.css?family=havakana">
|
<link rel="stylesheet" href="static/havakana.css?family=havakana">
|
||||||
<title>Index</title>
|
<title>Index</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="header">
|
<div class="block right content">
|
||||||
Internetica Galactica
|
<form action="" class="guestbook-form">
|
||||||
</div>
|
<div class="input-groups">
|
||||||
<div class="main">
|
<div class="fr">
|
||||||
<div class="navbar">
|
<div class="input-group input-group-name">
|
||||||
<ul>
|
<label for="name">Name:</label>
|
||||||
<li><a href="base.html">Home</a></li>
|
<input type="text" name="name" class="input">
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
<div class="input-group input-group-email">
|
||||||
<div class="content">
|
<label for="email">Email:</label>
|
||||||
Dit is eigenlijk 1 van die dingen die steeds kan veranderen
|
<input type="text" name="email" class="input">
|
||||||
Deze doet dat
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="input-group input-group-website">
|
||||||
<div class="content">
|
<label for="website">Website:</label>
|
||||||
<p class="p-h1">My friends!</p>
|
<input type="text" name="website" class="input">
|
||||||
<p>These are some of the websites of my friends!</p>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
176
ui/menu.go
Normal file
176
ui/menu.go
Normal 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")
|
||||||
|
}
|
6
web/blocks/blogposts/testpost1.html
Normal file
6
web/blocks/blogposts/testpost1.html
Normal 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>
|
22
web/blocks/pages/about.html
Normal file
22
web/blocks/pages/about.html
Normal 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>
|
65
web/blocks/pages/galaxies.html
Normal file
65
web/blocks/pages/galaxies.html
Normal 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>
|
28
web/blocks/pages/intro.html
Normal file
28
web/blocks/pages/intro.html
Normal 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>
|
1
web/blocks/pages/undertale.html
Normal file
1
web/blocks/pages/undertale.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>Test</p>
|
8
web/blocks/snippets/bannerpanel.html
Normal file
8
web/blocks/snippets/bannerpanel.html
Normal 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>
|
0
web/blocks/snippets/test.html
Normal file
0
web/blocks/snippets/test.html
Normal file
46
web/static/form.css
Normal file
46
web/static/form.css
Normal 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;
|
||||||
|
}
|
BIN
web/static/images/gnu-head-sm.png
Normal file
BIN
web/static/images/gnu-head-sm.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.6 KiB |
BIN
web/static/images/hot1.gif
Normal file
BIN
web/static/images/hot1.gif
Normal file
Binary file not shown.
After ![]() (image error) Size: 384 B |
BIN
web/static/images/kanata-1.gif
Normal file
BIN
web/static/images/kanata-1.gif
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.4 MiB |
BIN
web/static/images/rin-2.gif
Normal file
BIN
web/static/images/rin-2.gif
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.1 MiB |
BIN
web/static/images/rin-len1.webp
Normal file
BIN
web/static/images/rin-len1.webp
Normal file
Binary file not shown.
After ![]() (image error) Size: 192 KiB |
BIN
web/static/images/ut-bg.webp
Normal file
BIN
web/static/images/ut-bg.webp
Normal file
Binary file not shown.
After ![]() (image error) Size: 45 KiB |
@ -1,9 +1,64 @@
|
|||||||
html {
|
html {
|
||||||
background-image: url(images/background_star.gif);
|
/* overflow-y: scroll; */
|
||||||
overflow-y: scroll;
|
margin: 0;
|
||||||
|
height: 99%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
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;
|
width: 900px;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@ -11,23 +66,30 @@ body {
|
|||||||
color:aliceblue;
|
color:aliceblue;
|
||||||
font-family: havakana;
|
font-family: havakana;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
box-sizing: border-box;
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.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: 3px solid;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border-color: rgb(252, 230, 255);
|
border-color: rgb(252, 230, 255);
|
||||||
@ -35,13 +97,33 @@ body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
float:left;
|
float:left;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
text-decoration: none;
|
padding: 5px 0px;
|
||||||
list-style: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar a, a:link, a:visited{
|
.block {
|
||||||
text-decoration: none;
|
border: 3px solid;
|
||||||
color:rgb(252, 230, 255)
|
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 {
|
.content {
|
||||||
@ -56,13 +138,28 @@ body {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-border {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.content-text {
|
.content-text {
|
||||||
max-width: 80%;
|
max-width: 75%;
|
||||||
|
width: auto;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blogpost-bar {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.blogpost-titles {
|
||||||
|
margin-left: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgb(252, 230, 255);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.content p {
|
.content p {
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
@ -71,10 +168,11 @@ body {
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
.gbp {
|
.guestbook-form-div {
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@ -200,6 +298,33 @@ div.comment-message p{
|
|||||||
margin-bottom: 5px;
|
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*/
|
/*Margin top 5px*/
|
||||||
.m-t5 {
|
.m-t5 {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
@ -211,6 +336,94 @@ div.comment-message p{
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt-20 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-10 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.fit {
|
.fit {
|
||||||
width: fit-content;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user