Compare commits

...

11 Commits

Author SHA1 Message Date
pluja 94de37c62a updates 2024-03-25 13:46:35 +01:00
pluja b4bff88bec style and wording 2024-03-25 13:38:39 +01:00
pluja d5fe437bb8 change id to comments 2024-03-25 11:43:23 +01:00
pluja 514df700c5 change link 2024-03-25 11:39:53 +01:00
pluja 36c2d4b80e parse special characters 2024-03-25 11:34:08 +01:00
pluja 88e97900c2 parse special characters for cactus id 2024-03-25 11:33:45 +01:00
pluja 71f9a8cfff change ID to prod 2024-03-25 11:18:04 +01:00
pluja fb4d43e4ed remove unused disgus 2024-03-25 11:17:30 +01:00
pluja ac9111f58b add changelog 2024-03-25 11:08:05 +01:00
pluja 1aa3bccc20 add cactus comments 2024-03-25 11:07:59 +01:00
pluja 41448ea966 logging improvements and ensure unique names 2024-03-25 11:00:08 +01:00
16 changed files with 124 additions and 2187 deletions

6
CHANGELOG.md Normal file
View File

@ -0,0 +1,6 @@
# v2024-03-25
* Add changelog file
* Migrate comments to Matrix. It is more widely used and more stable, also allows for web-less comments and chat.
* Ensure service names are unique.
* Improved logging.

View File

@ -4,7 +4,7 @@ This is the official repository of the KYCNOT.ME project.
## Comments
Comments are based on [Disgus](). I'm using a slightly modified version of the [Disgus]() project, to fit the KYCNOT.me theme and needs.
Comments are based on [Cactus](https://cactus.chat).
## ToS Checker

View File

@ -1,11 +1,14 @@
package database
import (
"errors"
"fmt"
"os"
"sort"
"strings"
"github.com/pluja/pocketbase"
"github.com/rs/zerolog/log"
)
type PbClient struct {
@ -49,7 +52,7 @@ func (p *PbClient) GetServices(filters, sort string) ([]Service, error) {
response, err := collection.List(params)
if err != nil {
return nil, err
return nil, errors.New("database error")
}
return response.Items, nil
@ -67,7 +70,7 @@ func (p *PbClient) GetServiceByNameOrUrl(id string) (*Service, error) {
response, err := collection.List(params)
if err != nil {
return nil, err
return nil, errors.New("database error")
}
if len(response.Items) > 0 {
@ -85,7 +88,7 @@ func (p *PbClient) GetServiceByNameOrUrl(id string) (*Service, error) {
})
return &service, nil
} else {
return nil, fmt.Errorf("Service not found.")
return nil, errors.New("service not found")
}
}
@ -99,7 +102,7 @@ func (p *PbClient) GetServiceById(id string) (*Service, error) {
response, err := collection.OneWithParams(id, params)
if err != nil {
return nil, err
return nil, errors.New("service not found")
}
return &response, nil
@ -110,7 +113,11 @@ func (p *PbClient) CreateService(service Service) error {
_, err := collection.Create(service)
if err != nil {
return err
if strings.Contains(err.Error(), "validation_not_unique") {
return errors.New("a service with this name already exsits, check out /pending page")
}
log.Debug().Err(err).Msg("could not create service")
return errors.New("could not create service")
}
return nil
@ -121,7 +128,7 @@ func (p *PbClient) UpdateService(id string, service Service) error {
err := collection.Update(id, service)
if err != nil {
return err
return fmt.Errorf("could not update: %v", err)
}
return nil
@ -132,7 +139,7 @@ func (p *PbClient) GetAttribute(id string) (*Attribute, error) {
response, err := collection.One(id)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not get service: %v", err)
}
return &response, nil
@ -155,7 +162,7 @@ func (p *PbClient) GetAttributes(filters, sort string) ([]Attribute, error) {
response, err := collection.List(params)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not list: %s", err)
}
return response.Items, nil
@ -166,7 +173,7 @@ func (p *PbClient) CreateAttribute(attribute Attribute) error {
_, err := collection.Create(attribute)
if err != nil {
return err
return fmt.Errorf("could not create attribute: %v", err)
}
return nil

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,11 @@
text-underline-offset: 0.2rem;
}
article div {
padding-top: .5em;
padding-bottom: .5em;
}
h1,
h2,
h3,
@ -210,7 +215,7 @@
<h3 id="request">request</h3>
<p>
You can request a new listing by visiting the
<a href="/request/service">Request</a> page.
<a href="/request/service">Request</a> page. The request form requires javascript to avoid spam. <a href="#javascript">Read more about the PoW captcha in this section.</a>.
</p>
<h3 id="listing-process">listing process</h3>
@ -357,8 +362,8 @@
<p>
If you like this project, or
<a target="_blank" href="https://pluja.dev">any of my other ones</a
>, you can support my work through these methods:
<a target="_blank" href="https://pluja.dev">any of my other projects</a
>, you can support me through these methods:
</p>
<div class="[&>span]:block space-y-6">
@ -541,10 +546,11 @@
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="contact">contact</h2>
<p>
I've discontinued instant messaging/email contacts due to misuse and management challenges that lead to leaving may messages unanswered. For questions or concerns, connect with me on the listed social media platforms.
Due to past misuse and difficulties in managing, I've decided to remove direct instant
messaging/email contacts. If you have any queries or concerns, connect with me on any of the listed social media platforms below.
</p>
<p>
For private communications, request through these platforms and I'll share an email or alternative messaging option.
For confidential communication, please reach out via the specified platforms and I can provide my email, matrix, xmpp, threema or a SimpleX link.
</p>
<div class="[&>span]:block [&>span>a]:!no-underline [&>span>a]:font-bold pb-2 space-y-2">
@ -562,7 +568,7 @@
rel="noreferrer"
href="https://njump.me/npub188x98j0r7l2fszeph6j7hj99h8xl07n989pskk5zd69d2fcksetq5mgcqf"
>🪶 Nostr</a
>
> <a class="mx-2 text-xs text-gray-400" href="/nostr">?</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://lemmy.world/u/pluja"
@ -573,33 +579,23 @@
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="comments">(nostr) comments</h2>
<h2 id="comments">(matrix) comments</h2>
<p>
Each service has a dedicated comments article. The comments are suppored
by <a href="https://usenostr.org">Nostr</a>, a decentralized structured
data sharing protocol used to build social networks.
Each service has a dedicated comments section. The comments are suppored
by <a rel="no-referrer" target="_blank" href="https://matrix.org">Matrix</a>, an open network for secure,
decentralised communication, thanks to <a rel="no-referrer" target="_blank" href="https://cactus.chat/">Cactus</a>.
</p>
<p>
<a href="/nostr"
>Click here for a quick guide on how to use Nostr comments.</a
>
To comment on a service's page, you just need a <a rel="no-referrer" target="_blank" href="https://matrix.org/try-matrix/">Matrix account</a>. This method gives users full control over who hosts their data. Users can log in using any Matrix server they choose.
</p>
<p>
To comment on a service's page, you just need a Nostr private key and a
secure
<a
rel="noreferrer"
href="https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions"
target="_blank"
>nostr siging extension (nip-07)</a
>
for logging in. I recommend using
<a href="https://getalby.com/" target="_blank">Alby</a> or
<a href="https://github.com/fiatjaf/nos2x" target="_blank">nos2x</a>
You can read and post comments in your browser, or continue the conversation using any other Matrix client. Comment sections are accesible from any homeserver on the Matrix network.
</p>
<p>The <a rel="no-referrer" target="_blank" href="https://gitlab.com/cactus-comments">source code</a> for the web client is LGPLv3 licensed, and the backend service is AGPLv3 licensed.</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
@ -642,22 +638,20 @@
<h3 id="javascript">javascript</h3>
<p>
KYCNOT.me does not use Javascript. Everything is rendered server-side.
This means that you can use KYCNOT.me with Javascript disabled.
KYCNOT.me does not require Javascript. Everything is rendered server-side.
You can use KYCNOT.me with Javascript disabled.
</p>
<p>Only things that <b>require</b> JavaScript to be enabled are:</p>
<p>Only things that do <b>require</b> JavaScript to be enabled are:</p>
<div class="[&>span]:block space-y-2">
<span>
<b>Service Requests</b>: The Proof-Of-Work captcha, that I coded myself, needs
JavaScript for the verfication. The code is public, and you can read
it yourself on the repo.
JavaScript for the verfication. It is an <a rel="no-referrer" target="_blank" href="https://codeberg.org/pluja/kycnot.me/src/branch/main/src/frontend/static/js/pow.js">open-source</a> 2.5kb javascript function.
</span>
<span>
<b>Comments</b>: The comment section on each of the services, needs
JavaScript since it needs to fetch and post to Nostr. The code that
makes this interaction is also fully open source and can be reviewed
in the repo as well.
JavaScript since it needs to fetch and post to Matrix. The code that
makes this interaction is also fully open source. Read more <a href="#comments">in this section.</a>
</span>
</div>
</div>
@ -667,7 +661,7 @@
<p>KYCnot.me offers a public API to get its data.</p>
<p>
If you make use of the API data, please mention kycnot.me as the source
If you make use of the API data, you should mention kycnot.me as the source
of the data.
</p>

View File

@ -19,19 +19,8 @@
<!-- Mastodon Verification -->
<link rel="me" href="https://fosstodon.org/@kycnotme">
<!-- Disgus Configuration -->
<!-- pubkey so it can be tagged for responses/alerts -->
<meta property="nostr:pubkey" content="npub1tuta00sz4wvvzymqcfq42cqhxal6puqpylxs4yf0z28z3ryvfh9qkqmv92" />
<!-- Nostr publish relays -->
<meta property="nostr:relay" content="wss://relay.exit.pub" />
<meta property="nostr:relay" content="wss://relay.snort.social" />
<meta property="nostr:relay" content="wss://relay.plebstr.com" />
<meta property="nostr:relay" content="wss://relay.camelus.app" />
<meta property="nostr:relay" content="wss://bitcoiner.social" />
<meta property="nostr:relay" content="wss://purplerelay.com" />
<!-- CSS -->
<link rel="stylesheet" href="/static/disgus/disgus.css">
<link rel="stylesheet" href="/static/css/balloon.min.css">
<!-- Site Config -->

View File

@ -98,7 +98,7 @@
<article class="md:p-4 md:max-w-5xl font-inter">
<div class="px-4 py-2 m-2 rounded-xl bg-gray-500/10">
<h2 id="why" class="font-bold text-lime-400">
how to use the nostr comments?
nostr?
</h2>
<p>
Start by reading
@ -204,14 +204,13 @@
</li>
</ul>
<h3 id="logging in" class="pt-4 w-full border-t border-white/20">
logging in
<h3 id="use" class="pt-4 w-full border-t border-white/20">
use
</h3>
<p>
After your account is set up, you will be able to log in to any platform
that uses the Nostr protocol. Go to any kycnot.me service page, and try
to log into the comment section.
that uses the Nostr protocol, and use it. Take a look <a target="_blank" href="https://nostr.net/">here</a> to find a client that suites your needs.
</p>
</div>
</article>

View File

@ -1,3 +1,8 @@
<head>
<script type="text/javascript" src="/static/js/cactus.js"></script>
<link rel="stylesheet" href="/static/css/cactus.css" type="text/css">
</head>
<section class="p-2 mt-8 mb-2">
<!-- Pending Approval Notice -->
{{if .Service.Pending}}
@ -226,27 +231,38 @@
<!-- Comments -->
<section class="flex flex-col justify-center items-center p-4 mb-16" id="discuss">
<a href="/about#nostr-comments" target="_blank" class="text-xl font-bold">
<a href="/about#comments" target="_blank" class="text-xl font-bold">
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4 messages" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M21 14l-3 -3h-7a1 1 0 0 1 -1 -1v-6a1 1 0 0 1 1 -1h9a1 1 0 0 1 1 1v10"></path>
<path d="M14 15v2a1 1 0 0 1 -1 1h-7l-3 3v-10a1 1 0 0 1 1 -1h2"></path>
</svg>
<span>Nostr Comments</span>
<span>Matrix Comments</span>
<span class="text-xs text-white/30">?</span>
</a>
<h2 class="text-xl font-bold"></h2>
<p class="pb-4 mt-1 max-w-sm text-xs text-center text-gray-500 text-opacity-75">Beware of fake reviews and accounts. Verify user profiles and conduct your own research.</p>
<!-- div with the ID disgus where you would like to display the comments & form -->
<div id="disgus" class="w-full max-w-xl">
<!-- Div where cactus will set the client-->
<div id="comment-section" class="w-full max-w-xl">
<noscript>
<p class="font-bold text-center text-yellow-300">
You must have JavaScript enabled to see and use comments. It just will load a 200kb script to interact with Nostr. The source code is available
<a target="_blank" class="underline" href="https://github.com/carlitoplatanito/disgus">here</a>.
<p class="mb-2 font-bold text-center text-yellow-300">
You can enable JavaScript to see and use comments here. It just will load a 150kb open-source script to interact with Matrix.
The source code is available <a target="_blank" class="underline" href="https://gitlab.com/cactus-comments/cactus-client/-/tree/main/src?ref_type=heads">here</a>.
</p>
<p class="font-bold text-center text-yellow-500/50">> If you still want to avoid using Javascript in the browser, you can use a Matrix Client to read comments: <a class="underline" href="https://matrix.to/#/%23comments_testing-knm-local_{{toId .Service.Name}}%3Acactus.chat">matrix.to</a></p>
</noscript>
</div>
</section>
<!-- this can go at the end of the body -->
<script type="module" src="/static/disgus/index.js" async></script>
<script>
initComments({
node: document.getElementById("comment-section"),
defaultHomeserverUrl: "https://matrix.cactus.chat:8448",
serverName: "cactus.chat",
siteName: "kycnot.me",
commentSectionId: "{{toId .Service.Name}}",
guestPostingEnabled: false
})
</script>

View File

@ -53,7 +53,7 @@ func (s *Server) handleApiPicture(c iris.Context) {
service, err := database.Pb.GetServiceById(id)
if err != nil || service.LogoURL == "" {
log.Error().Err(err).Msg("GetPicture: Could not get service or logo URL is empty")
log.Debug().Err(err).Msg("GetPicture: Could not get service or logo URL is empty")
respondWithPlaceholder(c, service.Name)
return
}
@ -76,7 +76,7 @@ func (s *Server) handleApiPicture(c iris.Context) {
bodyBytes, _ := io.ReadAll(resp.Body)
if len(bodyBytes) == 0 {
log.Error().Msg("GetPicture: Empty response body")
log.Debug().Msg("GetPicture: Empty response body")
respondWithPlaceholder(c, service.Name)
return
}

View File

@ -13,7 +13,7 @@ func (s *Server) handleApiService(c iris.Context) {
service, err := database.Pb.GetServiceByNameOrUrl(serviceName)
if err != nil {
log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
log.Debug().Err(err).Msgf("Could not get service %v from database", serviceName)
c.HTML("<h3>%s</h3>", err.Error())
return
}

View File

@ -7,6 +7,7 @@ import (
"net/http"
"os"
"path"
"regexp"
"strings"
"time"
@ -138,6 +139,22 @@ func (s *Server) RegisterViews() {
"safe": func(s string) template.HTML {
return template.HTML(s)
},
"toId": func(s string) string {
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
log.Debug().Err(err).Msg("toId error")
return strings.Replace(strings.ToLower(s), " ", "-", -1)
}
processedString := reg.ReplaceAllString(s, "-")
regReplace, err := regexp.Compile("-+")
if err != nil {
log.Debug().Err(err).Msg("toId error")
return strings.Replace(strings.ToLower(s), " ", "-", -1)
}
processedString = regReplace.ReplaceAllString(processedString, "-")
return strings.ToLower(processedString)
},
"randomElem": func(vs []string) string {
if len(vs) == 0 {
return ""

View File

@ -3,6 +3,7 @@ package server
import (
"fmt"
"math/rand"
"net/url"
"strings"
"github.com/kataras/iris/v12"
@ -65,7 +66,7 @@ func (s *Server) handleIndex(c iris.Context) {
)
if err != nil {
log.Error().Err(err).Msg("Could not get services from Pocketbase")
log.Debug().Err(err).Msg("Could not get services from Pocketbase")
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -100,7 +101,7 @@ func (s *Server) handleService(c iris.Context) {
service, err := database.Pb.GetServiceByNameOrUrl(serviceName)
if err != nil {
log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
log.Debug().Err(err).Msgf("Could not get service %v from database", serviceName)
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -130,7 +131,7 @@ func (s *Server) handleScoreSummary(c iris.Context) {
service, err := database.Pb.GetServiceByNameOrUrl(serviceName)
if err != nil {
log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
log.Debug().Err(err).Msgf("Could not get service %v from database", serviceName)
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -146,7 +147,7 @@ func (s *Server) handleAttribute(c iris.Context) {
// Get service from database by name
attribute, err := database.Pb.GetAttribute(attributeId)
if err != nil {
log.Error().Err(err).Msgf("Could not get attribute %v from database", attributeId)
log.Debug().Err(err).Msgf("Could not get attribute %v from database", attributeId)
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -157,7 +158,7 @@ func (s *Server) handleAttribute(c iris.Context) {
"-score",
)
if err != nil {
log.Error().Err(err).Msgf("Could not get services with attribute %v from database", attributeId)
log.Debug().Err(err).Msgf("Could not get services with attribute %v from database", attributeId)
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -183,7 +184,7 @@ func (s *Server) handleRequestServiceForm(c iris.Context) {
attributes, err := database.Pb.GetAttributes("", "-rating")
if err != nil {
log.Error().Err(err).Msg("Could not get attributes from database")
log.Debug().Err(err).Msg("Could not get attributes from database")
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -236,7 +237,7 @@ func (s *Server) handleNostr(c iris.Context) {
func (s *Server) handlePending(c iris.Context) {
services, err := database.Pb.GetServices("pending=true", "score")
if err != nil {
log.Error().Err(err).Msg("Could not get services from database")
log.Debug().Err(err).Msg("Could not get services from database")
c.HTML("<h3>%s</h3>", err.Error())
return
}
@ -278,26 +279,26 @@ func (s *Server) handlePostRequestServiceForm(c iris.Context) {
log.Debug().Msg("Handling request service form")
var data RequestFormData
if err := c.ReadForm(&data); err != nil {
log.Error().Err(err).Msg("Could not parse form data")
log.Debug().Err(err).Msg("Could not parse form data")
c.Redirect("/request/service?error=Invalid%20Form", iris.StatusSeeOther)
return
}
log.Printf("Nonce: %v, ID: %v", data.PowNonce, data.PowId)
if !s.PowChallenger.PowVerifyProof(data.PowId, data.PowNonce) {
log.Error().Msg("Invalid PoW")
log.Debug().Msg("Invalid PoW")
c.Redirect("/request/service?error=Invalid%20Captcha", iris.StatusSeeOther)
return
}
if data.KYCLevel < 0 || data.KYCLevel >= 4 {
log.Error().Msgf("Invalid KYC Level value: %v", c.FormValue("kyc_level"))
log.Debug().Msgf("Invalid KYC Level value: %v", c.FormValue("kyc_level"))
c.Redirect("/request/service?error=Invalid%20KYC%20Level", iris.StatusSeeOther)
return
}
if len(data.Attributes) < 1 {
log.Error().Msgf("Invalid number of attributes: %v", len(data.Attributes))
log.Debug().Msgf("Invalid number of attributes: %v", len(data.Attributes))
c.Redirect("/request/service?error=You%20must%20select%20at%20least%203%20attributes", iris.StatusSeeOther)
return
}
@ -346,8 +347,8 @@ func (s *Server) handlePostRequestServiceForm(c iris.Context) {
// Save service to database
err := database.Pb.CreateService(service)
if err != nil {
log.Error().Err(err).Msg("Could not save service to database")
c.Redirect("/request/service?error=internal-error", iris.StatusSeeOther)
log.Debug().Err(err).Msg("Could not save service to database")
c.Redirect(fmt.Sprintf("/request/service?error=%s", url.QueryEscape(err.Error())), iris.StatusSeeOther)
return
}

View File

@ -26,11 +26,7 @@ func InitTosScraperDaemon() {
trigerScraping()
}
for {
if config.Conf.Dev {
//duration = 1 * time.Hour
log.Debug().Bool("DevMode", config.Conf.Dev).Msgf("Next scraping in %v", duration)
}
log.Info().Msgf("Next scraping in %v", duration)
// Set the ticker for that duration
ticker := time.NewTicker(duration)
@ -58,7 +54,7 @@ func InitTosScraperDaemon() {
}
func trigerScraping() {
log.Debug().Msg("Starting scraper...")
log.Info().Msg("Starting ToS analysis...")
// Get all the Services from the DB
services, err := database.Pb.GetServices("pending=false && listed=true", "")
if err != nil {