Compare commits
2 Commits
main
...
eb920c80fc
Author | SHA1 | Date | |
---|---|---|---|
eb920c80fc | |||
1069d10f8e |
.drone.yml
.drone/helm
.gitignoreDockerfileMakefileREADME.mdapi
components
aboutpage.gocontent-view.gogalaxiespage.goguestbookform.goguestbookpanel.goheader.gohomepage.gohomepanel.gomodal.gonavbar.goupdater.go
design-desktop.drawioentity
go.modgo.summain.gosrc
aboutpage.goblock.gobloglinks.goblogpage.goemptypage.gogalaxiespage.goguestbook.goheader.gohomepage.gohtml-doc.gohtml.gohttp.gomain.gomenu.gomisc.gomusic.gomusicplayer.gonavbar.gopage.goundertale.go
test-website
ui
web
313
.drone.yml
313
.drone.yml
@ -1,90 +1,6 @@
|
|||||||
#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: production
|
name: default
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
@ -92,49 +8,71 @@ trigger:
|
|||||||
branch:
|
branch:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
# PRODUCTION!!!!
|
# STAGING!!!!!!!!!!!!!
|
||||||
steps:
|
steps:
|
||||||
- name: build-wasm
|
- name: docker
|
||||||
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:
|
||||||
cache_from:
|
username:
|
||||||
- "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
|
||||||
tags:
|
build_args:
|
||||||
- latest-${DRONE_COMMIT_SHA:0:8}
|
- APIURL=https://api.nicecock.eu/api/testingcomment
|
||||||
depends_on:
|
tags:
|
||||||
- build-wasm
|
- dev
|
||||||
- build-server
|
- ${DRONE_COMMIT_SHA:0:8}
|
||||||
|
- 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
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
|
||||||
|
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:
|
||||||
@ -144,11 +82,10 @@ 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:latest-${DRONE_COMMIT_SHA:0:8}"
|
- "image=dutchellie/proper-website-2:${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:
|
||||||
@ -157,131 +94,3 @@ 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.2
|
version: v0.0.1
|
@ -31,9 +31,5 @@ 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: {{ tpl .Values.ingress.name . }}
|
name: {{ .Values.ingress.name }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- tpl (. | toYaml) $ | nindent 4 }}
|
{{- 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 }}
|
||||||
- {{ tpl . $ | quote }}
|
- {{ . | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
secretName: {{ tpl (.secretName | toYaml) $ }}
|
secretName: {{ .secretName }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.ingress.hosts }}
|
||||||
- host: {{ tpl .host $ | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
{{- range .paths }}
|
{{- range .paths }}
|
||||||
@ -29,7 +29,7 @@ spec:
|
|||||||
pathType: {{ .pathType }}
|
pathType: {{ .pathType }}
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ tpl $.Values.service.name $ }}
|
name: {{ $.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: {{ tpl .Values.service.name . }}
|
name: {{ .Values.service.name }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
{{- with .Values.service.annotations }}
|
{{- with .Values.service.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
|
@ -6,7 +6,6 @@ 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,24 +1,17 @@
|
|||||||
baseURL: old.quenten.nl
|
|
||||||
name: newsite-prod
|
name: newsite-prod
|
||||||
containerEnv:
|
|
||||||
- name: APIURL
|
|
||||||
value: https://api.quenten.nl/api
|
|
||||||
service:
|
service:
|
||||||
name: "{{ .Values.name }}"
|
name: newsite-prod
|
||||||
ingress:
|
ingress:
|
||||||
name: "{{ .Values.name }}"
|
name: newsite-prod
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
|
external-dns.alpha.kubernetes.io/hostname: "newsite.dutchellie.nl"
|
||||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
|
||||||
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
|
|
||||||
proxy_hide_header X-Frame-Options ;
|
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- "{{ .Values.baseURL }}"
|
- newsite.dutchellie.nl
|
||||||
secretName: "{{ .Values.name }}-tls"
|
secretName: newsite-tls
|
||||||
hosts:
|
hosts:
|
||||||
- host: "{{ .Values.baseURL }}"
|
- host: newsite.dutchellie.nl
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
@ -1,24 +1,17 @@
|
|||||||
baseURL: staging.quenten.nl
|
|
||||||
name: newsite-staging
|
name: newsite-staging
|
||||||
containerEnv:
|
|
||||||
- name: APIURL
|
|
||||||
value: https://api.quenten.nl/api/testing
|
|
||||||
service:
|
service:
|
||||||
name: "{{ .Values.name }}"
|
name: newsite-staging
|
||||||
ingress:
|
ingress:
|
||||||
name: "{{ .Values.name }}"
|
name: newsite-staging
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-staging"
|
cert-manager.io/cluster-issuer: letsencrypt-staging
|
||||||
external-dns.alpha.kubernetes.io/hostname: "{{ .Values.baseURL }}"
|
external-dns.alpha.kubernetes.io/hostname: "newsite.staging.dutchellie.nl"
|
||||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
|
||||||
add_header Content-Security-Policy "frame-ancestors 'self' https://forestofunix.xyz";
|
|
||||||
proxy_hide_header X-Frame-Options ;
|
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- "{{ .Values.baseURL }}"
|
- newsite.staging.dutchellie.nl
|
||||||
secretName: "{{ .Values.name }}-tls"
|
secretName: newsite-staging-tls
|
||||||
hosts:
|
hosts:
|
||||||
- host: "{{ .Values.baseURL }}"
|
- host: newsite.staging.dutchellie.nl
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,4 @@
|
|||||||
.vscode
|
.vscode
|
||||||
app
|
app
|
||||||
web/*.wasm
|
web/*.wasm
|
||||||
staticsite
|
staticsite
|
||||||
node_modules
|
|
25
Dockerfile
25
Dockerfile
@ -1,17 +1,16 @@
|
|||||||
#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 -o web/app.wasm -ldflags="-X 'main.ApiURL=$APIURL'" ./src
|
RUN GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o web/app.wasm
|
||||||
#RUN go build -o app -ldflags="-X 'main.ApiURL=$APIURL'" ./src
|
RUN go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=$APIURL'" -o app
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest AS staging
|
||||||
RUN apk --no-cache add ca-certificates
|
RUN apk --no-cache add ca-certificates
|
||||||
WORKDIR /root
|
WORKDIR /root/
|
||||||
RUN mkdir ./web
|
COPY --from=builder /project/web ./web/
|
||||||
COPY ./web ./web
|
COPY --from=builder /project/app ./
|
||||||
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,26 +1,16 @@
|
|||||||
APIURL_prod := https://api.quenten.nl/api
|
APIURL_prod := https://api.nicecock.eu/api/comment
|
||||||
APIURL_staging := https://api.quenten.nl/api/testing
|
APIURL_staging := https://api.nicecock.eu/api/testingcomment
|
||||||
|
|
||||||
build:
|
build:
|
||||||
GOARCH=wasm GOOS=js go build -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
|
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o web/app.wasm
|
||||||
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_staging}'" ./src
|
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_staging}'" -o app
|
||||||
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 -o web/app.wasm -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
|
GOARCH=wasm GOOS=js go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o web/app.wasm
|
||||||
go build -o app -ldflags="-X 'main.ApiURL=${APIURL_prod}'" ./src
|
go build -ldflags="-X 'dutchellie.nl/DutchEllie/proper-website-2/components.ApiURL=${APIURL_prod}'" -o app
|
||||||
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,23 +1,14 @@
|
|||||||
# proper-website-2
|
# proper-website-2
|
||||||
|
|
||||||
[](https://drone.dutchellie.nl/DutchEllie/proper-website-2)
|
[](https://drone.nicecock.eu/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
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
||||||
}
|
|
47
components/aboutpage.go
Normal file
47
components/aboutpage.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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")
|
||||||
|
}
|
23
components/content-view.go
Normal file
23
components/content-view.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
81
components/galaxiespage.go
Normal file
81
components/galaxiespage.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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")
|
||||||
|
}
|
102
components/guestbookform.go
Normal file
102
components/guestbookform.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
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"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
87
components/guestbookpanel.go
Normal file
87
components/guestbookpanel.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
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")
|
||||||
|
}
|
17
components/header.go
Normal file
17
components/header.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
app.Compo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *header) Render() app.UI {
|
||||||
|
return app.Div().
|
||||||
|
Class("border-solid border-4 border-borderpink rounded").
|
||||||
|
Body(
|
||||||
|
app.H1().
|
||||||
|
Class("font-[anisha] bg-cool text-aliceblue text-[80px] text-center").
|
||||||
|
Text("Internetica Galactica"),
|
||||||
|
)
|
||||||
|
}
|
67
components/homepage.go
Normal file
67
components/homepage.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
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("mt-1.5 w-[900px] mx-auto ")
|
||||||
|
}
|
49
components/homepanel.go
Normal file
49
components/homepanel.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
|
|
||||||
|
type homePanel struct {
|
||||||
|
app.Compo
|
||||||
|
|
||||||
|
onShowClick func()
|
||||||
|
updateAvailable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *homePanel) Render() app.UI {
|
||||||
|
return app.Div().
|
||||||
|
Class("flex flex-nowrap text-aliceblue").
|
||||||
|
Body(
|
||||||
|
app.Div().
|
||||||
|
Class("flex-auto").
|
||||||
|
Body(
|
||||||
|
app.P().Text("Welcome, internet surfer!").Class("p-h1"),
|
||||||
|
app.P().
|
||||||
|
Class("").
|
||||||
|
Text(`Welcome to my website! Whether you came here by accident or were linked to it, I welcome you!
|
||||||
|
Have a look around my page to see all the stuff I put on it!
|
||||||
|
I put in a lot of effort, probably the most out of any project I have attempted, so it would be nice
|
||||||
|
if you left a nice comment down in the guestbook.
|
||||||
|
There is no signup required (unlike the stupid modern web where that's "essential").
|
||||||
|
Go crazy, write whatever you want! Just be nice!
|
||||||
|
Above all, enjoy yourself in my little online webspace!!!`),
|
||||||
|
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,4 +1,4 @@
|
|||||||
package main
|
package components
|
||||||
|
|
||||||
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
||||||
|
|
25
components/navbar.go
Normal file
25
components/navbar.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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("border-solid border-4 rounded border-borderpink text-aliceblue bg-cool float-left relative w-[180px] font-[havakana] text-center")
|
||||||
|
}
|
27
components/updater.go
Normal file
27
components/updater.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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()
|
||||||
|
}
|
1
design-desktop.drawio
Normal file
1
design-desktop.drawio
Normal file
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="app.diagrams.net" modified="2022-03-14T17:54:08.495Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36" etag="OBbzwV6CMD5jW8t-1HUz" version="17.1.2" type="browser"><diagram name="Page-1" id="03018318-947c-dd8e-b7a3-06fadd420f32">5VhRb5swEP41PLbCGEjy2CRdq6ntpnVSnx1wwIrB1Jgm7a/fGewQAlVTqUyLlofE/s6c8Xff+ew4eJHtbiQp0nsRU+54brxz8NLxPIRDF3408togkwlugESy2AxqgUf2Rg1onksqFtOyM1AJwRUrumAk8pxGqoMRKcW2O2wteHfWgiS0BzxGhPfRJxar1KDIdVvDLWVJaqaeBsawItEmkaLKzXyOh9f1pzFnxPoy48uUxGJ7AOFrBy+kEKppZbsF5ZpbS1vz3Ld3rPv3ljRXpzwQXSf323T2nbBw++NhdvdTTMmFidUL4ZXh45aSmErzyurV0lQvlGpXroPn25Qp+liQSFu3IAzAUpVx6CFolhuqotR2lBSbPbca6b+5WcwLlYruDiCzkhsqMqrkKwwxVs+qzKjOqnDbhjAIggZLD6KHrOyIkU2yd91SBw3D3ieY9HtMnhuHaNonEeEBEj1/LBKDHokP5IUlRDGR9+iEZaouZ4SzJId2BKSAhvFck8Eg16+MIWNxrB+fS1qyN7KqXelgFILlql5NMHeCpfZVKVE2u1UbgYXgAvwuc5FrL2vG+RH0BXHBbnAZdCLjuf3ITNyBwIwVl/Dsxe3jE8WN/WAkEic9EucEapos9WRQRbyQZJqnfFUWNQkh1/pewWYcJrolYZTIdIHUyv9f0sGbHaeD7/djORtIBzxWOszOLh18NP1wr/fRAIdoNhaJCA2w2Eg+RVbyT5RHMJ81wEStzYKFBe6EpDo94OhY6d+41iKEhSm9AHhtx1s0R8kSjpJUVdpKYlawkkUsT6BHOTOjSoie9qFBVpWZiOvMy4raJ8sjFrO4yrXrSn9xsoL59Whl56b1OTDJiZ6Gs+eKXB6spGgX8mEqn5pvJWhMLwQvg7b3W4Delhfee0oUoKM1r8+mKewINNc7Qke/HYF+hRxtar4eqexAjoMVDk1Gk6PXk+NNRUu1EmJT3ytkdm5JjgeS3HO9AVZH2ylR/4Lxy1axla1+x3WuLobDRe4fYzw80vEQ42g2/auMn99F5Fi3gXdicfJHO/Ci/k3E6nQt6j2/pTN8roQ1XDRnpCsYgKbFrjVabR/sKftC0Hj88lpw+vFvSAHjFoAAdRMnHLjh7K8zhyEPPx9x6Lb/stS2g7+y8PUf</diagram></mxfile>
|
@ -1,15 +0,0 @@
|
|||||||
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,6 +9,4 @@ 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,15 +2,6 @@ module dutchellie.nl/DutchEllie/proper-website-2
|
|||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require github.com/maxence-charriere/go-app/v9 v9.3.3
|
||||||
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 (
|
require github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
|
||||||
)
|
|
||||||
|
15
go.sum
15
go.sum
@ -1,16 +1,9 @@
|
|||||||
|
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=
|
||||||
@ -20,16 +13,10 @@ 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=
|
||||||
|
104
main.go
Normal file
104
main.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
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/newstyle.css",
|
||||||
|
"/web/static/adreena.css",
|
||||||
|
"/web/static/anisha.css",
|
||||||
|
"/web/static/havakana.css",
|
||||||
|
},
|
||||||
|
Scripts: []string{
|
||||||
|
"https://cdn.tailwindcss.com",
|
||||||
|
"/web/static/custom.js",
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
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
86
src/block.go
@ -1,86 +0,0 @@
|
|||||||
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]
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
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
387
src/guestbook.go
@ -1,387 +0,0 @@
|
|||||||
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,16 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "github.com/maxence-charriere/go-app/v9/pkg/app"
|
|
||||||
|
|
||||||
type header struct {
|
|
||||||
app.Compo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *header) Render() app.UI {
|
|
||||||
return app.Div().
|
|
||||||
Class("header").
|
|
||||||
Body(
|
|
||||||
app.Text("Internetica Galactica"),
|
|
||||||
//&updater{},
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
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
63
src/html.go
@ -1,63 +0,0 @@
|
|||||||
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
41
src/http.go
@ -1,41 +0,0 @@
|
|||||||
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
129
src/main.go
@ -1,129 +0,0 @@
|
|||||||
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
110
src/menu.go
@ -1,110 +0,0 @@
|
|||||||
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
213
src/misc.go
@ -1,213 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
51
src/music.go
51
src/music.go
@ -1,51 +0,0 @@
|
|||||||
// 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(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
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)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
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
127
src/page.go
@ -1,127 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
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,41 +4,20 @@
|
|||||||
<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="../web/static/style.css">
|
<link rel="stylesheet" href="static/adreena.css">
|
||||||
<link rel="stylesheet" href="../web/static/form.css">
|
<link rel="stylesheet" href="static/anisha.css">
|
||||||
<link rel="stylesheet" href="static/anisha.css?family=anisha">
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link rel="stylesheet" href="static/adreena.css?family=adreena">
|
|
||||||
<link rel="stylesheet" href="static/havakana.css?family=havakana">
|
|
||||||
<title>Index</title>
|
<title>Index</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="block right content">
|
<div class="w-[900px] ">
|
||||||
<form action="" class="guestbook-form">
|
test
|
||||||
<div class="input-groups">
|
|
||||||
<div class="fr">
|
|
||||||
<div class="input-group input-group-name">
|
|
||||||
<label for="name">Name:</label>
|
|
||||||
<input type="text" name="name" class="input">
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-email">
|
|
||||||
<label for="email">Email:</label>
|
|
||||||
<input type="text" name="email" class="input">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-website">
|
|
||||||
<label for="website">Website:</label>
|
|
||||||
<input type="text" name="website" class="input">
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-message">
|
|
||||||
<label for="message">Message:</label>
|
|
||||||
<textarea name="message" cols="30" rows="5" class="input"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="submit-field">
|
|
||||||
<input type="submit" value="Send!">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
176
ui/menu.go
176
ui/menu.go
@ -1,176 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
<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>
|
|
@ -1,22 +0,0 @@
|
|||||||
<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>
|
|
@ -1,65 +0,0 @@
|
|||||||
<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>
|
|
@ -1,28 +0,0 @@
|
|||||||
<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 +0,0 @@
|
|||||||
<p>Test</p>
|
|
@ -1,8 +0,0 @@
|
|||||||
<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>
|
|
11
web/static/custom.js
Normal file
11
web/static/custom.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
'cool': '#362730',
|
||||||
|
'borderpink': '#fce6ff',
|
||||||
|
'aliceblue': '#f0f8ff',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
.input-groups {
|
|
||||||
display:flex;
|
|
||||||
flex-wrap:wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-name {
|
|
||||||
width: 50%;
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-email {
|
|
||||||
width: 50%;
|
|
||||||
float:right;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-website {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-message {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-field {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fr {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
Binary file not shown.
Before ![]() (image error) Size: 1.6 KiB |
Binary file not shown.
Before ![]() (image error) Size: 384 B |
Binary file not shown.
Before ![]() (image error) Size: 1.4 MiB |
Binary file not shown.
Before ![]() (image error) Size: 1.1 MiB |
Binary file not shown.
Before ![]() (image error) Size: 192 KiB |
Binary file not shown.
Before ![]() (image error) Size: 45 KiB |
4
web/static/newstyle.css
Normal file
4
web/static/newstyle.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
html {
|
||||||
|
background-image: url(images/background_star.gif);
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
@ -1,64 +1,9 @@
|
|||||||
html {
|
html {
|
||||||
/* overflow-y: scroll; */
|
background-image: url(images/background_star.gif);
|
||||||
margin: 0;
|
overflow-y: scroll;
|
||||||
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;
|
||||||
@ -66,64 +11,37 @@ 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: #fce6ff;
|
||||||
|
background-color: #362730;
|
||||||
|
font-size: 5em;
|
||||||
|
font-family: anisha;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
|
border: 3px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-color: rgb(252, 230, 255);
|
||||||
|
background-color: rgb(54, 39, 48);
|
||||||
|
position: relative;
|
||||||
|
float:left;
|
||||||
|
width: 250px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar a, a:link, a:visited{
|
.navbar a, a:link, a:visited{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color:#fce6ff
|
color:rgb(252, 230, 255)
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
float:left;
|
|
||||||
max-width: 256px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
float:right;
|
|
||||||
max-width: 614px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftbar {
|
|
||||||
border: 3px solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: rgb(252, 230, 255);
|
|
||||||
background-color: rgb(54, 39, 48);
|
|
||||||
position: relative;
|
|
||||||
float:left;
|
|
||||||
width: 250px;
|
|
||||||
padding: 5px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block {
|
|
||||||
border: 3px solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: rgb(252, 230, 255);
|
|
||||||
background-color: rgb(54, 39, 48);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftbarblock-nop {
|
|
||||||
float:left;
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftbarblock {
|
|
||||||
float:left;
|
|
||||||
width: 250px;
|
|
||||||
padding: 5px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentblock {
|
|
||||||
float:right;
|
|
||||||
width: 614px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -138,28 +56,13 @@ body {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-border {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-text {
|
.content-text {
|
||||||
max-width: 75%;
|
max-width: 80%;
|
||||||
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%;
|
||||||
@ -168,11 +71,10 @@ body {
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
.guestbook-form-div {
|
.gbp {
|
||||||
margin: 0px 0px 10px 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@ -298,33 +200,6 @@ 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;
|
||||||
@ -336,94 +211,6 @@ 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