112 Commits

Author SHA1 Message Date
ngn
b447c8cf13 Merge pull request 'Update dependency svelte to v5.34.7' (#80) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 41s
Reviewed-on: #80
2025-06-20 11:36:23 +03:00
ngn
e212d43bf2 Merge pull request 'Update dependency @sveltejs/kit to v2.22.0' (#81) from renovate/sveltejs-kit-2.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #81
2025-06-20 11:36:13 +03:00
2bf94c0f3c Update dependency @sveltejs/kit to v2.22.0 2025-06-20 07:05:19 +00:00
3f49d8294a Update dependency svelte to v5.34.7 2025-06-19 17:04:56 +00:00
ngn
323b76fe98 Merge pull request 'Update dependency svelte to v5.34.4' (#79) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 42s
Reviewed-on: #79
2025-06-18 02:24:41 +03:00
f714bc6eaa Update dependency svelte to v5.34.4 2025-06-17 21:04:15 +00:00
ngn
8ad2778dd0 Merge pull request 'Update dependency @sveltejs/kit to v2.21.5' (#77) from renovate/sveltejs-kit-2.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 38s
Reviewed-on: #77
2025-06-14 20:19:29 +03:00
ngn
0d99a7da69 Merge pull request 'Update dependency svelte to v5.34.3' (#78) from renovate/svelte-5.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #78
2025-06-14 20:19:17 +03:00
c049f6c44a Update dependency svelte to v5.34.3 2025-06-14 17:05:11 +00:00
9acbe212ad Update dependency @sveltejs/kit to v2.21.5 2025-06-13 07:04:12 +00:00
ngn
aa29cca4f4 Merge pull request 'Update dependency svelte to v5.34.1' (#76) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 48s
Reviewed-on: #76
2025-06-12 11:04:53 +03:00
afc0a30660 Update dependency svelte to v5.34.1 2025-06-12 02:03:58 +00:00
ngn
647634addc Merge pull request 'Update dependency @sveltejs/kit to v2.21.4' (#74) from renovate/sveltejs-kit-2.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 37s
Reviewed-on: #74
2025-06-10 20:37:43 +03:00
ngn
dba65911a6 Merge pull request 'Update dependency svelte to v5.33.19' (#75) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 39s
Reviewed-on: #75
2025-06-10 20:36:40 +03:00
1067d0a3fd Update dependency svelte to v5.33.19 2025-06-10 17:04:40 +00:00
1ffd51b641 Update dependency @sveltejs/kit to v2.21.4 2025-06-10 17:04:36 +00:00
ngn
cd97df5f21 Merge pull request 'Update dependency svelte to v5.33.18' (#71) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 43s
Reviewed-on: #71
2025-06-10 19:41:54 +03:00
ngn
12dedf3700 Merge pull request 'Update dependency @sveltejs/kit to v2.21.3' (#72) from renovate/sveltejs-kit-2.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #72
2025-06-10 19:41:42 +03:00
ngn
ade745cdc5 Merge pull request 'Update golang Docker tag to v1.24.4' (#73) from renovate/golang-1.x into main
All checks were successful
Build the docker image for the API / build (push) Successful in 2m54s
Reviewed-on: #73
2025-06-10 19:41:32 +03:00
ab5977eaae Update dependency @sveltejs/kit to v2.21.3 2025-06-09 02:03:38 +00:00
39813d246f Update dependency svelte to v5.33.18 2025-06-08 12:04:00 +00:00
975626fc5c Update golang Docker tag to v1.24.4 2025-06-05 21:04:27 +00:00
ngn
1f47d59760 Merge pull request 'Update dependency svelte to v5.33.13' (#70) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 47s
Reviewed-on: #70
2025-06-02 16:13:18 +03:00
38abe411a8 Update dependency svelte to v5.33.13 2025-06-02 02:04:01 +00:00
ngn
e7216d51e3 Merge pull request 'Update dependency svelte to v5.33.12' (#69) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 45s
Reviewed-on: #69
2025-06-02 00:56:30 +03:00
2b244645c6 Update dependency svelte to v5.33.12 2025-06-01 21:04:14 +00:00
ngn
43589f7ddc Merge pull request 'Update dependency svelte to v5.33.11' (#68) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 58s
Reviewed-on: #68
2025-06-01 14:10:29 +03:00
b43a219fb3 Update dependency svelte to v5.33.11 2025-05-31 21:09:20 +00:00
ngn
7bf92f9049 Merge pull request 'Update dependency svelte to v5.33.6' (#67) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 54s
Reviewed-on: #67
2025-05-29 10:40:39 +03:00
577195cb7f Update dependency svelte to v5.33.6 2025-05-29 07:04:18 +00:00
ngn
fc64b742bb Merge pull request 'Update dependency svelte to v5.33.1' (#66) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 1m7s
Reviewed-on: #66
2025-05-23 16:50:39 +03:00
922eaeb124 Update dependency svelte to v5.33.1 2025-05-23 02:05:57 +00:00
ngn
65ec5a51aa Merge pull request 'Update dependency svelte to v5.33.0' (#63) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 1m8s
Reviewed-on: #63
2025-05-22 23:34:57 +03:00
ngn
8036b0c1e1 Merge pull request 'Update module github.com/gofiber/fiber/v2 to v2.52.8' (#65) from renovate/github.com-gofiber-fiber-v2-2.x into main
All checks were successful
Build the docker image for the API / build (push) Successful in 2m53s
Reviewed-on: #65
2025-05-22 23:34:41 +03:00
ngn
c505698b0b fix the pages directory in doc server Dockerfile
All checks were successful
Build the docker image for the doc server / build (push) Successful in 1m1s
Signed-off-by: ngn <ngn@ngn.tf>
2025-05-22 20:15:03 +03:00
ngn
4e08375748 Merge branch 'main' of https://git.ngn.tf/ngn/website
Some checks failed
Build the docker image for the doc server / build (push) Failing after 15s
2025-05-22 20:12:41 +03:00
ngn
912bf616b6 update doc server to ctorm 1.8.1
Signed-off-by: ngn <ngn@ngn.tf>
2025-05-22 20:12:05 +03:00
d1ddbf2605 Update dependency svelte to v5.33.0 2025-05-22 17:07:27 +00:00
66b01bd58d Update module github.com/gofiber/fiber/v2 to v2.52.8 2025-05-22 07:06:48 +00:00
ngn
01468670e3 Merge pull request 'Update dependency marked to v15.0.12' (#62) from renovate/marked-15.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 1m11s
Reviewed-on: #62
2025-05-20 15:48:20 +03:00
ngn
1f09aae11e revert ce1ac7b09e
All checks were successful
Build the docker image for the doc server / build (push) Successful in 11s
revert Merge pull request 'Update ghcr.io/ngn13/ctorm Docker tag to v1.8' (#61) from renovate/ghcr.io-ngn13-ctorm-1.x into main

Reviewed-on: #61
2025-05-20 15:47:59 +03:00
ngn
ce1ac7b09e Merge pull request 'Update ghcr.io/ngn13/ctorm Docker tag to v1.8' (#61) from renovate/ghcr.io-ngn13-ctorm-1.x into main
Some checks failed
Build the docker image for the doc server / build (push) Has been cancelled
Reviewed-on: #61
2025-05-20 15:47:32 +03:00
f9faddf660 Update dependency marked to v15.0.12 2025-05-20 07:07:09 +00:00
945f1b84f2 Update ghcr.io/ngn13/ctorm Docker tag to v1.8 2025-05-20 02:07:44 +00:00
ngn
a9d2633107 Merge branch 'main' of https://git.ngn.tf/ngn/website 2025-05-20 01:17:34 +03:00
ngn
506d16e0c9 update the readme formatting, remove old assets
Signed-off-by: ngn <ngn@ngn.tf>
2025-05-20 01:17:04 +03:00
ngn
30c3b0c37b Merge pull request 'Update dependency dompurify to v3.2.6' (#59) from renovate/dompurify-3.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 1m8s
Reviewed-on: #59
2025-05-20 01:13:59 +03:00
ngn
2da4f81a6b Merge pull request 'Update dependency svelte to v5.31.1' (#60) from renovate/svelte-5.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #60
2025-05-20 01:13:49 +03:00
cc482fc0ad Update dependency svelte to v5.31.1 2025-05-19 21:21:28 +00:00
51ea22fbc5 Update dependency dompurify to v3.2.6 2025-05-19 17:05:37 +00:00
ngn
58595d5eb3 Merge pull request 'Update dependency @sveltejs/kit to v2.21.1' (#50) from renovate/sveltejs-kit-2.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 1m13s
Reviewed-on: #50
2025-05-19 16:26:10 +03:00
ngn
671d44017f Merge pull request 'Update github.com/ngn13/ortam digest to 8dea816' (#57) from renovate/github.com-ngn13-ortam-digest into main
All checks were successful
Build the docker image for the API / build (push) Successful in 2m40s
Reviewed-on: #57
2025-05-19 16:25:55 +03:00
ngn
95d24b734b Merge pull request 'Update dependency svelte to v5.30.2' (#49) from renovate/svelte-5.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #49
2025-05-19 16:25:33 +03:00
ngn
c2e31fb440 Merge pull request 'Update Node.js to v23.11.1' (#58) from renovate/node-23.x into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #58
2025-05-19 16:25:22 +03:00
2d3dab5646 Update dependency @sveltejs/kit to v2.21.1 2025-05-18 21:09:17 +00:00
4b2e12b374 Update dependency svelte to v5.30.2 2025-05-18 12:09:46 +00:00
c6323cae38 Update Node.js to v23.11.1 2025-05-18 12:09:31 +00:00
e05d42a956 Update github.com/ngn13/ortam digest to 8dea816 2025-05-18 12:09:27 +00:00
ngn
426c474016 Merge pull request 'Update module github.com/mattn/go-sqlite3 to v1.14.28' (#51) from renovate/github.com-mattn-go-sqlite3-1.x into main
All checks were successful
Build the docker image for the API / build (push) Successful in 6m28s
Reviewed-on: #51
2025-05-18 14:18:43 +03:00
ngn
91ca908980 Merge pull request 'Update dependency marked to v15.0.11' (#52) from renovate/marked-15.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 3m31s
Reviewed-on: #52
2025-05-18 14:18:32 +03:00
ngn
5a61508359 Merge pull request 'Update dependency vite to v5.4.19' (#53) from renovate/vite-5.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #53
2025-05-18 14:18:22 +03:00
ngn
86c0e3cc97 Merge pull request 'Update golang Docker tag to v1.24.3' (#54) from renovate/golang-1.x into main
Some checks failed
Build the docker image for the API / build (push) Has been cancelled
Reviewed-on: #54
2025-05-18 14:18:10 +03:00
ngn
1afa33f28b Merge pull request 'Update dependency @sveltejs/adapter-auto to v6.0.1' (#55) from renovate/sveltejs-adapter-auto-6.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #55
2025-05-18 14:17:54 +03:00
ngn
a22b1e67c8 Merge pull request 'Update dependency prettier-plugin-svelte to v3.4.0' (#56) from renovate/prettier-plugin-svelte-3.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #56
2025-05-18 14:17:38 +03:00
f477b4525b Update dependency prettier-plugin-svelte to v3.4.0 2025-05-14 17:06:36 +00:00
462d35ea0d Update dependency @sveltejs/adapter-auto to v6.0.1 2025-05-12 21:03:05 +00:00
693c94274a Update golang Docker tag to v1.24.3 2025-05-06 21:03:55 +00:00
af2e5b49ae Update dependency vite to v5.4.19 2025-04-30 07:03:16 +00:00
33a31dac32 Update dependency marked to v15.0.11 2025-04-25 02:03:31 +00:00
00b7227d4d Update module github.com/mattn/go-sqlite3 to v1.14.28 2025-04-16 17:03:50 +00:00
ngn
2f9d38cd45 Merge pull request 'Update dependency @sveltejs/adapter-auto to v6' (#42) from renovate/sveltejs-adapter-auto-6.x into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 33s
Reviewed-on: #42
2025-04-13 01:12:44 +03:00
ngn
e68dd1d317 Merge pull request 'Update dependency svelte to v5.26.2' (#46) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 37s
Reviewed-on: #46
2025-04-13 01:12:02 +03:00
ngn
fe834d2778 Merge pull request 'Update dependency @sveltejs/kit to v2.20.5' (#47) from renovate/sveltejs-kit-2.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #47
2025-04-13 01:10:46 +03:00
ngn
686f51dcde Merge pull request 'Update dependency vite to v5.4.18' (#48) from renovate/vite-5.x-lockfile into main
Some checks are pending
Build the docker image for the frontend application / build (push) Waiting to run
Reviewed-on: #48
2025-04-13 01:10:35 +03:00
ngn
dc43bdb3af remove the old config library files from the API
All checks were successful
Build the docker image for the API / build (push) Successful in 2m36s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-13 01:07:20 +03:00
ngn
05185cb82a use the ortam config library in the API
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-13 01:06:37 +03:00
ee3fa10096 Update dependency svelte to v5.26.2 2025-04-12 17:02:55 +00:00
0e4457d5fb Update dependency vite to v5.4.18 2025-04-10 07:03:05 +00:00
b95c51193e Update dependency @sveltejs/kit to v2.20.5 2025-04-09 21:03:23 +00:00
ngn
322d8d5c39 Merge pull request 'Update dependency svelte to v5.25.8' (#45) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 34s
Reviewed-on: #45
2025-04-08 05:04:24 +03:00
ngn
904e9f6d15 Merge pull request 'Update dependency marked to v15.0.8' (#44) from renovate/marked-15.x-lockfile into main
Some checks are pending
Build the docker image for the frontend application / build (push) Waiting to run
Reviewed-on: #44
2025-04-08 05:04:13 +03:00
cc4abc85fe Update dependency svelte to v5.25.8 2025-04-08 02:03:45 +00:00
73cfcea923 Update dependency marked to v15.0.8 2025-04-08 02:03:40 +00:00
ngn
90af3d0500 update the app_url config option
All checks were successful
Build the docker image for the API / build (push) Successful in 2m6s
Build the docker image for the frontend application / build (push) Successful in 37s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-08 03:07:14 +03:00
ngn
8d16273540 add /api route to the API documentation
All checks were successful
Build the docker image for the doc server / build (push) Successful in 14s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-08 03:00:02 +03:00
ngn
4fb78c0ab7 update frontend app version
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 35s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-08 02:55:58 +03:00
ngn
a57a4955ba fix internal api url
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-08 02:54:23 +03:00
ngn
ccf0d8abf9 make stuff work without js
All checks were successful
Build the docker image for the API / build (push) Successful in 2m7s
Build the docker image for the frontend application / build (push) Successful in 42s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-08 02:39:37 +03:00
ngn
b312e40204 use the correct package manager
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 36s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 06:04:34 +03:00
ngn
a67bd74ced fix dockerfile by adding required build programs
Some checks failed
Build the docker image for the frontend application / build (push) Failing after 10s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 06:03:00 +03:00
ngn
9a72d3f95d dump nerdfonts from the website to use locally
Some checks failed
Build the docker image for the frontend application / build (push) Failing after 20s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 05:58:46 +03:00
ngn
66c96ae312 update the example compose file
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 05:16:56 +03:00
ngn
f779a03ae5 update app dockerfile to include new envs
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 35s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 05:12:08 +03:00
ngn
bcb48d789b update the app URL env var name in API config
All checks were successful
Build the docker image for the API / build (push) Successful in 2m18s
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 04:57:29 +03:00
ngn
aa69525912 Merge branch 'main' of https://git.ngn.tf/ngn/website
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 44s
2025-04-07 04:49:00 +03:00
ngn
0134bd2ab1 add env vars for configuring onion and I2P urls
Signed-off-by: ngn <ngn@ngn.tf>
2025-04-07 04:46:47 +03:00
ngn
84e477b23e Merge pull request 'Update dependency dompurify to v3.2.5' (#43) from renovate/dompurify-3.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 35s
Reviewed-on: #43
2025-04-06 00:05:18 +03:00
515dcf6a09 Update dependency dompurify to v3.2.5 2025-04-05 21:03:17 +00:00
ngn
a50cc1ff7f Merge pull request 'Update dependency @sveltejs/kit to v2.20.4' (#41) from renovate/sveltejs-kit-2.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 40s
Reviewed-on: #41
2025-04-05 23:50:09 +03:00
ngn
f2ec9479b1 Merge pull request 'Update module github.com/mattn/go-sqlite3 to v1.14.27' (#40) from renovate/github.com-mattn-go-sqlite3-1.x into main
All checks were successful
Build the docker image for the API / build (push) Successful in 2m18s
Reviewed-on: #40
2025-04-05 23:43:34 +03:00
ngn
e1eae3c05b Merge pull request 'Update dependency vite to v5.4.17' (#39) from renovate/vite-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 38s
Reviewed-on: #39
2025-04-05 23:38:39 +03:00
ngn
53ab201ab3 Merge pull request 'Update golang Docker tag to v1.24.2' (#36) from renovate/golang-1.x into main
All checks were successful
Build the docker image for the API / build (push) Successful in 3m4s
Reviewed-on: #36
2025-04-05 23:38:18 +03:00
ngn
c9792fb6be Merge pull request 'Update Node.js to v23.11.0' (#35) from renovate/node-23.x into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 40s
Reviewed-on: #35
2025-04-05 23:24:26 +03:00
ngn
68808a14f9 Merge pull request 'Update dependency prettier to v3.5.3' (#34) from renovate/prettier-3.x-lockfile into main
Some checks failed
Build the docker image for the frontend application / build (push) Has been cancelled
Reviewed-on: #34
2025-04-05 23:24:16 +03:00
ngn
4343192a2c Merge pull request 'Update dependency svelte to v5.25.7' (#28) from renovate/svelte-5.x-lockfile into main
All checks were successful
Build the docker image for the frontend application / build (push) Successful in 36s
Reviewed-on: #28
2025-04-05 23:22:31 +03:00
20fb3cd685 Update dependency svelte to v5.25.7 2025-04-05 17:03:22 +00:00
906ace188b Update dependency vite to v5.4.17 2025-04-05 17:03:16 +00:00
f2a1a21a8c Update dependency prettier to v3.5.3 2025-04-05 17:03:12 +00:00
389a9d3df4 Update module github.com/mattn/go-sqlite3 to v1.14.27 2025-04-02 17:04:40 +00:00
921fe4255d Update dependency @sveltejs/adapter-auto to v6 2025-04-02 07:03:12 +00:00
f7e4442de5 Update Node.js to v23.11.0 2025-04-01 21:03:03 +00:00
e4d1fc21fb Update golang Docker tag to v1.24.2 2025-04-01 21:03:00 +00:00
72 changed files with 564 additions and 541 deletions

View File

@ -28,8 +28,8 @@ jobs:
cd app
docker build --build-arg WEBSITE_REPORT_URL=https://git.ngn.tf/ngn/website/issues/new \
--build-arg WEBSITE_SOURCE_URL=https://git.ngn.tf/ngn/website \
--build-arg WEBSITE_APP_URL=https://ngn.tf \
--build-arg WEBSITE_API_URL=https://api.ngn.tf \
--build-arg WEBSITE_DOC_URL=http://doc:7003 \
--build-arg WEBSITE_API_URL=http://api:7002 \
--build-arg WEBSITE_API_PATH=/api \
--tag ${{env.REGISTRY}}/${{env.IMAGE}}:latest .
docker push ${{env.REGISTRY}}/${{env.IMAGE}}:latest

141
README.md
View File

@ -4,85 +4,112 @@
![](https://git.ngn.tf/ngn/website/actions/workflows/build-api.yml/badge.svg)
![](https://git.ngn.tf/ngn/website/actions/workflows/build-doc.yml/badge.svg)
This repo contains all the source code for my personal website, [ngn.tf](https://ngn.tf)
All code is licensed under AGPL version 3 (see [LICENSE.txt](LICENSE.txt))
This repo contains all the source code for my personal website,
[ngn.tf](https://ngn.tf) All code is licensed under AGPL version 3 (see
[LICENSE.txt](LICENSE.txt))
## Directory structure
### `app`
Contains frontend application, written with SvelteKit. It supports full SSR.
Contains modified CSS from [github-markdown-css](https://github.com/sindresorhus/github-markdown-css)
and fonts from [NerdFonts](https://www.nerdfonts.com/)
Contains modified CSS from
[github-markdown-css](https://github.com/sindresorhus/github-markdown-css) and
fonts from [NerdFonts](https://www.nerdfonts.com/)
### `api`
Contains the API server, written in Go. It uses the [Fiber](https://github.com/gofiber/fiber) web
framework which offers an [Express](https://expressjs.com/) like experience. I choose Fiber since I've used
worked with express a lot in the past. However previously the I was using [Gin](https://github.com/gin-gonic/gin)
(see history section).
API stores all the data in a local SQLite(3) database. Go doesn't support SQLite3 out of the box so
I'm using [mattn's sqlite3 driver](https://github.com/mattn/go-sqlite3).
Contains the API server, written in Go. It uses the
[Fiber](https://github.com/gofiber/fiber) web framework which offers an
[Express](https://expressjs.com/) like experience. I choose Fiber since I've
used worked with express a lot in the past. However previously the I was using
[Gin](https://github.com/gin-gonic/gin) (see history section).
API stores all the data in a local SQLite(3) database. Go doesn't support
SQLite3 out of the box so I'm using
[mattn's sqlite3 driver](https://github.com/mattn/go-sqlite3).
### `doc`
Contains the documentation server, written in C. It uses the [ctorm](https://github.com/ngn13/ctorm) web
framework, which is a framework that I myself wrote. Unlike the frontend application or the API server, it's not
accessable by public, the frontend application gets the documentation content from this server and renders it using
SSR. The reason I don't use the API for hosting the documentation content is that I want a separate server for hosting
Contains the documentation server, written in C. It uses the
[ctorm](https://github.com/ngn13/ctorm) web framework, which is a framework that
I myself wrote. Unlike the frontend application or the API server, it's not
accessable by public, the frontend application gets the documentation content
from this server and renders it using SSR. The reason I don't use the API for
hosting the documentation content is that I want a separate server for hosting
static content, API is only for hosting dynamic stuff.
### `admin`
The frontend application does not contain an admin interface, I do the administration stuff (such as adding news posts,
adding services etc.) using the python script in this directory. This script can be installed on to the PATH by running
the Makefile install script. After installation it can be used by running `admin_script`.
The frontend application does not contain an admin interface, I do the
administration stuff (such as adding news posts, adding services etc.) using the
python script in this directory. This script can be installed on to the PATH by
running the Makefile install script. After installation it can be used by
running `admin_script`.
## Deployment
Easiest way to deploy is to use docker. There is `compose.yml` and a `run.sh` script in the [deploy](deploy/) directory
that can be used to startup all the docker containers. Configuration options are passed during build time for the frontend
application, and for others it's passed with environment variables.
Easiest way to deploy is to use docker. There is `compose.yml` and a `run.sh`
script in the [deploy](deploy/) directory that can be used to startup all the
docker containers. Configuration options are passed during build time for the
frontend application, and for others it's passed with environment variables.
## History
Some nostalgic history/changelog stuff (just for the major version numbers):
- **v0.1 (late 2020 - early 2021)**: First ever version of my website, it was just a simple HTML/CSS page,
I never published any of the source code and I wiped the local copy on my USB drive in early 2022, I still
remember what it looked like though, it looked like I made entire website in microsoft paint... while blindfoled,
so yeah it was shit.
- **v0.1 (late 2020 - early 2021)**: First ever version of my website, it was
just a simple HTML/CSS page, I never published any of the source code and I
wiped the local copy on my USB drive in early 2022, I still remember what it
looked like though, it looked like I made entire website in microsoft paint...
while blindfoled, so yeah it was shit.
- **v1.0 (early 2021 - late 2022)**: This version was actualy hosted on my github.io page, and all the source code
was (and still is) avaliable, it was just a simple static site, [here is a screenshot](assets/githubio.png).
- **v1.0 (early 2021 - late 2022)**: This version was actualy hosted on my
github.io page, and all the source code was (and still is) avaliable, it was
just a simple static site, [here is a screenshot](assets/githubio.png).
- **vLOST (late 2022 - early 2023)**: As I learned more JS, I decided to rewrite (and rework) my website with one
of the fancy JS frameworks. I decided to go with Svelte. Not the kit version, at the time svelte did not support SSR.
I do not remember writting an API for it so I guess I just updated it everytime I wanted to add content? It was pretty
much like a static website and was hosted on `ngn13.fun` as at this point I had my own hosting. The source code for
this website was in a deleted github repository of mine, I looked for a local copy on my old hard drive but I wasn't able
to find it. I also do not remember how it looked like, sooo this version is pretty much lost :(
- **vLOST (late 2022 - early 2023)**: As I learned more JS, I decided to rewrite
(and rework) my website with one of the fancy JS frameworks. I decided to go
with Svelte. Not the kit version, at the time svelte did not support SSR. I do
not remember writting an API for it so I guess I just updated it everytime I
wanted to add content? It was pretty much like a static website and was hosted
on `ngn13.fun` as at this point I had my own hosting. The source code for this
website was in a deleted github repository of mine, I looked for a local copy
on my old hard drive but I wasn't able to find it. I also do not remember how
it looked like, sooo this version is pretty much lost :(
- **v2.0 (early 2023 - late 2023)**: After I discovered what SSR is, I decided to rewrite and rework my website one more
time in NuxtJS. I had really "fun" time using vue stuff. As NuxtJS supported server-side code, this website had its own
built in API. This website was also hosted on `ngn13.fun`. This also the first version that lives on this git repository.
- **v2.0 (early 2023 - late 2023)**: After I discovered what SSR is, I decided
to rewrite and rework my website one more time in NuxtJS. I had really "fun"
time using vue stuff. As NuxtJS supported server-side code, this website had
its own built in API. This website was also hosted on `ngn13.fun`. This also
the first version that lives on this git repository.
- **v3.0 (2023 august - 2023 november)**: In agust of 2023, I decided to rewrite and rework my website again, this time
I was going with SvelteKit as I haven't had the greatest experience with NuxtJS. SvelteKit was really fun to work with
and I got my new website done pretty quickly. (I don't wanna brag or something but I really imporeved the CSS/styling
stuff ya know). I also wrote a new API with Go and Gin. I did not publish the source code for the API, the code lived
on my local git server until I deleted it when I was done with 6.0. This website was hosted on `ngn13.fun` as well.
- **v3.0 (2023 august - 2023 november)**: In agust of 2023, I decided to rewrite
and rework my website again, this time I was going with SvelteKit as I haven't
had the greatest experience with NuxtJS. SvelteKit was really fun to work with
and I got my new website done pretty quickly. (I don't wanna brag or something
but I really imporeved the CSS/styling stuff ya know). I also wrote a new API
with Go and Gin. I did not publish the source code for the API, the code lived
on my local git server until I deleted it when I was done with 6.0. This
website was hosted on `ngn13.fun` as well.
- **v4.0 (2023 november - 2024 october)**: In this version the frontend was still similar to 3.0, the big changes are in
the API. I rewrote the API with Fiber. This version was the first version hosted on `ngn.tf` which is my new domain name.
Here is a [picture of the index](assets/4.0_index.png) and the [blog page](assets/4.0_blog.png).
- **v4.0 (2023 november - 2024 october)**: In this version the frontend was
still similar to 3.0, the big changes are in the API. I rewrote the API with
Fiber. This version was the first version hosted on `ngn.tf` which is my new
domain name. Here is a [picture of the index](assets/4.0_index.png) and the
[blog page](assets/4.0_blog.png).
- **v5.0 (2024 october - 2025 january)**: This version just had simple frontend UI changes compared to 4.0, at this
point I was thinking about doing a massive rework (which I did with 6.0), however I was working on some other shit at
the time, so I just did some small changes with the limited time I had for this project.
- **v5.0 (2024 october - 2025 january)**: This version just had simple frontend
UI changes compared to 4.0, at this point I was thinking about doing a massive
rework (which I did with 6.0), however I was working on some other shit at the
time, so I just did some small changes with the limited time I had for this
project.
- **v6.0 (2025 january - ...)**: The current major version of my website, frontend had a massive rework, API has been
cleaned up and extended to do status checking for the services I host. The `doc` server has been added to the mix
so I can host static documentation. The most important thing about this version is that it adds multi-language support,
so literally everything on the website (including the API and documentation content) is localized for both English
and Turkish, which was something I wanted to do for the longest time ever.
Damn it has been 4 years since I wrote that shit HTML page huh? Time flies...
## Screenshots (from v6.0)
![](assets/6.0_index.png)
![](assets/6.0_services.png)
- **v6.0 (2025 january - ...)**: The current major version of my website,
frontend had a massive rework, API has been cleaned up and extended to do
status checking for the services I host. The `doc` server has been added to
the mix so I can host static documentation. The most important thing about
this version is that it adds multi-language support, so literally everything
on the website (including the API and documentation content) is localized for
both English and Turkish, which was something I wanted to do for the longest
time ever.

View File

@ -1,4 +1,4 @@
FROM golang:1.24.0
FROM golang:1.24.4
WORKDIR /api

View File

@ -3,107 +3,47 @@ package config
import (
"fmt"
"net/url"
"os"
"github.com/ngn13/ortam"
)
type Type struct {
Options []Option
Count int
Debug bool // should display debug messgaes?
AppUrl *url.URL // frontend application URL for the website
Password string // admin password
Host string // host the server should listen on
IPHeader string // header that should be checked for obtaining the client IP
Interval string // service status check interval
Timeout string // timeout for the service status check
Limit string // if the service responds slower than this limit, it will be marked as "slow"
}
func (c *Type) Find(name string, typ uint8) (*Option, error) {
for i := 0; i < c.Count; i++ {
if c.Options[i].Name != name {
continue
}
if c.Options[i].Type != typ {
return nil, fmt.Errorf("bad option type")
}
return &c.Options[i], nil
func Load() (*Type, error) {
var conf = Type{
Debug: false,
Password: "",
Host: "0.0.0.0:7002",
IPHeader: "X-Real-IP",
Interval: "1h",
Timeout: "15s",
Limit: "5s",
}
return nil, fmt.Errorf("option not found")
}
func (c *Type) Load() (err error) {
var (
env_val string
env_name string
opt *Option
exists bool
)
// default options
c.Options = []Option{
{Name: "debug", Value: "false", Type: OPTION_TYPE_BOOL, Required: true}, // should display debug messgaes?
{Name: "app_url", Value: "http://localhost:7001/", Type: OPTION_TYPE_URL, Required: true}, // frontend application URL for the website
{Name: "password", Value: "", Type: OPTION_TYPE_STR, Required: true}, // admin password
{Name: "host", Value: "0.0.0.0:7002", Type: OPTION_TYPE_STR, Required: true}, // host the server should listen on
{Name: "ip_header", Value: "X-Real-IP", Type: OPTION_TYPE_STR, Required: false}, // header that should be checked for obtaining the client IP
{Name: "interval", Value: "1h", Type: OPTION_TYPE_STR, Required: false}, // service status check interval
{Name: "timeout", Value: "15s", Type: OPTION_TYPE_STR, Required: false}, // timeout for the service status check
{Name: "limit", Value: "5s", Type: OPTION_TYPE_STR, Required: false}, // if the service responds slower than this limit, it will be marked as "slow"
}
c.Count = len(c.Options)
for i := 0; i < c.Count; i++ {
opt = &c.Options[i]
env_name = opt.Env()
if env_val, exists = os.LookupEnv(env_name); exists {
opt.Value = env_val
}
if opt.Value == "" && opt.Required {
return fmt.Errorf("please specify a value for the config option \"%s\" (\"%s\")", opt.Name, env_name)
}
if err = opt.Load(); err != nil {
return fmt.Errorf("failed to load option \"%s\" (\"%s\"): %s", opt.Name, env_name, err.Error())
}
}
return nil
}
func (c *Type) GetStr(name string) string {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_STR); err != nil {
return ""
}
return opt.TypeValue.Str
}
func (c *Type) GetBool(name string) bool {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_BOOL); err != nil {
return false
}
return opt.TypeValue.Bool
}
func (c *Type) GetURL(name string) *url.URL {
var (
opt *Option
err error
)
if opt, err = c.Find(name, OPTION_TYPE_URL); err != nil {
return nil
}
return opt.TypeValue.URL
if err := ortam.Load(&conf, "WEBSITE"); err != nil {
return nil, err
}
if conf.AppUrl == nil {
conf.AppUrl, _ = url.Parse("http://localhost:7001/")
}
if conf.Password == "" {
return nil, fmt.Errorf("password is not specified")
}
if conf.Host == "" {
return nil, fmt.Errorf("host address is not specified")
}
return &conf, nil
}

View File

@ -1,49 +0,0 @@
package config
import (
"fmt"
"net/url"
"strings"
)
const (
OPTION_TYPE_STR = 0
OPTION_TYPE_BOOL = 1
OPTION_TYPE_URL = 2
)
type Option struct {
Name string
Value string
Required bool
Type uint8
TypeValue struct {
URL *url.URL
Str string
Bool bool
}
}
func (o *Option) Env() string {
return strings.ToUpper(fmt.Sprintf("WEBSITE_%s", o.Name))
}
func (o *Option) Load() (err error) {
err = nil
switch o.Type {
case OPTION_TYPE_STR:
o.TypeValue.Str = o.Value
case OPTION_TYPE_BOOL:
o.TypeValue.Bool = "1" == o.Value || "true" == strings.ToLower(o.Value)
case OPTION_TYPE_URL:
o.TypeValue.URL, err = url.Parse(o.Value)
default:
return fmt.Errorf("invalid option type")
}
return err
}

View File

@ -1,10 +1,11 @@
module github.com/ngn13/website/api
go 1.21.3
go 1.24.0
require (
github.com/gofiber/fiber/v2 v2.52.6
github.com/mattn/go-sqlite3 v1.14.24
github.com/gofiber/fiber/v2 v2.52.8
github.com/mattn/go-sqlite3 v1.14.28
github.com/ngn13/ortam v0.0.0-20250421004351-8dea81680817
)
require (

View File

@ -1,17 +1,11 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gofiber/fiber/v2 v2.52.8 h1:xl4jJQ0BV5EJTA2aWiKw/VddRpHrKeZLF0QPUxqn0x4=
github.com/gofiber/fiber/v2 v2.52.8/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -19,12 +13,16 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/ngn13/ortam v0.0.0-20250412195317-e76e62a7a305 h1:1YxtSMwR14PklXNlZxIqcmfpiq2+G98YNmhSuz7GKCQ=
github.com/ngn13/ortam v0.0.0-20250412195317-e76e62a7a305/go.mod h1:MSJZ4ZstrLvVEvivbp9hhup+iL8rvtpgKcYaF3DSOKk=
github.com/ngn13/ortam v0.0.0-20250421004351-8dea81680817 h1:WkHM4w51N5jCsWcDVcPsXz3zhi/kCfNp/VGh2uPjwsk=
github.com/ngn13/ortam v0.0.0-20250421004351-8dea81680817/go.mod h1:MSJZ4ZstrLvVEvivbp9hhup+iL8rvtpgKcYaF3DSOKk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@ -35,7 +33,5 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

View File

@ -36,18 +36,18 @@ func main() {
app *fiber.App
stat status.Type
conf config.Type
conf *config.Type
db database.Type
err error
)
if err = conf.Load(); err != nil {
if conf, err = config.Load(); err != nil {
util.Fail("failed to load the configuration: %s", err.Error())
return
}
if !conf.GetBool("debug") {
if !conf.Debug {
util.Debg = func(m string, v ...any) {}
}
@ -56,7 +56,7 @@ func main() {
return
}
if err = stat.Setup(&conf, &db); err != nil {
if err = stat.Setup(conf, &db); err != nil {
util.Fail("failed to setup the status checker: %s", err.Error())
return
}
@ -75,7 +75,7 @@ func main() {
c.Set("Access-Control-Allow-Methods", "PUT, DELETE, GET") // POST can be sent from HTML forms, so I prefer PUT for API endpoints
c.Locals("status", &stat)
c.Locals("config", &conf)
c.Locals("config", conf)
c.Locals("database", &db)
return c.Next()
@ -121,9 +121,9 @@ func main() {
}
// start the app
util.Info("starting web server on %s", conf.GetStr("host"))
util.Info("starting web server on %s", conf.Host)
if err = app.Listen(conf.GetStr("host")); err != nil {
if err = app.Listen(conf.Host); err != nil {
util.Fail("failed to start the web server: %s", err.Error())
}

View File

@ -21,7 +21,7 @@ func admin_log(c *fiber.Ctx, m string) error {
func AuthMiddleware(c *fiber.Ctx) error {
conf := c.Locals("config").(*config.Type)
if c.Get("Authorization") != conf.GetStr("password") {
if c.Get("Authorization") != conf.Password {
return util.ErrAuth(c)
}

View File

@ -7,8 +7,7 @@ import (
func GET_Index(c *fiber.Ctx) error {
conf := c.Locals("config").(*config.Type)
app := conf.GetURL("app_url")
// redirect to the API documentation
return c.Redirect(app.JoinPath("/doc/api").String())
return c.Redirect(conf.AppUrl.JoinPath("/doc/api").String())
}

View File

@ -40,7 +40,6 @@ func GET_News(c *fiber.Ctx) error {
db := c.Locals("database").(*database.Type)
conf := c.Locals("config").(*config.Type)
app := conf.GetURL("app_url")
lang := c.Params("lang")
if lang == "" || len(lang) != 2 {
@ -63,10 +62,10 @@ func GET_News(c *fiber.Ctx) error {
})
if feed, err = util.Render("views/news.xml", fiber.Map{
"app_url": conf.AppUrl,
"updated": time.Now().Format(time.RFC3339),
"entries": entries,
"lang": lang,
"app": app,
}); err != nil {
return util.ErrInternal(c, err)
}

View File

@ -66,29 +66,24 @@ func (s *Type) loop() {
func (s *Type) Setup(conf *config.Type, db *database.Type) error {
var (
dur time.Duration
iv, to, lm string
err error
dur time.Duration
err error
)
iv = conf.GetStr("interval")
to = conf.GetStr("timeout")
lm = conf.GetStr("limit")
if iv == "" || to == "" || lm == "" {
if conf.Interval == "" || conf.Timeout == "" || conf.Limit == "" {
s.disabled = true
return nil
}
if dur, err = util.GetDuration(iv); err != nil {
if dur, err = util.GetDuration(conf.Interval); err != nil {
return err
}
if s.timeout, err = util.GetDuration(iv); err != nil {
if s.timeout, err = util.GetDuration(conf.Timeout); err != nil {
return err
}
if s.limit, err = util.GetDuration(iv); err != nil {
if s.limit, err = util.GetDuration(conf.Limit); err != nil {
return err
}

View File

@ -10,10 +10,9 @@ import (
func IP(c *fiber.Ctx) string {
conf := c.Locals("config").(*config.Type)
ip_header := conf.GetStr("ip_header")
if ip_header != "" && c.Get(ip_header) != "" {
return strings.Clone(c.Get(ip_header))
if conf.IPHeader != "" && c.Get(conf.IPHeader) != "" {
return strings.Clone(c.Get(conf.IPHeader))
}
return c.IP()

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
<title>{{.app.Host}} news</title>
<title>{{.app_url.Host}} news</title>
<updated>{{.updated}}</updated>
<subtitle>News and updates about my projects and self-hosted services</subtitle>
<link href="{{.app.JoinPath "/news"}}"></link>
<link href="{{.app_url.JoinPath "/news"}}"></link>
{{ range .entries }}
<entry>
<title>{{.Title}}</title>

1
app/.gitignore vendored
View File

@ -8,3 +8,4 @@ node_modules
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
nerdfonts.*

View File

@ -1,22 +1,24 @@
# build the application with node
FROM node:23.7.0 AS build
FROM node:23.11.1 AS build
ARG WEBSITE_REPORT_URL
ARG WEBSITE_SOURCE_URL
ARG WEBSITE_APP_URL
ARG WEBSITE_API_URL
ARG WEBSITE_DOC_URL
ARG WEBSITE_API_URL
ARG WEBSITE_API_PATH
ENV WEBSITE_REPORT_URL=$WEBSITE_REPORT_URL
ENV WEBSITE_SOURCE_URL=$WEBSITE_SOURCE_URL
ENV WEBSITE_APP_URL=$WEBSITE_APP_URL
ENV WEBSITE_API_URL=$WEBSITE_API_URL
ENV WEBSITE_DOC_URL=$WEBSITE_DOC_URL
ENV WEBSITE_API_URL=$WEBSITE_API_URL
ENV WEBSITE_API_PATH=$WEBSITE_API_PATH
WORKDIR /app
COPY . /app
RUN npm install && npm run build
RUN apt install -y make sed wget
RUN npm install
RUN make
# run it with bun (a lot faster)
FROM oven/bun:latest AS main

View File

@ -1,10 +1,25 @@
all:
NF_CSS = static/css/nerdfonts.css
NF_WOFF = static/assets/nerdfonts.woff2
all: $(NF_CSS)
npm run build
$(NF_CSS): $(NF_WOFF)
wget "https://www.nerdfonts.com/assets/css/webfont.css" -O $@
sed 's/\.\.\/fonts\/Symbols-2048-em Nerd Font Complete\.woff2/\/assets\/nerdfonts\.woff2/g' -i $@
$(NF_WOFF):
wget "https://www.nerdfonts.com/assets/fonts/Symbols-2048-em%20Nerd%20Font%20Complete.woff2" -O $@
run: $(NF_CSS)
npm run dev
format:
npm run format
run:
npm run dev
clean:
rm -rf build
rm $(NF_CSS)
rm $(NF_WOFF)
.PHONY: format
.PHONY: format run clean

125
app/package-lock.json generated
View File

@ -1,19 +1,19 @@
{
"name": "website",
"version": "6.1",
"version": "6.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "website",
"version": "6.1",
"version": "6.3",
"dependencies": {
"dompurify": "^3.2.3",
"marked": "^15.0.6",
"svelte-i18n": "^4.0.1"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/kit": "^2.15.1",
"@sveltejs/vite-plugin-svelte": "^4.0.3",
@ -831,15 +831,21 @@
"win32"
]
},
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
"license": "MIT",
"peerDependencies": {
"acorn": "^8.9.0"
}
},
"node_modules/@sveltejs/adapter-auto": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-4.0.0.tgz",
"integrity": "sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-6.0.1.tgz",
"integrity": "sha512-mcWud3pYGPWM2Pphdj8G9Qiq24nZ8L4LB7coCUckUEy5Y7wOWGJ/enaZ4AtJTcSm5dNK1rIkBRoqt+ae4zlxcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"import-meta-resolve": "^4.1.0"
},
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
@ -861,23 +867,25 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.20.4",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.4.tgz",
"integrity": "sha512-B3Y1mb1Qjt57zXLVch5tfqsK/ebHe6uYTcFSnGFNwRpId3+fplLgQK6Z2zhDVBezSsPuhDq6Pry+9PA88ocN6Q==",
"version": "2.22.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.0.tgz",
"integrity": "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/cookie": "^0.6.0",
"acorn": "^8.14.1",
"cookie": "^0.6.0",
"devalue": "^5.1.0",
"esm-env": "^1.2.2",
"import-meta-resolve": "^4.1.0",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^3.0.0"
"sirv": "^3.0.0",
"vitefu": "^1.0.6"
},
"bin": {
"svelte-kit": "svelte-kit.js"
@ -886,9 +894,9 @@
"node": ">=18.13"
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3 || ^6.0.0"
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
@ -957,9 +965,10 @@
"optional": true
},
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@ -967,14 +976,6 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-typescript": {
"version": "1.4.13",
"resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
"integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
"peerDependencies": {
"acorn": ">=8.9.0"
}
},
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
@ -1083,9 +1084,9 @@
"dev": true
},
"node_modules/dompurify": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz",
"integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==",
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@ -1203,9 +1204,9 @@
}
},
"node_modules/esrap": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.3.tgz",
"integrity": "sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==",
"version": "1.4.9",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.9.tgz",
"integrity": "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@ -1298,17 +1299,6 @@
"node": ">= 0.4"
}
},
"node_modules/import-meta-resolve": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
"integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
"dev": true,
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/intl-messageformat": {
"version": "10.7.11",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.11.tgz",
@ -1395,9 +1385,9 @@
}
},
"node_modules/marked": {
"version": "15.0.7",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz",
"integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
"version": "15.0.12",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
@ -1526,9 +1516,9 @@
}
},
"node_modules/prettier": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
@ -1542,9 +1532,9 @@
}
},
"node_modules/prettier-plugin-svelte": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz",
"integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz",
"integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@ -1660,21 +1650,21 @@
}
},
"node_modules/svelte": {
"version": "5.19.10",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.19.10.tgz",
"integrity": "sha512-7lId+z36IZWzuo0N0oGOStEPi3/Wv1VQEnIzMmDaLDSlJSruKplhhVr+NaZ0Wh7ZILfOjbeU7PbTjqmQQYZF4A==",
"version": "5.34.7",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.34.7.tgz",
"integrity": "sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
"acorn-typescript": "^1.4.13",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
"esrap": "^1.4.3",
"esrap": "^1.4.8",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
@ -2159,9 +2149,9 @@
"license": "ISC"
},
"node_modules/vite": {
"version": "5.4.15",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz",
"integrity": "sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==",
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2219,12 +2209,17 @@
}
},
"node_modules/vitefu": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.3.tgz",
"integrity": "sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz",
"integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==",
"dev": true,
"license": "MIT",
"workspaces": [
"tests/deps/*",
"tests/projects/*"
],
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0"
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
},
"peerDependenciesMeta": {
"vite": {

View File

@ -1,6 +1,6 @@
{
"name": "website",
"version": "6.1",
"version": "6.3",
"private": true,
"scripts": {
"dev": "vite dev",
@ -10,7 +10,7 @@
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/kit": "^2.15.1",
"@sveltejs/vite-plugin-svelte": "^4.0.3",

View File

@ -1,9 +1,14 @@
import { browser } from "$app/environment";
import { urljoin } from "$lib/util.js";
const api_version = "v1";
const api_url = urljoin(import.meta.env.WEBSITE_API_URL, api_version);
function api_urljoin(path = null, query = {}) {
let api_url = "";
if (browser) api_url = urljoin(import.meta.env.WEBSITE_API_PATH, api_version);
else api_url = urljoin(import.meta.env.WEBSITE_API_URL, api_version);
return urljoin(api_url, path, query);
}

View File

@ -1,14 +1,16 @@
<script>
import { urljoin, color, date_from_ts } from "$lib/util.js";
import { color, date_from_ts } from "$lib/util.js";
import { api_get_metrics } from "$lib/api.js";
import Link from "$lib/link.svelte";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
let data = {};
let show_counter = false,
data = {};
onMount(async () => {
show_counter = true;
data = await api_get_metrics(fetch);
});
</script>
@ -20,28 +22,28 @@
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/license")} bold={true}
>{$_("footer.license")}</Link
>
<Link link="/doc/license" bold={true}>{$_("footer.license")}</Link>
</span>
<span>/</span>
<span>
<Link link={urljoin(import.meta.env.WEBSITE_APP_URL, "doc/privacy")} bold={true}
>{$_("footer.privacy")}</Link
>
<Link link="/doc/privacy" bold={true}>{$_("footer.privacy")}</Link>
</span>
</div>
<span class="counter">
{$_("footer.number", {
values: {
total: data.total,
since: date_from_ts(data.since),
},
})}
{#if data.number % 1000 == 0}
<span style="color: var(--{color()})">({$_("footer.wow")})</span>
{/if}
</span>
{#if show_counter}
<span class="counter">
{$_("footer.number", {
values: {
total: data.total,
since: date_from_ts(data.since),
},
})}
{#if data.number % 1000 == 0}
<span style="color: var(--{color()})">({$_("footer.wow")})</span>
{/if}
</span>
{:else}
<span class="counter">{$_("footer.js")}</span>
{/if}
</footer>
<style>

View File

@ -1,7 +1,5 @@
<script>
import { api_urljoin } from "$lib/api.js";
import { app_url } from "$lib/util.js";
export let desc, title;
</script>
@ -16,7 +14,6 @@
<meta property="og:title" content="[ngn.tf] | {title}" />
<meta property="og:description" content={desc} />
<meta property="og:url" content={app_url()} />
<link
rel="alternate"

View File

@ -1,12 +1,14 @@
<script>
import { browser } from "$app/environment";
import { color } from "$lib/util.js";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
export let picture = "";
export let title = "";
let title_cur = "";
let title_cur = title;
let show_animation = false;
function animate(title) {
if (!browser) return;
@ -24,13 +26,21 @@
}
}
onMount(() => {
show_animation = true;
});
$: animate(title);
</script>
<header>
<div>
<h1 class="title" style="color: var(--{color()})">{title_cur}</h1>
<h1 class="cursor" style="color: var(--{color()})">_</h1>
{#if show_animation}
<h1 class="title" style="color: var(--{color()})">{title_cur}</h1>
<h1 class="cursor" style="color: var(--{color()})">_</h1>
{:else}
<h1 class="title" style="color: var(--{color()})">{title}</h1>
{/if}
</div>
<img src="/profile/{picture}.png" alt="" />
</header>

View File

@ -1,7 +1,9 @@
<script>
import { locale_list, locale_select, locale_index } from "$lib/locale.js";
import { onMount } from "svelte";
let len = locale_list.length;
let show = false;
function get_next(indx) {
let new_indx = 0;
@ -15,11 +17,17 @@
function next() {
locale_select(get_next($locale_index).code);
}
onMount(() => {
show = true;
});
</script>
<button on:click={next}>
{get_next($locale_index).icon}
</button>
{#if show}
<button on:click={next}>
{get_next($locale_index).icon}
</button>
{/if}
<style>
button {

View File

@ -19,28 +19,18 @@ function color() {
}
function click() {
let audio = new Audio("/click.wav");
let audio = new Audio("/assets/click.wav");
audio.play();
}
function urljoin(url, path = null, query = {}) {
function urljoin(url, path = null) {
if (undefined === url || null === url) return;
let url_len = url.length;
if (url[url.length - 1] != "/") url += "/";
if (url[url_len - 1] != "/") url += "/";
if (null === path || "" === path) url = new URL(url);
else if (path[0] === "/") url = new URL(path.slice(1), url);
else url = new URL(path, url);
for (let k in query) url.searchParams.append(k, query[k]);
return url.href;
}
function app_url(path = null, query = {}) {
return urljoin(import.meta.env.WEBSITE_APP_URL, path, query);
if (null === path || "" === path) return url;
if (path[0] === "/") return url + path.slice(1);
return url + path;
}
function time_from_ts(ts) {
@ -68,4 +58,4 @@ function date_from_ts(ts) {
}).format(new Date(ts * 1000));
}
export { color, click, urljoin, app_url, time_from_ts, date_from_ts };
export { color, click, urljoin, time_from_ts, date_from_ts };

View File

@ -73,6 +73,7 @@
"license": "License",
"privacy": "Privacy",
"number": "Visited {total} times since {since}",
"wow": "wow!!"
"wow": "wow!!",
"js": "Enable javascript to display all the elements"
}
}

View File

@ -73,6 +73,7 @@
"license": "Lisans",
"privacy": "Gizlilik",
"number": "{since} tarihinden beri {total} kez ziyaret edildi",
"wow": "vay be!!"
"wow": "vay be!!",
"js": "Tüm elementleri görüntelemek için javascript'i açın"
}
}

View File

@ -21,7 +21,7 @@
</main>
<style>
@import "/global.css";
@import "/css/global.css";
main {
display: flex;

View File

@ -37,7 +37,7 @@
{/if}
<style>
@import "/markdown.css";
@import "/css/markdown.css";
main {
padding: 50px;

View File

@ -7,9 +7,11 @@
import { api_urljoin } from "$lib/api.js";
import { locale, _ } from "svelte-i18n";
import { onMount } from "svelte";
let { data } = $props();
let services = $state(data.services);
let show_input = $state(false);
function change(input) {
let value = input.target.value.toLowerCase();
@ -31,6 +33,10 @@
return s.desc[$locale] !== "" && s.desc[$locale] !== null && s.desc[$locale] !== undefined;
});
}
onMount(() => {
show_input = true;
});
</script>
<Head title="services" desc="my self-hosted services and projects" />
@ -41,7 +47,9 @@
{:else}
<main>
<div class="title">
<input oninput={change} type="text" placeholder={$_("services.search")} />
{#if show_input}
<input oninput={change} type="text" placeholder={$_("services.search")} />
{/if}
<div>
<Link icon="nf-fa-feed" link={api_urljoin("/news/" + $locale)}>{$_("services.feed")}</Link>
</div>

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

7
app/static/css/font.css Normal file
View File

@ -0,0 +1,7 @@
@font-face {
font-family: "Ubuntu";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("/assets/ubuntu.woff2") format("woff2");
}

View File

@ -1,4 +1,12 @@
/*
* animations.css: stuff like the cursor animation
* webfont.css: webfont dumped from https://www.nerdfonts.com/assets/css/webfont.css
* font.css: the main font (Ubuntu)
*/
@import "./animations.css";
@import "./nerdfonts.css";
@import "./font.css";
:root {
@ -31,7 +39,8 @@
rgba(20, 20, 20, 0.3) 0px 18px 36px -18px inset;
--text-shadow: 3px 2px 8px rgba(50, 50, 50, 0.8);
--background: linear-gradient(rgba(11, 11, 11, 0.808), rgba(1, 1, 1, 0.96)), url("/banner.png");
--background: linear-gradient(rgba(11, 11, 11, 0.808), rgba(1, 1, 1, 0.96)),
url("/assets/banner.png");
--profile-size: 220px;
}

View File

@ -1,10 +0,0 @@
/* im using nerd fonts btw */
@import "https://www.nerdfonts.com/assets/css/webfont.css";
@font-face {
font-family: "Ubuntu";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("/ubuntu.woff2") format("woff2");
}

View File

@ -1,2 +1,3 @@
User-Agent: *
Disallow: /doc/
Disallow: /api/

View File

@ -3,21 +3,38 @@ import { defineConfig } from "vite";
import { fileURLToPath } from "url";
import { readFileSync } from "fs";
function env_from(prefix, object) {
for (const [key, value] of Object.entries(object)) {
let type = typeof value;
let name = prefix + "_" + key.toUpperCase();
switch (type) {
case "object":
env_from(name, value);
break;
case "string":
if (process.env[name] === undefined) process.env[name] = value;
break;
}
}
}
const default_env = {
REPORT_URL: "https://github.com/ngn13/website/issues",
SOURCE_URL: "https://github.com/ngn13/website",
APP_URL: "http://localhost:7001",
API_URL: "http://localhost:7002",
DOC_URL: "http://localhost:7003",
source_url: "https://git.ngn.tf/ngn/website",
report_url: "https://git.ngn.tf/ngn/website/issues",
doc_url: "http://localhost:7003",
api: {
url: "http://localhost:7002",
path: "http://localhost:7002",
},
};
const file = fileURLToPath(new URL("package.json", import.meta.url));
const json = readFileSync(file, "utf8");
const pkg = JSON.parse(json);
const package_file = fileURLToPath(new URL("package.json", import.meta.url));
const package_json = readFileSync(package_file, "utf8");
const package_data = JSON.parse(package_json);
for (let env in default_env) {
if (process.env["WEBSITE_" + env] === undefined) process.env["WEBSITE_" + env] = default_env[env];
}
env_from("WEBSITE", default_env);
export default defineConfig({
plugins: [sveltekit()],
@ -31,6 +48,6 @@ export default defineConfig({
strictPort: true,
},
define: {
pkg: pkg,
pkg: package_data,
},
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

View File

@ -7,19 +7,20 @@ services:
args:
WEBSITE_SOURCE_URL: "http://github.com/ngn13/website"
WEBSITE_REPORT_URL: "http://github.com/ngn13/website/issues"
WEBSITE_APP_URL: "http://localhost:7001"
WEBSITE_API_URL: "http://localhost:7002"
WEBSITE_DOC_URL: "http://doc:7003"
WEBSITE_API_URL: "http://api:7002"
WEBSITE_API_PATH: "http://localhost:7002"
security_opt:
- "no-new-privileges:true"
cap_drop:
- ALL
ports:
- "127.0.0.1:7001:7001"
restart: unless-stopped
depends_on:
- api
- doc
read_only: true
restart: unless-stopped
api:
container_name: "website_api"
@ -34,28 +35,28 @@ services:
- "127.0.0.1:7002:7002"
volumes:
- ./data.db:/api/data.db:rw
restart: unless-stopped
environment:
WEBSITE_DEBUG: "false"
WEBSITE_APP_URL: "http://localhost:7001/"
WEBSITE_APP_URL: "http://localhost:7001"
WEBSITE_PASSWORD: "change_me"
WEBSITE_HOST: "0.0.0.0:7002"
WEBSITE_IP_HEADER: "X-Real-IP"
WEBSITE_INTERVAL: "1h"
WEBSITE_TIMEOUT: "15s"
WEBSITE_LIMIT: "5s"
restart: unless-stopped
doc:
container_name: "website_doc"
image: website_doc
read_only: true
build:
context: ./doc
security_opt:
- "no-new-privileges:true"
cap_drop:
- ALL
restart: unless-stopped
environment:
WEBSITE_HOST: "0.0.0.0:7003"
WEBSITE_DOCS_DIR: "./docs"
read_only: true
restart: unless-stopped

View File

@ -81,7 +81,7 @@ BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 120
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4

35
doc/.clang-tidy Normal file
View File

@ -0,0 +1,35 @@
---
# "gnu-zero-variadic-macro-arguments" ignored because we are using GNU99
# standart
# "clang-diagnostic-language-extension-token" is ignored because we need the
# asm() extension token
# "DeprecatedOrUnsafeBufferHandling" ignored because C11 "_s" functions are not
# secure either
Checks: >-
clang-diagnostic-*,
-clang-diagnostic-gnu-zero-variadic-macro-arguments,
-clang-diagnostic-language-extension-token,
clang-analyzer-*,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
portability-*,
performance-*,
WarningsAsErrors: '*'
HeaderFileExtensions:
- ''
- h
- hh
- hpp
- hxx
ImplementationFileExtensions:
- c
- cc
- cpp
- cxx
HeaderFilterRegex: '.*'
ExcludeHeaderFilterRegex: ''
FormatStyle: file
SystemHeaders: false
...

View File

@ -1,9 +1,9 @@
FROM ghcr.io/ngn13/ctorm:1.7
FROM ghcr.io/ngn13/ctorm:1.8.1
WORKDIR /doc
COPY Makefile ./
COPY docs ./docs
COPY pages ./pages
COPY inc ./inc
COPY src ./src

View File

@ -26,10 +26,13 @@ $(DISTDIR)/%.o: src/%.c
format:
clang-format -i -style=file $(CSRCS) $(HSRCS)
lint:
clang-tidy --warnings-as-errors --config= $(CSRCS) $(HSRCS)
clean:
rm -rf $(DISTDIR)
run:
./doc.elf
.PHONY: format clean run
.PHONY: format lint clean run

View File

@ -1,4 +0,0 @@
{
"title": "privacy",
"desc": "Learn how I respect your privacy"
}

View File

@ -1,4 +0,0 @@
{
"title": "gizlilik",
"desc": "Gizliliğinize nasıl önem verdiğimi öğrenin"
}

View File

@ -3,13 +3,13 @@
#include <stdbool.h>
#include <dirent.h>
#include "util.h"
#include "file.h"
typedef struct {
DIR *dir;
util_file_t *file;
char name[NAME_MAX + 1];
char *lang;
DIR *dir;
file_t *file;
char name[NAME_MAX + 1];
char *lang;
} docs_t;
bool docs_init(docs_t *docs, char *dir);

10
doc/inc/file.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
typedef struct {
char *content;
int64_t size;
} file_t;
file_t *file_load(int dirfd, char *path);
void file_free(file_t *file);

View File

@ -4,16 +4,8 @@
#include <stdint.h>
#include <ctype.h>
typedef struct {
char *content;
uint64_t size;
} util_file_t;
#define util_toupper(str) \
for (char *c = str; *c != 0; c++) \
#define util_toupper(str) \
for (char *c = str; *c != 0; c++) \
*c = toupper(*c)
uint64_t util_endswith(char *str, char *suf);
void util_send(ctorm_res_t *res, uint16_t code, cJSON *json);
util_file_t *util_file_load(int dirfd, char *path);
void util_file_free(util_file_t *file);
bool util_parse_doc_name(char *name, char **lang, const char *ext);
uint64_t util_endswith(char *str, char *suf);
void util_send(ctorm_res_t *res, uint16_t code, cJSON *json);

View File

@ -1,9 +1,9 @@
My website's API, [api.ngn.tf](https://api.ngn.tf), stores information about my
self-hosted services, it also allows me to publish news and updates about these
services using an Atom feed and it keeps track of visitor metrics. The API itself is
written in Go and uses SQLite for storage.
My website's API, stores information about my self-hosted services, it also allows me
to publish news and updates about these services using an Atom feed and it keeps track
of visitor metrics.
This documentation contains information about all the available API endpoints.
This documentation contains information about all the available API endpoints. All the
endpoints can be accessed using the `/api` route.
## Version 1 Endpoints
Each version 1 endpoint, can be accessed using the `/v1` route.

View File

@ -1,9 +1,9 @@
Websitemin API, [api.ngn.tf](https://api.ngn.tf), self-host edilen servisler hakkında bilgileri
tutuyor, bu servisler hakkında haberleri ve güncellemeleri bir Atom feed'i aracılığı ile
paylaşmama izin veriyor ve ziyartçi metriklerini takip ediyor. API'ın kendisi Go ile yazıldı ve
veritabanı olarak SQLite kullanıyor.
Websitemin API, self-host edilen servisler hakkında bilgileri tutuyor, bu servisler hakkında
haberleri ve güncellemeleri bir Atom feed'i aracılığı ile paylaşmama izin veriyor ve ziyartçi
metriklerini takip ediyor.
Bu dökümentasyon tüm erişeme açık API endpoint'leri hakkında bilgiler içeriyor.
Bu dökümentasyon tüm erişime açık API endpoint'leri hakkında bilgiler içeriyor. Tüm endpoint'lere
`/api` yolu ile erişilebilir.
## Versyion 1 Endpoint'leri
Tüm versiyon 1 endpoint'leri `/v1` yolu ile erişilebilir.

View File

@ -0,0 +1,4 @@
{
"title": "privacy",
"desc": "Privacy policy"
}

View File

@ -0,0 +1,4 @@
{
"title": "gizlilik",
"desc": "Gizlilik ilkesi"
}

View File

@ -11,22 +11,25 @@
option_t options[] = {
// name value requied
{"host", "0.0.0.0:7003", true }, // host the server should listen on
{"docs_dir", "./docs", true }, // documentation directory
{"", NULL, false},
{"host", "0.0.0.0:7003", true }, // host the server should listen on
{"dir", "./pages", true }, // documentation pages directory
{"", NULL, false},
};
bool config_load(config_t *conf) {
bzero(conf, sizeof(*conf));
memset(conf, 0, sizeof(*conf));
char name_env[OPT_NAME_MAX + 10], name_copy[OPT_NAME_MAX], *value = NULL;
conf->options = options;
for (option_t *opt = conf->options; opt->value != NULL; opt++, conf->count++) {
strcpy(name_copy, opt->name);
for (option_t *opt = conf->options; opt->value != NULL;
opt++, conf->count++) {
// convert option name to environment variable name
strncpy(name_copy, opt->name, OPT_NAME_MAX);
util_toupper(name_copy);
snprintf(name_env, sizeof(name_env), "WEBSITE_%s", name_copy);
// attempt to load the value from the environment
if ((value = getenv(name_env)) != NULL)
opt->value = value;
@ -36,7 +39,9 @@ bool config_load(config_t *conf) {
if (!opt->required || NULL != opt->value)
continue;
ctorm_fail("please specify a value for the required config option: %s (%s)", opt->name, name_env);
ctorm_fail("please specify a value for the required config option: %s (%s)",
opt->name,
name_env);
errno = EFAULT;
return false;
}

View File

@ -1,6 +1,6 @@
#include <linux/limits.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
@ -9,7 +9,7 @@
#define DOCS_LANG_CODE_LEN 2
bool __docs_parse_name(docs_t *docs, char *ext) {
bool _docs_parse_name(docs_t *docs, char *ext) {
// check the extension
uint64_t ext_pos = util_endswith(docs->name, ext);
@ -22,7 +22,8 @@ bool __docs_parse_name(docs_t *docs, char *ext) {
// example.en\0json\0
// |
// `--- find this
for (docs->lang = docs->name; *docs->lang != 0 && *docs->lang != '.'; docs->lang++)
for (docs->lang = docs->name; *docs->lang != 0 && *docs->lang != '.';
docs->lang++)
continue;
if (*docs->lang != '.')
@ -39,11 +40,11 @@ bool __docs_parse_name(docs_t *docs, char *ext) {
return strlen(docs->lang) == DOCS_LANG_CODE_LEN && *docs->name != 0;
}
void __docs_clean(docs_t *docs) {
void _docs_clean(docs_t *docs) {
if (NULL == docs->file)
return;
util_file_free(docs->file);
file_free(docs->file);
docs->file = NULL;
}
@ -53,7 +54,7 @@ bool docs_init(docs_t *docs, char *dir) {
return false;
}
bzero(docs, sizeof(*docs));
memset(docs, 0, sizeof(*docs));
return NULL != (docs->dir = opendir(dir));
}
@ -64,15 +65,15 @@ char *docs_next(docs_t *docs, char *name, bool content) {
}
struct dirent *ent = NULL;
__docs_clean(docs);
_docs_clean(docs);
while (NULL != (ent = readdir(docs->dir))) {
if (*ent->d_name == '.')
continue;
strcpy(docs->name, ent->d_name);
strncpy(docs->name, ent->d_name, NAME_MAX);
if (!__docs_parse_name(docs, content ? ".md" : ".json"))
if (!_docs_parse_name(docs, content ? ".md" : ".json"))
continue;
if (NULL == name || strncmp(docs->name, name, NAME_MAX) == 0)
@ -84,7 +85,7 @@ char *docs_next(docs_t *docs, char *name, bool content) {
return NULL;
}
if (NULL == (docs->file = util_file_load(dirfd(docs->dir), ent->d_name)))
if (NULL == (docs->file = file_load(dirfd(docs->dir), ent->d_name)))
return NULL;
return docs->file->content;
@ -99,8 +100,8 @@ void docs_free(docs_t *docs) {
if (NULL == docs)
return;
__docs_clean(docs);
_docs_clean(docs);
closedir(docs->dir);
bzero(docs, sizeof(*docs));
memset(docs, 0, sizeof(*docs));
}

56
doc/src/file.c Normal file
View File

@ -0,0 +1,56 @@
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "file.h"
file_t *file_load(int dirfd, char *path) {
if (NULL == path) {
errno = EINVAL;
return NULL;
}
file_t *file = NULL;
int fd = -1;
// open the taarget file
if ((fd = openat(dirfd, path, O_RDONLY)) < 0)
goto end; // errno set by open
// allocate a new file structure
if (NULL == (file = calloc(1, sizeof(file_t))))
goto end; // errno set by malloc
// calculate the file size
if ((file->size = lseek(fd, 0, SEEK_END)) < 0)
goto end;
// memory map the file
if (NULL ==
(file->content = mmap(0, file->size, PROT_READ, MAP_PRIVATE, fd, 0)))
goto end; // errno set by mmap
end:
if (fd != -1)
close(fd);
if (NULL != file && NULL == file->content) {
free(file);
return NULL;
}
return file;
}
void file_free(file_t *file) {
if (NULL == file)
return;
munmap(file->content, file->size);
free(file);
}

View File

@ -1,3 +1,4 @@
#include <ctorm/app.h>
#include <ctorm/ctorm.h>
#include <stdlib.h>
@ -19,25 +20,28 @@ int main() {
return EXIT_FAILURE;
}
// initialize the app config
ctorm_config_new(&app_config);
app_config.disable_logging = true;
app = ctorm_app_new(&app_config);
// create a new app
app = ctorm_app_new(&app_config);
// middlewares
MIDDLEWARE_ALL(app, "/*", route_cors);
MIDDLEWARE_ALL(app, "/*/*", route_cors);
ALL(app, "/*", route_cors);
ALL(app, "/*/*", route_cors);
// routes
GET(app, "/list", route_list);
GET(app, "/get/:name", route_get);
ctorm_app_all(app, route_notfound);
ctorm_app_default(app, route_notfound);
ctorm_app_local(app, "config", &conf);
ctorm_info("starting the web server on %s", host);
if (!ctorm_app_run(app, host))
ctorm_fail("failed to start the app: %s", ctorm_geterror());
ctorm_fail("failed to start the app: %s", ctorm_error());
ctorm_app_free(app);
return EXIT_SUCCESS;

View File

@ -3,7 +3,8 @@
void route_cors(ctorm_req_t *req, ctorm_res_t *res) {
RES_SET("Access-Control-Allow-Origin", "*");
RES_SET("Access-Control-Allow-Headers",
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, "
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, "
"Authorization, accept, origin, Cache-Control, "
"X-Requested-With");
RES_SET("Access-Control-Allow-Methods", "PUT, DELETE, GET");
}

View File

@ -1,31 +1,30 @@
#include <cjson/cJSON.h>
#include <dirent.h>
#include <linux/limits.h>
#include <cjson/cJSON.h>
#include <ctorm/ctorm.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include "routes.h"
#include "config.h"
#include "util.h"
#include "docs.h"
void route_get(ctorm_req_t *req, ctorm_res_t *res) {
config_t *conf = REQ_LOCAL("config");
char *doc_name = REQ_PARAM("name");
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL;
config_t *conf = REQ_LOCAL("config");
char *name = REQ_PARAM("name");
char *dir = config_get(conf, "dir"), *doc_data = NULL;
cJSON *json = NULL, *doc_json = NULL;
docs_t docs;
if (NULL == doc_name) {
if (NULL == name) {
ctorm_fail("documentation name not specified (how did that even happend)");
util_send(res, 500, NULL);
goto end;
}
if (!docs_init(&docs, docs_dir)) {
ctorm_fail("docs_init failed: %s", ctorm_geterror());
if (!docs_init(&docs, dir)) {
ctorm_fail("docs_init failed: %s", ctorm_error());
util_send(res, 500, NULL);
goto end;
}
@ -36,7 +35,7 @@ void route_get(ctorm_req_t *req, ctorm_res_t *res) {
goto end;
}
while (NULL != (doc_data = docs_next(&docs, doc_name, false))) {
while (NULL != (doc_data = docs_next(&docs, name, false))) {
if (NULL == (doc_json = cJSON_Parse(doc_data))) {
ctorm_fail("failed to parse JSON: %s (%s)", docs.name, docs.lang);
continue;
@ -53,7 +52,7 @@ void route_get(ctorm_req_t *req, ctorm_res_t *res) {
docs_reset(&docs);
while (NULL != (doc_data = docs_next(&docs, doc_name, true))) {
while (NULL != (doc_data = docs_next(&docs, name, true))) {
if (NULL == (doc_json = cJSON_GetObjectItem(json, docs.lang)))
continue;

View File

@ -3,21 +3,21 @@
#include <ctorm/ctorm.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include "routes.h"
#include "config.h"
#include "util.h"
#include "docs.h"
void route_list(ctorm_req_t *req, ctorm_res_t *res) {
config_t *conf = REQ_LOCAL("config");
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL;
config_t *conf = REQ_LOCAL("config");
char *dir = config_get(conf, "dir"), *doc_data = NULL;
cJSON *array = NULL, *json = NULL, *doc_json = NULL;
docs_t docs;
if (!docs_init(&docs, docs_dir)) {
ctorm_fail("docs_init failed: %s", ctorm_geterror());
if (!docs_init(&docs, dir)) {
ctorm_fail("docs_init failed: %s", ctorm_error());
util_send(res, 500, NULL);
goto end;
}
@ -31,7 +31,8 @@ void route_list(ctorm_req_t *req, ctorm_res_t *res) {
while (NULL != (doc_data = docs_next(&docs, NULL, false))) {
if (NULL == (array = cJSON_GetObjectItem(json, docs.lang)) &&
NULL == (array = cJSON_AddArrayToObject(json, docs.lang))) {
ctorm_fail("failed to create an array object for the language %s", docs.lang);
ctorm_fail(
"failed to create an array object for the language %s", docs.lang);
continue;
}

View File

@ -1,17 +1,8 @@
#include <linux/limits.h>
#include <ctorm/ctorm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdint.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <errno.h>
#include "util.h"
@ -57,45 +48,3 @@ void util_send(ctorm_res_t *res, uint16_t code, cJSON *json) {
RES_JSON(json);
}
util_file_t *util_file_load(int dirfd, char *path) {
if (NULL == path) {
errno = EINVAL;
return NULL;
}
util_file_t *file = NULL;
struct stat buf;
int fd = -1;
if (NULL == (file = malloc(sizeof(util_file_t))))
goto end; // errno set by malloc
if ((fd = openat(dirfd, path, O_RDONLY)) < 0)
goto end; // errno set by open
if (fstat(fd, &buf) < 0)
goto end; // errno set by fstat
if (NULL == (file->content = mmap(0, (file->size = buf.st_size), PROT_READ, MAP_PRIVATE, fd, 0)))
goto end; // errno set by mmap
end:
if (fd != -1)
close(fd);
if (NULL == file->content) {
free(file);
return NULL;
}
return file;
}
void util_file_free(util_file_t *file) {
if (NULL == file)
return;
munmap(file->content, file->size);
free(file);
}