Merge pull request 'Give that frontend a bit of a kick' (#2) from css-update into main

Reviewed-on: #2
This commit is contained in:
sad 2024-03-11 14:57:35 -07:00
commit ac99f27ab0
19 changed files with 4580 additions and 278 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
data/ data/
comments.db/ comments.db/
notes notes
sass/node_modules

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 vxfemboy
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,21 +1,30 @@
# TCP-WIKI # TCP-WIKI
[![Go Report Card](https://goreportcard.com/badge/github.com/vxfemboy/tcp-wiki)](https://goreportcard.com/report/github.com/vxfemboy/tcp-wiki)
Feel free to commit, leave suggestions/ideas, issues, or really anything <3 ![screenshot](examples/tcp-wiki.png "TCP-WIKI")
# What is TCP-WIKI ? ### What is TCP-WIKI ?
<center><img src="https://tcp.ac/i/TIZzK" alt="example screenshot" width="100" height="400"></center>
TCP.WIKI is a secure and verifiable wiki platform designed for projects, code, courses, documents, articles, blogs, tutorials, and more. TCP.WIKI is a secure, minimal, and verifiable wiki platform designed for projects, code, courses, documents, articles, blogs, tutorials, and more.
### Project Goals ### Project Goals
The aim is to provide a secure, minimal, and easily verifiable wiki environment that supports a wide range of content types, from technical documentation, to educational materials, to blogs, and more. The aim is to provide a secure, minimal, and easily verifiable wiki environment that supports a wide range of content types, from technical documentation, to educational materials, to blogs, and more.
#### Features:
* Full support for pages in Markdown and HTML
* Pull from GIT for live updates or usage of local directory
* No Javascript required
## Setup ## Setup
First clone this repository: First clone this repository:
```bash ```bash
git clone https://git.tcp.direct/S4D/tcp-wiki.git git clone https://github.com/vxfemboy/tcp-wiki.git
``` ```
Then you have to cd into the repo's folder and run/compile: Then you have to cd into the repo's folder and run/compile:
```bash ```bash
@ -31,12 +40,12 @@ All you have to do is modify the following lines in the `config.toml` file:
```toml ```toml
[Git] [Git]
UseGit = true # Set to false to use LocalPath UseGit = true # Set to false to use LocalPath
RepoURL = "https://git.tcp.direct/S4D/tcp-wiki.git" # Your Repo Here RepoURL = "https://github.com/vxfemboy/tcp-wiki.git" # Your Repo Here
Branch = "main" # Your Repo Branch Here Branch = "main" # Your Repo Branch Here
LocalPath = "data" # Directory to clone the git repo too LocalPath = "data" # Directory to clone the git repo too
``` ```
Change the `RepoURL` line `https://git.tcp.direct/S4D/tcp-wiki.git` to your repo link, Change the `RepoURL` line `https://github.com/vxfemboy/tcp-wiki.git` to your repo link,
change `main` to your specific repo's branch and you should be good to go! change `main` to your specific repo's branch and you should be good to go!
#### Want to use a local directory other then git repo? #### Want to use a local directory other then git repo?
@ -52,9 +61,23 @@ LocalPath = "/home/crazy/blog" # The directory of your project
``` ```
make sure to also set `LocalPath` to the directory of your project make sure to also set `LocalPath` to the directory of your project
> ### Want to use your own theme/layout? #### Want to use your own theme/layout?
>
> Have a look at the `assets/` directory for the templates Have a look at the `assets/` directory for the template `_layout.html` this is the main file that will be used
using [tailwindscss](https://tailwindcss.com/) and [daisyui](https://daisyui.com/) as a css library
to setup this for live modifications do:
```
cd sass
npm install
npx tailwindcss -i ./tail.css -o ../assets/main.css --watch
```
and to minify your css for production usage:
```
npx tailwindcss -o ../assets/main.css --minify
```
> ### ❤️ Feel free to commit, leave suggestions/ideas, issues, or really anything ❤️
## TODO ## TODO
@ -77,6 +100,6 @@ make sure to also set `LocalPath` to the directory of your project
- [ ] set security controls per page - [ ] set security controls per page
- [ ] auto refresh on post - [ ] auto refresh on post
- [x] dynamically generated links for all avaiable pages - [x] dynamically generated links for all avaiable pages
- [x] sitemap - kinda - [x] sitemap - kinda (needs limited)
- [x] working pages - [x] working pages
- [ ] image support - [x] image support

View File

@ -7,62 +7,78 @@
<title>TCP.WIKI</title> <title>TCP.WIKI</title>
<link rel="stylesheet" href="/assets/main.css"> <link rel="stylesheet" href="/assets/main.css">
</head> </head>
<body> <body>
<header> <header>
<h1>TCP.WIKI</h1> <div class="navbar bg-base-200">
<div class="flex-1">
<h1 class="btn btn-ghost text-xl">TCP.WIKI</h1>
</div>
<nav> <nav>
<ul> <div class="flex-none">
<li><a href="/">Home</a></li> <ul class="menu menu-horizontal px-1">
<li><a href="/">Home</a></li>
<!-- Add more links to your menubar here --> <!-- Add more links to your menubar here -->
<li><a href="/what">What</a></li> <li><a href="/what">What</a></li>
<li><a href="/who">Who</a></li> <li><a href="/who">Who</a></li>
<li class="dropdown dropdown-bottom dropdown-end">
<details>
<summary>
Pages
</summary>
<div class="dropdown-content">
<ul class="z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
{{ range .Pages }}
<li><a href="/{{ . }}">{{ . }}</a></li>
{{ end }}
</ul>
</div>
</details>
</li>
</ul> </ul>
</div>
</nav> </nav>
</header>
<main>
<div class="content">
{{ .Content }}
</div>
<hr>
{{ if .UseGit }}
<div class="page-info">
{{- if eq .Author .LastModifier -}}
Authored: {{ .Author }} @ {{ .AuthoredDate.Format "2006/01/02" }}
{{- else -}}
Authored: {{ .Author }} @ {{ .AuthoredDate.Format "2006/01/02" }} - Modified: {{ .LastModifier }} @ {{ .LastModifiedDate.Format "2006/01/02" }}
{{- end -}}
</div>
{{ end }}
<div class="comments">
<h2>Comments</h2>
{{ range .Comments }}
<div class="comment">
<p>{{ .Content }}</p>
<p><small>{{ .Author }} at {{ .Date }}</small></p>
</div>
{{ end }}
</div>
</main>
<form method="POST" action="/submit_comment">
<input type="hidden" name="path" value="{{ .Path }}">
<div class="form-group">
<label for="author">Name:</label>
<input type="text" id="author" name="author">
</div>
<div class="form-group">
<label for="body">Comment:</label>
<textarea id="content" name="content"></textarea>
</div>
<button type="submit">Submit</button>
</form>
<div class="sidebar">
<h2>Pages</h2>
<ul>
{{ range .Pages }}
<li><a href="/{{ . }}">{{ . }}</a></li>
{{ end }}
</ul>
</div> </div>
</body> </header>
<main class="bg-base-300">
<div class="prose content-center">
{{ .Content }}
</div>
{{ if .UseGit }}
<footer class="footer px-10 py-4 border-t bg-base-200 text-base-content border-base-300">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-info shrink-0 w-6 h-6"><path stroke="oklch(var(--s))" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="md:justify-self-end text-secondary">
{{- if eq .Author .LastModifier -}}
Authored: {{ .Author }} @ {{ .AuthoredDate.Format "2006/01/02" }}
{{- else -}}
Authored: {{ .Author }} @ {{ .AuthoredDate.Format "2006/01/02" }} - Modified: {{ .LastModifier }} @ {{ .LastModifiedDate.Format "2006/01/02" }}
{{- end -}}
</div>
</footer>
{{ end }}
<div class="card bg-base-100 shadow-xl bg-base-250 text-neutral-content">
<div class="card-body">
<h2 class="card-title">Comments</h2>
{{ range .Comments }}
<div class="chat chat-start">
<p class="chat-header">{{ .Author }}</p>
<p class="chat-bubble">{{ .Content }}</p>
<time class="chat-footer text-xs opacity-50">Sent at {{ .Date }}<time>
</div>
{{ end }}
<form method="POST" action="/submit_comment">
<input type="hidden" name="path" value="{{ .Path }}"></input>
<div class="form-control">
<label for="author" class="Label">Name:</label>
<input type="text" id="author" name="author" class="input input-bordered max-w-md" placeholder="Your Name" required></input>
</div>
<div class="form-control space-y-0.5">
<label for="content" class="label">Comment:</label>
<textarea id="content" name="content" class="textarea textarea-bordered max-w-md" required placeholder="Your Comment Here"></textarea>
<button class="btn btn-secondary sm:btn-sm md:btn-md lg:btn-l max-w-xs" type="submit">Submit</button>
</div>
</form>
</div>
</div>
</main>
</body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ Port = ":8080"
[Git] [Git]
UseGit = true UseGit = true
RepoURL = "https://git.tcp.direct/S4D/tcp-wiki.git" RepoURL = "https://github.com/vxfemboy/tcp-wiki.git"
Branch = "main" Branch = "main"
LocalPath = "data" LocalPath = "data"

45
examples/markdown.md Normal file
View File

@ -0,0 +1,45 @@
# MARKDOWN FORMATTING EXAMPLES
#### ASCII ART CODE BLOCK
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.;ll;. .,;.
.;dd;. ..,:. ':dx; ',. ..,:.
.cxo, .;;;:cll;. lodx: 'd. .;;;:cll;.
'. ,dxdx'odooxxl. .cxo. 'lxd, 'odooxxl.
xd; ... 'oxxx.. 'dkd. .lxc:dxxo:;,'. .. 'dkd.
oxd' .cxd, ,dxdo .lxx; 'oxxxdl;'..'ll. .lxx;
,dxl..cxxl. .cxd, ;xxl. ,dxo' .ck:. ;xxl.
.cxx,.lxxxc. ;xx, .oxd' ;dd' .lkc. .oxd'
,dxl..cxxl. .cxd, .lxx: ;xo. .ok: .lxx:
.cxx,.lxxxc. ;xx, ;xxl. .cxl. ,dx; ;xxl.
'dkl;oxxxx: :xo. 'dxd' ....'''dx; ;xd' 'dxd' ....''
.cxddxxccxd,.lkl. .cxxocloooddx :ko. ;xo. .cxxocloooddx
'dxxxd..lxocdx:......,:lc;,'...... ,xd, ;xl. ......,:lc;,'......
.cxxxc. ,dxxxd'xxdolc;'. 'oo' ,xo. xxdolc;'.
;xkd' .cxxxl ,''.. .. .ld'.,.,''..
.,;' 'dxxc 'oc.
cxxl .;.
.:l:
```

79
examples/pages/page-1.md Normal file
View File

@ -0,0 +1,79 @@
# MARKDOWN FORMATTING EXAMPLES
#### ASCII ART CODE BLOCK
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.;ll;. .,;.
.;dd;. ..,:. ':dx; ',. ..,:.
.cxo, .;;;:cll;. lodx: 'd. .;;;:cll;.
'. ,dxdx'odooxxl. .cxo. 'lxd, 'odooxxl.
xd; ... 'oxxx.. 'dkd. .lxc:dxxo:;,'. .. 'dkd.
oxd' .cxd, ,dxdo .lxx; 'oxxxdl;'..'ll. .lxx;
,dxl..cxxl. .cxd, ;xxl. ,dxo' .ck:. ;xxl.
.cxx,.lxxxc. ;xx, .oxd' ;dd' .lkc. .oxd'
,dxl..cxxl. .cxd, .lxx: ;xo. .ok: .lxx:
.cxx,.lxxxc. ;xx, ;xxl. .cxl. ,dx; ;xxl.
'dkl;oxxxx: :xo. 'dxd' ....'''dx; ;xd' 'dxd' ....''
.cxddxxccxd,.lkl. .cxxocloooddx :ko. ;xo. .cxxocloooddx
'dxxxd..lxocdx:......,:lc;,'...... ,xd, ;xl. ......,:lc;,'......
.cxxxc. ,dxxxd'xxdolc;'. 'oo' ,xo. xxdolc;'.
;xkd' .cxxxl ,''.. .. .ld'.,.,''..
.,;' 'dxxc 'oc.
cxxl .;.
.:l:
```
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.colclll;. .. :Oc. cKx. ..
.xNNkl:'. .:kx..dW0, .kNk. .:kx.
.kNx. .dk, .;dkx:. ;KWX: 'OWx. .dk, .;dkx:.
'ONo. :XNklxOxc. :OxxXl c0k' :XNklxOxc.
,xl. .. :XK: .;xNNKd:' 'dk:.;Xx. 'xOc. .;xNNKd:'
.lXx. .dXd..dNO. .,lxOxOXWd. .'. ,Kk,cOd. ...'' .,lxOxOXWd.
.dNl ,0WXx.'ONl .cxOOd:. ;KWd. '0XOd,...',:cllllclkXKd. cxOOd:. ;KWd.
'00':XKOXO,cX0, ;xo,. ;KWd. .cKN0dllllll:;'...'oKk; ;xo,. ;KWd.
dNloNd.oX0ldXd. ;XWd. .:x:.cxONk;.. .:00c. ;XWd. .:x:
:XOOXc cKKkKXc :XWd. ,d0kl' ;Xk. 'xXx. :XWd. ,d0kl'
.ONNK, ,ONNNK: :XWd.'ckOo' .OK, :KK: :XWd.'ckOo'
.dNWO. .oXNWO' :XWX00kc. dNc .oXO' :XWX00kc.
lNNo ;0NNd .;lOKOo;. :Xx. .xXx. .;lOKOo;.
:Kk. .dOl.;okOkd:. .l:. .kWXx .;okOkd:.
... . ;xo:'. ,oc;'. ;xo:'.
```

79
examples/pages/page-2.md Normal file
View File

@ -0,0 +1,79 @@
# MARKDOWN FORMATTING EXAMPLES
#### ASCII ART CODE BLOCK
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.;ll;. .,;.
.;dd;. ..,:. ':dx; ',. ..,:.
.cxo, .;;;:cll;. lodx: 'd. .;;;:cll;.
'. ,dxdx'odooxxl. .cxo. 'lxd, 'odooxxl.
xd; ... 'oxxx.. 'dkd. .lxc:dxxo:;,'. .. 'dkd.
oxd' .cxd, ,dxdo .lxx; 'oxxxdl;'..'ll. .lxx;
,dxl..cxxl. .cxd, ;xxl. ,dxo' .ck:. ;xxl.
.cxx,.lxxxc. ;xx, .oxd' ;dd' .lkc. .oxd'
,dxl..cxxl. .cxd, .lxx: ;xo. .ok: .lxx:
.cxx,.lxxxc. ;xx, ;xxl. .cxl. ,dx; ;xxl.
'dkl;oxxxx: :xo. 'dxd' ....'''dx; ;xd' 'dxd' ....''
.cxddxxccxd,.lkl. .cxxocloooddx :ko. ;xo. .cxxocloooddx
'dxxxd..lxocdx:......,:lc;,'...... ,xd, ;xl. ......,:lc;,'......
.cxxxc. ,dxxxd'xxdolc;'. 'oo' ,xo. xxdolc;'.
;xkd' .cxxxl ,''.. .. .ld'.,.,''..
.,;' 'dxxc 'oc.
cxxl .;.
.:l:
```
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.colclll;. .. :Oc. cKx. ..
.xNNkl:'. .:kx..dW0, .kNk. .:kx.
.kNx. .dk, .;dkx:. ;KWX: 'OWx. .dk, .;dkx:.
'ONo. :XNklxOxc. :OxxXl c0k' :XNklxOxc.
,xl. .. :XK: .;xNNKd:' 'dk:.;Xx. 'xOc. .;xNNKd:'
.lXx. .dXd..dNO. .,lxOxOXWd. .'. ,Kk,cOd. ...'' .,lxOxOXWd.
.dNl ,0WXx.'ONl .cxOOd:. ;KWd. '0XOd,...',:cllllclkXKd. cxOOd:. ;KWd.
'00':XKOXO,cX0, ;xo,. ;KWd. .cKN0dllllll:;'...'oKk; ;xo,. ;KWd.
dNloNd.oX0ldXd. ;XWd. .:x:.cxONk;.. .:00c. ;XWd. .:x:
:XOOXc cKKkKXc :XWd. ,d0kl' ;Xk. 'xXx. :XWd. ,d0kl'
.ONNK, ,ONNNK: :XWd.'ckOo' .OK, :KK: :XWd.'ckOo'
.dNWO. .oXNWO' :XWX00kc. dNc .oXO' :XWX00kc.
lNNo ;0NNd .;lOKOo;. :Xx. .xXx. .;lOKOo;.
:Kk. .dOl.;okOkd:. .l:. .kWXx .;okOkd:.
... . ;xo:'. ,oc;'. ;xo:'.
```

79
examples/pages/page-3.md Normal file
View File

@ -0,0 +1,79 @@
# MARKDOWN FORMATTING EXAMPLES
#### ASCII ART CODE BLOCK
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.;ll;. .,;.
.;dd;. ..,:. ':dx; ',. ..,:.
.cxo, .;;;:cll;. lodx: 'd. .;;;:cll;.
'. ,dxdx'odooxxl. .cxo. 'lxd, 'odooxxl.
xd; ... 'oxxx.. 'dkd. .lxc:dxxo:;,'. .. 'dkd.
oxd' .cxd, ,dxdo .lxx; 'oxxxdl;'..'ll. .lxx;
,dxl..cxxl. .cxd, ;xxl. ,dxo' .ck:. ;xxl.
.cxx,.lxxxc. ;xx, .oxd' ;dd' .lkc. .oxd'
,dxl..cxxl. .cxd, .lxx: ;xo. .ok: .lxx:
.cxx,.lxxxc. ;xx, ;xxl. .cxl. ,dx; ;xxl.
'dkl;oxxxx: :xo. 'dxd' ....'''dx; ;xd' 'dxd' ....''
.cxddxxccxd,.lkl. .cxxocloooddx :ko. ;xo. .cxxocloooddx
'dxxxd..lxocdx:......,:lc;,'...... ,xd, ;xl. ......,:lc;,'......
.cxxxc. ,dxxxd'xxdolc;'. 'oo' ,xo. xxdolc;'.
;xkd' .cxxxl ,''.. .. .ld'.,.,''..
.,;' 'dxxc 'oc.
cxxl .;.
.:l:
```
```
.'ckKo..'. ;dd;
.'codkXNk.'kKo. .,. ,ONXkc.
..;lodoc,.,kx'.dNWO. cKNO' .xNNNKkl;.
'c:'.',;cldOXOc'. . :XNNNd. ;KX0: .dNN0dkKKkl;.
cXNXOxdol;c0Wo .kW0dKNd'dWOc. .xWK; .;okKKxc'
,dkl. .OWo :XWo.,ONKKWk;. 'OWk. .:xXXx:.
,KN: oWX: .dNNWx'. '' :XNl .':ldkO0O:
:N0' .xW0' .lKKc. .kXd. .xNKdoooc;'...
dWx. .kWO. .. ;KNl .,codkXWx'
.kWo .OWO. '0Wd.lxkxl,..xWk.
.ONl .kW0' .oNX:,c,. cNK,
.kWo lNNl ,kX0: 'ONl
cNO. .xNKc. ..'cxKOc. .kNl
.c0c .cOKOxooodddddc' .,.
.' .,,;,,...
.colclll;. .. :Oc. cKx. ..
.xNNkl:'. .:kx..dW0, .kNk. .:kx.
.kNx. .dk, .;dkx:. ;KWX: 'OWx. .dk, .;dkx:.
'ONo. :XNklxOxc. :OxxXl c0k' :XNklxOxc.
,xl. .. :XK: .;xNNKd:' 'dk:.;Xx. 'xOc. .;xNNKd:'
.lXx. .dXd..dNO. .,lxOxOXWd. .'. ,Kk,cOd. ...'' .,lxOxOXWd.
.dNl ,0WXx.'ONl .cxOOd:. ;KWd. '0XOd,...',:cllllclkXKd. cxOOd:. ;KWd.
'00':XKOXO,cX0, ;xo,. ;KWd. .cKN0dllllll:;'...'oKk; ;xo,. ;KWd.
dNloNd.oX0ldXd. ;XWd. .:x:.cxONk;.. .:00c. ;XWd. .:x:
:XOOXc cKKkKXc :XWd. ,d0kl' ;Xk. 'xXx. :XWd. ,d0kl'
.ONNK, ,ONNNK: :XWd.'ckOo' .OK, :KK: :XWd.'ckOo'
.dNWO. .oXNWO' :XWX00kc. dNc .oXO' :XWX00kc.
lNNo ;0NNd .;lOKOo;. :Xx. .xXx. .;lOKOo;.
:Kk. .dOl.;okOkd:. .l:. .kWXx .;okOkd:.
... . ;xo:'. ,oc;'. ;xo:'.
```

BIN
examples/tcp-wiki.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

2
go.mod
View File

@ -19,6 +19,7 @@ require (
github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -29,6 +30,7 @@ require (
github.com/plar/go-adaptive-radix-tree v1.0.4 // indirect github.com/plar/go-adaptive-radix-tree v1.0.4 // indirect
github.com/sergi/go-diff v1.3.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect github.com/skeema/knownhosts v1.2.1 // indirect
github.com/tenkoh/goldmark-img64 v0.1.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.17.0 // indirect golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85 // indirect golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85 // indirect

4
go.sum
View File

@ -65,6 +65,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@ -231,6 +233,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tenkoh/goldmark-img64 v0.1.1 h1:JHTqUuvCZYzXbu+QouNk+c/w8qpZ3sAj+qFgr3pWXGg=
github.com/tenkoh/goldmark-img64 v0.1.1/go.mod h1:m3Z5ytQWSf1Lcdv2cXWXSRB7epGz5ka7kUeXTDSgDwk=
github.com/tidwall/btree v0.2.2/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= github.com/tidwall/btree v0.2.2/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/redcon v1.4.0/go.mod h1:IGzxyoKE3Ea5AWIXo/ZHP+hzY8sWXaMKr7KlFgcWSZU= github.com/tidwall/redcon v1.4.0/go.mod h1:IGzxyoKE3Ea5AWIXo/ZHP+hzY8sWXaMKr7KlFgcWSZU=

1465
sass/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

7
sass/package.json Normal file
View File

@ -0,0 +1,7 @@
{
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"daisyui": "^4.7.2",
"tailwindcss": "^3.4.1"
}
}

4
sass/tail.css Normal file
View File

@ -0,0 +1,4 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwindcss/typography;

12
sass/tailwind.config.js Normal file
View File

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["../assets/**/*.{html,js}"],
theme: {
extend: {},
},
daisyui: {
themes: ["forest"]
},
plugins: [require("@tailwindcss/typography"), require("daisyui")],
}

View File

@ -5,53 +5,53 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"path/filepath"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"github.com/BurntSushi/toml"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/prologic/bitcask" "github.com/prologic/bitcask"
"github.com/BurntSushi/toml"
) )
type Config struct { type Config struct {
Server struct { Server struct {
Port string Port string
} }
Git struct { Git struct {
UseGit bool UseGit bool
RepoURL string RepoURL string
Branch string Branch string
LocalPath string LocalPath string
} }
Database struct { Database struct {
Path string Path string
} }
} }
var commentsDB *bitcask.Bitcask var commentsDB *bitcask.Bitcask
func main() { func main() {
var config Config var config Config
if _, err := toml.DecodeFile("config.toml", &config); err != nil { if _, err := toml.DecodeFile("config.toml", &config); err != nil {
log.Fatalf("Failed to load config: %v", err) log.Fatalf("Failed to load config: %v", err)
} }
if config.Git.UseGit { if config.Git.UseGit {
err := cloneRepository(config.Git.RepoURL, config.Git.LocalPath) err := cloneRepository(config.Git.RepoURL, config.Git.LocalPath)
if err != nil && err != git.ErrRepositoryAlreadyExists { if err != nil && err != git.ErrRepositoryAlreadyExists {
log.Fatalf("Failed to clone repository: %v", err) log.Fatalf("Failed to clone repository: %v", err)
} }
} else { } else {
if _, err := os.Stat(config.Git.LocalPath); os.IsNotExist(err) { if _, err := os.Stat(config.Git.LocalPath); os.IsNotExist(err) {
os.MkdirAll(config.Git.LocalPath, os.ModePerm) os.MkdirAll(config.Git.LocalPath, os.ModePerm)
} }
} }
var err error var err error
commentsDB, err = bitcask.Open(config.Database.Path) commentsDB, err = bitcask.Open(config.Database.Path)
if err != nil { if err != nil {
@ -59,13 +59,13 @@ func main() {
} }
defer commentsDB.Close() defer commentsDB.Close()
fs := http.FileServer(http.Dir("./assets")) fs := http.FileServer(http.Dir("./assets"))
http.Handle("/assets/", http.StripPrefix("/assets/", fs)) http.Handle("/assets/", http.StripPrefix("/assets/", fs))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
handler(&config, w, r) handler(&config, w, r)
}) })
http.HandleFunc("/submit_comment", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/submit_comment", func(w http.ResponseWriter, r *http.Request) {
submitCommentHandler(w, r) submitCommentHandler(w, r)
}) })
srv := &http.Server{Addr: config.Server.Port} srv := &http.Server{Addr: config.Server.Port}
@ -98,14 +98,14 @@ func handler(config *Config, w http.ResponseWriter, r *http.Request) {
return return
} }
if config.Git.UseGit { if config.Git.UseGit {
err := pullRepository(config.Git.LocalPath, config.Git.Branch) err := pullRepository(config.Git.LocalPath, config.Git.Branch)
if err != nil { if err != nil {
log.Printf("Failed to pull repository: %v", err) log.Printf("Failed to pull repository: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError) http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return return
} }
} }
filePath := strings.TrimPrefix(r.URL.Path, "/") filePath := strings.TrimPrefix(r.URL.Path, "/")
if filePath == "" { if filePath == "" {
@ -114,41 +114,41 @@ func handler(config *Config, w http.ResponseWriter, r *http.Request) {
log.Printf("Rendering file %q from path %q", filePath, r.URL.Path) log.Printf("Rendering file %q from path %q", filePath, r.URL.Path)
// Set the Content Security Policy // Set the Content Security Policy
csp := "default-src 'self'; img-src 'self'; script-src 'self'; style-src 'self';" csp := "default-src 'self'; font-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self';"
w.Header().Set("Content-Security-Policy", csp) w.Header().Set("Content-Security-Policy", csp)
markdownFiles, err := listMarkdownFiles(config.Git.LocalPath) markdownFiles, err := listMarkdownFiles(config.Git.LocalPath)
if err != nil { if err != nil {
log.Printf("Error listing markdown files: %v", err) log.Printf("Error listing markdown files: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError) http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return return
} }
err = renderPage(w, r, config, filePath, commentsDB, markdownFiles) err = renderPage(w, r, config, filePath, commentsDB, markdownFiles)
if err != nil { if err != nil {
log.Printf("Failed to render page: %v", err) log.Printf("Failed to render page: %v", err)
http.Error(w, "File not found", http.StatusNotFound) http.Error(w, "File not found", http.StatusNotFound)
} }
} }
func listMarkdownFiles(localPath string) ([]string, error) { func listMarkdownFiles(localPath string) ([]string, error) {
var files []string var files []string
err := filepath.Walk(localPath, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(localPath, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
if !info.IsDir() && strings.HasSuffix(path, ".md") { if !info.IsDir() && strings.HasSuffix(path, ".md") {
relPath, err := filepath.Rel(localPath, path) relPath, err := filepath.Rel(localPath, path)
if err != nil { if err != nil {
return err return err
} }
relPath = strings.Replace(relPath, string(os.PathSeparator), "/", -1) relPath = strings.Replace(relPath, string(os.PathSeparator), "/", -1)
files = append(files, relPath) files = append(files, relPath)
} }
return nil return nil
}) })
return files, err return files, err
} }

View File

@ -11,9 +11,12 @@ import (
"time" "time"
"github.com/prologic/bitcask" "github.com/prologic/bitcask"
img64 "github.com/tenkoh/goldmark-img64"
"github.com/yuin/goldmark" "github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting" highlighting "github.com/yuin/goldmark-highlighting"
"github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
) )
type Page struct { type Page struct {
@ -24,64 +27,74 @@ type Page struct {
AuthoredDate *time.Time AuthoredDate *time.Time
LastModifier string LastModifier string
LastModifiedDate *time.Time LastModifiedDate *time.Time
Pages []string Pages []string
UseGit bool UseGit bool
} }
func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath string, commentsDB *bitcask.Bitcask, pages []string) error { func renderPage(w http.ResponseWriter, r *http.Request, config *Config, filePath string, commentsDB *bitcask.Bitcask, pages []string) error {
var content []byte var content []byte
var err error var err error
if config.Git.UseGit { if config.Git.UseGit {
content, err = readFileFromRepo(config.Git.LocalPath, filePath) content, err = readFileFromRepo(config.Git.LocalPath, filePath)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
fullPath := filepath.Join(config.Git.LocalPath, filePath) fullPath := filepath.Join(config.Git.LocalPath, filePath)
content, err = ioutil.ReadFile(fullPath) content, err = ioutil.ReadFile(fullPath)
if err != nil { if err != nil {
return err return err
} }
} }
ext := filepath.Ext(filePath) ext := filepath.Ext(filePath)
switch ext { switch ext {
case ".md": case ".md":
renderMarkdown(w, r, content, commentsDB, filePath, pages, config) renderMarkdown(w, r, content, commentsDB, filePath, pages, config)
case ".html", ".css": case ".html", ".css":
renderStatic(w, content, ext) renderStatic(w, content, ext)
default: default:
return fmt.Errorf("unsupported file format") return fmt.Errorf("unsupported file format")
} }
return nil return nil
} }
func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, commentsDB *bitcask.Bitcask, filePath string, pages []string, config *Config) { func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, commentsDB *bitcask.Bitcask, filePath string, pages []string, config *Config) {
md := goldmark.New( md := goldmark.New(
goldmark.WithExtensions( goldmark.WithExtensions(
extension.GFM, extension.GFM, // images should probably be base64 encoded https://github.com/tenkoh/goldmark-img64 for extra performance
highlighting.NewHighlighting( extension.Table,
highlighting.WithStyle("monokai"), highlighting.NewHighlighting(
highlighting.WithStyle("monokai"),
), ),
img64.Img64,
), // does this code below do anything useful?
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithHardWraps(),
img64.WithParentPath(config.Git.LocalPath),
), ),
) )
var author, lastModifier string var author, lastModifier string
var authoredDate, lastModifiedDate *time.Time var authoredDate, lastModifiedDate *time.Time
var err error var err error
if config.Git.UseGit { if config.Git.UseGit {
var ad, lmd time.Time var ad, lmd time.Time
author, ad, lastModifier, lmd, err = getAuthorAndLastModification(config.Git.LocalPath, filePath) author, ad, lastModifier, lmd, err = getAuthorAndLastModification(config.Git.LocalPath, filePath)
if err != nil { if err != nil {
http.Error(w, "Error fetching author and last modification date", http.StatusInternalServerError) http.Error(w, "Error fetching author and last modification date", http.StatusInternalServerError)
return return
} }
authoredDate = &ad authoredDate = &ad
lastModifiedDate = &lmd lastModifiedDate = &lmd
} }
var mdBuf bytes.Buffer var mdBuf bytes.Buffer
err = md.Convert(content, &mdBuf) err = md.Convert(content, &mdBuf)
@ -90,13 +103,12 @@ func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, comm
return return
} }
layout, err := ioutil.ReadFile("assets/_layout.html")
layout, err := ioutil.ReadFile("assets/_layout.html") if err != nil {
if err != nil { log.Printf("Error reading _layout.html: %v", err)
log.Printf("Error reading _layout.html: %v", err) http.Error(w, "Layout not found", http.StatusInternalServerError)
http.Error(w, "Layout not found", http.StatusInternalServerError) return
return }
}
comments, err := getComments(commentsDB, r.URL.Path) comments, err := getComments(commentsDB, r.URL.Path)
if err != nil && err != bitcask.ErrKeyNotFound { if err != nil && err != bitcask.ErrKeyNotFound {
@ -112,8 +124,8 @@ func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, comm
AuthoredDate: authoredDate, AuthoredDate: authoredDate,
LastModifier: lastModifier, LastModifier: lastModifier,
LastModifiedDate: lastModifiedDate, LastModifiedDate: lastModifiedDate,
Pages: pages, Pages: pages,
UseGit: config.Git.UseGit, UseGit: config.Git.UseGit,
} }
t, err := template.New("layout").Parse(string(layout)) t, err := template.New("layout").Parse(string(layout))
@ -126,7 +138,7 @@ func renderMarkdown(w http.ResponseWriter, r *http.Request, content []byte, comm
err = t.Execute(&buf, page) err = t.Execute(&buf, page)
if err != nil { if err != nil {
log.Printf("Error executing template: %v", err) log.Printf("Error executing template: %v", err)
http.Error(w, "Error rendering layout", http.StatusInternalServerError) http.Error(w, "Error rendering layout", http.StatusInternalServerError)
return return
} }