start my forum
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@ -13,3 +13,9 @@
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
about.i2p
|
||||
tmp.txt
|
||||
CercaForum
|
||||
onionkeys
|
||||
i2pkeys
|
||||
tlskeys
|
44
LICENSE
44
LICENSE
@ -1,21 +1,31 @@
|
||||
MIT License
|
||||
about.i2p
|
||||
Copyright (C) 2021 idk
|
||||
|
||||
Copyright (c) 2022 idk
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
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:
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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.
|
||||
Cerca
|
||||
Copyright (C) 2021 Alexander Cobleigh & the Cerca Developers
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
@ -1,2 +1,3 @@
|
||||
# about.i2p
|
||||
A "[cerca](github.com/cblgh/cerca)" based forum which exists on I2P only
|
||||
|
||||
An easy-to-selfhost "[cerca](github.com/cblgh/cerca)" based forum which exists on I2P only.
|
||||
|
52
about/html/about.html
Normal file
52
about/html/about.html
Normal file
@ -0,0 +1,52 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<article>
|
||||
<h1> About </h1>
|
||||
<p>This is an alternative, small-internet forum for people in the I2P community.</p>
|
||||
<p>The forum software was derived from <a href="https://github.com/cblgh/cerca">Cerca</a>, which was created by <a href="https://cblgh.org">cblgh</a> at the start of 2022.</p>
|
||||
<p>I chose it because it's easy-to-setup, build, and modify, does not require a database server, and is written in an understandable way in a memory-safe language.</p>
|
||||
<p>It has a unique, highly useful registration-verification procedure which I have adapted for I2P users.</p>
|
||||
|
||||
<p>If you have an I2P site, you can: <a href="/register">register</a> an account.</p>
|
||||
<p>If you do not have an I2P site, you can still register, but it's slightly more involved for now: <a href="/register">register</a> an account. If you're a passerby, feel free to read the <a href="/">public threads</a>.</p>
|
||||
|
||||
<h2>Code of conduct</h2>
|
||||
<p>This forum's code of conduct shall be defined in a forum thread and referenced here at a later date.</p>
|
||||
|
||||
<h2>Forum syntax</h2>
|
||||
<p>Posts in the forum are made using <a href="https://en.wikipedia.org/wiki/Markdown#Examples">Markdown
|
||||
syntax</a>.</p>
|
||||
<p><b>**Bold text**</b> and <i>*italics*</i></p>
|
||||
|
||||
<ul>
|
||||
<li>* lists</li>
|
||||
<li>* like </li>
|
||||
<li>* this </li>
|
||||
</ul>
|
||||
|
||||
<p><blockquote>> Blockquote</blockquote></p>
|
||||
|
||||
<p><code>`typewriter text`</code></p>
|
||||
|
||||
<!-- leave the <pre><code> blocks from reformatting! they render all their spacing :)-->
|
||||
<pre><code>```
|
||||
blocks of
|
||||
code like
|
||||
this
|
||||
```
|
||||
</code></pre>
|
||||
|
||||
<p>Create links like <code>[this](url)</code>, and embed images like: <code></code>. Note how the image
|
||||
syntax's exclamation mark precedes the regular link syntax.</p>
|
||||
|
||||
<p>Each post in the thread can be referenced like <code>[this post](#12)</code>, where 12 is the post number which can be
|
||||
found at each post timestamp.</p>
|
||||
|
||||
<pre><code>this is one paragraph.
|
||||
this belongs to the same paragraph.
|
||||
|
||||
this is a new paragraph
|
||||
</code></pre>
|
||||
</article>
|
||||
</main>
|
||||
{{ template "footer" . }}
|
BIN
about/html/assets/favicon.png
Executable file
BIN
about/html/assets/favicon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
about/html/assets/toopie.png
Normal file
BIN
about/html/assets/toopie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
1
about/html/assets/toopie.svg
Normal file
1
about/html/assets/toopie.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 181 KiB |
12
about/html/change-password-success.html
Normal file
12
about/html/change-password-success.html
Normal file
@ -0,0 +1,12 @@
|
||||
{{ template "head" . }}
|
||||
<h1>Change password</h1>
|
||||
<p>Your password was successfully changed! Please store it somewhere safe.</p>
|
||||
|
||||
{{ if ne .Data.Keypair "" }}
|
||||
<p> Here is your newly regenerated keypair, please save it somewhere in case you forget your password and need to reset it.</p>
|
||||
<pre class="selectable">
|
||||
{{ .Data.Keypair }}
|
||||
</pre>
|
||||
{{ end }}
|
||||
<p>Go back to the <a href="/">index</a>.</p>
|
||||
{{ template "footer" . }}
|
23
about/html/change-password.html
Normal file
23
about/html/change-password.html
Normal file
@ -0,0 +1,23 @@
|
||||
{{ template "head" . }}
|
||||
<h1>Change password</h1>
|
||||
<p>Use this page to change your password. If needed, you can also regenerate your password reset keypair—used to reset a
|
||||
forgotten password without admin help.</p>
|
||||
<form method="post" action="{{.Data.Action}}">
|
||||
<div>
|
||||
<label type="text" for="password-old">Current password:</label>
|
||||
<input type="password" minlength="9" required id="password-old" name="password-old" aria-describedby="password-help">
|
||||
</div>
|
||||
<div>
|
||||
<label type="text" for="password-new">New password:</label>
|
||||
<input type="password" style="margin-bottom: 0;" minlength="9" required id="password-new" name="password-new" aria-describedby="password-help">
|
||||
<div><small id="password-help">Must be at least 9 characters long.</small></div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" value="true" name ="reset-keypair" id="reset-keypair">
|
||||
<label for="reset-keypair" style="display: inline-block;">I also want to generate a new keypair</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit">
|
||||
</div>
|
||||
</form>
|
||||
{{ template "footer" . }}
|
4
about/html/footer.html
Normal file
4
about/html/footer.html
Normal file
@ -0,0 +1,4 @@
|
||||
{{ define "footer" }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
6
about/html/generic-message.html
Normal file
6
about/html/generic-message.html
Normal file
@ -0,0 +1,6 @@
|
||||
{{ template "head" . }}
|
||||
<h2>{{ .Data.Title }}</h2>
|
||||
<p>{{ .Data.Message }}</p>
|
||||
{{ if .Data.Link }}
|
||||
<p>{{ .Data.LinkMessage }} <a href="{{.Data.Link}}">{{.Data.LinkText}}</a>.</p>
|
||||
{{ end }}
|
218
about/html/head.html
Normal file
218
about/html/head.html
Normal file
@ -0,0 +1,218 @@
|
||||
{{ define "head" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Forum — {{ .Title }}</title>
|
||||
|
||||
<style>
|
||||
/* reset */
|
||||
*,*::after,*::before{box-sizing:border-box}blockquote,body,dd,dl,figure,h1,h2,h3,h4,p{margin:0}ul[role='list'],ol[role='list']{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}button,input,select,textarea{font:inherit}@media (prefers-reduced-motion: reduce){html:focus-within{scroll-behavior:auto}*,*::after,*::before{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}
|
||||
/* end reset */
|
||||
/* below is the minimum viable css for simply getting the forum out the door; contributions, both in css and
|
||||
design mockups, welcome from the community for v2,v3,...,vn :) */
|
||||
br { margin-bottom: unset; }
|
||||
html {
|
||||
max-width: 50rem;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
article h1, article h2, article h3 {
|
||||
/* normalize post titles */
|
||||
font-size: 1rem;
|
||||
}
|
||||
#logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: block;
|
||||
}
|
||||
ul[type="menu"] {
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
grid-column-gap: 0.5rem;
|
||||
}
|
||||
li { margin-bottom: 0rem; }
|
||||
ul { padding-left: 1rem; }
|
||||
h1, h2 { margin-bottom: 1rem; }
|
||||
p { margin-bottom: 1rem; }
|
||||
blockquote { padding-left: 1rem; border-left: 3px solid black }
|
||||
div { margin-bottom: 2rem; }
|
||||
textarea { min-height: 10rem; }
|
||||
article > section { margin-bottom: 0.5rem; }
|
||||
article { margin-bottom: 2rem; }
|
||||
form div, label { margin-bottom: 0; }
|
||||
h1 a:visited,
|
||||
a:not([class]) {
|
||||
color: black;
|
||||
}
|
||||
a:visited {
|
||||
color: #666; /* for lack of better imagination */
|
||||
}
|
||||
.selectable {
|
||||
-webkit-touch-callout: all;
|
||||
-webkit-user-select: all;
|
||||
-khtml-user-select: all;
|
||||
-moz-user-select: all;
|
||||
-ms-user-select: all;
|
||||
user-select: all;
|
||||
}
|
||||
pre { overflow: auto; }
|
||||
table { border:2px solid; }
|
||||
th, td { text-align:left; padding: 2px 7px; border: 1px solid; }
|
||||
th { font-weight: bold; }
|
||||
tr:hover td { background:#e2cca2; }
|
||||
|
||||
.post-container {
|
||||
max-width: 30rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.post-container > * {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body { padding: 2rem; background: wheat; }
|
||||
* { margin-bottom: 1rem; }
|
||||
|
||||
.visually-hidden {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
header nav {
|
||||
float: right;
|
||||
}
|
||||
|
||||
header ul > li {
|
||||
display: inline-block;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@supports (display: flex) {
|
||||
header nav {
|
||||
float: unset;
|
||||
}
|
||||
|
||||
header ul > li {
|
||||
display: unset;
|
||||
margin-left: unset;
|
||||
}
|
||||
}
|
||||
|
||||
header > a {
|
||||
background-image: url('/assets/toopie.png');
|
||||
background-size: 100%;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
header svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header details {
|
||||
cursor: pointer;
|
||||
}
|
||||
header details, header details summary {
|
||||
margin-bottom: unset;
|
||||
}
|
||||
header details a:visited {
|
||||
color: black;
|
||||
}
|
||||
header details ul {
|
||||
position: absolute;
|
||||
}
|
||||
header details ul > li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@supports (display: flex) {
|
||||
header > a {
|
||||
background-image: none;
|
||||
background-size: unset;
|
||||
background-repeat: unset;
|
||||
}
|
||||
|
||||
header svg {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- <link href="/assets/style.css" rel="stylesheet"> -->
|
||||
|
||||
<link rel="icon" href="/assets/favicon.png">
|
||||
<!-- <link rel="icon" href="/assets/favicon.svg" type="image/svg+xml"> -->
|
||||
<link rel="shortcut icon" href="/assets/favicon.png">
|
||||
<link rel="apple-touch-icon" href="/assets/favicon.png">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<!-- <meta name="description" content=""> -->
|
||||
|
||||
<!-- <meta property="og:title" content=""> -->
|
||||
<!-- <meta property="og:description" content=""> -->
|
||||
<!-- <meta property="og:image" content="/assets/favicon.png"> -->
|
||||
<!-- <meta property="og:image:alt" content=""> -->
|
||||
<!-- <meta property="og:locale" content="en_US"> -->
|
||||
<!-- <meta property="og:type" content="website"> -->
|
||||
<!-- <meta name="twitter:card" content=""> -->
|
||||
<!-- <meta property="og:url" content=""> -->
|
||||
<!-- -->
|
||||
<!-- <link rel="canonical" href=""> -->
|
||||
|
||||
<!-- <link rel="search" type="application/opensearchdescription+xml" title="" href="/assets/opensearch.xml"> -->
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a style="margin-bottom: 0; height: 48px;" href="/" aria-label="Home">
|
||||
<object data="/assets/toopie.svg" type="image/svg+xml" width="100" height="100"></object>
|
||||
</a>
|
||||
<nav>
|
||||
<ul type="menu">
|
||||
|
||||
{{ if eq .Title "threads" }}
|
||||
<li>
|
||||
<details>
|
||||
<summary>sort</summary>
|
||||
<ul>
|
||||
<li> <a href="/?sort=posts">recent posts</a></li>
|
||||
<li> <a href="/">most recent thread</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ if .QuickNav }}
|
||||
<li><a href="#bottom">bottom</a></li>
|
||||
{{end}}
|
||||
<li><a href="/about">about</a></li>
|
||||
{{ if .LoggedIn }}
|
||||
<li><a href="/logout">logout</a></li>
|
||||
{{ else }}
|
||||
<li><a href="/login">login</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
{{ end }}
|
8
about/html/html.go
Normal file
8
about/html/html.go
Normal file
@ -0,0 +1,8 @@
|
||||
package html
|
||||
|
||||
import "embed"
|
||||
|
||||
// Templates contain the raw HTML of all of our templates.
|
||||
//
|
||||
//go:embed *.html assets/*
|
||||
var Templates embed.FS
|
12
about/html/index.html
Normal file
12
about/html/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
{{ range $index, $thread := .Data.Threads }}
|
||||
<h2><a href="{{$thread.Slug}}">{{ $thread.Title }}</a></h2>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ if .LoggedIn }}
|
||||
<aside>
|
||||
<p> <a href="/thread/new">Start a new thread</a></p>
|
||||
</aside>
|
||||
{{ end }}
|
||||
{{ template "footer" . }}
|
16
about/html/login-component.html
Normal file
16
about/html/login-component.html
Normal file
@ -0,0 +1,16 @@
|
||||
{{ define "login-component" }}
|
||||
<form method="post" action="/login">
|
||||
<div style="display: grid;">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" name="username" id="username">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" name="password" id="password" style="margin-bottom:0;" aria-describedby="password-help">
|
||||
<div><small id="password-help">Must be at least 9 characters long.</small></div>
|
||||
</div>
|
||||
<input type="submit" value="Enter" style="margin-top:1rem;">
|
||||
</div>
|
||||
</form>
|
||||
{{ end }}
|
16
about/html/login.html
Normal file
16
about/html/login.html
Normal file
@ -0,0 +1,16 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<h1>Login</h1>
|
||||
<p>This forum is for the <a href="http://i2p-prokekt.i2p">I2P</a> community. Don't have an account yet? <a href="/register">Register</a> one. </p>
|
||||
<div style="max-width: 20rem">
|
||||
{{ template "login-component" . }}
|
||||
<p><a href="/reset">Forgot your password?</a></p>
|
||||
</div>
|
||||
{{ if .Data.FailedAttempt }}
|
||||
<p><b>Failed login attempt:</b> incorrect password, wrong username, or a non-existent user.</p>
|
||||
{{ else if .LoggedIn }}
|
||||
<p> You are already logged in. Would you like to <a href="/logout">log out</a>?</p>
|
||||
{{ end }}
|
||||
</p>
|
||||
</main>
|
||||
{{ template "footer" . }}
|
14
about/html/new-thread.html
Normal file
14
about/html/new-thread.html
Normal file
@ -0,0 +1,14 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<h1>Create thread</h1>
|
||||
<form method="POST">
|
||||
<div class="post-container" >
|
||||
<label for="title">Title:</label>
|
||||
<input required name="title" type="text" id="Title">
|
||||
<label for="content">Content:</label>
|
||||
<textarea required name="content" id="content" placeholder="Tabula rasa"></textarea>
|
||||
<button type="submit">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
{{ template "footer" . }}
|
45
about/html/password-reset.html
Normal file
45
about/html/password-reset.html
Normal file
@ -0,0 +1,45 @@
|
||||
{{ template "head" . }}
|
||||
<p>On this page we'll go through a few steps to securely reset your password—without resorting to any emails!</p>
|
||||
<p>First up: what was your username?</p>
|
||||
{{ if eq .Data.Action "/reset/generate" }}
|
||||
<form method="post" action="{{.Data.Action}}">
|
||||
<label type="text" for="username">Username:</label>
|
||||
<input required id="username" name="username">
|
||||
<div>
|
||||
<input type="submit" value="Generate payload">
|
||||
</div>
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ if eq .Data.Action "/reset/submit" }}
|
||||
<input disabled value="{{ .Data.Username }}">
|
||||
<p>Now, first copy the snippet (aka <i>proof payload</i>) below:</p>
|
||||
<pre style="user-select: all;">
|
||||
<code>{{ .Data.Payload }}</code>
|
||||
</pre>
|
||||
<p>Follow the <b>tool instructions</b> to finalize the password reset.</p>
|
||||
<details>
|
||||
<summary>Tool instructions</summary>
|
||||
<ul>
|
||||
<li><a href="https://github.com/cblgh/cerca/releases/tag/pwtool-v1">Download the tool</a></li>
|
||||
<li>Run as:<br><code>pwtool --payload <proof payload from above> --keypair <path to file with yr keypair from registration></code>
|
||||
</li>
|
||||
<li>Copy the generated proof and paste below</li>
|
||||
<li>(Remember to save your password :)</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<form method="post" action="{{.Data.Action}}">
|
||||
<input type="hidden" required id="username" name="username" value="{{ .Data.Username }}">
|
||||
<input type="hidden" required id="payload" name="payload" value="{{ .Data.Payload }}">
|
||||
<label for="proof">Proof</label>
|
||||
<input type="text" required id="proof" name="proof">
|
||||
<label for="password">New password</label>
|
||||
<input type="password" minlength="9" required id="password" name="password" aria-describedby="password-help">
|
||||
<div>
|
||||
<input type="submit" value="Change password">
|
||||
</div>
|
||||
</form>
|
||||
{{ end }}
|
||||
{{ template "footer" . }}
|
18
about/html/register-success.html
Normal file
18
about/html/register-success.html
Normal file
@ -0,0 +1,18 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<h1>Register</h1>
|
||||
<p>You now have an account! Welcome. Visit the <a href="/">index</a> to read and reply to threads, or start a new one.</p>
|
||||
<p>There's just one more thing: <b>save the key displayed below</b>. It is a <a href="https://en.wikipedia.org/wiki/Public-key_cryptography">keypair</a>
|
||||
describing your forum identity, with a private part that only you know; the forum only stores the public portion.</p>
|
||||
|
||||
<p>With this keypair you will be able to reset your account if you ever lose your password—and without having to share your email (or require
|
||||
email infrastructure on the forum's part).</p>
|
||||
<p><i><b>This keypair will only be displayed once</i></b></p>
|
||||
<code>
|
||||
<pre class="selectable">
|
||||
{{ .Data.Keypair }}
|
||||
</pre>
|
||||
</code>
|
||||
</main>
|
||||
|
||||
{{ template "footer" . }}
|
53
about/html/register.html
Normal file
53
about/html/register.html
Normal file
@ -0,0 +1,53 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<h1>Register</h1>
|
||||
|
||||
<p>This forum is for the <a href="http://i2p-projekt.i2p">I2P</a> community. To register, you need to host a file inside of I2P containing a verification code.</p>
|
||||
<p>If you're a passerby, feel free to read the <a href="/">public threads</a>.</p>
|
||||
|
||||
<p>Your verification code is <b>{{ .Data.VerificationCode }}</b></p>
|
||||
<details>
|
||||
<summary>Verification instructions</summary>
|
||||
<p>You can host your registration-verification code on your I2P site, or you can present your registration "ephemerally" at the command-line.</p>
|
||||
<ul>
|
||||
<li><b>I2P site:</b> Host a plaintext file somewhere on your I2P site containing the verification code from above.
|
||||
Pass the link to the hosted file as the verification link below.</li>
|
||||
<li><b>One-time hosting:</b> Use this tool: <a href="https://git.idk.i2p/idk/singlefile">git.idk.i2p/idk/singlefile</a> and pass the flag <code>--string={{ .Data.VerificationCode }}</code>.
|
||||
Pass the link to the hosted file as the verification link below.</li>
|
||||
<li><b>One-time hosting:</b> Use literally any file-hosting tool which can be proxied to I2P.
|
||||
Pass the link to the hosted file as the verification link below.</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<form method="post">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" required id="username" name="username">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" minlength="9" required id="password" name="password" aria-describedby="password-help" style="margin-bottom:0;">
|
||||
<div style="margin-bottom:1rem;"><small id="password-help">Must be at least 9 characters long.</small></div>
|
||||
<label for="verificationlink">Verification link:</label>
|
||||
<input type="text" required id="verification link" name="verificationlink">
|
||||
<input type="hidden" name="verificationcode" value="{{.Data.VerificationCode}}">
|
||||
<div>
|
||||
<div style="display: none;">
|
||||
<input type="checkbox" required id="coc" checked="true">
|
||||
<label for="coc" style="display: none;">I am not a robot</label>
|
||||
</div>
|
||||
<div style="display: none;">
|
||||
<input type="checkbox" required id="coc2" checked="true">
|
||||
<label style="display: none;" for="coc2">Yes, I am actually not a robot</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" value="Register">
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
{{ if .Data.ErrorMessage }}
|
||||
<div>
|
||||
<p><b> {{ .Data.ErrorMessage }} </b></p>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
</main>
|
||||
{{ template "footer" . }}
|
44
about/html/thread.html
Normal file
44
about/html/thread.html
Normal file
@ -0,0 +1,44 @@
|
||||
{{ template "head" . }}
|
||||
<main>
|
||||
<h1>{{ .Data.Title }}</h1>
|
||||
{{ $userID := .LoggedInID }}
|
||||
{{ $threadURL := .Data.ThreadURL }}
|
||||
{{ range $index, $post := .Data.Posts }}
|
||||
<article id="{{ $post.ID }}">
|
||||
<section aria-label="Post meta">
|
||||
{{ if eq $post.AuthorID $userID }}
|
||||
<span style="float: right;" aria-label="Delete this post">
|
||||
<form style="display: inline-block;" method="POST" action="/post/delete/{{ $post.ID }}"
|
||||
onsubmit="return confirm('Delete post for all posterity?');"
|
||||
>
|
||||
<button style="background-color: transparent; border: 0; padding: 0;" type="submit">delete</button>
|
||||
<input type="hidden" name="thread" value="{{ $threadURL }}">
|
||||
</form>
|
||||
</span>
|
||||
{{ end }}
|
||||
<span class="visually-hidden">Author:</span>
|
||||
<span><b>{{ $post.Author }}</b>
|
||||
<span class="visually-hidden"> responded:</span>
|
||||
</span>
|
||||
<a href="#{{ $post.ID }}">
|
||||
<span style="margin-left: 0.5rem; font-style: italic;">
|
||||
<time datetime="{{ $post.Publish | formatDate }}">{{ $post.Publish | formatDateRelative }}</time>
|
||||
</span>
|
||||
</a>
|
||||
</section>
|
||||
{{ $post.Content }}
|
||||
</article>
|
||||
{{ end }}
|
||||
{{ if .LoggedIn }}
|
||||
<section aria-label="Respond into this thread">
|
||||
<form method="POST">
|
||||
<div id="bottom" class="post-container" >
|
||||
<label class="visually-hidden" for="content">Your answer:</label>
|
||||
<textarea required name="content" id="content" placeholder="Tabula rasa"></textarea>
|
||||
<button type="submit">Post</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ template "footer" . }}
|
90
about/shim.go
Normal file
90
about/shim.go
Normal file
@ -0,0 +1,90 @@
|
||||
package about
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"cerca/database"
|
||||
"cerca/server"
|
||||
|
||||
"github.com/eyedeekay/about.i2p/about/html"
|
||||
)
|
||||
|
||||
func SafeDirectory(u *server.CercaForum) string {
|
||||
if u.Directory == "" {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
u.Directory = filepath.Join(dir, "CercaForum")
|
||||
}
|
||||
os.MkdirAll(u.Directory, 0755)
|
||||
return u.Directory
|
||||
}
|
||||
|
||||
func generateTemplates() (*template.Template, error) {
|
||||
views := []string{
|
||||
"about",
|
||||
"footer",
|
||||
"generic-message",
|
||||
"head",
|
||||
"index",
|
||||
"login",
|
||||
"login-component",
|
||||
"new-thread",
|
||||
"register",
|
||||
"register-success",
|
||||
"thread",
|
||||
"password-reset",
|
||||
"change-password",
|
||||
"change-password-success",
|
||||
}
|
||||
|
||||
rootTemplate := template.New("root")
|
||||
|
||||
for _, view := range views {
|
||||
newTemplate, err := rootTemplate.Funcs(server.TemplateFuncs).ParseFS(html.Templates, fmt.Sprintf("%s.html", view))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get files: %w", err)
|
||||
}
|
||||
rootTemplate = newTemplate
|
||||
}
|
||||
|
||||
return rootTemplate, nil
|
||||
}
|
||||
|
||||
// NewServer sets up a new CercaForum object. Always use this to initialize
|
||||
// new CercaForum objects. Pass the result to http.Serve() with your choice
|
||||
// of net.Listener.
|
||||
func NewServer(allowlist []string, sessionKey, dir string) (*server.CercaForum, error) {
|
||||
server.Templates = template.Must(generateTemplates())
|
||||
s := &server.CercaForum{
|
||||
ServeMux: http.ServeMux{},
|
||||
Directory: dir,
|
||||
}
|
||||
|
||||
dbpath := filepath.Join(SafeDirectory(s), "forum.db")
|
||||
db := database.InitDB(dbpath)
|
||||
|
||||
/* note: be careful with trailing slashes; go's default handler is a bit sensitive */
|
||||
// TODO (2022-01-10): introduce middleware to make sure there is never an issue with trailing slashes
|
||||
handler := server.NewRequestHandler(db, sessionKey, allowlist)
|
||||
s.ServeMux.HandleFunc("/reset/", handler.ResetPasswordRoute)
|
||||
s.ServeMux.HandleFunc("/about", handler.AboutRoute)
|
||||
s.ServeMux.HandleFunc("/logout", handler.LogoutRoute)
|
||||
s.ServeMux.HandleFunc("/login", handler.LoginRoute)
|
||||
s.ServeMux.HandleFunc("/register", handler.RegisterRoute)
|
||||
s.ServeMux.HandleFunc("/post/delete/", handler.DeletePostRoute)
|
||||
s.ServeMux.HandleFunc("/thread/new/", handler.NewThreadRoute)
|
||||
s.ServeMux.HandleFunc("/thread/", handler.ThreadRoute)
|
||||
s.ServeMux.HandleFunc("/robots.txt", handler.RobotsRoute)
|
||||
s.ServeMux.HandleFunc("/", handler.IndexRoute)
|
||||
|
||||
fileserver := http.FileServer(http.FS(html.Templates))
|
||||
s.ServeMux.Handle("/assets/", fileserver) //http.StripPrefix("/assets/", fileserver))
|
||||
return s, nil
|
||||
}
|
30
go.mod
Normal file
30
go.mod
Normal file
@ -0,0 +1,30 @@
|
||||
module github.com/eyedeekay/about.i2p
|
||||
|
||||
replace cerca => ../../cblgh/cerca
|
||||
|
||||
replace cerca/html => ./about/html
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
cerca v0.0.0-00010101000000-000000000000
|
||||
github.com/eyedeekay/onramp v0.0.0-20220829050101-64cb1842d0f0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/carlmjohnson/requests v0.22.1 // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/eyedeekay/i2pkeys v0.0.0-20220804220722-1048b5ce6ba7 // indirect
|
||||
github.com/eyedeekay/sam3 v0.33.3 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.9 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.17 // indirect
|
||||
github.com/synacor/argon2id v0.0.0-20190318165710-18569dfc600b // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
|
||||
)
|
128
go.sum
Normal file
128
go.sum
Normal file
@ -0,0 +1,128 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/carlmjohnson/requests v0.22.1 h1:YoifpEbpJW4LPRX/+0dJe3vTLducEE9Ib10k6lElIUM=
|
||||
github.com/carlmjohnson/requests v0.22.1/go.mod h1:Hw4fFOk3xDlHQbNRTGo4oc52TUTpVEq93sNy/H+mrQM=
|
||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eyedeekay/cerca v0.0.0-20220928210258-c1db73ed7f7f h1:H1xZhRMnBjkWJHXbpr+JsRxVMnGW97cgzT+bqrFp2B8=
|
||||
github.com/eyedeekay/cerca v0.0.0-20220928210258-c1db73ed7f7f/go.mod h1:hV4ou4nTIo3Q8nRsRb9Bcja9Kue8HM4WoMKPCCeEA+Q=
|
||||
github.com/eyedeekay/goSam v0.32.31-0.20210122211817-f97683379f23/go.mod h1:UgJnih/LpotwKriwVPOEa6yPDM2NDdVrKfLtS5DOLPE=
|
||||
github.com/eyedeekay/i2pkeys v0.0.0-20220310052025-204d4ae6dcae/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||
github.com/eyedeekay/i2pkeys v0.0.0-20220804220722-1048b5ce6ba7 h1:or9gYJ9VQN8YGoC0yKRC5lAWna2TaDMGZH/JFhwoJ4Q=
|
||||
github.com/eyedeekay/i2pkeys v0.0.0-20220804220722-1048b5ce6ba7/go.mod h1:W9KCm9lqZ+Ozwl3dwcgnpPXAML97+I8Jiht7o5A8YBM=
|
||||
github.com/eyedeekay/onramp v0.0.0-20220829050101-64cb1842d0f0 h1:IM2FwYZmxCVJs/dPhe2xHHquSf002SbJZjceeemJZmo=
|
||||
github.com/eyedeekay/onramp v0.0.0-20220829050101-64cb1842d0f0/go.mod h1:JVDy5GNVb395nl8KmV0a8L56VrS7MOqz6d8orD2QNeQ=
|
||||
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||
github.com/eyedeekay/sam3 v0.33.3 h1:VeN4nYWWeGfnnnHIPg0fjk+Kt2DI+6RSYcJrb2M7C4c=
|
||||
github.com/eyedeekay/sam3 v0.33.3/go.mod h1:sPtlI4cRm7wD0UywOzLPvvdY1G++vBSK3n+jiIGqWlU=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
|
||||
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df h1:M7mdNDTRraBcrHZg2aOYiFP9yTDajb6fquRZRpXnbVA=
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/renameio v1.0.0/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y=
|
||||
github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/riobard/go-x25519 v0.0.0-20190716001027-10cc4d8d0b33/go.mod h1:BjmVxzAnkLeoEbqHEerI4eSw6ua+RaIB0S4jMV21RAs=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/synacor/argon2id v0.0.0-20190318165710-18569dfc600b h1:Yu/2y+2iAAcTRfdlMZ3dEdb1aYWXesDDaQjb7xLgy7Y=
|
||||
github.com/synacor/argon2id v0.0.0-20190318165710-18569dfc600b/go.mod h1:RQlLg9p2W+/d3q6xRWilTA2R4ltKiwEmzoI1urnKm9U=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 h1:N9Vc/rorQUDes6B9CNdIxAn5jODGj2wzfrei2x4wNj4=
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
78
main.go
Normal file
78
main.go
Normal file
@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"cerca/util"
|
||||
|
||||
"github.com/eyedeekay/about.i2p/about"
|
||||
"github.com/eyedeekay/onramp"
|
||||
)
|
||||
|
||||
func readAllowlist(location string) []string {
|
||||
ed := util.Describe("read allowlist")
|
||||
data, err := os.ReadFile(location)
|
||||
ed.Check(err, "read file")
|
||||
list := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
var processed []string
|
||||
for _, fullpath := range list {
|
||||
u, err := url.Parse(fullpath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
processed = append(processed, u.Host)
|
||||
}
|
||||
return processed
|
||||
}
|
||||
|
||||
func complain(msg string) {
|
||||
fmt.Printf("cerca: %s\n", msg)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var allowlistLocation string
|
||||
var sessionKey string
|
||||
var dir string
|
||||
flag.StringVar(&allowlistLocation, "allowlist", "", "domains which can be used to read verification codes from during registration")
|
||||
flag.StringVar(&sessionKey, "authkey", "", "session cookies authentication key")
|
||||
flag.StringVar(&dir, "dir", "", "directory to run in")
|
||||
flag.Parse()
|
||||
if len(sessionKey) == 0 {
|
||||
complain("please pass a random session auth key with --authkey")
|
||||
} else if len(allowlistLocation) == 0 {
|
||||
complain("please pass a file containing the verification code domain allowlist")
|
||||
}
|
||||
|
||||
garlic, err := onramp.NewGarlic("about.i2p", "127.0.0.1:7656", []string{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
http.DefaultClient.Transport = &http.Transport{
|
||||
Dial: garlic.Dial,
|
||||
}
|
||||
allowList := readAllowlist(allowlistLocation)
|
||||
allowList = append(allowList, "*.i2p")
|
||||
allowList = append(allowList, "*.b32.i2p")
|
||||
if ln, err := garlic.ListenTLS(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
allowList = append(allowList, ln.Addr().String())
|
||||
if cercaServer, err := about.NewServer(allowList, sessionKey, dir); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if err := http.Serve(ln, cercaServer); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
log.Println("Exited gracefully")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user