update to v2025

This commit is contained in:
pluja 2024-12-09 16:29:21 +01:00
parent 4fa109caf6
commit 3711136c66
189 changed files with 24157 additions and 4788 deletions

View File

@ -1,16 +0,0 @@
# configurations
.idea
# crawlee and apify storage folders
apify_storage
crawlee_storage
storage
# pocketbase
pocketbase
pb_data
# installed files
node_modules
proofs/

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 100
semi = false
quote_type = single
[*.{json,yaml,yml,toml,jsonc}]
quote_type = double
[*.{html,htm}]
max_line_length = 140

8
.env.example Normal file
View File

@ -0,0 +1,8 @@
DONATION_XMR_ADDRESS="FAKE_VALUE"
DONATION_XMR_ADDRESS="FAKE_VALUE"
WEB_DOMAIN="kycnot.me"
ONION_ADDRESS="http://kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion"
OPENAI_API_KEY="sk-xxxxxxxxxxx"
SECRET_TOKEN="axxxxxxxxxxxxxxxxxxxxxx" # openssl rand -hex 32

1
.gitattributes vendored
View File

@ -1 +0,0 @@
*.css linguist-detectable=false

29
.gitignore vendored
View File

@ -1,12 +1,21 @@
dev.sh
blog.yml
node_modules
*.fiber.gz
style.css
src/ent/*
!src/ent/schema/
!src/ent/generate.go
.env
/articles
TODO.md
pocketbase
pb_data
mathesar*
/database/
/src/.env
*_templ.go*
*_templ*
src/frontend/static/css/style.css
src/frontend/static/images
pb.py
*.py
*.override.yml
BLOG*
local_*
.env
.env.*
!.env.example
docker/tor
docker/i2p
!docker/i2p/tunnels.conf

View File

@ -1,3 +0,0 @@
src/ent/*
!src/ent/schema/
!src/ent/generate.go

10
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
"recommendations": [
"a-h.templ",
"davidanson.vscode-markdownlint",
"bradlc.vscode-tailwindcss",
"wayou.vscode-todo-highlight",
"esbenp.prettier-vscode",
"editorconfig.editorconfig"
]
}

30
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": null,
"[templ]": {
"editor.defaultFormatter": "a-h.templ"
},
"[go]": {
"editor.defaultFormatter": "golang.go"
},
"[markdown]": {
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
},
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml"
},
"tailwindCSS.includeLanguages": {
"templ": "html"
},
"emmet.includeLanguages": {
"templ": "html"
},
"gopls": {
"ui.semanticTokens": true,
"formatting.gofumpt": true
},
"files.exclude": {
"**/node_modules": true
},
"editor.tabSize": 2
}

View File

@ -1,6 +0,0 @@
# 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

@ -1,11 +1,29 @@
# KYCNOT.ME OFFICIAL REPOSITORY
# kycnot.me source code
This is the official repository of the KYCNOT.ME project.
Source code of the [kycnot.me](https://kycnot.me) website (v2025).
## ToS Checker
![banner](/src/frontend/static/assets/banner.webp)
The ToS checker is an automated ai-driven tool that checks the ToS of a website.
## Project structure
## Official Tor Onionsite
- `src`: Project code.
- `frontend`: Generates the website HTML, CSS and JS.
- `static`: Static files.
- `templates`: [Templ](https://templ.guide/) templates.
- `internal`: Internal backend packages:
- `cache`: Cache implementation.
- `database`: Database client.
- `ent`: Database ent-go models.
- `models`: Models.
- `server`: Server/API implementation.
- `utils`: Utilities.
`kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion`
## Tech Stack
- [Go](https://go.dev/)
- [Templ](https://templ.guide/)
- [ent-go](https://entgo.io/)
- [Tailwind CSS](https://tailwindcss.com/)
- [MariaDB](https://mariadb.org/)
- [Docker](https://www.docker.com/)
- [just](https://just.systems/)

View File

@ -1,18 +1,10 @@
name: "kycnotme_v2024"
services:
website:
labels:
caddy: "kycnotme.localhost"
caddy.reverse_proxy: "{{upstreams 4488}}"
pocketbase:
database:
ports:
- 8090:80
labels:
caddy: "pocketbase.localhost"
caddy.reverse_proxy: "{{upstreams 80}}"
caddy.request_body.max_size: 10MB
- 127.0.0.1:3306:3306
volumes:
- ./pocketbase/data:/pb_data
- ./pocketbase/public:/pb_public
- ./local_database/data:/var/lib/mysql:Z
app:
ports:
- 127.0.0.1:1337:1337

View File

@ -1,93 +1,49 @@
name: "kycnotme_v2024"
networks:
caddy:
external: true
volumes:
pb_data: {}
pb_public: {}
pb_migrations: {}
database:
services:
website:
container_name: kycnotme-website
build: ./src
networks:
caddy:
default:
volumes:
- ./src/frontend/templates:/app/frontend/templates
- ./src/frontend/static/proofs:/app/frontend/static/proofs
env_file:
- .env
depends_on:
pocketbase:
condition: service_healthy
labels:
caddy: "${WEB_DOMAIN}"
caddy.reverse_proxy: "{{upstreams 4488}}"
caddy.header.Referrer-Policy: "no-referrer"
caddy.header.Strict-Transport-Security: "max-age=31536000; includeSubdomains; preload;"
caddy.encode: zstd gzip
caddy.header.Onion-Location: ${ONION_ADDRESS}{path}
ofelia.enabled: "true"
ofelia.job-exec.toscheck.schedule: "0 0 1 * *"
ofelia.job-exec.toscheck.command: "kycnotme -scrap"
pocketbase:
image: spectado/pocketbase:latest
container_name: kycnotme-pocketbase
restart: always
volumes:
- pb_data:/pb_data
- pb_public:/pb_public
networks:
caddy: {}
default:
aliases:
- "database"
labels:
caddy: "${PB_DOMAIN}"
caddy.reverse_proxy: "{{upstreams 80}}"
caddy.request_body.max_size: 250MB
caddy.header.X-Robots-Tag: "noindex"
caddy.header.Strict-Transport-Security: "max-age=31536000; includeSubdomains; preload;"
healthcheck: #optional (recommended) since v0.10.0
test: wget --no-verbose --tries=1 --spider http://localhost:80/api/health || exit 1
interval: 5s
timeout: 5s
retries: 5
tor:
image: goldy/tor-hidden-service:latest
profiles: ["tor"]
container_name: kycnotme-tor
restart: always
networks:
caddy: {}
default: {}
links:
- website
volumes:
- ./docker/tor/hidden_service:/var/lib/tor/hidden_service
database:
image: mariadb:11
restart: unless-stopped
container_name: kycnot-database
environment:
SERVICE1_TOR_SERVICE_HOSTS: 80:website:4488
SERVICE1_TOR_SERVICE_VERSION: '3'
i2pd:
build:
context: ./docker/i2p/
profiles: ["i2p"]
container_name: kycnotme-i2p
restart: always
networks:
caddy: {}
default: {}
#ports:
# - ":14447" # socks
MARIADB_USER: ${MARIADB_USER:-kycnot}
MARIADB_PASSWORD: ${MARIADB_PASSWORD:-kycnot}
MARIADB_DATABASE: ${MARIADB_DATABASE:-kycnot}
MARIADB_RANDOM_ROOT_PASSWORD: 1
volumes:
- ./docker/i2p/data:/root/.i2pd
labels:
caddy: ${I2P_ADDRESS}
caddy.reverse_proxy: "website:4488"
- database:/var/lib/mysql:Z
- ./docker/database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 3
app:
build:
context: ./src
restart: unless-stopped
container_name: kycnot-app
depends_on:
database:
condition: service_healthy
environment:
DATABASE_URI: "${MARIADB_USER:-kycnot}:${MARIADB_PASSWORD:-kycnot}@tcp(database:3306)/${MARIADB_DATABASE:-kycnot}?parseTime=True"
SECRET_TOKEN: "${SECRET_TOKEN}"
OPENAI_API_KEY: "${OPENAI_API_KEY}"
ROOT_DIR: "/app"
env_file: .env
volumes:
- ./src/frontend/static/images:/app/frontend/static/images
healthcheck:
test:
[
"CMD-SHELL",
"curl -k -s http://localhost:1337 | grep -i '<title>KYCNOT.ME</title>' || exit 1",
]
start_period: 10s
interval: 10s
timeout: 5s
retries: 3

4
docker/database/init.sql Normal file
View File

@ -0,0 +1,4 @@
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED VIA unix_socket WITH GRANT OPTION;
CREATE DATABASE IF NOT EXISTS `nocodb`;
GRANT ALL ON `nocodb`.* TO `kycnot`;

View File

@ -1,10 +0,0 @@
FROM alpine:latest
RUN apk --update --no-cache add i2pd && rm -rf /var/cache/apk/*
COPY i2pd.conf /i2p/i2pd.conf
COPY tunnels.conf /i2p/tunnels.conf
#EXPOSE 14447
ENTRYPOINT ["i2pd", "--conf=/i2p/i2pd.conf"]

View File

@ -1,12 +0,0 @@
ipv4 = true
ipv6 = false
bandwidth = P
daemon = false
tunconf = /i2p/tunnels.conf
log = file
logfile = /i2p/log
logclftime = true

14
docker/i2p/tunnels.conf Normal file → Executable file
View File

@ -1,11 +1,5 @@
[KYCNotMeServer]
[kycnotme]
type = http
host = caddy
port = 80
keys = KYCNotMe.dat
#[SOCKS]
#type = socks
#address = localhost
#port = 14447
#keys = SOCKS.dat
host = app
port = 1337
keys = kycnotme.keys

View File

@ -1,19 +0,0 @@
default: deploy
dev +command:
docker compose -f docker-compose.yml -f docker-compose.dev.yml {{command}}
deploy build="":
#!/bin/bash
git pull
if [ -z "{{ build }}" ]; then
build_flag=""
else
build_flag="--build"
fi
docker compose up --pull=always -d $build_flag
if [ -z "{{ build }}" ]; then docker compose restart website; fi
docker system prune -f

View File

@ -1,28 +0,0 @@
# What makes a good non-kyc service?
1. Accepts at least one anonymous payment method:
- Bitcoin
- Cash
- Monero (even better)
2. KYC Level
0. BEST
1. ACCEPTABLE
2. NOT GOOD
3. BAD
3. Verified
1. Better if it is
4. TosReviews
1. As few as possible warnings.
5. Onion available
1. Good to have
6. Attributes
- GOOD: bonus
- INFO: nothing
- WARNING: penalty
- BAD: penalty

View File

@ -1 +1,5 @@
.env
frontend/static/images
images
node_modules
*_templ.go
*_templ.txt

4
src/.env.example Normal file
View File

@ -0,0 +1,4 @@
DATABASE_URI="kycnot:kycnot@tcp(localhost:3306)/kycnot?parseTime=True"
SECRET_TOKEN="FAKE_VALUE" # openssl rand -hex 32
ROOT_DIR="FAKE_VALUE" # Path to the root of the project
OPENAI_API_KEY="FAKE_VALUE"

View File

@ -1,33 +1,34 @@
FROM devopsworks/golang-upx:latest AS builder
# Builder stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o kycnotme . && \
upx kycnotme
RUN chmod +x kycnotme
# Tailwind CSS compiler
FROM node:latest AS tailwind
RUN go install github.com/a-h/templ/cmd/templ@latest
RUN templ generate -path ./frontend/templates/
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o kycnot .
RUN chmod a+rx kycnot
RUN mkdir -p /app/frontend/static/images
# Node stage
FROM node:alpine AS node
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
COPY ./package.json .
COPY ./package-lock.json .
COPY ./tailwind.config.js .
COPY ./frontend/ /app/
COPY ./frontend/tailwind.config.js .
COPY ./frontend/package.json ./frontend/package-lock.json ./
COPY ./frontend/static ./static
COPY ./frontend/templates ./templates
RUN npm ci && \
npx tailwindcss -i ./static/css/input.css -o ./static/css/style.css --minify
RUN npm i && \
npx tailwindcss -i /app/static/css/input.css -o /app/style.css --minify
FROM alpine:latest AS certs
RUN apk --update add ca-certificates
FROM scratch
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Final stage
FROM alpine:3 AS final
WORKDIR /app
COPY ./frontend /app/frontend
COPY --from=tailwind /app/style.css /app/frontend/static/css/style.css
COPY --from=builder /app/kycnotme /bin/usr/kycnotme
ENV PATH="/bin/usr:${PATH}"
COPY --from=builder /app/kycnot /app/kycnot
COPY ./frontend/static /app/frontend/static
COPY --from=node /app/static/css/style.css /app/frontend/static/css/style.css
RUN apk add --no-cache curl
EXPOSE 4488
ENTRYPOINT ["kycnotme"]
ENV TZ="Europe/Warsaw"
ENV PATH="/app:$PATH"
EXPOSE 1337
CMD ["/app/kycnot", "serve"]

View File

@ -1,19 +0,0 @@
package config
type Config struct {
Dev bool
Debug bool
Cache bool
Scraper bool
ListenAddr string
Donations Donations
}
type Donations struct {
Xmr string
Btc string
BtcSilent string
Lnn string
}
var Conf Config

View File

@ -1,7 +0,0 @@
package database
const (
AttributeOpenSource = "l0p2hlenm8cm7jp"
AttributeP2PNetwork = "lpkke4dygq0yoly"
AttributeP2PTrading = "5bcqjb2xzbel9g9"
)

View File

@ -1,77 +0,0 @@
package database
type Service struct {
Category string `json:"category"`
CollectionID string `json:"collectionId"`
CollectionName string `json:"collectionName"`
Comments []string `json:"comments,omitempty"`
Created string `json:"created"`
Description string `json:"description"`
Expand map[string][]Attribute `json:"expand,omitempty"`
Attributes []string `json:"attributes,omitempty"`
ID string `json:"id"`
KycLevel int `json:"kyc_level"`
Listed bool `json:"listed"`
LogoURL string `json:"logo_url"`
Name string `json:"name"`
OnionUrls []string `json:"onion_urls"`
Pending bool `json:"pending"`
Score int `json:"score"`
Tags []string `json:"tags"`
TosReviews []TosReview `json:"tos_reviews,omitempty"`
LastTosReview string `json:"last_tos_review"`
TosUrls []string `json:"tos_urls"`
Type string `json:"type"`
Referral string `json:"referral"`
Updated string `json:"updated"`
Urls []string `json:"urls"`
Verified bool `json:"verified"`
Xmr bool `json:"xmr"`
Btc bool `json:"btc"`
Fiat bool `json:"fiat"`
Cash bool `json:"cash"`
Lightning bool `json:"lightning"`
Notes []string `json:"notes,omitempty"`
SupportMethods []Support `json:"support"`
}
type Support struct {
Platform string `json:"platform"`
Link string `json:"Link"`
}
type TosReview struct {
Title string `json:"title"`
Details string `json:"details"`
Section string `json:"section"`
Warning bool `json:"warning"`
Reference string `json:"reference"`
}
type Attribute struct {
CollectionID string `json:"collectionId"`
CollectionName string `json:"collectionName"`
Created string `json:"created"`
Description string `json:"description"`
ID string `json:"id"`
Rating string `json:"rating"`
Title string `json:"title"`
Updated string `json:"updated"`
}
type Announcement struct {
Title string
Body string
Link string
Warning bool
Active bool
}
type Change struct {
Created string `json:"created"`
Summary string `json:"summary"`
ID string `json:"id"`
Title string `json:"title"`
Updated string `json:"updated"`
Expand map[string]Service `json:"expand,omitempty"`
}

View File

@ -1,220 +0,0 @@
package database
import (
"errors"
"fmt"
"os"
"sort"
"strings"
"github.com/pluja/pocketbase"
"github.com/rs/zerolog/log"
)
type PbClient struct {
Client *pocketbase.Client
}
var Pb *PbClient
// InitPocketbase initializes the Pocketbase client
func InitPocketbase() error {
// Initialize Pb if not already done
if Pb == nil {
Pb = &PbClient{}
}
// Create a new pocketbase client
Pb.Client = pocketbase.NewClient(os.Getenv("PB_URL"),
pocketbase.WithAdminEmailPassword(os.Getenv("PB_ADMIN_EMAIL"), os.Getenv("PB_ADMIN_PASSWORD")))
return nil
}
// GetServices retrieves services from the pocketbase client
func (p *PbClient) GetServices(filters, sort string) ([]Service, error) {
collection := pocketbase.CollectionSet[Service](p.Client, "services")
params := pocketbase.ParamsList{
Page: 1,
Size: 200,
Expand: "attributes",
Fields: "*,expand.attributes.*",
}
if filters != "" {
params.Filters = filters
}
if sort != "" {
params.Sort = sort
}
response, err := collection.List(params)
if err != nil {
return nil, errors.New("database error")
}
return response.Items, nil
}
func (p *PbClient) GetChanges() ([]Change, error) {
collection := pocketbase.CollectionSet[Change](p.Client, "changes")
params := pocketbase.ParamsList{
Page: 1,
Size: 200,
Expand: "service",
Fields: "*,expand.service.*",
Sort: "-created",
}
response, err := collection.List(params)
if err != nil {
log.Error().Err(err).Msg("err")
return nil, errors.New("database error")
}
return response.Items, nil
}
func (p *PbClient) GetServiceByNameOrUrl(id string) (*Service, error) {
collection := pocketbase.CollectionSet[Service](p.Client, "services")
params := pocketbase.ParamsList{
Page: 1,
Size: 1,
Fields: "*,expand.attributes.*",
Expand: "attributes",
Filters: fmt.Sprintf("((name='%v') || (urls~'%v'))", id, id),
}
response, err := collection.List(params)
if err != nil {
return nil, errors.New("database error")
}
if len(response.Items) > 0 {
service := response.Items[0]
// Sort attributes by rating
sort.SliceStable(service.Expand["attributes"], func(i, j int) bool {
ratingOrder := map[string]int{
"bad": 4,
"warn": 3,
"good": 2,
"info": 1,
}
return ratingOrder[service.Expand["attributes"][i].Rating] > ratingOrder[service.Expand["attributes"][j].Rating]
})
return &service, nil
} else {
return nil, errors.New("service not found")
}
}
func (p *PbClient) GetServiceById(id string) (*Service, error) {
collection := pocketbase.CollectionSet[Service](p.Client, "services")
params := pocketbase.ParamsList{
Fields: "*,expand.attributes.*",
Expand: "attributes",
}
response, err := collection.OneWithParams(id, params)
if err != nil {
return nil, errors.New("service not found")
}
return &response, nil
}
func (p *PbClient) CreateService(service Service) error {
collection := pocketbase.CollectionSet[Service](p.Client, "services")
_, err := collection.Create(service)
if err != nil {
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
}
func (p *PbClient) UpdateService(id string, service Service) error {
collection := pocketbase.CollectionSet[Service](p.Client, "services")
err := collection.Update(id, service)
if err != nil {
return fmt.Errorf("could not update: %v", err)
}
return nil
}
func (p *PbClient) GetAttribute(id string) (*Attribute, error) {
collection := pocketbase.CollectionSet[Attribute](p.Client, "attributes")
response, err := collection.One(id)
if err != nil {
return nil, fmt.Errorf("could not get service: %v", err)
}
return &response, nil
}
func (p *PbClient) GetAttributes(filters, sort string) ([]Attribute, error) {
collection := pocketbase.CollectionSet[Attribute](p.Client, "attributes")
params := pocketbase.ParamsList{
Page: 1,
Size: 250,
}
if filters != "" {
params.Filters = filters
}
if sort != "" {
params.Sort = sort
}
response, err := collection.List(params)
if err != nil {
return nil, fmt.Errorf("could not list: %s", err)
}
return response.Items, nil
}
func (p *PbClient) CreateAttribute(attribute Attribute) error {
collection := pocketbase.CollectionSet[Attribute](p.Client, "attributes")
_, err := collection.Create(attribute)
if err != nil {
return fmt.Errorf("could not create attribute: %v", err)
}
return nil
}
func (p *PbClient) GetAnnouncement() *Announcement {
collection := pocketbase.CollectionSet[Announcement](p.Client, "announcements")
params := pocketbase.ParamsList{
Page: 1,
Size: 1,
Filters: "active=true",
}
response, err := collection.List(params)
if err != nil {
return nil
}
if len(response.Items) < 1 {
return nil
}
return &response.Items[0]
}

View File

@ -1,22 +0,0 @@
# APP
ROOT_DIR="./"
LISTEN_ADDR="127.0.0.1:4488"
POW_DIFFICULTY=4
POW_INCREASE_EVERY_CHALLENGES=20
# DONATIONS
XMR_ADDRESS="82xxxx28_monero_address_ZU2xxxx"
BTC_ADDRESS="bc1q_bitcoin_address"
LNN_ADDRESS="https://getalby.com/p/username"
# OPEN_AI
OPENAI_API_KEY="sk-xxxxx"
ANTHROPIC_API_KEY="sk-ant-xxxx"
# CRAWLER
CRAWLER_API_URL="http://localhost:3011"
# POCKETBASE
PB_URL="http://localhost:8022"
PB_ADMIN_EMAIL="user@pocketbase.io"
PB_ADMIN_PASSWORD="password"

1
src/frontend/.nvmrc Normal file
View File

@ -0,0 +1 @@
22

File diff suppressed because it is too large Load Diff

16
src/frontend/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"devDependencies": {
"@iconify-json/jam": "^1.2.1",
"@iconify-json/tabler": "^1.2.7",
"@iconify/tailwind": "^1.1.3",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"concurrently": "^8.2.2",
"tailwindcss": "^3.4.14"
},
"scripts": {
"tailwind:dev": "tailwindcss -i static/css/input.css -o static/css/style.css --watch",
"tailwind:build": "tailwindcss -i static/css/input.css -o static/css/style.css --minify",
"build": "npm run tailwind:build"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 KiB

View File

@ -0,0 +1,24 @@
<svg width="2048" height="1080" viewBox="0 0 2048 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="2048" height="1080" fill="#111" />
<g filter="url(#blur)">
<circle cx="2169.5" cy="544.5" r="634.5" stroke="#415023" stroke-width="200" />
<circle cx="-263.5" cy="420.5" r="587.5" stroke="#415023" stroke-width="200" />
</g>
<rect width="2048" height="1080" filter="url(#noise)" />
<defs>
<filter id="blur" x="-1325.3" y="-641.3" width="4603.6" height="2294.6" filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="187.15" result="effect1_foregroundBlur_1_4" />
</filter>
<filter id="noise">
<feTurbulence type="fractalNoise" baseFrequency="0.3" numOctaves="2" result="noise" />
<feColorMatrix type="matrix" values="0 0 0 0 0
0 0.4 0 0 0
0 0 0 0 0
0 0 0 0.09 0" result="greenNoise" />
<feBlend mode="multiply" in="SourceGraphic" in2="greenNoise" />
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_ZCsl{animation:spinner_qV4G 1.2s cubic-bezier(0.52,.6,.25,.99) infinite}.spinner_gaIW{animation-delay:.6s}@keyframes spinner_qV4G{0%{r:0;opacity:1}100%{r:11px;opacity:0}}</style><circle class="spinner_ZCsl" cx="12" cy="12" r="0"/><circle class="spinner_ZCsl spinner_gaIW" cx="12" cy="12" r="0"/></svg>

Before

Width:  |  Height:  |  Size: 400 B

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,15 @@
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'inter';
src: url('../fonts/inter.ttf') format('truetype');
@layer base {
}
@font-face {
font-family: 'samo';
src: url('../fonts/samo.ttf') format('truetype');
}
font-family: 'inter';
src: url('../fonts/inter.ttf') format('truetype');
}
@font-face {
font-family: 'samo';
src: url('../fonts/samo.ttf') format('truetype');
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,69 +1,103 @@
async function computeProof(challenge, difficulty) {
let nonce = 0;
const leadingZeros = '0'.repeat(difficulty);
const POW = {
challenge: null,
difficulty: null,
lastNonce: null,
async fetchChallenge() {
const response = await fetch('/api/pow')
if (!response.ok) throw new Error('Failed to fetch challenge')
const data = await response.json()
if (data.error) throw new Error(data.error)
this.challenge = data.challenge
this.difficulty = data.difficulty
},
async computeProof(challenge, difficulty) {
const leadingZeros = '0'.repeat(difficulty)
const encoder = new TextEncoder()
let nonce = 0
while (true) {
const candidate = `${challenge}:${nonce}`;
const hash = new TextEncoder().encode(candidate);
const hashArrayBuffer = await crypto.subtle.digest('SHA-256', hash);
const hashHex = Array.from(new Uint8Array(hashArrayBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
if (hashHex.startsWith(leadingZeros)) {
return nonce;
}
nonce++;
await new Promise(resolve => setTimeout(resolve, 0)); // Yield control to avoid freezing the UI
const candidate = `${challenge}:${nonce}`
const hash = encoder.encode(candidate)
const hashArrayBuffer = await crypto.subtle.digest('SHA-256', hash)
const hashHex = Array.from(new Uint8Array(hashArrayBuffer))
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
if (hashHex.startsWith(leadingZeros)) {
console.log(hashHex)
return nonce.toString()
}
nonce++
if (nonce % 1000 === 0) {
await new Promise((resolve) => setTimeout(resolve, 0))
}
}
},
async verifyProof(challenge, nonce) {
const url = `/api/pow/verify/${encodeURIComponent(challenge)}/${encodeURIComponent(nonce)}`
const response = await fetch(url, { method: 'GET' })
if (!response.ok) throw new Error('Network response was not ok')
return response.json()
},
updateUI(element, state) {
const spinner = document.querySelector('#pow-spinner')
const text = document.querySelector('#pow-text')
const submit = document.querySelector('#submit-btn')
const nonceInput = document.querySelector('#pow-nonce')
const idInput = document.querySelector('#pow-id')
element.className = 'pow-button'
spinner.classList.toggle('hidden', state !== 'computing')
element.classList.add(
state === 'computing'
? 'bg-gray-700'
: state === 'verified'
? 'bg-lime-700'
: state === 'failed'
? 'bg-red-700'
: 'bg-blue-900'
)
if (state === 'verified') {
element.classList.add('disabled')
nonceInput.value = this.lastNonce
idInput.value = this.challenge
}
element.classList.toggle('cursor-pointer', state !== 'verified')
text.textContent =
state === 'computing'
? 'Computing...'
: state === 'verified'
? '✅ Verified!'
: state === 'failed'
? '❌ Failed!'
: 'An error occurred!'
submit.classList.toggle('hidden', state !== 'verified')
},
async startPow() {
const element = document.querySelector('#start-pow')
if (!element) throw new Error('Start POW element not found')
this.updateUI(element, 'computing')
try {
await this.fetchChallenge()
this.lastNonce = await this.computeProof(this.challenge, this.difficulty)
const response = await this.verifyProof(this.challenge, this.lastNonce)
console.log(response)
const valid = response.status === 200 && response.valid === true
this.updateUI(element, valid ? 'verified' : 'failed')
} catch (error) {
console.error('Error:', error)
this.updateUI(element, 'error')
}
},
init() {
const element = document.querySelector('#start-pow')
if (element) {
element.addEventListener('click', () => this.startPow())
}
},
}
function startPow() {
const element = document.querySelector('#start-pow');
element.addEventListener('click', async () => {
const difficulty = parseInt(element.getAttribute('data-pow-d'), 10);
const challenge = element.getAttribute('data-pow-c');
const id = element.getAttribute('data-pow');
const spinner = document.querySelector('#pow-spinner');
spinner.classList.remove('hidden');
element.classList.remove('bg-blue-900');
element.classList.add('bg-gray-700');
const text = document.querySelector('#pow-text');
text.innerText = 'Computing...';
try {
const nonce = await computeProof(challenge, difficulty);
spinner.classList.add('hidden');
const url = `/api/g/pow/verify/${id}/${nonce}`;
const response = await fetch(url);
const data = await response.json();
if (data.valid) {
element.classList.remove('bg-gray-700');
element.classList.remove('cursor-pointer');
element.classList.add('bg-lime-700');
element.classList.add('disabled');
text.innerText = '✅ Verified!';
// Show submit button
const submit = document.querySelector('#submit-btn');
submit.classList.remove('hidden');
// Set pow-nonce form input value to nonce
const nonceInput = document.querySelector('#pow-nonce');
nonceInput.value = nonce;
} else {
element.classList.remove('bg-gray-700');
element.classList.add('bg-red-700');
text.innerText = '❌ Failed!';
}
} catch (error) {
console.error('Error:', error);
spinner.classList.add('hidden');
text.innerText = 'An error occurred!';
}
});
}
document.addEventListener('DOMContentLoaded', () => POW.init())

View File

@ -16,26 +16,26 @@ powChallenger.Difficulty = 4
// Form page handler
func (s *Server) handleGetFormPage(c *fiber.Ctx) error {
// Create a new PoW challenge with 16 char challenge
challenge, id, difficulty, err := s.PowChallenger.PowGenerateChallenge(16)
if err != nil {
return err
}
challenge, id, difficulty, err := s.PowChallenger.PowGenerateChallenge(16)
if err != nil {
return err
}
reverse := true
return c.Render("my_form", fiber.Map{
"Title": "Some Form",
"Pow": fiber.Map{
"Challenge": challenge,
"Difficulty": difficulty,
"Id": id,
},
})
reverse := true
return c.Render("my_form", fiber.Map{
"Title": "Some Form",
"Pow": fiber.Map{
"Challenge": challenge,
"Difficulty": difficulty,
"Id": id,
},
})
}
```
### HTML
```
```html
<form action="" method="POST">
<noscript>
<p class="my-2 font-bold text-yellow-500 uppercase">
@ -76,4 +76,4 @@ func (s *Server) FormHandler(w http.ResponseWriter, r *http.Request) {
// PoW is valid
}
```
```

View File

@ -1,13 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEZczexBYJKwYBBAHaRw8BAQdA+X+cNkOv5SmauKLLw+xcEkcDQjVubZ/4Xj3d
5DNvgru0F3BsdWphIDxhZG1pbkBreWNub3QubWU+iJMEExYKADsWIQQEPOrx9H8d
Txv0tFhweQHG2o41CgUCZczexAIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX
gAAKCRBweQHG2o41CjwVAQDpcxgeXK+2KiVzMkmLCXiAYU0hRhQmA4xwXDkqYxn7
dwD+KzHpcnTwnrK5ZLn3+ti9DVeURTv1lFf9TVZTKgovOAy4OARlzN7EEgorBgEE
AZdVAQUBAQdAvrCLlUmBr/RswvfC4hOGa5Som7CGQp0WzpkyETZmRh0DAQgHiHgE
GBYKACAWIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZczexAIbDAAKCRBweQHG2o41
ClZ1AQCwZ1XrRYLuM9d5mLXzbZVRjb5INoVXFrZbx9ZkAxKgFAD/dmJI/bUxPcVZ
f6cwP2HYnxD/i2C6ltaLK0SRrxI2XAM=
=Tnvi
-----END PGP PUBLIC KEY BLOCK-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,35 +0,0 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
BITCOIN BLOCK HEIGHT: 865573
MONERO BLOCK HEIGHT: 3258771
CAUTION: Always begin by testing the service with minimal amounts. For larger transactions, divide them into smaller batches rather than processing the entire amount at once. This approach helps mitigate potential risks and verify consistent service performance.
exch.cx service has been successfully tested.
- - I create an exchange: 0.00097516 BTC->LTC
- https://kycnot.me/static/proofs/exch/create_exchange.webp
- https://kycnot.me/static/proofs/exch/exchange_created.webp
- - I send the funds to the shown address
- INPUT TXID: a4abb6b785c8f4e356971c204aad06fe9c6da1be7ed0bbf33ff370ba78cd9d23
- - The funds are detected quickly by exch.
- https://kycnot.me/static/proofs/exch/exchange_confirm_input.webp
- - After some time, the input is confirmed (1 confirmation)
- - The exchange sends the funds to the receive address:
- https://kycnot.me/static/proofs/exch/exchange_confirm_output.webp
- OUTPUT TXID: 5964e047d85275f85a7a9d2e527f09235ce6ba4205222e4ccab8e8c86a5914c0
- - Exchange is completed successfully:
- https://kycnot.me/static/proofs/exch/exchange_sent.webp
NOTE: The output amount was higher than the initially calculated amount due to price fluctuation during the exchange.
DISCLAIMER: This proof only demonstrates that the service was operational during my testing. It does not guarantee future reliability or legitimacy. The service may become unreliable or fraudulent over time. This verification is based solely on current observations and should not be considered a long-term endorsement. Tests may be performed on a regular basis.
-----BEGIN PGP SIGNATURE-----
iHUEARYKAB0WIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZwz0yQAKCRBweQHG2o41
CpwsAP4jFfRdeDOQ6DwUDcx1fmjoQzr0Cs134n17zWc9XPNAOAEAmM+S/4C5vt4a
rapX21onZr00pQFmAhpQUOGvknDDIww=
=GTzI
-----END PGP SIGNATURE-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1,23 +0,0 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
BITCOIN BLOCK HEIGHT: 865188
MONERO BLOCK HEIGHT: 3256820
ppq.ai service has been successfully tested.
* Add funds with Monero:
https://kycnot.me/static/proofs/ppq/before_payment.png
* Sent funds from wallet. Received almost instantly.
https://kycnot.me/static/proofs/ppq/payment_received.png
* Test chatting and generating images:
https://kycnot.me/static/proofs/ppq/works.png
DISCLAIMER: This proof only demonstrates that the service was operational during my testing. It does not guarantee future reliability or legitimacy. The service may become unreliable or fraudulent over time. This verification is based solely on current observations and should not be considered a long-term endorsement. Tests may be performed on a regular basis.
-----BEGIN PGP SIGNATURE-----
iHUEARYKAB0WIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZwz01QAKCRBweQHG2o41
CjNTAQCDDSG/BEesIy+3y3esIx0OR4M+mj2Av6Sp/oDnY6t1wQD9Fy6yWFJoE4j6
I5hVHpTvQoePgWPH4TL3s7B7tZZJ0AM=
=BF3i
-----END PGP SIGNATURE-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,19 +0,0 @@
BITCOIN BLOCK HEIGHT: 865186
MONERO BLOCK HEIGHT: 3256808
simsup.net service has been successfully tested.
* Bought a Data-Only Esim on simsup.net:
https://kycnot.me/static/proofs/simsup/order.png
* I received an order confirmation mail, however the esim was not provided due to some issue:
https://kycnot.me/static/proofs/simsup/mail.png
* I contacted with support (first time I spoke to them):
https://kycnot.me/static/proofs/simsup/telegram.png
* They were quickly responsive and solved the issue.
* Sim data was provided:
https://kycnot.me/static/proofs/simsup/mail_2.png
https://kycnot.me/static/proofs/simsup/esim.png
* Configured the esim on my device
* Data was available and it worked fine
DISCLAIMER: This proof only demonstrates that the service was operational during my testing. It does not guarantee future reliability or legitimacy. The service may become unreliable or fraudulent over time. This verification is based solely on current observations and should not be considered a long-term endorsement. Tests may be performed on a regular basis.

View File

@ -1,29 +0,0 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
BITCOIN BLOCK HEIGHT: 865186
MONERO BLOCK HEIGHT: 3256808
simsup.net service has been successfully tested.
* Bought a Data-Only Esim on simsup.net:
https://kycnot.me/static/proofs/simsup/order.png
* I received an order confirmation mail, however the esim was not provided due to some issue:
https://kycnot.me/static/proofs/simsup/mail.png
* I contacted with support (first time I spoke to them):
https://kycnot.me/static/proofs/simsup/telegram.png
* They were quickly responsive and solved the issue.
* Sim data was provided:
https://kycnot.me/static/proofs/simsup/mail_2.png
https://kycnot.me/static/proofs/simsup/esim.png
* Configured the esim on my device
* Data was available and it worked fine
DISCLAIMER: This proof only demonstrates that the service was operational during my testing. It does not guarantee future reliability or legitimacy. The service may become unreliable or fraudulent over time. This verification is based solely on current observations and should not be considered a long-term endorsement. Tests may be performed on a regular basis.
-----BEGIN PGP SIGNATURE-----
iHUEARYKAB0WIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZwz02wAKCRBweQHG2o41
Cp+kAQCd/Cyl2kUEby7V78Vwtd3PI16TXROFDbC9wG3DwYJWOQD/R6fuCf0kMnTX
yYK1/4p7MZxN9wdA2m2lgUl56x3wWAw=
=Y/JU
-----END PGP SIGNATURE-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,29 @@
/** @type {import('tailwindcss').Config} */
const { addDynamicIconSelectors } = require('@iconify/tailwind');
module.exports = {
content: ['./templates/**/*.{html,templ,js}', './static/js/*.js'],
theme: {
extend: {
fontFamily: {
samo: ['samo', 'monospace'],
inter: ['inter', 'sans-serif'],
},
colors: {
background: 'var(--kycnot-background)',
primary: 'var(--kycnot-primary)',
'primary-emphasis': 'var(--kycnot-primary-emphasis)',
'primary-dark': 'var(--kycnot-primary-dark)',
'primary-light': 'var(--kycnot-primary-light)',
'kycnot-text': 'var(--kycnot-text)',
'kycnot-text-fade': 'var(--kycnot-text-fade)',
'kycnot-text-select': 'var(--kycnot-text-select)',
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
addDynamicIconSelectors(),
],
};

View File

@ -0,0 +1,7 @@
<!-- This file is to make TailwindCSS and Iconify load this extra icons, which are not directly used in any class-->
<i class="icon-[tabler--hash]"></i>
<i class="icon-[tabler--mail]"></i>
<i class="icon-[tabler--link]"></i>
<i class="icon-[tabler--brand-mastodon]"></i>
<i class="icon-[tabler--brand-telegram]"></i>

View File

@ -1,652 +0,0 @@
<style>
article p {
margin-bottom: 1rem;
font-size: 1.05rem;
}
p {
margin-top: 1rem;
}
article a {
color: rgb(163 230 53);
text-decoration: underline;
text-underline-offset: 0.2rem;
}
article div {
padding-top: .5em;
padding-bottom: .5em;
}
h1,
h2,
h3,
h4 {
margin-top: 1rem;
margin-bottom: 0.3rem;
font-weight: bold;
font-family: samo;
color: rgb(163 230 53);
}
h1 {
font-size: 3rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
margin-top: 1.1em;
font-size: 1rem;
}
h3::before {
content: "~ ";
}
article blockquote {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
padding-left: 1rem;
border-left: 0.3rem solid rgb(163 230 53);
color: rgb(142, 146, 150);
}
code {
background: #2b282872;
border: 1px solid #494242;
border-radius: 0.2rem;
font-weight: bold;
border-left: 3px solid #89f333;
color: #d3cccc;
font-family: samo;
max-width: 100%;
overflow: scorll;
padding: 1em 1em;
display: block;
word-wrap: break-word;
max-width: 90vw;
margin-top: .2em;
}
hr {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
</style>
<div id="main" class="flex flex-col items-center justify-center">
<div class="w-full max-w-4xl p-4">
<div class="space-x-1 text-xs font-bold text-center lowercase text-lime-500 md:text-sm font-samo">
<a href="#why">Why</a>
·
<a href="#what">What</a>
·
<a href="#btcxmr">Only Bitcoin and Monero?</a>
·
<a href="#listings">About listings</a>
·
<a href="#tos">ToS reviews</a>
·
<a href="#support">Support</a>
·
<a href="#contact">contact</a>
·
<a href="#comments">comments</a>
·
<a href="#transparency">transparency</a>
·
<a href="#privacy">privacy</a>
.
<a href="#api">api</a>
·
<a href="#disclaimer">disclaimer</a>
</div>
</div>
<article class="max-w-5xl [&>div]:px-4 [&>div]:py-1 font-inter">
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="why" class="font-bold text-lime-400">why kycnot.me?</h2>
<p>
Cryptocurrencies were created to revolutionize the way we pay for goods
and services, aiming to eliminate reliance on centralized entities such
as banks and governments that control our economy.
</p>
<p>
Exchanges that enforce KYC (Know Your Customer) operate similarly to traditional banks. Users are
required to provide identification, such as a photo of their ID, to use
these exchanges. Moreover, most of these exchanges are centralized,
meaning that users do not own their keys. In short, this implies that
the cryptocurrencies belong to the exchange and not the user. These
requirements go against the decentralized and self-sovereign nature of cryptocurrencies.
</p>
<p>
With KYCNOT.ME, I hope to provide people with trustworthy alternatives for buying, exchanging, trading, and
using cryptocurrencies
without having to disclose their identity, thus preserving the right to privacy. I want to help preserve the
original
ethos of cryptocurrencies.
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="what" class="font-bold text-lime-400">what's KYC?</h2>
<div class="space-y-2">
<p>
<strong>KYC</strong> stands for <strong>"Know Your Customer"</strong>,
a process designed to protect financial institutions against fraud,
corruption, money laundering and terrorist financing. Or at least this
is what they want you to believe.
</p>
<p>
The truth is that KYC is a direct
attack on our privacy and puts us in disadvantage against the
governments. True criminals don't care about KYC policies. True
criminals know perfectly how to avoid such policies. In fact, they
normally use the FIAT system and don't even need to use
cryptocurrencies. Banks are the biggest money launders, the
<a rel="noreferrer"
href="https://www.reuters.com/business/hsbc-fined-85-mln-anti-money-laundering-failings-2021-12-17/">HSBC
scandal</a>,
<a rel="noreferrer"
href="https://www.reuters.com/article/us-nordea-bnk-moneylaundering-idUSKCN1QL11S">Nordea</a>
or
<a rel="noreferrer"
href="https://www.reuters.com/article/us-europe-moneylaundering-swedbank/swedbank-hit-with-record-386-million-fine-over-baltic-money-laundering-breaches-idUSKBN2163LU">Swedbank</a>
are just some examples.
</p>
<p>
Chainalysis found that only 0.34% of the transaction volume with cryptocurrencies in 2023 was attributable to
criminal
activity. Bitcoin's share of this is significantly lower with 25%. Illicit transactions with Euros accounted
for 1% of
the EU's GDP or €110 billion in 2010. <a
href="https://www.chainalysis.com/blog/2024-crypto-crime-report-introduction/">[1]</a> <a
href="https://transparency.eu/priority/financial-flows-crime/">[2]</a>
</p>
<p>
KYC only affects small individuals like you and me. It is an annoying
procedure that forces us to hand our personal information to a
third party in order to buy, use or unlock our funds. We should start
boycotting companies that enforce such practices. We should start using
cryptocurrencies as they were intended to be used: without barriers.
</p>
</div>
<h3 id="other-acronyms-of-interest">other acronyms of interest</h3>
<div class="[&>span]:block space-y-2">
<span><strong>SoF</strong>: Source of Funds</span>
<span><strong>KYC</strong>: Know Your Customer</span>
<span><strong>ToS</strong>: Terms of Service</span>
</div>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="btcxmr">why only Bitcoin and Monero?</h2>
<p>
<strong>Bitcoin</strong>: it's the initial spark of the decentralized money.
A solid project with a strong community. It is the most well-known and widespread cryptocurrency.
</p>
<p>
<strong>Monero</strong>: if digital cash was to exist, it should be like
Monero. Fungible, private by design, fast and pretty low fees. Also, one
of the oldest cryptocurrencies around with a very active community.
</p>
<p>
No other currencies will be added. Most sites listed here also accept
other cryptocurrencies, such as Ethereum or Litecoin.
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="listings">listings</h2>
<h3 id="request">request</h3>
<p>
You can request a new listing by visiting the
<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>
<p>
The listing process is usually slow. I first need to review the service,
read online reviews, gather evidence and history of good reviews /
opinions, test it myself (i can't always do this), read the terms of
service, privacy policy, etc...
</p>
<p>
I do this on my own free time, so I'm slow at it. If you feel like a
<a href="/pending">pending service</a> deserves to be listed, you can
<a href="#contact">contact me</a> and I will try to speed up the review
process.
</p>
<h3 id="attributes">what is an attribute?</h3>
<p>
An attribute is a feature that a service has. An attribute can be either
good, a warning, bad or informational. Attributes are not limited, the
list of attributes will grow if needed, you can see a full list of the
attributes available on the Attributes page.
</p>
<p>
You can click on any point to see a detailed description of what it
means. The point page also shows all the listings that have that point.
This is also a useful way to find listings that have a specific feature.
</p>
<h3 id="search">search</h3>
<p>
Filtering services in kycnot.me is very easy. In the main page, you will
see 3 possible filters:
</p>
<div class="[&>span]:block space-y-2">
<span>
<b>Type filter</b>: Lets you choose between seeing all the listings
(default), only exchanges, or only services.
</span>
<span>
<b>Search bar</b>: The search bar is the most powerful filter. You can
use it to perform a full-text search on the listings. The search will
look for the text you enter in the name, description, keywords and
category. The search is not case-sensitive.
</span>
<span>
<b>Currency filter</b>: Lets you choose between seeing all the
listings (default), only listings that accept a certain currency. If
you choose more than one option, it will show all the listings that
accept all of the selected currencies.
</span>
</div>
<h3 id="levels">kyc Levels</h3>
<p>
KYCnot.me has a KYC level system. These levels allow you to quickly
identify the kind of KYC practices a service may have. There are 4
levels going from 0 to 3. The higher the level, the more invasive the
KYC practices are.
</p>
<h3 id="scoring">scoring</h3>
<p>
Each listing has a score that is calculated based on the attributes that
it has. The score does not reflect how good an exchange is overall, but
it seeks to give a score in relation to its KYC practices. An exchange
could have an awful UI/UX, but if it is very NO-KYC friendly, it will
still get a high score.
</p>
<p>The scoring algorithm is <a target="_blank"
href="https://codeberg.org/pluja/kycnot.me/src/branch/main/src/utils/score.go">open source</a>,
pull requests to improve it are welcome.
It runs once every hour, meaning scores cannot be manually modified since they will be recalculated by the
algorithm hourly. You can visit
<a href="https://kycnot.me/api/v1/service/localmonero/summary">a service's score breakdown</a> to see how it is
calculated.
</p>
<h3 id="order">order</h3>
<p>
The listings are sorted in a decreasing order by score. For listings
that share the same score (tie), the order is random every time within
that score range.
</p>
<h3 id="pending">pending</h3>
<p>
You can find the list of services that have been requested and are
pending for approval here: <a href="/pending">/pending</a>
</p>
<h3 id="verification">verification</h3>
<p class="mt-2">
You will see that some services show a blue badge with a tick. This
means that the service has been tested personally by me.
</p>
<p class="mt-2">
To check the service, I make a trade on the site, or test the service
manually. With this, I can verify that the service does not perform KYC
practices on regular basis, and that it is not a scam, at least at the
time of the verification.
</p>
<p class="mt-2">
These checks are done randomly and without prior notice to the service.
For this reason, and as the list is in constant growth, not all services
have been verified yet and it takes me some time to verify them.
</p>
<p class="mt-2">
A verified service does not mean that the service is safe to use
blindly, it just means that at the time of the verification, the service
was not performing KYC practices on regular basis, and that it was not a
scam. It is still recommended to do your own research before using any
service, since I can't test all the services frequently enough.
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="tos">terms of service auto-reviews</h2>
<p>
KYCnot.me features an AI-powered tool that automatically reviews Terms of Service (ToS) for all the different
services. It breaks down the ToS content, highlights the main points, and translates them into
easy-to-understand language, aiming to simplify the comprehension of any service's ToS.
</p>
<p>
For this, I am using OpenAI's GPT4-turbo model, which proved to be the
most reliable and provides the best results.
</p>
<p>
<b>Important note:</b> The automated tool generally delivers reliable and solid results. But it might flag false
positives or irrelevant lines, if you find something's off, please <a href="#contact">contact me</a>.
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="support">support this project</h2>
<p>
If you like this project, or
<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">
<!-- Monero-->
<span>
<span class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 monero-filled" width="24" height="24"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M15 11.414v4.586a1 1 0 0 0 1 1l4.66 .001a10 10 0 0 1 -17.32 0l4.66 -.001l.117 -.007a1 1 0 0 0 .883 -.993v-4.585l2.293 2.292l.094 .083a1 1 0 0 0 1.32 -.083l2.293 -2.293zm2 -8.074a10 10 0 0 1 4.54 11.66h-4.54v-6c0 -.89 -1.077 -1.337 -1.707 -.707l-3.293 3.292l-3.293 -3.292l-.084 -.076c-.637 -.514 -1.623 -.07 -1.623 .783v6h-4.54a9.991 9.991 0 0 1 -.46 -3l.005 -.324a10 10 0 0 1 14.995 -8.336z"
stroke-width="0" fill="currentColor" />
</svg>
<span class="font-bold uppercase">Monero</span>
</span>
<p class="mt-1 text-xs text-white/50">
> If your wallet supports <a rel="noreferrer" target="_blank" href="https://openalias.org/">OpenAlias</a>, you can use
<span class="font-bold">kycnot.me</span> in the address field.
</p>
<code>{{.Xmr}}</code>
<details class="my-2 mt-2 rounded-lg cursor-pointer">
<summary class="flex items-center space-x-1 text-sm text-white/60">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 icon icon-tabler icon-tabler-qrcode" width="24"
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M7 17l0 .01" />
<path d="M14 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M7 7l0 .01" />
<path d="M4 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M17 7l0 .01" />
<path d="M14 14l3 0" />
<path d="M20 14l0 .01" />
<path d="M14 14l0 3" />
<path d="M14 20l3 0" />
<path d="M17 17l3 0" />
<path d="M20 17l0 3" />
</svg>
<span class="underline"> Click for QR Code </span>
</summary>
<img class="my-4 size-60" src="/static/assets/xmr.webp" alt="Monero address QR code" />
</details>
</span>
<!--End Monero-->
<hr class="border-gray-300/10">
<span>
<span class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 bitcoin-filled" width="24" height="24"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M17 3.34a10 10 0 1 1 -15 8.66l.005 -.324a10 10 0 0 1 14.995 -8.336zm-4 2.66a1 1 0 0 0 -1 1h-1a1 1 0 0 0 -2 0a1 1 0 1 0 0 2v6a1 1 0 0 0 0 2c0 1.333 2 1.333 2 0h1a1 1 0 0 0 2 0v-.15c1.167 -.394 2 -1.527 2 -2.85l-.005 -.175a3.063 3.063 0 0 0 -.734 -1.827c.46 -.532 .739 -1.233 .739 -1.998c0 -1.323 -.833 -2.456 -2 -2.85v-.15a1 1 0 0 0 -1 -1zm.09 7c.492 0 .91 .437 .91 1s-.418 1 -.91 1h-2.09v-2h2.09zm0 -4c.492 0 .91 .437 .91 1c0 .522 -.36 .937 -.806 .993l-.104 .007h-2.09v-2h2.09z"
stroke-width="0" fill="currentColor" />
</svg>
<span>
<span class="font-bold uppercase">Bitcoin</span>
<span>via</span>
<a target="_blank" class="font-bold capitalize" href="https://silentpayments.xyz">silent
payments</a></span>
</span>
<p class="mt-1 text-xs text-white/50">
> If you want to donate without Silent Payments, request an address via any of the <a href="#contact">contact</a> methods.
</p>
<code>{{.BtcSilent}}</code>
<details class="my-2 rounded-lg cursor-pointer">
<summary class="flex items-center space-x-1 text-sm text-white/60">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 icon icon-tabler icon-tabler-qrcode" width="24"
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M7 17l0 .01" />
<path d="M14 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M7 7l0 .01" />
<path d="M4 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z" />
<path d="M17 7l0 .01" />
<path d="M14 14l3 0" />
<path d="M20 14l0 .01" />
<path d="M14 14l0 3" />
<path d="M14 20l3 0" />
<path d="M17 17l3 0" />
<path d="M20 17l0 3" />
</svg>
<span class="underline"> Click for QR Code </span>
</summary>
<img class="my-4 size-60" src="/static/assets/btcsp.webp" alt="Bitcoin Address QR Code" />
</details>
</span>
<span class="pb-4 space-y-2">
<span class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 tabler-bolt" width="24" height="24"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M13 3l0 7l6 0l-8 11l0 -7l-6 0l8 -11" />
</svg>
<a target="_blank" rel="noreferrer" href="{{.Lnn}}">Tip via Lightning Network</a>
</span>
</span>
</div>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="contact">contact</h2>
<p>
If you have any queries or concerns, you can connect with me on any of the listed social media
platforms below.
</p>
<p>
For confidential communication (e2ee), please reach out via SimpleX link.
</p>
<div class="[&>span]:block [&>span>a]:!no-underline [&>span>a]:font-bold space-y-2">
<span>
<a target="_blank" rel="noreferrer"
href="https://simplex.chat/contact#/?v=2&smp=smp%3A%2F%2F0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU%3D%40smp8.simplex.im%2FcgKHYUYnpAIVoGb9lxb0qEMEpvYIvc1O%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAIW_JSq8wOsLKG4Xv4O54uT2D_l8MJBYKQIFj1FjZpnU%253D%26srv%3Dbeccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion">💬 SimpleX Chat</a> <a
class="mx-2 text-xs text-gray-400" href="https://simplex.chat">?</a>
</span>
<span>
<a target="_blank" 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://fosstodon.org/@kycnotme">🦣 Mastodon</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://x.com/kycnot">🇽 Twitter (X)</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://lemmy.world/u/pluja">🐭 Lemmy</a>
</span>
</div>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="comments">comments</h2>
<p>
Each service has a dedicated comments section. The comments are suppored
by a self-hosted instance of <a rel="no-referrer" target="_blank" href="https://comentario.app">Comentario</a>. An open-source and lightweight (20kb) commenting engine.
</p>
<p>
To comment on a service's page, you can log in with Twitter, Github or Gitlab. Anonymous comments are also possible, but these need to be approved before being public to avoid abuse.
</p>
<p>The <a rel="no-referrer" target="_blank" href="https://gitlab.com/commento/commento">source code</a> for Comentario is licensed under MIT license.</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="transparency">transparency</h2>
<p>
In order to increase trust from visitors, KYCNOT.me strives to be as
transparent as possible. Here are some of the ways in which I try to
achieve this:
</p>
<div class="[&>span]:block space-y-2">
<span>
* <a href="https://codeberg.org/pluja/kycnot.me">Open Source Code</a> -
A commit history, and full disclosure of the scoring algorithm is
provided.
</span>
<span>
* Comment section on each page, available on a self-hosted <a href="https://comentario.app">Comentario</a> instance.
</span>
<span>* All changes are recorded on the <a href="/changelog">changelog</a></span>
<span>* See any service's score breakdown: <a href="/api/v1/service/bisq/summary">bisq example</a></span>
<span>* Open API to get all the listings data</span>
<span>
* Open scoring algorithm. All services go under the same algorithm,
meaning that the score is not subjective.
</span>
<span>* Test proofs: I am slowly adding proofs for each verified service, providing a PGP signed proof that I have personally tested the service. To see a proof, click the "verified" badge next to a serivce's name, inside the service page.</span>
</div>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="privacy">privacy</h2>
<p>
KYCNOT.me does not have trackers and never will. It does not make any
third party connections from the frontend. No user data of any kind is
collected. A "no-referrer" policy is enforced. Tor and I2P sites are available
(check the footer).
</p>
<h3 id="javascript">javascript</h3>
<p>
KYCNOT.me does not require Javascript. Everything is rendered server-side.
You can use KYCNOT.me with Javascript disabled.
</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. 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 Matrix. The code that
makes this interaction is also fully open source. Read more <a href="#comments">in this section.</a>
</span>
</div>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="api">api</h2>
<p>KYCnot.me offers a public API to get its data.</p>
<p>
If you make use of the API data, you should mention kycnot.me as the source
of the data.
</p>
<p>
<b>example:</b>
<a href="/api/v1/service/bisq">/api/v1/service/bisq</a>
</p>
<p>
<b>example 2:</b>
<a href="/api/v1/service/bisq.network">/api/v1/service/bisq.network</a>
</p>
<p>
<b>rate limit:</b>
5req/min (with bursts of 5)
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="referrals">referrals</h2>
<p>
<span>
Referrals help me cover the site and maintenance costs. The default website link
is the referral, if there is any. But there's always a referral-free
option, that is shown with a
</span>
<svg xmlns="http://www.w3.org/2000/svg" class="inline size-5 link-off" width="24" height="24"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l3 -3m2 -2l1 -1" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path d="M3 3l18 18" />
<path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
<span>icon. Feel free to choose.</span>
</p>
</div>
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id="disclaimer">disclaimer</h2>
<p>
This website does not provide any financial advice. Always do your
own research before using any service. This site is for informational
purposes only. I'm not responsible for any loss of funds or any other
damage that may occur by using any of the services listed here. Use at
your own risk.
</p>
</div>
</article>
</div>

View File

@ -0,0 +1,580 @@
package templates
import (
slugger "github.com/gosimple/slug"
"os"
)
// In golang, maps do not preserve order.
var SectionsFunc = map[string]func() templ.Component{
"why kycnot.me?": why,
"what is kyc?": what,
"why only btc and xmr?": onlyBtcXmr,
"listings": listings,
"automated tos reviews": automatedTosReviews,
"support": support,
"contact": contact,
"comments": comments,
"transparency": transparency,
"privacy": privacy,
"api": api,
"disclaimer": disclaimer,
}
// We use a slice to determine the order of keys.
var sectionOrder = []string{
"why kycnot.me?",
"what is kyc?",
"why only btc and xmr?",
"listings",
"automated tos reviews",
"support",
"contact",
"comments",
"transparency",
"privacy",
"api",
"disclaimer",
}
templ About() {
@Base("About") {
@inlineCss()
<div id="main" class="flex flex-col justify-center items-center">
<div class="p-4 w-full max-w-4xl">
<div class="space-x-2 text-xs font-bold text-center text-lime-500 lowercase md:text-sm font-samo">
for _, secTitle := range sectionOrder {
<a href={ templ.SafeURL("#" + slugger.Make(secTitle)) }>{ secTitle }</a>
·
}
</div>
</div>
<article class="max-w-5xl [&>div]:px-4 [&>div]:py-1 font-inter">
for _, secTitle := range sectionOrder {
<div class="m-2 rounded-xl bg-gray-500/10">
<h2 id={ slugger.Make(secTitle) } class="font-bold text-lime-400 text-md">{ secTitle }</h2>
<div class="space-y-2">
@SectionsFunc[secTitle]()
</div>
</div>
}
</article>
</div>
}
}
templ why() {
<p>
Cryptocurrencies were created to revolutionize the way we pay for goods
and services, aiming to eliminate reliance on centralized entities such
as banks and governments that control our economy.
</p>
<p>
Exchanges that enforce KYC (Know Your Customer) operate similarly to traditional banks. Users are
required to provide identification, such as a photo of their ID, to use
these exchanges. Moreover, most of these exchanges are centralized,
meaning that users do not own their keys. In short, this implies that
the cryptocurrencies belong to the exchange and not the user. These
requirements go against the decentralized and self-sovereign nature of cryptocurrencies.
</p>
<p>
With KYCNOT.ME, I hope to provide people with trustworthy alternatives for buying, exchanging, trading, and
using cryptocurrencies without having to disclose their identity, thus preserving the right to privacy.
I want to help preserve the original ethos of cryptocurrencies.
</p>
}
templ what() {
<p>
<strong>KYC</strong> stands for <strong>"Know Your Customer"</strong>,
a process designed to protect financial institutions against fraud,
corruption, money laundering and terrorist financing. Or at least this
is what they want you to believe.
</p>
<p>
The truth is that KYC is a direct
attack on our privacy and puts us in disadvantage against the
governments. True criminals don't care about KYC policies. True
criminals know perfectly how to avoid such policies. In fact, they
normally use the FIAT system and don't even need to use
cryptocurrencies. Banks are the biggest money launders, the
<a
rel="noreferrer"
href="https://www.reuters.com/business/hsbc-fined-85-mln-anti-money-laundering-failings-2021-12-17/"
>
HSBC
scandal
</a>,
<a rel="noreferrer" href="https://www.reuters.com/article/us-nordea-bnk-moneylaundering-idUSKCN1QL11S">Nordea</a>
or
<a
rel="noreferrer"
href="https://www.reuters.com/article/us-europe-moneylaundering-swedbank/swedbank-hit-with-record-386-million-fine-over-baltic-money-laundering-breaches-idUSKBN2163LU"
>Swedbank</a>
are just some examples.
</p>
<p>
Chainalysis found that only 0.34% of the transaction volume with cryptocurrencies in 2023 was attributable to
criminal
activity. Bitcoin's share of this is significantly lower with 25%. Illicit transactions with Euros accounted
for 1% of
the EU's GDP or €110 billion in 2010. <a
href="https://www.chainalysis.com/blog/2024-crypto-crime-report-introduction/"
>[1]</a> <a
href="https://transparency.eu/priority/financial-flows-crime/"
>[2]</a>
</p>
<p>
KYC only affects small individuals like you and me. It is an annoying
procedure that forces us to hand our personal information to a
third party in order to buy, use or unlock our funds. We should start
boycotting companies that enforce such practices. We should start using
cryptocurrencies as they were intended to be used: without barriers.
</p>
}
templ onlyBtcXmr() {
<p>
<strong>Bitcoin</strong>: it's the initial spark of the decentralized money.
A solid project with a strong community. It is the most well-known and widespread cryptocurrency.
</p>
<p>
<strong>Monero</strong>: if digital cash was to exist, it should be like
Monero. Fungible, private by design, fast and pretty low fees. Also, one
of the oldest cryptocurrencies around with a very active community.
</p>
<p>
No other currencies will be added. Most sites listed here also accept
other cryptocurrencies, such as Ethereum or Litecoin.
</p>
}
templ listings() {
<h3 id="request">request</h3>
<p>
You can request a new listing by visiting the
<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>
<p>
The listing process is usually slow. I first need to review the service,
read online reviews, gather evidence and history of good reviews /
opinions, test it myself (i can't always do this), read the terms of
service, privacy policy, etc...
</p>
<p>
I do this on my own free time, so I'm slow at it. If you feel like a
<a href="/pending">pending service</a> deserves to be listed, you can
<a href="#contact">contact me</a> and I will try to speed up the review
process.
</p>
<h3 id="attributes">what is an attribute?</h3>
<p>
An attribute is a feature that a service has. An attribute can be either
good, a warning, bad or informational. Attributes are not limited, the
list of attributes will grow if needed, you can see a full list of the
attributes available on the Attributes page.
</p>
<p>
You can click on any point to see a detailed description of what it
means. The point page also shows all the listings that have that point.
This is also a useful way to find listings that have a specific feature.
</p>
<h3 id="search">search</h3>
<p>
Filtering services in kycnot.me is very easy. In the main page, you will
see 3 possible filters:
</p>
<div class="[&>span]:block space-y-2">
<span>
<b>Type filter</b>: Lets you choose between seeing all the listings
(default), only exchanges, or only services.
</span>
<span>
<b>Search bar</b>: The search bar is the most powerful filter. You can
use it to perform a full-text search on the listings. The search will
look for the text you enter in the name, description, keywords and
category. The search is not case-sensitive.
</span>
<span>
<b>Currency filter</b>: Lets you choose between seeing all the
listings (default), only listings that accept a certain currency. If
you choose more than one option, it will show all the listings that
accept all of the selected currencies.
</span>
</div>
<h3 id="levels">kyc Levels</h3>
<p>
KYCnot.me has a KYC level system. These levels allow you to quickly
identify the kind of KYC practices a service may have. There are 4
levels going from 0 to 3. The higher the level, the more invasive the
KYC practices are.
</p>
<h3 id="scoring">scoring</h3>
<p>
Each listing has a score that is calculated based on the attributes that
it has. The score does not reflect how good an exchange is overall, but
it seeks to give a score in relation to its KYC practices. An exchange
could have an awful UI/UX, but if it is very NO-KYC friendly, it will
still get a high score.
</p>
<p>
The scoring algorithm is <a
target="_blank"
href="https://codeberg.org/pluja/kycnot.me/src/branch/main/src/utils/score.go"
>open source</a>,
pull requests to improve it are welcome.
It runs once every hour, meaning scores cannot be manually modified since they will be recalculated by the
algorithm hourly. You can visit
<a href="https://kycnot.me/api/v1/service/localmonero/summary">a service's score breakdown</a> to see how it is
calculated.
</p>
<h3 id="order">order</h3>
<p>
The listings are sorted in a decreasing order by score. For listings
that share the same score (tie), the order is random every time within
that score range.
</p>
<h3 id="pending">pending</h3>
<p>
You can find the list of services that have been requested and are
pending for approval here: <a href="/pending">/pending</a>
</p>
<h3 id="verification">verification</h3>
<p class="mt-2">
You will see that some services show a blue badge with a tick. This
means that the service has been tested personally by me.
</p>
<p class="mt-2">
To check the service, I make a trade on the site, or test the service
manually. With this, I can verify that the service does not perform KYC
practices on regular basis, and that it is not a scam, at least at the
time of the verification.
</p>
<p class="mt-2">
These checks are done randomly and without prior notice to the service.
For this reason, and as the list is in constant growth, not all services
have been verified yet and it takes me some time to verify them.
</p>
<p class="mt-2">
A verified service does not mean that the service is safe to use
blindly, it just means that at the time of the verification, the service
was not performing KYC practices on regular basis, and that it was not a
scam. It is still recommended to do your own research before using any
service, since I can't test all the services frequently enough.
</p>
}
templ automatedTosReviews() {
<p>
KYCnot.me features an AI-powered tool that automatically reviews Terms of Service (ToS) for all the different
services. It breaks down the ToS content, highlights the main points, and translates them into
easy-to-understand language, aiming to simplify the comprehension of any service's ToS.
</p>
<p>
For this, I am using OpenAI's GPT4-turbo model, which proved to be the
most reliable and provides the best results.
</p>
<p>
<b>Important note:</b> The automated tool generally delivers reliable and solid results. But it might flag false
positives or irrelevant lines, if you find something's off, please <a href="#contact">contact me</a>.
</p>
}
templ support() {
<p>
If you like this project, or
<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">
<!-- Monero-->
<span>
<span class="flex items-center space-x-2">
<i class="icon-[tabler--coin-monero-filled]"></i>
<span class="font-bold uppercase">Monero</span>
</span>
<p class="mt-1 text-xs text-white/50">
> If your wallet supports <a rel="noreferrer" target="_blank" href="https://openalias.org/">OpenAlias</a>,
you can use
<span class="font-bold">kycnot.me</span> in the address field.
</p>
<code>{ os.Getenv("DONATION_XMR_ADDRESS") }</code>
<details class="my-2 mt-2 rounded-lg cursor-pointer">
<summary class="flex items-center space-x-1 text-sm text-white/60">
<i class="icon-[tabler--qrcode]"></i>
<span class="underline">Click for QR Code </span>
</summary>
<img class="my-4 size-60" src="/static/assets/xmr.webp" alt="Monero address QR code"/>
</details>
</span>
<!--End Monero-->
<hr class="border-gray-300/10"/>
<span>
<span class="flex items-center space-x-2">
<i class="icon-[tabler--coin-bitcoin-filled]"></i>
<span>
<span class="font-bold uppercase">Bitcoin</span>
<span>via</span>
<a target="_blank" class="font-bold capitalize" href="https://silentpayments.xyz">
silent
payments
</a>
</span>
</span>
<p class="mt-1 text-xs text-white/50">
> If you want to donate without Silent Payments, request an address via any of the <a
href="#contact"
>contact</a> methods.
</p>
<code>{ os.Getenv("DONATION_SBTC_ADDRESS") }</code>
<details class="my-2 rounded-lg cursor-pointer">
<summary class="flex items-center space-x-1 text-sm text-white/60">
<i class="icon-[tabler--qrcode]"></i>
<span class="underline">Click for QR Code </span>
</summary>
<img class="my-4 size-60" src="/static/assets/btcsp.webp" alt="Bitcoin Address QR Code"/>
</details>
</span>
<span class="pb-4 space-y-2">
<span class="flex items-center space-x-2">
<i class="icon-[tabler--bolt]"></i>
<a target="_blank" rel="noreferrer" href="https://getalby.com/p/kycnotme">Tip via Lightning Network</a>
</span>
</span>
</div>
}
templ contact() {
<p>
If you have any queries or concerns, you can connect with me on any of the listed social media
platforms below.
</p>
<p>
For confidential communication (e2ee), please reach out via SimpleX link.
</p>
<div class="[&>span]:block [&>span>a]:!no-underline [&>span>a]:font-bold space-y-2">
<span>
<a
target="_blank"
rel="noreferrer"
href="https://simplex.chat/contact#/?v=2&smp=smp%3A%2F%2F0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU%3D%40smp8.simplex.im%2FcgKHYUYnpAIVoGb9lxb0qEMEpvYIvc1O%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAIW_JSq8wOsLKG4Xv4O54uT2D_l8MJBYKQIFj1FjZpnU%253D%26srv%3Dbeccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion"
>
💬
SimpleX Chat
</a> <a class="mx-2 text-xs text-gray-400" target="_blank" href="https://simplex.chat">?</a>
</span>
<span>
<a
target="_blank"
rel="noreferrer"
href="https://njump.me/npub188x98j0r7l2fszeph6j7hj99h8xl07n989pskk5zd69d2fcksetq5mgcqf"
>🪶 Nostr</a> <a
class="mx-2 text-xs text-gray-400"
target="_blank"
href="https://usenostr.org"
>?</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://fosstodon.org/@kycnotme">🦣 Mastodon</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://x.com/kycnot">Twitter (X)</a>
</span>
<span>
<a target="_blank" rel="noreferrer" href="https://lemmy.world/u/pluja">🐭 Lemmy</a>
</span>
</div>
}
templ comments() {
<p>
Each service has a dedicated comments section. The comments are suppored
by a self-hosted instance of <a rel="no-referrer" target="_blank" href="https://comentario.app">Comentario</a>. An
open-source and lightweight (20kb) commenting engine.
</p>
<p>
To comment on a service's page, you can log in with Twitter, Github or Gitlab. Anonymous comments are also possible,
but these need to be approved before being public to avoid abuse.
</p>
<p id="comment-moderation">
Comments are moderated and require manual approval before being published. I approve all legitimate comments, but remove spam, scams, or low-quality submissions. Moderation may take a few days depending on when I review pending comments.
</p>
<p>The <a rel="no-referrer" target="_blank" href="https://gitlab.com/commento/commento">source code</a> for Comentario is licensed under MIT license.</p>
}
templ transparency() {
<p>
In order to increase trust from visitors, KYCNOT.me strives to be as
transparent as possible. Here are some of the ways in which I try to
achieve this:
</p>
<div class="[&>span]:block space-y-2">
<span>
* <a href="https://codeberg.org/pluja/kycnot.me">Open Source Code</a> -
A commit history, and full disclosure of the scoring algorithm is
provided.
</span>
<span>
* Comment section on each page, available on a self-hosted <a href="https://comentario.app">Comentario</a>
instance.
</span>
<span>* All changes are recorded on the <a href="/changelog">changelog</a></span>
<span>* See any service's score breakdown: <a href="/api/v1/service/bisq/summary">bisq example</a></span>
<span>* Open API to get all the listings data</span>
<span>
* Open scoring algorithm. All services go under the same algorithm,
meaning that the score is not subjective.
</span>
<span>
* Test proofs: I am slowly adding proofs for each verified service, providing a PGP signed proof that I have
personally tested the service. To see a proof, click the "verified" badge next to a serivce's name, inside the
service page.
</span>
</div>
}
templ privacy() {
<p>
KYCNOT.me does not have trackers and never will. It does not make any
third party connections from the frontend. No user data of any kind is
collected. A "no-referrer" policy is enforced. Tor and I2P sites are available
(check the footer).
</p>
<h3 id="javascript">javascript</h3>
<p>
KYCNOT.me does not require Javascript. Everything is rendered server-side.
You can use KYCNOT.me with Javascript disabled.
</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. 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 Matrix. The code that
makes this interaction is also fully open source. Read more <a href="#comments">in this section.</a>
</span>
</div>
}
templ api() {
<p>KYCnot.me offers a public API to get its data.</p>
<p>
If you make use of the API data, you should mention kycnot.me as the source
of the data.
</p>
<p>
<b>example:</b>
<a href="/api/v1/service/bisq">/api/v1/service/bisq</a>
</p>
<p>
<b>example 2:</b>
<a href="/api/v1/service/bisq.network">/api/v1/service/bisq.network</a>
</p>
<p>
<b>rate limit:</b>
5req/min (with bursts of 5)
</p>
}
templ disclaimer() {
<p>
This website does not provide any financial advice. Always do your
own research before using any service. This site is for informational
purposes only. I'm not responsible for any loss of funds or any other
damage that may occur by using any of the services listed here. Use at
your own risk.
</p>
}
templ inlineCss() {
<style>
article p {
margin-bottom: 1rem;
font-size: 1.05rem;
}
p {
margin-top: 1rem;
}
article a {
color: rgb(163 230 53);
text-decoration: underline;
text-underline-offset: 0.2rem;
}
article div {
padding-top: .5em;
padding-bottom: .5em;
}
h1,
h2,
h3,
h4 {
margin-top: 1rem;
margin-bottom: 0.3rem;
font-weight: bold;
font-family: samo;
color: rgb(163 230 53);
}
h1 {
font-size: 3rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
margin-top: 1.1em;
font-size: 1rem;
}
h3::before {
content: "~ ";
}
article blockquote {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
padding-left: 1rem;
border-left: 0.3rem solid rgb(163 230 53);
color: rgb(142, 146, 150);
}
code {
background: #2b282872;
border: 1px solid #494242;
border-radius: 0.2rem;
font-weight: bold;
border-left: 3px solid #89f333;
color: #d3cccc;
font-family: samo;
max-width: 100%;
overflow: scorll;
padding: 1em 1em;
display: block;
word-wrap: break-word;
max-width: 90vw;
margin-top: .2em;
}
hr {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
</style>
}

View File

@ -1,45 +0,0 @@
<section class="pt-14">
<div class="flex flex-col justify-center items-center space-x-4">
<!--<h1 class="text-2xl font-bold capitalize-first">{{.Attribute.Title}}</h1>-->
<!-- Description -->
<div class="p-4 m-4 {{if eq "info" .Attribute.Rating}} border rounded-lg border-blue-600/40 bg-blue-600/20
{{end}} {{if eq "good" .Attribute.Rating}} border rounded-lg border-green-600/40 bg-green-600/20 {{end}}
{{if eq "warn" .Attribute.Rating}} border rounded-lg border-yellow-600/40 bg-yellow-600/20 {{end}} {{if
eq "bad" .Attribute.Rating}} border rounded-lg border-red-600/40 bg-red-600/20 {{end}} ">
<h3 class="mb-3 text-lg font-bold uppercase border-b md:text-xl border-b-white/20">
{{if eq "info" .Attribute.Rating}}
🔵
{{end}}
{{if eq "good" .Attribute.Rating}}
{{end}}
{{if eq "warn" .Attribute.Rating}}
⚠️
{{end}}
{{if eq "bad" .Attribute.Rating}}
🛑
{{end}}
{{.Attribute.Title}}
</h3>
<p class="max-w-lg">
{{if ne .Attribute.Description ""}}
{{.Attribute.Description | safe}}
{{else}}
{{.Attribute.Name}} does not have a description.
{{end}}
</p>
</div>
</div>
</section>
<!-- Services list -->
<section class="flex flex-col justify-center items-center px-1 mx-2 mt-6">
<h2 class="text-lg font-bold text-center uppercase">Services with this point:</h2>
<div class="grid grid-cols-1 gap-3 mx-auto mt-2 max-w-6xl md:grid-cols-2 lg:grid-cols-3">
{{range .Services}}
{{partial "partials/service_card" .}}
{{end}}
</div>
</section>

View File

@ -0,0 +1,52 @@
package templates
import (
"kycnot.me/frontend/templates/partials"
"kycnot.me/internal/ent"
"kycnot.me/internal/ent/attribute"
)
templ Attribute(attr *ent.Attribute) {
@Base(attr.Title) {
<section class="pt-14">
<div class="flex flex-col justify-center items-center space-x-4">
<div
class={
"p-4 m-4 border rounded-lg",
templ.KV("border-blue-600/40 bg-blue-600/20", attr.Rating == attribute.RatingInfo),
templ.KV("border-green-600/40 bg-green-600/20", attr.Rating == attribute.RatingGood),
templ.KV("border-yellow-600/40 bg-yellow-600/20", attr.Rating == attribute.RatingWarn),
templ.KV("border-red-600/40 bg-red-600/20", attr.Rating == attribute.RatingBad),
}
>
<h3 class="mb-3 text-lg font-bold uppercase border-b md:text-xl border-b-white/20">
<i
class={
"text-lg",
templ.KV("text-blue-500 icon-[tabler--info-circle-filled]", attr.Rating == attribute.RatingInfo),
templ.KV("text-green-500 icon-[tabler--circle-check-filled]", attr.Rating == attribute.RatingGood),
templ.KV("text-yellow-500 icon-[tabler--alert-triangle-filled]", attr.Rating == attribute.RatingWarn),
templ.KV("text-red-500 icon-[tabler--alert-triangle-filled]", attr.Rating == attribute.RatingBad),
}
></i>
{ attr.Title }
</h3>
if attr.Description != "" {
<p class="max-w-lg">
@templ.Raw(attr.Description)
</p>
}
</div>
</div>
</section>
<!-- Services list -->
<section class="flex flex-col justify-center items-center px-1 mx-2 mt-12">
<h2 class="mb-3 text-lg font-bold text-center uppercase">Also have this attribute:</h2>
<div class="grid grid-cols-1 gap-3 mx-auto mt-2 max-w-6xl md:grid-cols-2 lg:grid-cols-3">
for _, s := range attr.Edges.Services {
@partials.ServiceCard(s)
}
</div>
</section>
}
}

View File

@ -0,0 +1,33 @@
package templates
import (
"kycnot.me/frontend/templates/partials"
"kycnot.me/internal/ent"
)
templ AttributeList(attributes []*ent.Attribute) {
@Base("Attributes") {
<section class="flex flex-col items-center w-full">
<header class="my-4 text-center">
<h2 class="text-xl">Service Attributes</h2>
<p class="mt-0.5 text-xs text-white/40">Click any attribute to see a detail</p>
</header>
<div class="mt-3 text-center text-gray-500">
<a href="/">
<span class="p-1 text-xs transition duration-200 hover:text-gray-400">All</span>
</a>
<a href="/?t=exchange">
<span class="p-1 text-xs transition duration-200 hover:text-gray-400">Exchanges</span>
</a>
<a href="/?t=service">
<span class="p-1 text-xs transition duration-200 hover:text-gray-400">Services</span>
</a>
</div>
<div class="mt-6 space-y-2 w-full max-w-lg">
for _, attribute := range attributes {
@partials.AttributeLine(attribute)
}
</div>
</section>
}
}

View File

@ -0,0 +1,115 @@
package templates
func buildTitle(part string) string {
if part != "" {
return part + " | KYCNOT.ME"
}
return "KYCNOT.ME"
}
templ Base(title string) {
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta
name="description"
content="Find the best NON-KYC exchanges and services to use and spend your crypto anonymously."
/>
<meta
name="keywords"
content="KYC, AML, Bitcoin, Crypto, Cryptocurrency, Exchange, Exchanges, Service, Services, Anonymous, Anonymity, Privacy, Pseudonymity, Pseudonymous, Non-KYC, No-KYC, No KYC, Monero, XMR, BTC"
/>
<meta property="og:title" content={ buildTitle(title) }/>
<meta
property="og:description"
content="Find the best NON-KYC exchanges and services to use and spend your crypto anonymously."
/>
<meta property="og:type" content="website"/>
<meta
http-equiv="onion-location"
content="http://kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion/"
/>
<meta property="og:image" content="https://kycnot.me/static/assets/banner.webp"/>
<meta property="thumbnail" content="https://kycnot.me/static/assets/banner.webp"/>
<meta name="twitter:image" content="https://kycnot.me/static/assets/banner.webp"/>
<meta property="og:url" content="https://kycnot.me"/>
<!-- Mastodon Verification -->
<link rel="me" href="https://fosstodon.org/@kycnotme"/>
<!-- CSS -->
<link rel="stylesheet" href="/static/css/balloon.min.css"/>
<!-- Site Config -->
<title>{ buildTitle(title) }</title>
<link rel="stylesheet" href="/static/css/style.css"/>
<link rel="shortcut icon" href="/static/assets/favicon.webp" type="image/x-icon"/>
</head>
<style>
.capitalize-first::first-letter {
text-transform: capitalize;
}
</style>
<body class="h-full text-gray-200 bg-fixed bg-black bg-center bg-cover font-samo" style="background-image: url('/static/assets/gradient.svg');">
<!-- Nav Bar -->
<nav class="flex justify-center items-center w-full bg-none">
<div class="flex flex-col justify-between items-center px-4 w-full max-w-6xl md:flex-row">
<!-- Logo -->
<a href="/">
<img src="/static/assets/logo_wide.svg" alt="Logo" class="pt-2 max-w-48 lg:max-w-48"/>
</a>
<!-- Primary Navbar items -->
<div class="[&>a]:inline [&>a]:text-xs">
<a href="/" class="px-1 py-1 text-gray-500 transition duration-300 hover:text-lime-500 md:text-base">
Home
</a>
<a
href="/request"
class="px-1 py-1 text-gray-500 transition duration-300 hover:text-lime-500 md:text-base"
>
Request
</a>
<a
href="/about"
class="px-1 py-1 text-gray-500 transition duration-300 hover:text-lime-500 md:text-base"
>
About
</a>
<a
href="/about#support"
class="px-1 py-1 text-gray-500 transition duration-300 md:text-base hover:text-lime-500"
>
Support
</a>
</div>
</div>
</nav>
<!---->
{ children... }
<footer class="my-6 space-x-4 text-md text-center *:text-lime-600">
<a
class="hover:text-lime-300"
href="https://nostr.com/npub188x98j0r7l2fszeph6j7hj99h8xl07n989pskk5zd69d2fcksetq5mgcqf"
>nostr</a>
<a class="hover:text-lime-300" href="https://fosstodon.org/@kycnotme">mastodon</a>
<a
class="hover:text-lime-300"
href="http://kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion/"
>tor</a>
<a
class="hover:text-lime-300"
href="http://nti3rj4j4disjcm2kvp4eno7otcejbbxv3ggxwr5tpfk4jucah7q.b32.i2p"
>i2p</a>
</footer>
</body>
</html>
<style>
.gradient {
background: rgb(5, 58, 24);
background: linear-gradient(153deg,
rgb(3, 53, 21) 0%,
rgb(0, 17, 4) 20%,
rgba(0, 0, 0, 1) 100%);
}
</style>
}

View File

@ -1,28 +0,0 @@
<section class="container px-4 py-12 mx-auto">
<h1 class="mb-10 text-3xl font-bold text-center text-white/80">Changelog</h1>
<div class="space-y-4">
{{ range .Changes }}
<div class="overflow-hidden border rounded-md border-white/20">
<div class="p-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-base font-semibold text-white/80">{{.Title}}</h3>
<span class="text-xs text-white/40">{{.Created}}</span>
</div>
<p class="mb-4 text-sm text-white/70">{{.Summary | safe}}</p>
{{ if .Expand.service }}
<div class="pt-2 mt-1 border-t border-white/10">
{{ with .Expand.service }}
<span class="text-sm text-white/50">
Related service: <a class="underline transition-colors duration-200 text-lime-700 hover:text-blue-300"
href="/service/{{.Name}}">{{ .Name }}</a>
</span>
{{ end }}
</div>
{{end}}
</div>
</div>
{{ end }}
</div>
</section>

View File

@ -0,0 +1,44 @@
package templates
import (
"fmt"
"kycnot.me/internal/ent"
)
func changeTitle(change *ent.Change) string {
if _, err := change.Edges.ServiceOrErr(); err != nil {
return change.Title
}
// If the service is not found, use the default title format.
return fmt.Sprintf("%s: %s", change.Edges.Service.Name, change.Title)
}
templ Changelog(changes []*ent.Change) {
@Base("Changelog") {
<div class="flex flex-col items-center">
<h1 class="my-4 text-xl font-bold">Changelog</h1>
if len(changes) == 0 {
<div class="mt-4 text-center">No changes found.</div>
} else {
for _, change := range changes {
<div
class={
"p-2 mt-4 w-full max-w-lg border", templ.KV("border-orange-500/40 bg-orange-500/10", change.Warning),
templ.KV("border-blue-600/40 bg-blue-600/10", change.Warning == false),
}
>
<h3 class="text-xs text-white/50">
{ change.CreatedAt.Format("January 2, 2006 - 15:04") } {
change.CreatedAt.Location().String() }
</h3>
<h2 class="font-bold uppercase">{ changeTitle(change) }</h2>
<p class="mt-1 text-sm font-normal text-white/80">
@templ.Raw(change.Summary)
</p>
</div>
}
}
</div>
}
}

View File

@ -1,105 +0,0 @@
{{if .Announcement}}
<a target="_blank" href="{{.Announcement.Link}}"
class="flex flex-col justify-center items-center p-2 mx-auto mt-4 space-x-2 max-w-xs rounded-lg border transition duration-200 {{if .Announcement.Warning}}border-yellow-900 hover:border-yellow-700{{else}} hover:border-lime-700 border-lime-900{{end}} bg-black/30">
<span class="flex justify-center items-center font-bold">
{{if .Announcement.Warning}}
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 text-yellow-600 size-5" width="24" height="24" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12 1.67c.955 0 1.845 .467 2.39 1.247l.105 .16l8.114 13.548a2.914 2.914 0 0 1 -2.307 4.363l-.195 .008h-16.225a2.914 2.914 0 0 1 -2.582 -4.2l.099 -.185l8.11 -13.538a2.914 2.914 0 0 1 2.491 -1.403zm.01 13.33l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -7a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z"
stroke-width="0" fill="currentColor" />
</svg>
{{else}}
<svg xmlns="http://www.w3.org/2000/svg" class="mr-1.5 text-lime-600 size-5" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="9" />
<line x1="12" y1="8" x2="12.01" y2="8" />
<polyline points="11 12 12 12 12 16 13 16" />
</svg>
{{end}}
{{.Announcement.Title}}
</span>
<span class="text-xs lowercase">{{.Announcement.Body}}</span>
</a>
{{end}}
<main class="py-4 text-lime-500">
<div class="px-4 pt-2">
<div class="mb-2 text-center bg-transparent md:grid">
<p class="font-mono text-sm font-bold uppercase md:text-base">{{.RandomPitch}}</p>
</div>
</div>
<!-- End Filters -->
<form action="/">
<input type="hidden" name="t" id="t" value="{{.Filters.Type}}" />
<div class="flex justify-center items-center mt-4">
<input
class="text-xs text-lime-500 bg-transparent rounded-lg border border-lime-500/60 placeholder-white/30"
placeholder="Search anything..." value="{{.Filters.Query}}" type="text" name="q" id="q">
</div>
<div class="flex justify-center items-center pt-2.5 space-x-2">
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="btc">btc</label>
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox" {{if eq "on" .Filters.Btc}}checked{{end}} name="btc" id="btc">
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="xmr">xmr</label>
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox" {{if eq "on" .Filters.Xmr}}checked{{end}} name="xmr" id="xmr">
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="ln">ln</label>
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox" {{if eq "on" .Filters.Ln}}checked{{end}} name="ln" id="ln">
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="fiat">fiat</label>
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox" {{if eq "on" .Filters.Fiat}}checked{{end}} name="fiat" id="fiat">
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="cash">cash</label>
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox" {{if eq "on" .Filters.Cash}}checked{{end}} name="cash" id="cash">
</span>
<button class="p-1 text-xs font-bold uppercase bg-none rounded-lg border border-lime-500/60"
type="submit">filter</button>
</div>
</form>
<div class="mt-3 text-center text-gray-500">
<a href="/">
<span
class="p-1 {{if eq "" .Filters.Type}}border-b border-lime-500/60 text-lime-500{{end}} text-xs">All</span>
</a>
<a href="/?t=exchange">
<span class="p-1 {{if eq "exchange" .Filters.Type}}border-b border-lime-500/60 text-lime-500{{end}}
text-xs">Exchanges</span>
</a>
<a href="/?t=service">
<span class="p-1 {{if eq "service" .Filters.Type}}border-b border-lime-500/60 text-lime-500{{end}}
text-xs">Services</span>
</a>
</div>
<!-- Filters -->
</main>
<!-- Services list -->
<section class="flex justify-center items-center px-3 pb-12 mt-4">
<div class="grid grid-cols-1 gap-3 max-w-6xl lg:grid-cols-3">
{{range .Services}}
{{partial "partials/service_card" .}}
{{end}}
</div>
</section>

View File

@ -0,0 +1,115 @@
package templates
import (
"kycnot.me/frontend/templates/partials"
"kycnot.me/internal/ent"
"kycnot.me/internal/models"
"math/rand"
)
func getRandomPitch() string {
pitches := []string{
"KYC-Free Crypto Freedom.",
"Goodbye KYC, hello privacy.",
"KYC is a scam. Don't fall for it.",
"Trade anonymously, live freely.",
"KYC-free zone ahead.",
"Privacy is not a crime.",
"No ID? No problem.",
"True financial independence.",
}
return pitches[rand.Intn(len(pitches))]
}
func getAnnouncementStyle(warning bool) string {
if warning {
return "border-yellow-900 hover:border-yellow-700"
}
return "hover:border-lime-700 border-lime-900"
}
templ Index(ann *ent.Announcement, services []*ent.Service, filters models.ServiceFilters) {
@Base("") {
if ann != nil {
<a target="_blank" href={ templ.URL(ann.Link) } class={ getAnnouncementStyle(ann.Warning) + " flex flex-col justify-center items-center p-2 mx-auto mt-4 space-x-2 max-w-xs rounded-lg border transition duration-200 bg-black/30" }>
<span class="flex justify-center items-center font-bold">
if ann.Warning {
<i class="icon-[tabler--alert-triangle-filled] mr-1.5 text-yellow-600"></i>
} else {
<i class="ti icon-[tabler--news] mr-1.5 text-lime-600"></i>
}
{ ann.Title }
</span>
<span class="text-xs lowercase">{ ann.Body }</span>
</a>
}
<main class="pt-4 pb-1 text-lime-500">
<div class="px-4 pt-2">
<div class="mb-2 text-center bg-transparent md:grid">
<p class="font-mono text-sm font-bold uppercase md:text-base">{ getRandomPitch() }</p>
</div>
</div>
// Filters
<form action="/" method="get">
<input type="hidden" name="t" id="t" value={ filters.Type }/>
// Search query
<div class="flex justify-center items-center mt-4">
<input
class="text-xs text-lime-500 bg-transparent rounded-lg border border-lime-500/60 placeholder-white/30"
placeholder="Search anything..."
value={ filters.Query }
type="text"
name="q"
id="q"
/>
</div>
// Currency checkboxes
<div class="flex justify-center items-center pt-2.5 space-x-2">
{{ currencies := []string{"xmr", "btc", "ln", "fiat", "cash"} }}
for _, currency := range currencies {
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for={ currency }>{ currency }</label>
if val, ok := filters.Currencies[currency]; ok {
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox"
checked?={ val }
name={ currency }
id={ currency }
/>
} else {
<input
class="text-lime-500 bg-transparent rounded-full appearance-none outline-none focus:ring-0 border-lime-500/60"
type="checkbox"
name={ currency }
id={ currency }
/>
}
</span>
}
<button class="p-1 text-xs font-bold text-lime-500 uppercase bg-none rounded-lg border transition duration-200 border-lime-500/60 hover:border-lime-500 hover:text-lime-400" type="submit">filter</button>
</div>
</form>
<div class="mt-3 text-center text-gray-500">
<a href="/">
<span class={ "p-1 text-xs transition duration-200 hover:text-gray-400", templ.KV("underline", filters.Type == "") }>All</span>
</a>
<a href="/?t=exchange">
<span class={ "p-1 text-xs transition duration-200 hover:text-gray-400", templ.KV("underline", filters.Type == "exchange") }>Exchanges</span>
</a>
<a href="/?t=service">
<span class={ "p-1 text-xs transition duration-200 hover:text-gray-400", templ.KV("underline", filters.Type == "service") }>Services</span>
</a>
</div>
// End Filters
</main>
// Services List
<section class="flex justify-center items-center px-3 pb-12 mt-4">
<div class="grid grid-cols-1 gap-3 max-w-6xl lg:grid-cols-3">
for _, service := range services {
@partials.ServiceCard(service)
}
</div>
</section>
}
}

View File

@ -1,116 +0,0 @@
<!doctype html>
<html lang='en' data-theme="dark">
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Find the best NON-KYC exchanges and services to use and spend your crypto anonymously.">
<meta name="keywords"
content="KYC, AML, Bitcoin, Crypto, Cryptocurrency, Exchange, Exchanges, Service, Services, Anonymous, Anonymity, Privacy, Pseudonymity, Pseudonymous, Non-KYC, No-KYC, No KYC, Monero, XMR, BTC">
<meta property="og:title" content="{{.Title}}" />
<meta property="og:description"
content="Find the best NON-KYC exchanges and services to use and spend your crypto anonymously." />
<meta property="og:type" content="website" />
<meta http-equiv="onion-location" content="http://kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion/" />
<meta property="og:image" content="https://kycnot.me/static/assets/banner.webp" />
<meta property="thumbnail" content="https://kycnot.me/static/assets/banner.webp">
<meta name="twitter:image" content="https://kycnot.me/static/assets/banner.webp">
<meta property="og:url" content="https://kycnot.me" />
<!-- Mastodon Verification -->
<link rel="me" href="https://fosstodon.org/@kycnotme">
<!-- CSS -->
<link rel="stylesheet" href="/static/css/balloon.min.css">
<!-- Site Config -->
<title>{{.Title}}</title>
<link rel='stylesheet' href='/static/css/style.css'>
<link rel="shortcut icon" href="/static/assets/favicon.webp" type="image/x-icon">
</head>
<style>
.capitalize-first::first-letter {
text-transform: capitalize;
}
</style>
<body class="h-full text-gray-200 bg-fixed bg-black bg-center bg-cover font-samo" style="background-image: url('/static/assets/gradient.webp');">
<!-- Nav Bar -->
<nav class="flex items-center justify-center w-full bg-none">
<div class="flex flex-col items-center justify-between w-full max-w-6xl px-4 md:flex-row">
<!-- Logo -->
<a href="/">
<img src="/static/assets/logo_wide.svg" alt="Logo" class="pt-2 max-w-48 lg:max-w-48" />
</a>
<!-- Primary Navbar items -->
<div class="[&>a]:inline [&>a]:text-xs">
<a
href="/"
class="px-1 py-1 font-semibold md:text-base {{if or (eq .Current "index") (eq .Current "")}} text-lime-500 border-lime-500 border-b-2 {{ else }} text-gray-500 hover:text-lime-500 {{ end }}"
>
Home
</a>
<a
href="/request/service"
class="px-1 py-1 text-gray-500 transition duration-300 md:text-base {{if or (eq .Current "request") (eq .Current "")}} text-lime-500 border-lime-500 border-b-2 {{ else }} text-gray-500 hover:text-lime-500 {{ end }}"
>
Request
</a>
<a
href="/about"
class="px-1 py-1 text-gray-500 transition duration-300 md:text-base {{if or (eq .Current "about") (eq .Current "")}} text-lime-500 border-lime-500 border-b-2 {{ else }} text-gray-500 hover:text-lime-500 {{ end }}"
>
About
</a>
<a
href="/about#support"
class="px-1 py-1 text-gray-500 transition duration-300 md:text-base hover:text-lime-500"
>
Support
</a>
</div>
</div>
</nav>
{{if .Error}}
<div class="flex items-center justify-center">
<p class="px-2 py-2 font-bold text-center rounded-lg bg-red-500/70 text-md text-neutral-100">
{{ .Error }}
</p>
</div>
{{end}}
{{if .Message}}
<div class="flex items-center justify-center">
<p class="px-2 py-2 font-bold text-center rounded-lg text-md bg-blue-500/70 text-neutral-100">
{{ .Message }}
</p>
</div>
{{end}}
<!---->
{{ template "content" . }}
<footer class="my-6 space-x-4 text-md text-center *:text-lime-600">
<a class="hover:text-lime-300" href="https://nostr.com/npub188x98j0r7l2fszeph6j7hj99h8xl07n989pskk5zd69d2fcksetq5mgcqf">nostr</a>
<a class="hover:text-lime-300" href="https://fosstodon.org/@kycnotme">mastodon</a>
<a class="hover:text-lime-300" href="http://kycnotmezdiftahfmc34pqbpicxlnx3jbf5p7jypge7gdvduu7i6qjqd.onion/">tor</a>
<!-- <a class="hover:text-lime-300" href="http://kycnot3724gujekedbe6pwoejfbnggdv26losox25iaokev5ctaq.b32.i2p/">i2p</a> -->
</footer>
</body>
</html>
<style>
.gradient {
background: rgb(5, 58, 24);
background: linear-gradient(153deg,
rgb(3, 53, 21) 0%,
rgb(0, 17, 4) 20%,
rgba(0, 0, 0, 1) 100%);
}
</style>

View File

@ -1,217 +0,0 @@
<style>
article {
margin-left: 1rem;
margin-right: 1rem;
font-size: 1rem;
}
article p {
margin-bottom: 1rem;
font-size: 1.05rem;
}
p {
margin-top: 1rem;
}
article a {
color: rgb(163 230 53);
text-decoration: underline;
text-underline-offset: 0.2rem;
}
h1,
h2,
h3,
h4 {
margin-top: 1rem;
margin-bottom: 0.3rem;
font-weight: bold;
font-family: samo;
color: rgb(163 230 53);
}
h1 {
font-size: 3rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
margin-top: 1.1em;
font-size: 1rem;
}
h3::before {
content: "~ ";
}
ul {
margin-top: 0.3rem;
margin-bottom: 0.5rem;
padding-left: 1rem;
list-style: square;
}
li {
margin-bottom: 0.15rem;
}
article a {
text-decoration: none;
}
article blockquote {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
padding-left: 1rem;
border-left: 0.3rem solid rgb(163 230 53);
color: rgb(142, 146, 150);
}
code {
background: #2b282872;
border: 1px solid #494242;
border-radius: 0.2rem;
font-weight: bold;
border-left: 3px solid #89f333;
color: #d3cccc;
font-family: samo;
max-width: 100%;
overflow: scorll;
padding: 1em 1em;
display: block;
word-wrap: break-word;
max-width: 90vw;
margin-top: 1em;
}
hr {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
</style>
<div id="main" class="flex flex-col justify-center items-center my-8">
<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">
nostr?
</h2>
<p>
Start by reading
<a target="_blank" href="https://usenostr.org" target="_blank"
>the nostr basics</a
>
to understand how Nostr works.
</p>
<h3 id="creating an account" class="pt-4 w-full border-t border-white/20">
creating an account
</h3>
<p>
Creating a Nostr account is as easy as generating a private key. From
the private key, your public key will be derived. The public key acts as
your <b>username</b>.
</p>
<p>
In this guide, we will use
<a
target="_blank"
href="https://github.com/nostr-protocol/nips/blob/master/07.md"
>Browser Signer Extensions</a
>
to generate the private key, these act as a vault for your private key,
and Nostr apps will interact with the extension, issuing sign requests.
This way, your private key never leaves your computer local storage.
</p>
<p>
For this, I recommend you use
<a target="_blank" href="https://github.com/reyamir/nostr-connect"
>Nostr Connect</a
>
extension. Install it on
<a
target="_blank"
href="https://chromewebstore.google.com/detail/nostr-connect/ampjiinddmggbhpebhaegmjkbbeofoaj"
>Chrome</a
>
or
<a
target="_blank"
href="https://addons.mozilla.org/en-US/firefox/addon/nostr-connect/"
>Firefox</a
>. You can also find a list of available Signers
<a
target="_blank"
href="https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions"
>here</a
>.
</p>
<p>
Then, open the extension, generate a private key and click save. That's
all, you are now part of Nostr.
</p>
<p>Other ways to generate a key:</p>
<ul>
<li>
Use key mining tools like
<a target="_blank" href="https://github.com/grunch/rana">rana</a>.
</li>
<li>
Use
<a target="_blank" href="https://github.com/fiatjaf/noscl"
>nostr CLI</a
>
</li>
<li>
Use platforms like
<a target="_blank" href="https://primal.net">primal</a>,
<a target="_blank" href="https://snort.social/">snort</a> or
<a target="_blank" href="https://coracle.social/">coracle</a>.
</li>
</ul>
<h3
id="configure your profile"
class="pt-4 w-full border-t border-white/20"
>
configure your profile
</h3>
<p>
Visit
<a target="_blank" href="https://metadata.nostr.com/"
>Nostr Profile Manager</a
>
and configure your profile:
</p>
<ul>
<li>Configure the Metadata fields.</li>
<li>
Add some relays. Check out
<a target="_blank" href="https://nostr.watch">nostr.watch</a> for a
list of relays.
</li>
</ul>
<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, 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>
</div>

View File

@ -1,39 +0,0 @@
<a href="/attr/{{.ID}}" class="block py-1.5 px-3 text-center text-sm border rounded-lg transition duration-300
{{if eq "good" .Rating }}border-green-900 bg-green-900/30 hover:bg-green-900/50 hover:border-green-700
{{else if eq "warn" .Rating }}border-amber-900 bg-amber-900/30 hover:bg-amber-900/50 hover:border-amber-700
{{else if eq "bad" .Rating }}border-red-900 bg-red-900/30 hover:bg-red-900/50 hover:border-red-700
{{else}}border-blue-900 bg-blue-900/30 hover:bg-blue-900/50 hover:border-blue-700
{{end}}">
<span class="flex items-center">
<!-- Icon -->
<span class="flex-shrink-0">
{{if eq "good" .Rating }}
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-green-600 circle-check-filled" 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="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z" stroke-width="0" fill="currentColor"></path>
</svg>
{{else if eq "warn" .Rating }}
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-amber-600 alert-triangle-filled" 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="M12 1.67c.955 0 1.845 .467 2.39 1.247l.105 .16l8.114 13.548a2.914 2.914 0 0 1 -2.307 4.363l-.195 .008h-16.225a2.914 2.914 0 0 1 -2.582 -4.2l.099 -.185l8.11 -13.538a2.914 2.914 0 0 1 2.491 -1.403zm.01 13.33l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -7a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z" stroke-width="0" fill="currentColor"></path>
</svg>
{{else if eq "bad" .Rating }}
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-600/75 alert-circle-filled" 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="M12 2c5.523 0 10 4.477 10 10a10 10 0 0 1 -19.995 .324l-.005 -.324l.004 -.28c.148 -5.393 4.566 -9.72 9.996 -9.72zm.01 13l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -8a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z" stroke-width="0" fill="currentColor"></path>
</svg>
{{else }}
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-blue-600 blue-circle-filled" 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="M12 2c5.523 0 10 4.477 10 10a10 10 0 0 1 -19.995 .324l-.005 -.324l.004 -.28c.148 -5.393 4.566 -9.72 9.996 -9.72zm0 9h-1l-.117 .007a1 1 0 0 0 0 1.986l.117 .007v3l.007 .117a1 1 0 0 0 .876 .876l.117 .007h1l.117 -.007a1 1 0 0 0 .876 -.876l.007 -.117l-.007 -.117a1 1 0 0 0 -.764 -.857l-.112 -.02l-.117 -.006v-3l-.007 -.117a1 1 0 0 0 -.876 -.876l-.117 -.007zm.01 -3l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007z" stroke-width="0" fill="currentColor"></path>
</svg>
{{end}}
</span>
<!-- End icon -->
<span class="flex-1 ml-2 overflow-hidden capitalize-first whitespace-nowrap overflow-ellipsis">
{{.Title}}
</span>
</span>
</a>

View File

@ -0,0 +1,37 @@
package partials
import (
"kycnot.me/internal/ent"
"kycnot.me/internal/ent/attribute"
)
templ AttributeLine(attr *ent.Attribute) {
<a
href={ templ.SafeURL("/attribute/" + attr.Slug) }
class={
"block py-1.5 px-3 text-center text-sm border rounded-lg transition duration-300",
templ.KV("border-green-900 bg-green-900/30 hover:bg-green-900/50 hover:border-green-700", attr.Rating == attribute.RatingGood),
templ.KV("border-red-900 bg-red-900/30 hover:bg-red-900/50 hover:border-red-700", attr.Rating == attribute.RatingBad),
templ.KV("border-blue-900 bg-blue-900/30 hover:bg-blue-900/50 hover:border-blue-700", attr.Rating == attribute.RatingInfo),
templ.KV("border-amber-900 bg-amber-900/30 hover:bg-amber-900/50 hover:border-amber-700", attr.Rating == attribute.RatingWarn),
}
>
<span class="flex items-center">
<span class="flex-shrink-0">
switch attr.Rating {
case attribute.RatingGood:
<i class="text-green-600 icon-[tabler--circle-check-filled]"></i>
case attribute.RatingBad:
<i class="icon-[tabler--alert-circle-filled] text-red-600/75"></i>
case attribute.RatingWarn:
<i class="icon-[tabler--alert-triangle-filled] text-amber-600"></i>
case attribute.RatingInfo:
<i class="text-blue-600 icon-[tabler--info-circle-filled]"></i>
}
</span>
<span class="flex-1 ml-2 overflow-hidden capitalize-first whitespace-nowrap overflow-ellipsis">
{ attr.Title }
</span>
</span>
</a>
}

View File

@ -1,118 +0,0 @@
<div
class="flex flex-col justify-center max-w-lg p-4 text-sm border rounded-lg md:text-base
{{if eq . 0}} border-green-500/20 bg-green-500/10
{{else if eq . 1}} border-lime-300/40 bg-lime-300/10
{{else if eq . 2}} border-amber-700/40 bg-amber-600/20
{{else if eq . 3}} border-red-600/40 bg-red-600/20
{{end}}
"
>
<h2 class="flex items-center space-x-2 text-lg font-bold">
{{if eq . 0}}
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-green-600 circle-check-filled"
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="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z"
stroke-width="0"
fill="currentColor"
></path>
</svg>
{{else if eq . 1}}
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-lime-400/70 circle-check"
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="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
<path d="M9 12l2 2l4 -4"></path>
</svg>
{{else if eq . 2}}
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-amber-600 alert-triangle-filled"
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="M12 1.67c.955 0 1.845 .467 2.39 1.247l.105 .16l8.114 13.548a2.914 2.914 0 0 1 -2.307 4.363l-.195 .008h-16.225a2.914 2.914 0 0 1 -2.582 -4.2l.099 -.185l8.11 -13.538a2.914 2.914 0 0 1 2.491 -1.403zm.01 13.33l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -7a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z"
stroke-width="0"
fill="currentColor"
></path>
</svg>
{{else if eq . 3}}
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-red-600 alert-octagon-filled"
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="M14.897 1a4 4 0 0 1 2.664 1.016l.165 .156l4.1 4.1a4 4 0 0 1 1.168 2.605l.006 .227v5.794a4 4 0 0 1 -1.016 2.664l-.156 .165l-4.1 4.1a4 4 0 0 1 -2.603 1.168l-.227 .006h-5.795a3.999 3.999 0 0 1 -2.664 -1.017l-.165 -.156l-4.1 -4.1a4 4 0 0 1 -1.168 -2.604l-.006 -.227v-5.794a4 4 0 0 1 1.016 -2.664l.156 -.165l4.1 -4.1a4 4 0 0 1 2.605 -1.168l.227 -.006h5.793zm-2.887 14l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -8a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z"
stroke-width="0"
fill="currentColor"
></path>
</svg>
{{end}}
<span> KYC LEVEL <b>{{.}}</b> </span>
</h2>
{{if eq . 0}}
<p class="mt-2 text-sm">
The exchange ToS <b class="font-bold">do not mention</b> that it will ever
request the user for a KYC verification.
</p>
{{else if eq . 1}}
<p class="mt-2 text-sm">
KYC is <b class="font-bold">not mentioned</b>, but this service
<b class="font-bold">reserves the right to share data</b> with
representatives of the authorities, block funds or reject transactions.
</p>
{{else if eq . 2}}
<p class="mt-2 text-sm">
The exchange
<b class="font-bold">may request KYC from any user at any time</b>,
typically triggered by an automated flag system, leading to a temporary
block of funds.
</p>
{{else if eq . 3}}
<p class="mt-2 text-sm">
KYC is <b class="font-bold">mandatory to use some features</b>. A non-KYCed
user can be required to verify their identity at any time or any moment for
any reason.
</p>
{{end}}
</div>

View File

@ -1,50 +0,0 @@
{{/* service_card.html */}}
<a href="/service/{{.Name}}" class="w-full max-w-md">
<div class="flex flex-col justify-between p-4 bg-gray-700 bg-opacity-30 rounded-lg border border-transparent shadow-lg backdrop-blur-md transition duration-500 hover:border-lime-600/70">
<div class="flex items-center">
<img src="/api/g/picture/{{.ID}}" alt="{{.Name}} logo" class="mr-4 w-12 h-12 rounded-full bg-white/10" />
<div class="flex flex-col justify-center items-center text-lg font-bold text-white md:text-xl">
<span class="flex items-center pb-1 w-full text-base font-bold">
{{if .Verified}}
<svg xmlns="http://www.w3.org/2000/svg" class="inline size-5 text-blue-400/80 discount-check-filled" 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="M12.01 2.011a3.2 3.2 0 0 1 2.113 .797l.154 .145l.698 .698a1.2 1.2 0 0 0 .71 .341l.135 .008h1a3.2 3.2 0 0 1 3.195 3.018l.005 .182v1c0 .27 .092 .533 .258 .743l.09 .1l.697 .698a3.2 3.2 0 0 1 .147 4.382l-.145 .154l-.698 .698a1.2 1.2 0 0 0 -.341 .71l-.008 .135v1a3.2 3.2 0 0 1 -3.018 3.195l-.182 .005h-1a1.2 1.2 0 0 0 -.743 .258l-.1 .09l-.698 .697a3.2 3.2 0 0 1 -4.382 .147l-.154 -.145l-.698 -.698a1.2 1.2 0 0 0 -.71 -.341l-.135 -.008h-1a3.2 3.2 0 0 1 -3.195 -3.018l-.005 -.182v-1a1.2 1.2 0 0 0 -.258 -.743l-.09 -.1l-.697 -.698a3.2 3.2 0 0 1 -.147 -4.382l.145 -.154l.698 -.698a1.2 1.2 0 0 0 .341 -.71l.008 -.135v-1l.005 -.182a3.2 3.2 0 0 1 3.013 -3.013l.182 -.005h1a1.2 1.2 0 0 0 .743 -.258l.1 -.09l.698 -.697a3.2 3.2 0 0 1 2.269 -.944zm3.697 7.282a1 1 0 0 0 -1.414 0l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.32 1.497l2 2l.094 .083a1 1 0 0 0 1.32 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z"
stroke-width="0" fill="currentColor"></path>
</svg>
{{end}}
<span class="flex-1 text-sm capitalize whitespace-nowrap sm:text-base ms-3">{{.Name}}</span>
<span
class="inline-flex justify-center items-center px-2 py-0.5 text-xs font-medium rounded ms-3
{{if ge .Score 10}}
bg-green-900 border-green-800
{{else if gt .Score 8}}
bg-green-800 border-green-700
{{else if gt .Score 7}}
bg-lime-900 border-lime-800
{{else if gt .Score 6}}
bg-lime-700/80 border-lime-600/80
{{else if gt .Score 5}}
bg-yellow-800 border-yellow-700
{{else if eq .Score 5}}
bg-orange-900 border-orange-800
{{else}}
bg-red-900 border-red-800
{{end}}
text-white/70">{{.Score}}</span>
</span>
<span class="block pr-1 mt-2 text-xs font-normal text-opacity-60 text-white/60">{{shortText .Description | safe}}</span>
</div>
</div>
<div class="flex justify-center items-center mt-4 text-xs">
{{if eq .Type "exchange"}}
<span class="px-2 py-1 mr-1 text-xs font-bold uppercase rounded border text-white/70 bg-zinc-900 border-zinc-700"> {{.Type}} </span>
{{else}}
<span class="px-2 py-1 mr-1 text-xs font-bold uppercase rounded border text-white/70 bg-zinc-900 border-zinc-700"> {{.Category}} </span>
{{end}} {{partial "partials/service_icons" .}}
</div>
</div>
</a>

View File

@ -0,0 +1,63 @@
package partials
import (
"fmt"
"kycnot.me/internal/ent"
)
func getScoreColor(s int) string {
switch {
case s >= 10:
return "bg-green-900 border-green-800"
case s > 8:
return "bg-green-800 border-green-700"
case s > 7:
return "bg-lime-900 border-lime-800"
case s > 6:
return "bg-lime-700/80 border-lime-600/80"
case s > 5:
return "bg-yellow-800 border-yellow-700"
case s == 5:
return "bg-orange-900 border-orange-800"
default:
return "bg-red-900 border-red-800"
}
}
templ ServiceCard(service *ent.Service) {
<a href={ templ.SafeURL(fmt.Sprintf("/service/%s", service.Slug)) } class="block w-full max-w-md">
<div
class="flex flex-col justify-between p-4 bg-gray-700 bg-opacity-30 rounded-lg border border-transparent shadow-lg backdrop-blur-md transition duration-500 hover:border-lime-600/70"
>
<div class="flex items-center">
<img src={ "/static/images/" + service.Slug + ".jpg" } alt={ service.Name + " logo" } class="mr-4 w-12 h-12 rounded-full bg-white/10"/>
<div class="flex flex-col justify-center items-center text-lg font-bold text-white md:text-xl">
<span class="flex items-center pb-1 w-full text-base font-bold">
if service.Verified {
<i class="icon-[tabler--rosette-discount-check-filled] inline text-xl text-blue-400/80"></i>
}
<span class="flex-1 text-sm whitespace-nowrap sm:text-base ms-2">{ service.Name }</span>
if !service.Pending {
<span class={ "inline-flex justify-center items-center px-2 py-0.5 text-xs font-medium rounded ms-3 text-white/70 " + getScoreColor(service.Score) }>{ fmt.Sprintf("%d", service.Score) }</span>
} else {
<span class={ "inline-flex justify-center items-center px-2 py-0.5 text-xs font-medium rounded ms-3 text-white/70 bg-gray-900 border-gray-800" }>PENDING</span>
}
</span>
<span class="px-1 mt-1.5 text-xs font-normal text-opacity-60 overflow-ellipsis text-white/60 line-clamp-2">
{ service.Description }
</span>
</div>
</div>
<div class="flex justify-center items-center mt-4 text-xs">
<span class="px-2 py-1 mr-1 text-xs font-bold uppercase rounded border text-white/70 bg-zinc-900 border-zinc-700">
if service.Type == "exchange" {
EXCHANGE
} else {
{ service.Category }
}
</span>
@ServiceIcons(service)
</div>
</div>
</a>
}

View File

@ -1,56 +0,0 @@
<!-- Monero -->
<span aria-label="XMR" data-balloon-pos="up" class="px-2 py-1 m-1 text-center text-white rounded border bg-zinc-900 border-zinc-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 {{if .Xmr}}text-orange-400/70{{else}} text-white/20 {{end}} tabler-coin-monero" 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="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
<path d="M4 16h4v-7l4 4l4 -4v7h4"></path>
</svg>
</span>
<!-- Bitcoin -->
<span aria-label="BTC" data-balloon-pos="up" class="px-2 py-1 m-1 text-center text-white rounded border bg-zinc-900 border-zinc-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 {{if .Btc}} text-amber-400/70 {{else}} text-white/20 {{end}} coin-bitcoin" 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="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
<path d="M9 8h4.09c1.055 0 1.91 .895 1.91 2s-.855 2 -1.91 2c1.055 0 1.91 .895 1.91 2s-.855 2 -1.91 2h-4.09"></path>
<path d="M10 12h4"></path>
<path d="M10 7v10v-9"></path>
<path d="M13 7v1"></path>
<path d="M13 16v1"></path>
</svg>
</span>
<!-- Lightning -->
<span aria-label="LN" data-balloon-pos="up" class="px-2 py-1 m-1 text-center text-white rounded border bg-zinc-900 border-zinc-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 {{if .Lightning}} text-sky-400/70 {{else}} text-white/20 {{end}} bolt" 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="M13 3l0 7l6 0l-8 11l0 -7l-6 0l8 -11"></path>
</svg>
</span>
<!-- Cash -->
<span aria-label="CASH" data-balloon-pos="up" class="px-2 py-1 m-1 text-center text-white rounded border bg-zinc-900 border-zinc-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 {{if .Cash}} text-yellow-300/70 {{else}} text-white/20 {{end}}" 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="M9 14c0 1.657 2.686 3 6 3s6 -1.343 6 -3s-2.686 -3 -6 -3s-6 1.343 -6 3z"></path>
<path d="M9 14v4c0 1.656 2.686 3 6 3s6 -1.344 6 -3v-4"></path>
<path d="M3 6c0 1.072 1.144 2.062 3 2.598s4.144 .536 6 0c1.856 -.536 3 -1.526 3 -2.598c0 -1.072 -1.144 -2.062 -3 -2.598s-4.144 -.536 -6 0c-1.856 .536 -3 1.526 -3 2.598z"></path>
<path d="M3 6v10c0 .888 .772 1.45 2 2"></path>
<path d="M3 11c0 .888 .772 1.45 2 2"></path>
</svg>
</span>
<!-- Fiat -->
<span aria-label="FIAT" data-balloon-pos="up" class="px-2 py-1 m-1 text-center text-white rounded border bg-zinc-900 border-zinc-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 {{if .Fiat}}text-green-400/70{{else}} text-white/20 {{end}} building-bank" 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="M3 21l18 0"></path>
<path d="M3 10l18 0"></path>
<path d="M5 6l7 -3l7 3"></path>
<path d="M4 10l0 11"></path>
<path d="M20 10l0 11"></path>
<path d="M8 14l0 3"></path>
<path d="M12 14l0 3"></path>
<path d="M16 14l0 3"></path>
</svg>
</span>

View File

@ -0,0 +1,41 @@
package partials
import "kycnot.me/internal/ent"
templ ServiceIcons(service *ent.Service) {
<span aria-label="XMR" data-balloon-pos="up" class="flex items-center justify-center py-1 px-1.5 mx-0.5 border rounded bg-zinc-900 border-zinc-700">
<span
class={ "icon-[tabler--coin-monero] text-base",
templ.KV("text-orange-500/80", service.Xmr),
templ.KV("text-white/30", !service.Xmr) }
></span>
</span>
<span aria-label="BTC" data-balloon-pos="up" class="flex items-center justify-center py-1 px-1.5 mx-0.5 border rounded bg-zinc-900 border-zinc-700">
<span
class={ "icon-[tabler--coin-bitcoin] text-base" ,
templ.KV("text-yellow-500/80", service.Btc),
templ.KV("text-white/30", !service.Btc) }
></span>
</span>
<span aria-label="Lightning" data-balloon-pos="up" class="flex items-center justify-center py-1 px-1.5 mx-0.5 border rounded bg-zinc-900 border-zinc-700">
<span
class={ "icon-[tabler--bolt] text-base" ,
templ.KV("text-blue-500/80", service.Lightning),
templ.KV("text-white/30", !service.Lightning) }
></span>
</span>
<span aria-label="Cash" data-balloon-pos="up" class="flex items-center justify-center py-1 px-1.5 mx-0.5 border rounded bg-zinc-900 border-zinc-700">
<span
class={ "icon-[tabler--cash] text-base" ,
templ.KV("text-green-500/80", service.Cash),
templ.KV("text-white/30", !service.Cash) }
></span>
</span>
<span aria-label="Fiat" data-balloon-pos="up" class="flex items-center justify-center py-1 px-1.5 mx-0.5 border rounded bg-zinc-900 border-zinc-700">
<span
class={ "icon-[tabler--building-bank] text-base" ,
templ.KV("text-purple-500/80", service.Fiat),
templ.KV("text-white/30", !service.Fiat) }
></span>
</span>
}

View File

@ -1,9 +0,0 @@
<div class="px-3 py-2 mt-2 border rounded-md w-full
{{if eq .Warning true}}border-amber-900 bg-amber-900/30{{end}}
{{if eq .Warning false}}border-blue-900 bg-blue-900/30{{end}}">
<h3 class="text-sm font-bold uppercase">{{.Title}}</h3>
<p class="my-1.5 text-sm">{{.Details}}</p>
{{if .Section}}
<p class="text-xs text-gray-400 text-opacity-40">ToS section: <span>"{{.Section}}"</span></p>
{{end}}
</div>

View File

@ -1,18 +0,0 @@
<main class="py-2 text-lime-500">
<div class="mb-2 text-center bg-transparent md:grid">
<p class="font-mono text-2xl font-bold uppercase md:text-base">Pending Services</p>
</div>
</main>
<!-- Services list -->
<section class="flex items-center justify-center p-12 px-3 pt-4 m-4">
{{if .Services}}
<div class="grid max-w-6xl grid-cols-1 gap-3 md:grid-cols-3 lg:grid-cols-3">
{{range .Services}}
{{partial "partials/service_card" .}}
{{end}}
</div>
{{else}}
<h2 class="my-8 text-xl font-bold text-center">No pending services.</h2>
{{end}}
</section>

View File

@ -1,236 +0,0 @@
<section class="flex flex-col items-center w-full py-8">
<h2 class="text-2xl font-bold">Request a service</h2>
<small class="opacity-70">*This form requires JavaScript, <a class="text-lime-600" href="/about#request">read more
here</a>.</small>
<form action="/new/service" method="POST" class="max-w-lg my-6 space-y-4 font-mono">
<div class="flex flex-col">
<label class="font-bold" for="type">Service Type</label>
<select name="type" id="type" required
class="p-2 text-white uppercase border rounded-lg bg-opacity-30 bg-zinc-900 border-white/30">
<option selected value="exchange">exchange</option>
<option value="service">service</option>
</select>
</div>
<div id="category-container" class="flex flex-col">
<label class="font-bold" for="category">Service Category</label>
<select name="category" id="category" required
class="p-2 text-white capitalize border rounded-lg bg-opacity-30 bg-zinc-900 border-white/30">
<option value="aggregator">aggregator</option>
<option value="atm">physical atm</option>
<option value="cloud">cloud storage</option>
<option value="email">email</option>
<option value="goods">goods</option>
<option value="hosting">hosting / domains</option>
<option value="market">market</option>
<option value="proxy store">proxy store</option>
<option value="sms">sms / phone number</option>
<option value="tools">tools</option>
<option value="vpn">vpn</option>
<option value="vps">server hosting (vps)</option>
</select>
</div>
<div class="flex flex-col">
<label class="font-bold" for="name">Service Name</label>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
placeholder="Awesome exchange" type="text" name="name" id="name" required>
</div>
<div class="flex flex-col">
<label class="font-bold" for="description">Description</label>
<textarea
class="p-2 font-mono text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
name="description" id="description"
placeholder="concise description about what the service is. max 500 characters." cols="30" rows="5"
required minlength="60" maxlength="500"></textarea>
</div>
<div class="flex flex-col">
<label class="font-bold" for="urls">Service URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
type="text" name="urls" id="urls" required placeholder="https://example.com">
</div>
<div class="flex flex-col">
<label class="font-bold" for="logo_url">Logo URL</label>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
type="text" name="logo_url" id="logo_url" placeholder="https://i.imgur.com/wwwHfCN.jpeg" required>
</div>
<div class="flex flex-col">
<label class="font-bold" for="tos_urls">ToS URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
type="text" name="tos_urls" id="tos_urls" required placeholder="https://example.com/terms">
</div>
<div class="flex flex-col">
<label class="font-bold" for="onion_urls">Tor Onion URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
type="text" name="onion_urls" id="onion_urls" placeholder="http://zbiowkw...onion">
</div>
<div class="flex flex-col">
<label class="font-bold" for="tags">Keywords</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white border rounded-lg placeholder-white/30 bg-opacity-30 bg-zinc-900 border-white/30"
type="text" name="tags" id="tags" required placeholder="private,p2p, fast">
</div>
<div class="flex flex-col items-center justify-center pt-4 space-x-2">
<h3 class="mb-2 text-xl">Accepted currencies</h3>
<div class="flex space-x-4">
<span class="flex flex-col items-center justify-center">
<label class="text-xs uppercase" for="btc">Bitcoin</label>
<input
class="rounded-full outline-none appearance-none bg-opacity-30 bg-zinc-900 focus:ring-0 border-lime-500 text-lime-500"
type="checkbox" {{if eq "on" .Filters.Btc}}checked{{end}} name="btc" id="btc">
</span>
<span class="flex flex-col items-center justify-center">
<label class="text-xs uppercase" for="xmr">MONERO</label>
<input
class="rounded-full outline-none appearance-none bg-opacity-30 bg-zinc-900 focus:ring-0 border-lime-500 text-lime-500"
type="checkbox" {{if eq "on" .Filters.Xmr}}checked{{end}} name="xmr" id="xmr">
</span>
<span class="flex flex-col items-center justify-center">
<label class="text-xs uppercase" for="ln">LIGHTNING</label>
<input
class="rounded-full outline-none appearance-none bg-opacity-30 bg-zinc-900 focus:ring-0 border-lime-500 text-lime-500"
type="checkbox" {{if eq "on" .Filters.Ln}}checked{{end}} name="ln" id="ln">
</span>
<span class="flex flex-col items-center justify-center">
<label class="text-xs uppercase" for="fiat">FIAT</label>
<input
class="rounded-full outline-none appearance-none bg-opacity-30 bg-zinc-900 focus:ring-0 border-lime-500 text-lime-500"
type="checkbox" {{if eq "on" .Filters.Fiat}}checked{{end}} name="fiat" id="fiat">
</span>
<span class="flex flex-col items-center justify-center">
<label class="text-xs uppercase" for="cash">CASH</label>
<input
class="rounded-full outline-none appearance-none bg-opacity-30 bg-zinc-900 focus:ring-0 border-lime-500 text-lime-500"
type="checkbox" {{if eq "on" .Filters.Cash}}checked{{end}} name="cash" id="cash">
</span>
</div>
</div>
<div class="space-y-4">
<h3 class="font-bold">KYC Level</h3>
<label class="flex flex-row items-center p-2 border rounded-lg border-white/10" for="kyclevel0">
<input type="radio" name="kyc_level" id="kyclevel0" value="0">
<p class="ml-3"><b class="text-lime-400">Level 0</b>: No KYC ever. The ToS do not mention nor enforce
any KYC/AML verification.</p>
</label>
<label class="flex flex-row items-center p-2 border rounded-lg border-white/10" for="kyclevel1">
<input type="radio" name="kyc_level" id="kyclevel1" value="1">
<p class="ml-3"><b class="text-lime-400">Level 1</b>: No KYC/AML procedures are mentioned as such,
however the service reserves the right to share acquired data with authorities, block funds or
reject transactions.</p>
</label>
<label class="flex flex-row items-center p-2 border rounded-lg border-white/10" for="kyclevel2">
<input type="radio" name="kyc_level" id="kyclevel2" value="2" checked>
<p class="ml-3"><b class="text-lime-400">Level 2</b>: KYC is not mandatory, but the service can require
KYC from any user at any time, and it may block funds. This could occur, for example, if a
transaction is flagged suspicious.</p>
</label>
<label class="flex flex-row items-center p-2 border rounded-lg border-white/10" for="kyclevel3">
<input type="radio" name="kyc_level" id="kyclevel3" value="3">
<p class="ml-3"><b class="text-lime-400">Level 3</b>: KYC is mandatory to use certain features of the
platform. Non-KYC users can be asked to verify their identity at any time, for any reason and may
have their service restricted or revoked.</p>
</label>
</div>
<h3 class="font-bold">Attributes</h3>
<small class="text-white/50">* Scrollable. You must check at least 3</small>
<div class="max-w-lg p-4 mx-4 mb-4 space-y-2 overflow-scroll border rounded-lg h-52 border-white/10">
{{range .Attributes}}
<span class="block p-1 border-b border-white/10">
<input type="checkbox" id="{{.ID}}" name="attributes" value="{{.ID}}">
<label class="ml-1
{{if eq .Rating " info"}}text-blue-400/80{{end}} {{if eq .Rating "good"
}}text-green-400/80{{end}} {{if eq .Rating "warn" }}text-yellow-400/80{{end}} {{if eq .Rating "bad"
}}text-red-400/80{{end}} " for=" {{.ID}}">{{.Title}}</label>
</span>
{{end}}
</div>
<noscript>
<p class="my-2 font-bold text-yellow-500 uppercase">
You need to enable JavaScript to complete this form.
</p>
</noscript>
<div id="start-pow"
class="flex items-center justify-center px-4 py-2 mt-2 mb-4 space-x-2 font-bold uppercase bg-blue-900 rounded-lg cursor-pointer"
data-pow="{{.Pow.Id}}" data-pow-c="{{.Pow.Challenge}}" data-pow-d="{{.Pow.Difficulty}}">
<input type="text" name="pow-nonce" id="pow-nonce" hidden required value="">
<input type="text" name="pow-id" id="pow-id" hidden required value="{{.Pow.Id}}">
<svg id="pow-spinner" class="hidden text-white fill-white" width="24" height="24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<style>
.spinner_ZCsl {
animation: spinner_qV4G 1.2s cubic-bezier(0.52, .6, .25, .99) infinite
}
.spinner_gaIW {
animation-delay: .6s
}
@keyframes spinner_qV4G {
0% {
r: 0;
opacity: 1
}
100% {
r: 11px;
opacity: 0
}
}
</style>
<circle class="spinner_ZCsl" cx="12" cy="12" r="0" />
<circle class="spinner_ZCsl spinner_gaIW" cx="12" cy="12" r="0" />
</svg>
<span id="pow-text">🧠 I'm Human</span>
</div>
<button id="submit-btn"
class="hidden w-full px-4 py-2 my-4 space-x-2 font-bold uppercase rounded-lg cursor-pointer bg-lime-900">
Submit
</button>
</form>
</section>
<script src="/static/js/pow.js"></script>
<script>
// Initialize PoW service
startPow();
// Toggle category select based on type select
document.addEventListener('DOMContentLoaded', (event) => {
const typeSelect = document.getElementById('type');
const categoryDiv = document.getElementById('category-container');
function toggleCategory() {
if (typeSelect.value === 'service') {
categoryDiv.style.display = 'flex'; // Show
} else {
categoryDiv.style.display = 'none'; // Hide
}
}
// Initial toggle based on the preselected option
toggleCategory();
// Add change event listener to the type select element
typeSelect.addEventListener('change', toggleCategory);
});
</script>

View File

@ -1,379 +0,0 @@
<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="flex flex-col items-center justify-center p-2 my-4 mt-8 mb-2 space-y-4 font-bold text-md md:text-md">
<!-- Pending Approval Notice -->
{{if .Service.Pending}}
<div class="flex items-center justify-center">
<a href="#" target="_blank" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-yellow-500 transition duration-300 border border-yellow-600 rounded bg-zinc-900 hover:border-yellow-600 hover:text-yellow-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-yellow-600 tabler-alert" 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>
<circle cx="12" cy="12" r="9"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
<span>Pending approval</span>
</a>
</div>
{{end}}
<!-- Not Listed Notice -->
{{if and (not .Service.Pending) (not .Service.Listed)}}
<div class="flex items-center justify-center">
<a href="#" target="_blank"
class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-yellow-500 transition duration-300 border border-yellow-600 rounded bg-zinc-900 hover:border-yellow-600 hover:text-yellow-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 text-yellow-600 tabler-alert" 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>
<circle cx="12" cy="12" r="9"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
<span>DELISTED</span>
</a>
</div>
{{end}}
<!-- Display Service Notes -->
{{if not (eq (len .Service.Notes) 0)}}
{{range .Service.Notes}}
{{if ne . ""}}
<p class="max-w-lg p-4 text-sm border rounded-lg md:text-base border-yellow-400/20 bg-yellow-500/10">
{{.}}
</p>
{{end}}
{{end}}
{{end}}
<!-- Service Header -->
<div class="flex items-center justify-center space-x-4">
<img class="rounded-full size-10 md:size-14 bg-white/25" src="/api/g/picture/{{.Service.ID}}" alt="" />
<span class="flex items-center justify-center capitalize">
<span class="text-xl font-bold md:text-2xl">{{.Service.Name}}</span>
{{if .Service.Verified}}
<a aria-label="Verified" data-balloon-pos="up" href="/static/proofs/{{lower .Service.Name}}/proof.txt.asc">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 ml-1 {{if .Service.Verified}}text-blue-400/80{{else}} text-white/20 {{end}} discount-check-filled" 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="M12.01 2.011a3.2 3.2 0 0 1 2.113 .797l.154 .145l.698 .698a1.2 1.2 0 0 0 .71 .341l.135 .008h1a3.2 3.2 0 0 1 3.195 3.018l.005 .182v1c0 .27 .092 .533 .258 .743l.09 .1l.697 .698a3.2 3.2 0 0 1 .147 4.382l-.145 .154l-.698 .698a1.2 1.2 0 0 0 -.341 .71l-.008 .135v1a3.2 3.2 0 0 1 -3.018 3.195l-.182 .005h-1a1.2 1.2 0 0 0 -.743 .258l-.1 .09l-.698 .697a3.2 3.2 0 0 1 -4.382 .147l-.154 -.145l-.698 -.698a1.2 1.2 0 0 0 -.71 -.341l-.135 -.008h-1a3.2 3.2 0 0 1 -3.195 -3.018l-.005 -.182v-1a1.2 1.2 0 0 0 -.258 -.743l-.09 -.1l-.697 -.698a3.2 3.2 0 0 1 -.147 -4.382l.145 -.154l.698 -.698a1.2 1.2 0 0 0 .341 -.71l.008 -.135v-1l.005 -.182a3.2 3.2 0 0 1 3.013 -3.013l.182 -.005h1a1.2 1.2 0 0 0 .743 -.258l.1 -.09l.698 -.697a3.2 3.2 0 0 1 2.269 -.944zm3.697 7.282a1 1 0 0 0 -1.414 0l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.32 1.497l2 2l.094 .083a1 1 0 0 0 1.32 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z"
stroke-width="0"
fill="currentColor"
></path>
</svg>
</a>
{{end}}
</span>
</div>
<!-- Links -->
<div class="flex flex-wrap items-center justify-center space-x-2">
{{if ne (len .Service.Urls) 0}}
<div class="inline-flex">
<a href="{{randomElem .Service.Urls}}{{.Service.Referral}}" target="_blank"
class="flex justify-center items-center px-2 py-1 space-x-1 text-center text-gray-300
{{if ne .Service.Referral ""}}rounded-l{{else}}rounded{{end}}
border border-gray-600 transition duration-300 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 tabler-world-www" 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="M19.5 7a9 9 0 0 0 -7.5 -4a8.991 8.991 0 0 0 -7.484 4"></path>
<path d="M11.5 3a16.989 16.989 0 0 0 -1.826 4"></path>
<path d="M12.5 3a16.989 16.989 0 0 1 1.828 4"></path>
<path d="M19.5 17a9 9 0 0 1 -7.5 4a8.991 8.991 0 0 1 -7.484 -4"></path>
<path d="M11.5 21a16.989 16.989 0 0 1 -1.826 -4"></path>
<path d="M12.5 21a16.989 16.989 0 0 0 1.828 -4"></path>
<path d="M2 10l1 4l1.5 -4l1.5 4l1 -4"></path>
<path d="M17 10l1 4l1.5 -4l1.5 4l1 -4"></path>
<path d="M9.5 10l1 4l1.5 -4l1.5 4l1 -4"></path>
</svg>
<span>Website</span>
</a>
{{if ne .Service.Referral ""}}
<a aria-label="No Referral" data-balloon-pos="up" href="{{randomElem .Service.Urls}}" class="flex items-center justify-center px-1 py-1 text-center text-gray-300 transition duration-300 border border-gray-600 rounded-r md:px-2 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="size-4 link-off" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l3 -3m2 -2l1 -1" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path d="M3 3l18 18" />
<path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
</a>
{{end}}
</div>
{{end}}
{{if ne (len .Service.OnionUrls) 0}}
<div class="inline-flex">
<a href="{{randomElem .Service.OnionUrls}}{{.Service.Referral}}" target="_blank"
class="flex justify-center items-center px-2 py-1 space-x-1 text-center text-purple-600
{{if ne .Service.Referral ""}}rounded-l{{else}}rounded{{end}}
border border-purple-900 transition duration-300 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 tabler-target" 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="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path>
<path d="M12 12m-5 0a5 5 0 1 0 10 0a5 5 0 1 0 -10 0"></path>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
</svg>
{{if eq (len .Service.Urls) 0}}
<span>Tor Only</span>
{{else}}
<span>Tor</span>
{{end}}
{{if ne .Service.Referral ""}}
<a aria-label="No Referral" data-balloon-pos="up" href="{{randomElem .Service.OnionUrls}}" class="flex items-center justify-center px-1 py-1 text-center text-purple-600 transition duration-300 border border-purple-900 rounded-r md:px-2 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="size-4 link-off" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l3 -3m2 -2l1 -1" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path d="M3 3l18 18" />
<path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
</a>
{{end}}
</a>
</div>
{{end}}
{{if ne (len .Service.TosUrls) 0}}
<a href="{{index .Service.TosUrls 0}}" target="_blank" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-gray-300 transition duration-300 border border-gray-600 rounded bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 tabler-report-search" 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="M8 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h5.697"></path>
<path d="M18 12v-5a2 2 0 0 0 -2 -2h-2"></path>
<path d="M8 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z"></path>
<path d="M8 11h4"></path>
<path d="M8 15h3"></path>
<path d="M16.5 17.5m-2.5 0a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0"></path>
<path d="M18.5 19.5l2.5 2.5"></path>
</svg>
<span>ToS</span>
</a>
{{end}}
<a aria-label="Discuss service" data-balloon-pos="up" href="#discuss" class="items-center justify-center hidden px-2 py-1 m-1 space-x-2 text-center transition duration-300 border rounded text-sky-400 border-sky-700 md:flex bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 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>
</a>
</div>
<!-- Usage Guide Button -->
<!--<div class="flex flex-col items-center justify-center my-1 text-sm">
<a href="https://example.com" target="_blank" class="flex items-center justify-center max-w-xs px-2 py-1 m-1 space-x-2 text-center transition duration-300 border rounded text-lime-400 border-lime-700 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 compass" 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="M8 16l2 -6l6 -2l-2 6l-6 2"></path>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
<path d="M12 3l0 2"></path>
<path d="M12 19l0 2"></path>
<path d="M3 12l2 0"></path>
<path d="M19 12l2 0"></path>
</svg>
<span>Usage Guide</span>
</a>
</div>-->
<!-- Icons -->
<a href="/about#icons" class="flex items-center justify-center max-w-md mt-4 text-xs"> {{partial "partials/service_icons" .Service}} </a>
<!-- Description -->
<p class="max-w-lg p-4 text-sm border rounded-lg md:text-base border-white/20 bg-gray-500/10">{{if ne .Service.Description ""}} {{.Service.Description | safe}} {{else}} This {{.Service.Type}} does not have a description. {{end}}</p>
</section>
<!-- KYC Level -->
<section class="flex items-center justify-center px-2">{{partial "partials/kyc_level" .Service.KycLevel}}</section>
<section class="flex flex-col items-center justify-center p-4">
<!-- Notes / Comments -->
{{if .Service.Comments}}
<div class="mb-5 space-y-1.5 max-w-lg">
{{range .Service.Comments}}
<p
class="block px-3 py-1.5 text-sm font-bold rounded-lg border transition duration-300 border-amber-300/30 bg-amber-300/10 hover:bg-amber-300/20 hover:border-amber-100/50">
{{ . | safe }}</p>
{{end}}
</div>
{{end}}
<!-- Attributes -->
{{if .Attributes}}
<div class="grid grid-cols-1 gap-2 mt-2 auto-cols-max md:grid-cols-2">{{range .Attributes}} {{partial "partials/attribute_line" .}} {{end}}</div>
{{end}}
</section>
<!-- SERVICE CONTACT -->
{{if gt (len .Service.SupportMethods) 0}}
<section id="contact" class="flex flex-col items-center justify-center p-4">
<h3 class="mb-2 font-bold text-center uppercase text-md">Contact</h3>
<ul
class="flex items-center justify-center gap-2 flex-wrap text-sm [&>li>a]:p-2 [&>li>a]:rounded [&>li>a]:bg-white/10 hover:[&>li>a]:bg-white/20 [&>li>a]:transition [&>li>a]:duration-200">
{{ range .Service.SupportMethods }}
{{ if eq .Platform "telegram"}}
<li class="text-center">
<a class="flex items-center justify-center space-x-2" href="https://t.me/{{.Link}}">
<svg class="size-5 fill-blue-300" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Telegram</title>
<path
d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
</svg>
<span>@{{.Link}}</span>
</a>
</li>
{{ else if or (eq .Platform "mail") (eq .Platform "email") }}
<li class="text-center">
<a class="flex items-center justify-center space-x-2">
<svg class="size-4 fill-white/80" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Mail</title>
<path
d="M15.61 12c0 1.99-1.62 3.61-3.61 3.61-1.99 0-3.61-1.62-3.61-3.61 0-1.99 1.62-3.61 3.61-3.61 1.99 0 3.61 1.62 3.61 3.61M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12c2.424 0 4.761-.722 6.76-2.087l.034-.024-1.617-1.879-.027.017A9.494 9.494 0 0 1 12 21.54c-5.26 0-9.54-4.28-9.54-9.54 0-5.26 4.28-9.54 9.54-9.54 5.26 0 9.54 4.28 9.54 9.54a9.63 9.63 0 0 1-.225 2.05c-.301 1.239-1.169 1.618-1.82 1.568-.654-.053-1.42-.52-1.426-1.661V12A6.076 6.076 0 0 0 12 5.93 6.076 6.076 0 0 0 5.93 12 6.076 6.076 0 0 0 12 18.07a6.02 6.02 0 0 0 4.3-1.792 3.9 3.9 0 0 0 3.32 1.805c.874 0 1.74-.292 2.437-.821.719-.547 1.256-1.336 1.553-2.285.047-.154.135-.504.135-.507l.002-.013c.175-.76.253-1.52.253-2.457 0-6.617-5.383-12-12-12" />
</svg>
<span>{{mailsafe .Link}}</span>
</a>
</li>
{{ else if eq .Platform "url" }}
<li class="text-center">
<a class="flex items-center justify-center space-x-2" href="{{.Link}}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon size-5 icon-tabler icon-tabler-external-link" width="44"
height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#ffffff" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6" />
<path d="M11 13l9 -9" />
<path d="M15 4h5v5" />
</svg>
<span>Contact Page</span>
</a>
</li>
{{ else if eq .Platform "simplex" }}
<li class="text-center hover:underline">
<a class="flex items-center justify-center space-x-2" class="size-5 fill-white" href="{{.Link}}">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="currentColor"
d="M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2zm4 13h2l.35-2h4L13 18h2l.35-2h2l.36-2h-2l.7-4h2l.35-2h-2l.36-2h-2l-.36 2h-4l.36-2h-2l-.36 2h-2l-.35 2h2l-.7 4h-2l-.36 2h2zm3.41-8h4l-.7 4h-4z" />
</svg>
<span>SimpleX</span>
</a>
</li>
{{else if eq .Platform "matrix" }}
<li class="text-center hover:underline">
<a class="flex items-center justify-center space-x-2" href="{{.Link}}">
<svg xmlns="http://www.w3.org/2000/svg" class="size-5 fill-white" width="1em" height="1em" viewBox="0 0 256 256">
<path fill="currentColor"
d="M208 32H48a16 16 0 0 0-16 16v160a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16M80 200H64a8 8 0 0 1-8-8V64a8 8 0 0 1 8-8h16a8 8 0 0 1 0 16h-8v112h8a8 8 0 0 1 0 16m80-40a8 8 0 0 1-8-8v-32a8 8 0 0 0-16 0v32a8 8 0 0 1-16 0v-32a8 8 0 0 0-16 0v32a8 8 0 0 1-16 0v-48a8 8 0 0 1 13.66-5.65a23.94 23.94 0 0 1 26.34 3.78A24 24 0 0 1 168 120v32a8 8 0 0 1-8 8m40 32a8 8 0 0 1-8 8h-16a8 8 0 0 1 0-16h8V72h-8a8 8 0 0 1 0-16h16a8 8 0 0 1 8 8Z" />
</svg>
<span>Matrix</span>
</a>
</li>
{{else if eq .Platform "x" }}
<li class="text-center hover:underline">
<a class="flex items-center justify-center space-x-2" href="{{.Link}}">
<svg xmlns="http://www.w3.org/2000/svg" class="size-5 fill-white" width="0.88em" height="1em" viewBox="0 0 448 512">
<path fill="currentColor"
d="M64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h320c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64zm297.1 84L257.3 234.6L379.4 396h-95.6L209 298.1L123.3 396H75.8l111-126.9L69.7 116h98l67.7 89.5l78.2-89.5zm-37.8 251.6L153.4 142.9h-28.3l171.8 224.7h26.3z" />
</svg>
<span>X</span>
</a>
</li>
{{else if eq .Platform "mastodon" }}
<li class="text-center hover:underline">
<a class="flex items-center justify-center space-x-2" href="{{.Link}}">
<svg xmlns="http://www.w3.org/2000/svg" class="size-5 fill-white" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="white"
d="M20.94 14c-.28 1.41-2.44 2.96-4.97 3.26c-1.31.15-2.6.3-3.97.24c-2.25-.11-4-.54-4-.54v.62c.32 2.22 2.22 2.35 4.03 2.42c1.82.05 3.44-.46 3.44-.46l.08 1.65s-1.28.68-3.55.81c-1.25.07-2.81-.03-4.62-.5c-3.92-1.05-4.6-5.24-4.7-9.5l-.01-3.43c0-4.34 2.83-5.61 2.83-5.61C6.95 2.3 9.41 2 11.97 2h.06c2.56 0 5.02.3 6.47.96c0 0 2.83 1.27 2.83 5.61c0 0 .04 3.21-.39 5.43M18 8.91c0-1.08-.3-1.91-.85-2.56c-.56-.63-1.3-.96-2.23-.96c-1.06 0-1.87.41-2.42 1.23l-.5.88l-.5-.88c-.56-.82-1.36-1.23-2.43-1.23c-.92 0-1.66.33-2.23.96C6.29 7 6 7.83 6 8.91v5.26h2.1V9.06c0-1.06.45-1.62 1.36-1.62c1 0 1.5.65 1.5 1.93v2.79h2.07V9.37c0-1.28.5-1.93 1.51-1.93c.9 0 1.35.56 1.35 1.62v5.11H18z" />
</svg>
<span>Mastodon</span>
</a>
</li>
{{else}}
<li class="text-center hover:underline">
<a class="flex items-center justify-center space-x-2" href="{{.Link}}">
<span>{{.Platform}}</span>
</a>
</li>
{{end}}
{{end}}
</ul>
</section>
{{end}}
<!-- END SERVICE CONTACT -->
<div class="w-full my-2 border-t-2 border-opacity-50 border-dashed border-t-lime-400/10"></div>
<!-- ToS Checker -->
<section class="flex flex-col items-center justify-center p-4" id="tos">
<a href="/about#tos-checker" class="text-xl font-bold">
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4 calendar-cog" 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="M12 21h-6a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v5"></path>
<path d="M16 3v4"></path>
<path d="M8 3v4"></path>
<path d="M4 11h16"></path>
<path d="M19.001 19m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path>
<path d="M19.001 15.5v1.5"></path>
<path d="M19.001 21v1.5"></path>
<path d="M22.032 17.25l-1.299 .75"></path>
<path d="M17.27 20l-1.3 .75"></path>
<path d="M15.97 17.25l1.3 .75"></path>
<path d="M20.733 20l1.3 .75"></path>
</svg>
<span>ToS Review</span>
<span class="text-xs text-white/30">?</span>
</a>
<p class="max-w-sm text-xs text-center text-gray-500 text-opacity-75">Automated, ai-driven monthly ToS reviews.</p>
<p class="max-w-sm mt-1 text-xs text-center text-gray-600 text-opacity-75">Last Check: {{ dateString .Service.LastTosReview }}</p>
{{if .Service.TosReviews}}
<div class="max-w-lg">
{{if eq 0 (len .Service.TosReviews)}}
<p class="max-w-md mt-2 font-bold text-center text-opacity-75 text-amber-400 text-md">Not ToS reviews for this service (yet).</p>
{{else if eq (len .Service.TosUrls) 0}}
<p class="max-w-md mt-2 font-bold text-center text-opacity-75 text-amber-400 text-md">This service has no ToS.</p>
{{else}} {{range .Service.TosReviews}} {{if .Warning}} {{partial "partials/tos_check" .}} {{end}} {{end}}
<details class="py-2">
<summary class="max-w-md mt-2 text-lg font-bold text-center text-blue-400 text-opacity-75 cursor-pointer">Show non-conflictive ToS reviews</summary>
{{range .Service.TosReviews}} {{if ne .Warning true}} {{partial "partials/tos_check" .}} {{end}} {{end}}
</details>
{{end}}
</div>
{{else}}
<p class="max-w-md mt-2 font-bold text-center text-opacity-75 text-amber-400 text-md">Not ToS reviews for this service (yet).</p>
{{end}}
</section>
<div class="w-full my-2 border-t-2 border-opacity-50 border-dashed border-t-lime-400/10"></div>
<!-- Comments -->
<section class="flex flex-col items-center justify-center p-4 mb-16" id="discuss">
<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>Comments</span>
<span class="text-xs text-white/30">?</span>
</a>
<h2 class="text-xl font-bold"></h2>
<p class="max-w-sm pb-4 mt-1 text-xs text-center text-gray-500 text-opacity-75">Alghough comments are moderated, beware of fake reviews. Always do your own research.</p>
<!-- Div where cactus will set the client-->
<div id="comment-section" class="w-full max-w-xl mx-2 break-all sm:mx-6">
<comentario-comments></comentario-comments>
<noscript>
<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 20kb open-source script to interact with our instance of Comentario.
The source code is available on <a target="_blank" class="underline" href="https://gitlab.com/commento/commento">Gitlab</a>.
</p>
</noscript>
</div>
</section>
<script defer src="https://comments.kycnot.me/comentario.js"></script>

View File

@ -0,0 +1,343 @@
package templates
import (
"fmt"
"kycnot.me/frontend/templates/partials"
"kycnot.me/internal/ent"
"kycnot.me/utils"
"strings"
)
func formatNote(n string) string {
if n == "" {
return ""
}
baseClasses := "max-w-lg p-2 text-sm border rounded-lg font-normal"
prefixes := map[string]struct {
icon string
classes string
}{
"info:": {"icon-[tabler--info-circle]", "border-blue-400/20 text-blue-50/80 bg-blue-500/10"},
"warning:": {"icon-[tabler--alert-triangle]", "border-yellow-400/20 text-yellow-50/80 bg-yellow-500/10"},
"warn:": {"icon-[tabler--alert-triangle]", "border-yellow-400/20 text-yellow-50/80 bg-yellow-500/10"},
"alert:": {"icon-[tabler--alert-circle]", "border-red-400/20 text-red-50/80 bg-red-500/10"},
}
lowerN := strings.ToLower(n)
for prefix, data := range prefixes {
if strings.HasPrefix(lowerN, prefix) {
return fmt.Sprintf(
"<div class='%s %s'><i class='mr-2 text-lg %s'></i><span>%s</span></div>",
baseClasses, data.classes, data.icon, strings.TrimPrefix(n, prefix),
)
}
}
return fmt.Sprintf("<p class='%s border-gray-400/20 bg-gray-500/10'>%s</p>", baseClasses, n)
}
templ Service(s *ent.Service) {
@Base(s.Name) {
<section class="flex flex-col justify-center items-center p-2 my-4 mt-8 mb-2 space-y-4 font-bold text-md md:text-md">
if s.PotentialScam {
<a
href="#"
target="_blank"
class="flex flex-col justify-center items-center px-2 py-2 m-1 space-x-2 text-center text-red-500 rounded border border-red-600 transition duration-300 bg-zinc-900 hover:border-red-600 hover:text-red-600"
>
<span>
<i class="text-xl text-red-500 icon-[tabler--alert-triangle]"></i>
<span>POTENTIAL SCAM</span>
<i class="text-xl text-red-500 icon-[tabler--alert-triangle]"></i>
</span>
<span class="text-xs">THIS SERVICE HAS BEEN MARKED AS A POTENTIAL SCAM</span>
</a>
}
if s.Pending {
<a
href="#"
target="_blank"
class="flex flex-col justify-center items-center px-2 py-2 m-1 space-x-2 text-center text-yellow-500 rounded border border-yellow-600 transition duration-300 bg-zinc-900 hover:border-yellow-600 hover:text-yellow-600"
>
<span>
<i class="text-xl text-yellow-500 icon-[tabler--alert-triangle]"></i>
<span>Pending approval</span>
<i class="text-xl text-yellow-500 icon-[tabler--alert-triangle]"></i>
</span>
<span class="text-xs">This service has not been reviewed</span>
</a>
}
if !s.Pending && !s.Listed {
<a
href="#"
target="_blank"
class="flex flex-col justify-center items-center px-2 py-2 m-1 space-x-2 text-center text-red-500 rounded border border-red-600 transition duration-300 bg-zinc-900 hover:border-red-600 hover:text-red-600"
>
<span>
<i class="text-xl text-red-500 icon-[tabler--alert-triangle]"></i>
<span>DELISTED</span>
<i class="text-xl text-red-500 icon-[tabler--alert-triangle]"></i>
</span>
<span class="text-xs">This service has been delisted</span>
</a>
}
// Notes are shown at the top of the service page
// Notes can have prefix: "alert:", "info:", "warning:" to style them
if len(s.Notes) > 0 {
for _, note := range s.Notes {
if note != "" {
@templ.Raw(formatNote(note))
}
}
}
// HEADER
<header class="flex justify-center items-center space-x-4">
<img class="rounded-full size-10 md:size-14 bg-white/25" src={ "/static/images/" + s.Slug + ".jpg" } alt=""/>
<span class="flex justify-center items-center">
<span class="text-xl font-bold md:text-2xl">{ s.Name }</span>
// VERIFIED
if s.Verified {
<a class="flex justify-center items-center" aria-label="Verified" data-balloon-pos="up" href={ templ.SafeURL(fmt.Sprintf("/static/proofs/%s/proof.txt.asc", s.Name)) }>
<i class="ml-2 text-2xl icon-[tabler--rosette-discount-check-filled] text-blue-400/80"></i>
</a>
}
</span>
</header>
// LINKS
<section class="flex flex-wrap justify-center items-center space-x-2">
// Website
if len(s.Urls) > 0 {
<span class="flex">
<a
class={
"flex items-center justify-center px-2 py-1 space-x-1 text-sm text-center text-gray-300 transition duration-300 border border-gray-600 bg-zinc-900 hover:border-lime-600 hover:text-lime-600",
templ.KV("rounded-l", s.Referral != ""),
templ.KV("rounded", s.Referral == ""),
}
href={ templ.SafeURL(fmt.Sprintf("%s%s", utils.GetRandomString(s.Urls), s.Referral)) }
target="_blank"
rel="noopener noreferrer"
>
<i class="icon-[tabler--world-www]"></i>
<span>Website</span>
</a>
if s.Referral != "" {
<a
aria-label="No Referral"
data-balloon-pos="up"
href={ templ.SafeURL(utils.GetRandomString(s.Urls)) }
class="flex justify-center items-center px-1 py-1 text-center text-gray-300 rounded-r border border-gray-600 transition duration-300 md:px-2 bg-zinc-900 hover:border-lime-600 hover:text-lime-600"
>
<i class="icon-[tabler--link-off]"></i>
</a>
}
</span>
}
// Tor Onion
if len(s.OnionUrls) > 0 {
<span class="flex">
<a
class={
"flex items-center justify-center px-2 py-1 space-x-1 text-sm text-center text-purple-600 transition duration-300 border border-purple-900 bg-zinc-900 hover:border-lime-600 hover:text-lime-600",
templ.KV("rounded-l", s.Referral != ""),
templ.KV("rounded", s.Referral == ""),
}
href={ templ.SafeURL(fmt.Sprintf("%s%s", utils.GetRandomString(s.OnionUrls), s.Referral)) }
target="_blank"
rel="noopener noreferrer"
>
<i class="icon-[tabler--target]"></i>
<span>
if len(s.Urls) == 0 {
<span>Tor Only</span>
} else {
<span>Tor</span>
}
</span>
</a>
if s.Referral != "" {
<a
aria-label="No Referral"
data-balloon-pos="up"
href={ templ.SafeURL(utils.GetRandomString(s.OnionUrls)) }
class="flex justify-center items-center px-1 py-1 text-center text-purple-600 rounded-r border border-purple-900 transition duration-300 md:px-2 bg-zinc-900 hover:border-lime-600 hover:text-lime-600"
>
<i class="icon-[tabler--link-off]"></i>
</a>
}
</span>
}
// Terms of Service
if len(s.TosUrls) > 0 {
<a href={ templ.SafeURL(s.TosUrls[0]) } target="_blank" class="flex justify-center items-center px-2 py-0.5 m-1 space-x-2 text-center text-gray-300 rounded border border-gray-600 transition duration-300 bg-zinc-900 hover:border-lime-600 hover:text-lime-600">
<i class="icon-[tabler--report-search]"></i>
<span>ToS</span>
</a>
}
</section>
// "LEAVE A REVIEW": Link to comments section
<a aria-label="Discuss this service" data-balloon-pos="up" href="#discuss" class="flex justify-center items-center px-2 py-0.5 space-x-2 text-sm text-center text-gray-400 rounded border border-gray-700 transition duration-300 bg-zinc-900 hover:border-gray-600 hover:text-gray-600">
<i class="icon-[tabler--messages]"></i>
<span>Leave a review</span>
</a>
<div class="flex justify-center items-center space-x-2">
@partials.ServiceIcons(s)
</div>
// Description
<p class="p-4 max-w-lg text-sm rounded-lg border md:text-base border-white/20 bg-gray-500/10">
@templ.Raw(s.Description)
</p>
</section>
// KYC LEVEL
<section class="flex justify-center items-center px-2">
<div
class={
"flex flex-col justify-center max-w-lg p-4 text-sm border rounded-lg md:text-base",
templ.KV("border-green-500/20 bg-green-500/10", s.KycLevel == 0),
templ.KV("border-lime-300/40 bg-lime-300/10", s.KycLevel == 1),
templ.KV("border-amber-700/40 bg-amber-600/20", s.KycLevel == 2),
templ.KV("border-red-600/40 bg-red-600/20", s.KycLevel == 3),
}
>
// KYC LEVEL TITLE
<h2 class={ "flex items-center space-x-2 text-lg font-bold" }>
<i
class={
"ti",
templ.KV("text-green-600 icon-[tabler--circle-check-filled]", s.KycLevel == 0),
templ.KV("text-lime-400/70 icon-[tabler--circle-check]", s.KycLevel == 1),
templ.KV("text-amber-600 icon-[tabler--alert-triangle-filled]", s.KycLevel == 2),
templ.KV("text-red-600 icon-[tabler--alert-octagon-filled]", s.KycLevel == 3),
}
></i>
<span>KYC LEVEL <b>{ fmt.Sprintf("%d", s.KycLevel) }</b> </span>
</h2>
// KYC LEVEL DESCRIPTION
<p class="mt-2 text-sm">
switch s.KycLevel {
case 0:
The exchange ToS <b class="font-bold">do not mention</b> that it will ever
request the user for a KYC verification.
case 1:
KYC is <b class="font-bold">not mentioned</b>, but this service
<b class="font-bold">reserves the right to share data</b> with
representatives of the authorities, block funds or reject transactions.
case 2:
The exchange
<b class="font-bold">may request KYC from any user at any time</b>,
typically triggered by an automated flag system, leading to a temporary
block of funds.
case 3:
KYC is <b class="font-bold">mandatory to use some features</b>. A non-KYCed
user can be required to verify their identity at any time or any moment for
any reason.
}
</p>
</div>
</section>
// Comments
if len(s.Comments) > 0 {
<section class="flex flex-col justify-center items-center p-4">
<div class="mb-5 space-y-1.5 max-w-lg">
for _, comment := range s.Comments {
if comment != "" {
@templ.Raw(formatNote(comment))
}
}
</div>
</section>
}
// Attributes
if len(s.Edges.Attributes) > 0 {
<section class="flex flex-col justify-center items-center p-4">
<div class="grid grid-cols-1 auto-cols-max gap-2 mt-2 md:grid-cols-2">
for _, attr := range s.Edges.Attributes {
@partials.AttributeLine(attr)
}
</div>
</section>
}
// Support Contact Methods
if len(s.Support) > 0 {
<section id="contact" class="flex flex-col justify-center items-center p-4">
<ul class="flex items-center justify-center gap-2 flex-wrap text-sm [&>li>a]:p-2 [&>li>a]:rounded [&>li>a]:bg-white/10 hover:[&>li>a]:bg-white/20 [&>li>a]:transition [&>li>a]:duration-200">
for _, sp := range s.Support {
<li class="text-center">
<a class="flex justify-center items-center space-x-2" href={ templ.SafeURL(sp.Link) }>
// Icons used in this item are limited to icons declared in src/frontend/templates/01-extra-icons.html
// TODO: Create key value map, for icons names and fallback if not found, to replace the extra-icons.html
<i class={ fmt.Sprintf("icon-[tabler--%s]", sp.Icon) }></i>
<span>{ sp.Text }</span>
</a>
</li>
}
</ul>
</section>
}
<div class="my-2 w-full border-t-2 border-opacity-50 border-dashed border-t-lime-400/10"></div>
// Terms of Service AI Automated reviews
<section class="flex flex-col justify-center items-center p-4" id="tos">
<a href="/about#tos-checker" class="text-xl font-bold">
<i class="icon-[tabler--calendar-cog]"></i>
<span>ToS Review</span>
<span class="text-xs text-white/30">?</span>
</a>
<p class="max-w-sm text-xs text-center text-gray-500 text-opacity-75">Automated, ai-driven monthly ToS reviews.</p>
if len(s.TosUrls) == 0 {
<p class="mt-2 max-w-md font-bold text-center text-amber-400 text-opacity-75 text-md">This service has no ToS.</p>
} else if len(s.TosReviews) == 0 {
<p class="mt-2 max-w-md font-bold text-center text-amber-400 text-opacity-75 text-md">No ToS reviews for this service (yet).</p>
} else {
<p class="mt-1 max-w-sm text-xs text-center text-gray-600 text-opacity-75">Last Check: { s.LastTosReview.Format("2006-01-02") }</p>
<div class="max-w-lg">
for _, tr := range s.TosReviews {
if tr.Warning {
<div
class={
"px-3 py-2 mt-2 border rounded-md w-full",
templ.KV("border-amber-900 bg-amber-900/30", tr.Warning),
templ.KV("border-blue-900 bg-blue-900/30", !tr.Warning),
}
>
<h3 class="text-sm font-bold uppercase">{ tr.Title }</h3>
<p class="my-1.5 text-sm">{ tr.Details }</p>
if tr.Section != "" {
<p class="text-xs text-gray-400 text-opacity-40">ToS section: <span>{ tr.Section }</span></p>
}
</div>
}
}
</div>
}
</section>
<div class="my-2 w-full border-t-2 border-opacity-50 border-dashed border-t-lime-400/10"></div>
<!-- Comments -->
<section class="flex flex-col justify-center items-center p-4 mb-16" id="discuss">
<a href="/about#comments" target="_blank" class="text-xl font-bold">
<i class="inline-block icon-[tabler--messages]"></i>
<span>Comments</span>
<span class="text-xs text-white/30">?</span>
</a>
<p class="pb-4 mt-1 max-w-sm text-xs text-center text-gray-500 text-opacity-75">
Alghough comments are <a class="underline hover:text-gray-400" href="/about#comment-moderation">moderated</a>, beware of fake reviews. Always do your own research.
</p>
<!-- Div where comentario will set the client-->
<div id="comment-section" class="mx-2 w-full max-w-xl break-all sm:mx-6">
<comentario-comments></comentario-comments>
<noscript>
<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 20kb open-source script to
interact with our instance of Comentario.
The source code is available on <a
target="_blank"
class="underline"
href="https://gitlab.com/commento/commento"
>Gitlab</a>.
</p>
</noscript>
</div>
</section>
// Comentario JS script
<script defer src="https://comments.kycnot.me/comentario.js"></script>
}
}

View File

@ -0,0 +1,33 @@
package templates
import (
"kycnot.me/frontend/templates/partials"
"kycnot.me/internal/ent"
)
templ ServicePendingList(pendingServices []*ent.Service) {
@Base("Pending Services") {
<main class="py-2 mt-4 text-lime-500">
<div class="mb-2 text-center bg-transparent md:grid">
<p class="font-mono text-2xl font-bold uppercase">
<i class="icon-[tabler--hourglass-high] text-lg"></i>
Pending Services
<i class="icon-[tabler--hourglass-high] text-lg"></i>
</p>
<p class="mt-1 text-sm">The following services have been requested and are pending for review.</p>
</div>
</main>
<!-- Services list -->
<section class="flex justify-center items-center px-3 pb-12 mt-4">
<div class="grid grid-cols-1 gap-3 max-w-6xl lg:grid-cols-3">
if len(pendingServices) > 0 {
for _, ps := range pendingServices {
@partials.ServiceCard(ps)
}
} else {
<h2 class="my-8 text-xl font-bold text-center">No pending services.</h2>
}
</div>
</section>
}
}

View File

@ -0,0 +1,330 @@
package templates
import (
"kycnot.me/internal/ent"
"kycnot.me/internal/ent/attribute"
"kycnot.me/internal/models"
)
func getAttributeColor(t attribute.Rating) string {
switch t {
case attribute.RatingBad:
return "text-red-400/80"
case attribute.RatingInfo:
return "text-blue-400/80"
case attribute.RatingGood:
return "text-green-400/80"
case attribute.RatingWarn:
return "text-yellow-400/80"
default:
return ""
}
}
templ RequestForm(opts *models.BaseOpts, attributes []*ent.Attribute) {
@Base("Request") {
if opts.Error != "" {
<div class="flex justify-center items-center">
<p class="px-2 py-2 font-bold text-center rounded-lg bg-red-500/70 text-md text-neutral-100">
{ opts.Error }
</p>
</div>
}
if opts.Message != "" {
<div class="flex justify-center items-center">
<p class="px-2 py-2 font-bold text-center rounded-lg text-md bg-blue-500/70 text-neutral-100">
{ opts.Message }
</p>
</div>
}
<section class="flex flex-col items-center py-8 w-full">
<h2 class="text-2xl font-bold">Request a service</h2>
<small class="opacity-70">*This form requires JavaScript, <a class="text-lime-600" href="/about#request">read more here</a>.</small>
<form action="/request" method="POST" class="my-6 space-y-4 max-w-lg font-mono">
<div class="flex flex-col">
<label class="font-bold" for="type">Service Type</label>
<select
name="type"
id="type"
required
class="p-2 text-white uppercase bg-opacity-30 rounded-lg border bg-zinc-900 border-white/30"
>
<option selected value="exchange">exchange</option>
<option value="service">service</option>
</select>
</div>
<div id="category-container" class="flex flex-col">
<label class="font-bold" for="category">Service Category</label>
<select
name="category"
id="category"
required
class="p-2 text-white uppercase bg-opacity-30 rounded-lg border bg-zinc-900 border-white/30"
>
<option value="aggregator">aggregator</option>
<option value="atm">physical atm</option>
<option value="cloud">cloud storage</option>
<option value="email">email</option>
<option value="goods">goods</option>
<option value="hosting">hosting / domains</option>
<option value="market">market</option>
<option value="proxy store">proxy store</option>
<option value="sms">sms / phone number</option>
<option value="tools">tools</option>
<option value="vpn">vpn</option>
<option value="ai">ai</option>
<option value="vps">server hosting (vps)</option>
</select>
</div>
<div class="flex flex-col">
<label class="font-bold" for="name">Service Name</label>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
placeholder="Awesome exchange"
type="text"
name="name"
id="name"
required
/>
</div>
<div class="flex flex-col">
<label class="font-bold" for="description">Description</label>
<textarea
class="p-2 font-mono text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
name="description"
id="description"
placeholder="concise description about what the service is. max 500 characters."
cols="30"
rows="5"
required
minlength="60"
maxlength="500"
></textarea>
</div>
<div class="flex flex-col">
<label class="font-bold" for="urls">Service URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
type="text"
name="urls"
id="urls"
required
placeholder="https://example.com"
/>
</div>
<div class="flex flex-col">
<label class="font-bold" for="logo_url">Logo URL</label>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
type="text"
name="logo_url"
id="logo_url"
placeholder="https://i.imgur.com/wwwHfCN.jpeg"
required
/>
</div>
<div class="flex flex-col">
<label class="font-bold" for="tos_urls">ToS URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
type="text"
name="tos_urls"
id="tos_urls"
required
placeholder="https://example.com/terms"
/>
</div>
<div class="flex flex-col">
<label class="font-bold" for="onion_urls">Tor Onion URL(s)</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
type="text"
name="onion_urls"
id="onion_urls"
placeholder="http://zbiowkw...onion"
/>
</div>
<div class="flex flex-col">
<label class="font-bold" for="tags">Keywords</label>
<small class="text-white/30">*If more than one, separate with commas.</small>
<input
class="p-2 text-white bg-opacity-30 rounded-lg border placeholder-white/30 bg-zinc-900 border-white/30"
type="text"
name="tags"
id="tags"
required
placeholder="private,p2p, fast"
/>
</div>
<div class="flex flex-col justify-center items-center pt-4 space-x-2">
<h3 class="mb-2 text-xl">Accepted currencies</h3>
<div class="flex space-x-4">
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="btc">BITCOIN</label>
<input
class="text-lime-500 bg-opacity-30 rounded-full border-lime-500 appearance-none outline-none bg-zinc-900 focus:ring-0"
type="checkbox"
name="btc"
id="btc"
/>
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="xmr">MONERO</label>
<input
class="text-lime-500 bg-opacity-30 rounded-full border-lime-500 appearance-none outline-none bg-zinc-900 focus:ring-0"
type="checkbox"
name="xmr"
id="xmr"
/>
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="ln">LIGHTNING</label>
<input
class="text-lime-500 bg-opacity-30 rounded-full border-lime-500 appearance-none outline-none bg-zinc-900 focus:ring-0"
type="checkbox"
name="ln"
id="ln"
/>
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="fiat">FIAT</label>
<input
class="text-lime-500 bg-opacity-30 rounded-full border-lime-500 appearance-none outline-none bg-zinc-900 focus:ring-0"
type="checkbox"
name="fiat"
id="fiat"
/>
</span>
<span class="flex flex-col justify-center items-center">
<label class="text-xs uppercase" for="cash">CASH</label>
<input
class="text-lime-500 bg-opacity-30 rounded-full border-lime-500 appearance-none outline-none bg-zinc-900 focus:ring-0"
type="checkbox"
name="cash"
id="cash"
/>
</span>
</div>
</div>
<div class="space-y-4">
<h3 class="font-bold">KYC Level</h3>
<label class="flex flex-row items-center p-2 rounded-lg border border-white/10" for="kyclevel0">
<input type="radio" name="kyc_level" id="kyclevel0" value="0"/>
<p class="ml-3">
<b class="text-lime-400">Level 0</b>: No KYC ever. The ToS do not mention nor enforce
any KYC/AML verification.
</p>
</label>
<label class="flex flex-row items-center p-2 rounded-lg border border-white/10" for="kyclevel1">
<input type="radio" name="kyc_level" id="kyclevel1" value="1"/>
<p class="ml-3">
<b class="text-lime-400">Level 1</b>: No KYC/AML procedures are mentioned as such,
however the service reserves the right to share acquired data with authorities, block funds or
reject transactions.
</p>
</label>
<label class="flex flex-row items-center p-2 rounded-lg border border-white/10" for="kyclevel2">
<input type="radio" name="kyc_level" id="kyclevel2" value="2" checked/>
<p class="ml-3">
<b class="text-lime-400">Level 2</b>: KYC is not mandatory, but the service can require
KYC from any user at any time, and it may block funds. This could occur, for example, if a
transaction is flagged suspicious.
</p>
</label>
<label class="flex flex-row items-center p-2 rounded-lg border border-white/10" for="kyclevel3">
<input type="radio" name="kyc_level" id="kyclevel3" value="3"/>
<p class="ml-3">
<b class="text-lime-400">Level 3</b>: KYC is mandatory to use certain features of the
platform. Non-KYC users can be asked to verify their identity at any time, for any reason and may
have their service restricted or revoked.
</p>
</label>
</div>
<h3 class="font-bold">Attributes</h3>
<small class="text-white/50">* Scrollable. You must check at least 3</small>
<div class="overflow-scroll p-4 mx-4 mb-4 space-y-2 max-w-lg h-52 rounded-lg border border-white/10">
for _, attr := range attributes {
<span class="block p-1 border-b border-white/10">
<input type="checkbox" id={ attr.ID } name="attributes" value={ attr.ID }/>
<label class={ getAttributeColor(attr.Rating) + " ml-1" } for={ attr.ID }>{ attr.Title }</label>
</span>
}
</div>
<noscript>
<p class="my-2 font-bold text-yellow-500 uppercase">
You need to enable JavaScript to complete this form.
</p>
</noscript>
<div
id="start-pow"
class="flex justify-center items-center px-4 py-2 mt-2 mb-4 space-x-2 font-bold uppercase bg-blue-900 rounded-lg cursor-pointer"
>
<input type="text" name="pow-nonce" id="pow-nonce" hidden required value=""/>
<input type="text" name="pow-id" id="pow-id" hidden required value=""/>
<svg
id="pow-spinner"
class="hidden text-white fill-white"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<style>
.spinner_ZCsl {
animation: spinner_qV4G 1.2s cubic-bezier(0.52, .6, .25, .99) infinite
}
.spinner_gaIW {
animation-delay: .6s
}
@keyframes spinner_qV4G {
0% {
r: 0;
opacity: 1
}
100% {
r: 11px;
opacity: 0
}
}
</style>
<circle class="spinner_ZCsl" cx="12" cy="12" r="0"></circle>
<circle class="spinner_ZCsl spinner_gaIW" cx="12" cy="12" r="0"></circle>
</svg>
<span id="pow-text">🧠 I'm Human</span>
</div>
<button
id="submit-btn"
class="hidden px-4 py-2 my-4 space-x-2 w-full font-bold uppercase bg-lime-900 rounded-lg cursor-pointer"
>
Submit
</button>
</form>
</section>
<script src="/static/js/pow.js"></script>
<script>
// Toggle category select based on type select
document.addEventListener('DOMContentLoaded', (event) => {
const typeSelect = document.getElementById('type');
const categoryDiv = document.getElementById('category-container');
function toggleCategory() {
if (typeSelect.value === 'service') {
categoryDiv.style.display = 'flex'; // Show
} else {
categoryDiv.style.display = 'none'; // Hide
}
}
// Initial toggle based on the preselected option
toggleCategory();
// Add change event listener to the type select element
typeSelect.addEventListener('change', toggleCategory);
});
</script>
}
}

View File

@ -1,72 +1,77 @@
module pluja.dev/kycnot.me
module kycnot.me
go 1.22
toolchain go1.22.1
go 1.23.1
require (
github.com/allegro/bigcache/v3 v3.1.0
github.com/chai2010/webp v1.1.1
github.com/dustin/go-humanize v1.0.1
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.6.0
github.com/iris-contrib/middleware/cors v0.0.0-20240502084239-34f27409ce72
entgo.io/ent v0.14.1
github.com/a-h/templ v0.2.793
github.com/dgraph-io/ristretto v0.2.0
github.com/go-sql-driver/mysql v1.8.1
github.com/gosimple/slug v1.14.0
github.com/joho/godotenv v1.5.1
github.com/kataras/iris/v12 v12.2.11
github.com/pluja/pocketbase v0.0.61
github.com/rs/zerolog v1.32.0
github.com/sashabaranov/go-openai v1.24.0
golang.org/x/image v0.16.0
golang.org/x/net v0.25.0
github.com/microcosm-cc/bluemonday v1.0.26
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/phuslu/log v1.0.113
github.com/samber/lo v1.47.0
github.com/sashabaranov/go-openai v1.35.7
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0
golang.org/x/image v0.23.0
)
require (
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
github.com/Joker/jade v1.1.3 // indirect
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
github.com/SierraSoftworks/multicast/v2 v2.0.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 // indirect
github.com/duke-git/lancet/v2 v2.3.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/flosch/pongo2/v4 v4.0.2 // indirect
github.com/go-resty/resty/v2 v2.13.1 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
github.com/iris-contrib/schema v0.0.6 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kataras/blocks v0.0.8 // indirect
github.com/kataras/golog v0.1.12 // indirect
github.com/kataras/golog v0.1.11 // indirect
github.com/kataras/pio v0.0.13 // indirect
github.com/kataras/sitemap v0.0.6 // indirect
github.com/kataras/tunnel v0.0.4 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/mailgun/raymond/v2 v2.0.48 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tdewolff/minify/v2 v2.20.21 // indirect
github.com/tdewolff/parse/v2 v2.7.14 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/tdewolff/minify/v2 v2.20.19 // indirect
github.com/tdewolff/parse/v2 v2.7.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/yosssi/ace v0.0.5 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,89 +1,44 @@
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 h1:GwdJbXydHCYPedeeLt4x/lrlIISQ4JTH1mRWuE5ZZ14=
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43/go.mod h1:uj3pm+hUTVN/X5yfdBexHlZv+1Xu5u5ZbZx7+CDavNU=
entgo.io/ent v0.14.1 h1:fUERL506Pqr92EPHJqr8EYxbPioflJo6PudkrEA8a/s=
entgo.io/ent v0.14.1/go.mod h1:MH6XLG0KXpkcDQhKiHfANZSzR55TJyPL5IGNpI8wpco=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
github.com/SierraSoftworks/multicast/v2 v2.0.0 h1:0mN2KN5VLc+xEnbvrXOlRTqoz4bzp6MIvp1vwnwkNGo=
github.com/SierraSoftworks/multicast/v2 v2.0.0/go.mod h1:+4a2KDy5y3Bf/K5O++7SNBlQ2qZrwj9T3dEVTxwM2K8=
github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY=
github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.48.16 h1:mcj2/9J/MJ55Dov+ocMevhR8Jv6jW/fAxbrn4a1JFc8=
github.com/aws/aws-sdk-go v1.48.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
github.com/aws/aws-sdk-go-v2/config v1.26.1/go.mod h1:ZB+CuKHRbb5v5F0oJtGdhFTelmrxd4iWO1lf0rQwSAg=
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 h1:v/WgB8NxprNvr5inKIiVVrXPuuTegM+K8nncFkr1usU=
github.com/aws/aws-sdk-go-v2/credentials v1.16.12/go.mod h1:X21k0FjEJe+/pauud82HYiQbEr9jRKY3kXEIQ4hXeTQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 h1:Keso8lIOS+IzI2MkPZyK6G0LYcK3My2LQ+T5bxghEAY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
github.com/duke-git/lancet/v2 v2.2.9 h1:ik02ZrFg/OU0lduLfmNqo73mAhpY2a3Fm1RUFcoEtqk=
github.com/duke-git/lancet/v2 v2.2.9/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
github.com/duke-git/lancet/v2 v2.3.1 h1:cYZHQp57CZKP41EFkV/7TGbUrmhjaPMI5vi3Q+9KJNo=
github.com/duke-git/lancet/v2 v2.3.1/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
@ -92,77 +47,51 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/ganigeorgiev/fexpr v0.4.0 h1:ojitI+VMNZX/odeNL1x3RzTTE8qAIVvnSSYPNAnQFDI=
github.com/ganigeorgiev/fexpr v0.4.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwro3Uin8eqoET7MDA4JnT8YgbCjc/g5E3k=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM=
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
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/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es=
github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
github.com/iris-contrib/middleware/cors v0.0.0-20240111010557-e34016a4d6ee h1:srr0HUvI9IAximtqxhF0+WSy9cv9Dd5YlH8RMEkHtsw=
github.com/iris-contrib/middleware/cors v0.0.0-20240111010557-e34016a4d6ee/go.mod h1:hAsE+pfCAz1M2cCfsEolTmaBwHCyzchRFyCk5I1+kDo=
github.com/iris-contrib/middleware/cors v0.0.0-20240502084239-34f27409ce72 h1:BuEwEv6gPS9pSX0+O5FIxxKUoRziXBFdWI1S4ivKQtY=
github.com/iris-contrib/middleware/cors v0.0.0-20240502084239-34f27409ce72/go.mod h1:DQJ0KlNPppOfMC+0x0ADeFQk0WmQMVU9rJQzFY4nUfA=
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
github.com/kataras/golog v0.1.11 h1:dGkcCVsIpqiAMWTlebn/ZULHxFvfG4K43LF1cNWSh20=
github.com/kataras/golog v0.1.11/go.mod h1:mAkt1vbPowFUuUGvexyQ5NFW6djEgGyxQBIARJ0AH4A=
github.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=
github.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=
github.com/kataras/iris/v12 v12.2.10 h1:rEJVM7qMoyhv8wpgkA1yGxibFcONE0jkJ70LFLibTAA=
github.com/kataras/iris/v12 v12.2.10/go.mod h1:z4+E+kLMqZ7U4WtDsYfFnG7BjMTXLkdzMAXLVMLnMNs=
github.com/kataras/iris/v12 v12.2.11 h1:sGgo43rMPfzDft8rjVhPs6L3qDJy3TbBrMD/zGL1pzk=
github.com/kataras/iris/v12 v12.2.11/go.mod h1:uMAeX8OqG9vqdhyrIPv8Lajo/wXTtAF43wchP9WHt2w=
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
@ -171,90 +100,72 @@ github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/phuslu/log v1.0.113 h1:Koq5A+8ourLX4vhkhW4HCJjo+jEtzMDhqvUUid/5m24=
github.com/phuslu/log v1.0.113/go.mod h1:F8osGJADo5qLK/0F88djWwdyoZZ9xDJQL1HYRHFEkS0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pluja/pocketbase v0.0.6 h1:Ah6Qsv3wIcRW+MzrHtVbwD6PkQcz3N5dQRt45NEaWrU=
github.com/pluja/pocketbase v0.0.6/go.mod h1:EPXgCTbmMRrirYA0++qkk25xT175zV8PrE0xFpCdUe0=
github.com/pluja/pocketbase v0.0.61 h1:p20lrZQbL9EzglbLG2Yw8Ng+vPgSZn+hsKlp/NUPHds=
github.com/pluja/pocketbase v0.0.61/go.mod h1:7TGMNQCFq9D7B2k2JdQUVvzUKRBqJKcj/x2+qjcU4Sc=
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/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.20.0 h1:gDatnEZWz4A+PjPuhllLOvirqKLxDkrI3onx27JccV4=
github.com/pocketbase/pocketbase v0.20.0/go.mod h1:uy7WOxXoICrwe8HlyR78vTvK0RdG5REkhDx4SvYi4FY=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sashabaranov/go-openai v1.19.4 h1:GbaDiqvgYCabyqzuIbcEeT6/ZX1nVfur+++oTBfOgks=
github.com/sashabaranov/go-openai v1.19.4/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.24.0 h1:4H4Pg8Bl2RH/YSnU8DYumZbuHnnkfioor/dtNlB20D4=
github.com/sashabaranov/go-openai v1.24.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.35.7 h1:icyrRbkYoKPa4rbO1WSInpJu3qDQrPEnsoJVZ6QymdI=
github.com/sashabaranov/go-openai v1.35.7/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tdewolff/minify/v2 v2.20.16 h1:/C8dtRkxLTIyUlKlBz46gDiktCrE8a6+c1gTrnPFz+U=
github.com/tdewolff/minify/v2 v2.20.16/go.mod h1:/FvxV9KaTrFu35J9I2FhRvWSBxcHj8sDSdwBFh5voxM=
github.com/tdewolff/minify/v2 v2.20.21 h1:8MCHcxXAVO8B7X+v07mwMWBIEtQo65e1JzBqDgZOQpU=
github.com/tdewolff/minify/v2 v2.20.21/go.mod h1:GYaLXFpIIwsX99apQHXfGdISUdlA98wmaoWxjT9C37k=
github.com/tdewolff/parse/v2 v2.7.11 h1:v+W45LnzmjndVlfqPCT5gGjAAZKd1GJGOPJveTIkBY8=
github.com/tdewolff/parse/v2 v2.7.11/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8=
github.com/tdewolff/parse/v2 v2.7.14/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
@ -272,119 +183,58 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
gocloud.dev v0.35.0 h1:x/Gtt5OJdT4j+ir1AXAIXb7bBnFawXAAaJptCUGk3HU=
gocloud.dev v0.35.0/go.mod h1:wbyF+BhfdtLWyUtVEWRW13hFLb1vXnV2ovEhYGQe3ck=
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20191026070338-33540a1f6037/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
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.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4=
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=

17
src/internal/cache/cache.go vendored Normal file
View File

@ -0,0 +1,17 @@
package cache
import "github.com/dgraph-io/ristretto"
var Cache *ristretto.Cache
func Init() {
var err error
Cache, err = ristretto.NewCache(&ristretto.Config{
NumCounters: 1e7, // number of keys to track frequency of (10M).
MaxCost: 1 << 30, // maximum cost of cache (1GB).
BufferItems: 64, // number of keys per Get buffer.
})
if err != nil {
panic(err)
}
}

View File

@ -0,0 +1,37 @@
// client.go
package database
import (
"context"
"os"
"entgo.io/ent/dialect"
_ "github.com/go-sql-driver/mysql"
"github.com/phuslu/log"
"kycnot.me/internal/ent"
"kycnot.me/utils"
)
var client *ent.Client
// Init initializes the database client.
func Init() {
var err error
// client, err = ent.Open(dialect.SQLite, "file:ent?mode=memory&cache=shared&_fk=1")
// client, err = ent.Open(dialect.SQLite, "file:./db.sql?cache=shared&_fk=1")
log.Printf("%s", os.Getenv("DATABASE_URI"))
client, err = ent.Open(dialect.MySQL, utils.Getenv("DATABASE_URI", "kycnot:kycnot@tcp(database:3306)/kycnot?parseTime=True"))
if err != nil {
log.Fatal().Msgf("failed opening connection to MYSQL: %v", err)
}
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatal().Msgf("failed creating schema resources: %v", err)
}
}
func Client() *ent.Client {
return client
}

Some files were not shown because too many files have changed in this diff Show More