mirror of
https://codeberg.org/pluja/kycnot.me
synced 2025-01-14 08:29:25 -05:00
initial commit
This commit is contained in:
commit
b8564a4af7
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
dev.sh
|
||||
node_modules
|
||||
*.fiber.gz
|
||||
style.css
|
||||
src/ent/*
|
||||
!src/ent/schema/
|
||||
!src/ent/generate.go
|
3
.opencommitignore
Normal file
3
.opencommitignore
Normal file
@ -0,0 +1,3 @@
|
||||
src/ent/*
|
||||
!src/ent/schema/
|
||||
!src/ent/generate.go
|
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# KYCNOT.ME OFFICIAL REPOSITORY
|
||||
|
||||
This is the official repository of the KYCNOT.ME project.
|
||||
|
||||
## Comments
|
||||
|
||||
Comments are based on [Disgus](). I'm using a slightly modified version of the [Disgus]() project, to fit the KYCNOT.me theme and needs.
|
||||
|
||||
## ToS Checker
|
||||
|
||||
The ToS checker is an automated ai-driven tool that checks the ToS of a website.
|
4
TODO.md
Normal file
4
TODO.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Todo
|
||||
|
||||
- [ ] Add simple points (features) system
|
||||
- [ ]
|
1054
package-lock.json
generated
Normal file
1054
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
package.json
Normal file
7
package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.6",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"tailwindcss": "^3.3.3"
|
||||
}
|
||||
}
|
5
src/.env
Normal file
5
src/.env
Normal file
@ -0,0 +1,5 @@
|
||||
DB_TYPE="sqlite"
|
||||
ROOT_DIR="./"
|
||||
LISTEN_ADDR="127.0.0.1:4488"
|
||||
POW_DIFFICULTY=4
|
||||
POW_INCREASE_EVERY_CHALLENGES=20
|
174
src/database/attributes.go
Normal file
174
src/database/attributes.go
Normal file
@ -0,0 +1,174 @@
|
||||
package database
|
||||
|
||||
type Attribute struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Rating string `json:"rating"`
|
||||
}
|
||||
|
||||
const (
|
||||
AttributeRatingInfo = "info"
|
||||
AttributeRatingGood = "good"
|
||||
AttributeRatingWarning = "warning"
|
||||
AttributeRatingBad = "bad"
|
||||
|
||||
AttributeRiskPreventionSystem = "auripr"
|
||||
AttributeKYCForSomeFeatures = "kmausfe"
|
||||
AttributeKYCIfObligedByLaw = "koblainpo"
|
||||
AttributeCustodialWallet = "cuwa"
|
||||
AttributeAccountNeeded = "acneus"
|
||||
AttributePrivateSourceCode = "socopr"
|
||||
AttributeNoCustomerSupport = "nocusu"
|
||||
AttributeBlockFundsIfFlagged = "blfuflsu"
|
||||
AttributeUnclearRefundPolicy = "unrepo"
|
||||
AttributeSellerWalletCustodial = "sewacu"
|
||||
AttributeRefundRequiresKYC = "repedi"
|
||||
AttributeToSNotScrapable = "tonosc"
|
||||
AttributePartnersMayEnforceKYC = "pamakyc"
|
||||
AttributeNonCustodialWallet = "nocuwa"
|
||||
AttributeOpenSource = "ossli"
|
||||
AttributeP2P = "p2p"
|
||||
AttributeNoRegistrationNeeded = "norene"
|
||||
AttributeNoPersonalInfoNeeded = "nopine"
|
||||
AttributeStrictNoKYCPolicy = "stnokyc"
|
||||
AttributeRefundNoKYC = "refnokyc"
|
||||
AttributeStrictNoLogPolicy = "stnolog"
|
||||
AttributeMobileAppAvailable = "moapp"
|
||||
AttributeNoJavaScriptNeeded = "nojs"
|
||||
AttributeTelegramBotAvailable = "tebot"
|
||||
AttributeAPIAvailable = "api"
|
||||
AttributeJavaScriptNeeded = "yesjs"
|
||||
)
|
||||
|
||||
var ServiceAttributes map[string]Attribute = map[string]Attribute{
|
||||
"auripr": {
|
||||
Title: "KYC may be enforced by an automated risk prevention system",
|
||||
Description: "If the trade is flagged suspicious by the service, the user will be required a KYC procedure in order to get a refund.",
|
||||
Rating: AttributeRatingBad,
|
||||
},
|
||||
"kmausfe": {
|
||||
Title: "KYC is mandatory to use some features",
|
||||
Description: "Some features offered by this service need KYC for the user to use them.",
|
||||
Rating: AttributeRatingBad,
|
||||
},
|
||||
"koblainpo": {
|
||||
Title: "May require KYC/SOF if obliged by law or internal policies",
|
||||
Description: "If obliged to do so by the law or in accordance with the service's internal policy, it may at any time introduce or amend mandatory identification / verification procedures and require the user to complete identification and/or verification and may also require to submit identification documents (KYC) or provide Source of Funds (SOF) information.",
|
||||
Rating: AttributeRatingBad,
|
||||
},
|
||||
"cuwa": {
|
||||
Title: "Custodial wallet",
|
||||
Description: "A custodial wallet is a type of crypto wallet where a third party holds and manages the private keys to your wallet and your assets in custody. The custodian is responsible for safeguarding your funds, and you entrust them with your private keys. Custodial wallets are usually provided by centralized crypto exchanges like Coinbase or Binance. Using a custodial wallet requires a great deal of trust in the institution.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"acneus": {
|
||||
Title: "Account needed to use the service",
|
||||
Description: "Users require creating and maintaining an account with the service in order to access and use its features. ",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"socopr": {
|
||||
Title: "Source code is private",
|
||||
Description: "The source code of the service is not publicly available. This means that the service cannot be audited by the community. This is not necessarily bad, but it is something to keep in mind.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"nocusu": {
|
||||
Title: "poor or no customer support",
|
||||
Description: "The service does not offer customer support, or the quality of service provided by the support team is inadequate or unsatisfactory.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"blfuflsu": {
|
||||
Title: "may block funds if flagged suspicious",
|
||||
Description: "User funds may pass through an analysis system that may flag the source of the funds as suspicious. If this happens, the user may be refunded or may be required a KYC procedure.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"unrepo": {
|
||||
Title: "unclear refund policy",
|
||||
Description: "This service has an unclear or nonexistent statement about how they manage user refunds.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"sewacu": {
|
||||
Title: "The seller wallet is custodial",
|
||||
Description: "The seller wallet is custodial, which means that the seller does not have control over the funds. This is a common practice in P2P exchanges, where the service needs to have control over the funds in order to be able to refund the buyer if the trade enters a dispute.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"repedi": {
|
||||
Title: "Refunds may require KYC or personal information disclosure",
|
||||
Description: "In certain cases, the refund process of these services may require the completion of a Know Your Customer (KYC) procedure or the disclosure of personal information. Some services, such as aggregators, usually don't control the KYC procedures of their partners so they fall in this category.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"tonosc": {
|
||||
Title: "ToS page is not possible to scrape",
|
||||
Description: "The ToS page has some mechanism that makes it impossible for KYCNOT.me to scrape its content. These methods include, for example, using a canvas and rendering the text on it.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"pamakyc": {
|
||||
Title: "Partners may enforce KYC policies",
|
||||
Description: "The service has partners that may enforce or implement KYC policies. This is common in aggregators, where the service does not control the KYC policies of their partners.",
|
||||
Rating: AttributeRatingWarning,
|
||||
},
|
||||
"nocuwa": {
|
||||
Title: "Non-custodial wallet",
|
||||
Description: "A non-custodial wallet is a type of crypto wallet where the user holds and manages the private keys to the wallet and the assets in custody. The user is responsible for safeguarding their funds, and they are the only ones with access to their private keys. Non-custodial wallets are usually provided by decentralized crypto exchanges like Uniswap or Sushiswap. Using a non-custodial wallet requires no trust in any institution.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"ossli": {
|
||||
Title: "The service is open source",
|
||||
Description: "The source code of the service is publicly available. This means that the service can be audited by the community. This is not necessarily good, but it is something to keep in mind.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"p2p": {
|
||||
Title: "Peer to peer",
|
||||
Description: "Peer-to-peer marketplaces are online platforms where people can trade goods, cryptocurrencies and services directly from each other, without the need for intermediaries like traditional retailers or service providers. In this case, the service is based on such type of market.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"norene": {
|
||||
Title: "No registration needed",
|
||||
Description: "Users can access and use the service without creating an account. This enables a faster and more convenient user experience while also offering enhanced privacy and anonymity.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"nopine": {
|
||||
Title: "No personal information needed",
|
||||
Description: "Users can create an account with these services without having to provide any personal information such as their name, address, or identification documents. This offers a high degree of privacy and anonymity to users who prefer to keep their personal information private.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"stnokyc": {
|
||||
Title: "Strict no-KYC policy",
|
||||
Description: "The service has a strict no-KYC policy, which means that it does not require users to complete any KYC procedure in order to access and use its features.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"refnokyc": {
|
||||
Title: "Refunds do not require KYC",
|
||||
Description: "The refund process of these services does not require the completion of a Know Your Customer (KYC) procedure or the disclosure of personal information.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"stnolog": {
|
||||
Title: "Strict no-log policy",
|
||||
Description: "The service has a strict no-log policy, which means that it does not collect or store any information about its users.",
|
||||
Rating: AttributeRatingGood,
|
||||
},
|
||||
"moapp": {
|
||||
Title: "Mobile app available",
|
||||
Description: "The service has a mobile app available for download.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"nojs": {
|
||||
Title: "No JavaScript needed",
|
||||
Description: "The service does not require the user to enable JavaScript in order to access and use its features.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"tebot": {
|
||||
Title: "Telegram bot available",
|
||||
Description: "The service has a Telegram bot available.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"api": {
|
||||
Title: "API available",
|
||||
Description: "The service has an API available.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
"yesjs": {
|
||||
Title: "JavaScript needed",
|
||||
Description: "The service requires the user to enable JavaScript in order to access and use its features.",
|
||||
Rating: AttributeRatingInfo,
|
||||
},
|
||||
}
|
23
src/database/cache.go
Normal file
23
src/database/cache.go
Normal file
@ -0,0 +1,23 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/allegro/bigcache/v3"
|
||||
)
|
||||
|
||||
var Cache *bigcache.BigCache
|
||||
|
||||
func InitCache() {
|
||||
cache, _ := bigcache.New(context.Background(), bigcache.Config{
|
||||
Shards: 1024,
|
||||
LifeWindow: 10 * time.Minute,
|
||||
CleanWindow: 2 * time.Minute,
|
||||
MaxEntriesInWindow: 1000 * 10 * 60,
|
||||
MaxEntrySize: 500,
|
||||
Verbose: false,
|
||||
HardMaxCacheSize: 0,
|
||||
})
|
||||
Cache = cache
|
||||
}
|
112
src/database/database.go
Normal file
112
src/database/database.go
Normal file
@ -0,0 +1,112 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"pluja.dev/kycnot.me/ent/schema"
|
||||
)
|
||||
|
||||
var Client *ent.Client
|
||||
|
||||
func AddFakeData() {
|
||||
sampleTosHighlight := schema.TosHighlight{
|
||||
Title: "Transaction monitoring",
|
||||
Text: "MEX relies on data analysis as a risk-assessment and suspicion detection tool. MEX performs a variety of compliance-related tasks, including capturing data, filtering, record-keeping, investigation management, and reporting.",
|
||||
Reference: "https://example.com/tos",
|
||||
}
|
||||
|
||||
_, err := Client.Service.Create().
|
||||
SetName("bisq").
|
||||
SetLogoURL("https://kycnot.me/static/img/bisq.webp").
|
||||
SetDescription("Buy and sell bitcoin for fiat (or cryptocurrencies) privately and securely using Bisq's peer-to-peer network and open-source desktop software.").
|
||||
SetUrls([]string{"https://bisq.network/"}).
|
||||
SetTosUrls([]string{"https://bisq.network/privacy-policy/"}).
|
||||
SetOnionUrls([]string{}).
|
||||
SetKycLevel(0).
|
||||
SetTags("market,p2p,buy,sell,anonymous").
|
||||
SetPending(false).
|
||||
SetListed(true).
|
||||
SetVerified(true).
|
||||
SetXmr(true).
|
||||
SetBtc(true).
|
||||
SetLightning(true).
|
||||
SetFiat(true).
|
||||
SetCash(true).
|
||||
SetType(service.TypeExchange).
|
||||
SetAttributes([]string{AttributeNonCustodialWallet, AttributeOpenSource, AttributeNoPersonalInfoNeeded, AttributeP2P}).
|
||||
SetTosHighlights(&[]schema.TosHighlight{sampleTosHighlight}).
|
||||
Save(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("failed creating service: %v", err)
|
||||
}
|
||||
|
||||
_, err = Client.Service.Create().
|
||||
SetName("localmonero").
|
||||
SetLogoURL("https://kycnot.me/static/img/localmonero.webp").
|
||||
SetDescription("Peer-to-peer Monero trading platform. A marketplace where users can buy and sell Monero to and from each other. You'll be able to buy and sell online with more than 60 currencies.").
|
||||
SetUrls([]string{"https://localmonero.co/"}).
|
||||
SetTosUrls([]string{"https://localmonero.co/faq"}).
|
||||
SetOnionUrls([]string{"http://nehdddktmhvqklsnkjqcbpmb63htee2iznpcbs5tgzctipxykpj6yrid.onion/"}).
|
||||
SetKycLevel(1).
|
||||
SetTags("market,p2p,buy,sell").
|
||||
SetPending(false).
|
||||
SetListed(true).
|
||||
SetVerified(true).
|
||||
SetXmr(true).
|
||||
SetBtc(true).
|
||||
SetLightning(false).
|
||||
SetFiat(true).
|
||||
SetCash(true).
|
||||
SetType(service.TypeExchange).
|
||||
SetAttributes([]string{AttributeNonCustodialWallet, AttributeOpenSource, AttributeNoPersonalInfoNeeded, AttributeP2P}).
|
||||
SetTosHighlights(&[]schema.TosHighlight{sampleTosHighlight}).
|
||||
Save(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("failed creating service: %v", err)
|
||||
}
|
||||
}
|
||||
func InitDb() {
|
||||
var err error
|
||||
|
||||
if os.Getenv("DB_TYPE") == "sqlite" {
|
||||
Client, err = ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("failed opening connection to sqlite: %v", err)
|
||||
}
|
||||
} else if os.Getenv("DB_TYPE") == "mysql" {
|
||||
Client, err = ent.Open("mysql", os.Getenv("DB_USER")+":"+os.Getenv("DB_PASS")+"@tcp("+os.Getenv("DB_HOST")+":"+os.Getenv("DB_PORT")+")/"+os.Getenv("DB_NAME"))
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("failed opening connection to mysql: %v", err)
|
||||
}
|
||||
} else if os.Getenv("DB_TYPE") == "postgres" {
|
||||
Client, err = ent.Open("postgres", "host="+os.Getenv("DB_HOST")+" port="+os.Getenv("DB_PORT")+" user="+os.Getenv("DB_USER")+" dbname="+os.Getenv("DB_NAME")+" password="+os.Getenv("DB_PASS")+" sslmode=disable")
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("failed opening connection to postgres: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Fatal().Msgf("failed opening connection to database: %v", err)
|
||||
}
|
||||
if err := Client.Schema.Create(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Run the auto migration tool.
|
||||
ctx := context.Background()
|
||||
if err := Client.Schema.Create(ctx); err != nil {
|
||||
log.Fatal().Msgf("failed creating schema resources: %v", err)
|
||||
}
|
||||
|
||||
if os.Getenv("DB_TYPE") == "sqlite" && os.Getenv("DEV_MODE") == "true" {
|
||||
AddFakeData()
|
||||
}
|
||||
}
|
||||
|
||||
func Close() {
|
||||
if err := Client.Close(); err != nil {
|
||||
log.Fatal().Msgf("failed closing database connection: %v", err)
|
||||
}
|
||||
}
|
3
src/ent/generate.go
Normal file
3
src/ent/generate.go
Normal file
@ -0,0 +1,3 @@
|
||||
package ent
|
||||
|
||||
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
|
48
src/ent/schema/service.go
Normal file
48
src/ent/schema/service.go
Normal file
@ -0,0 +1,48 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
// Service holds the schema definition for the Service entity.
|
||||
type Service struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
type TosHighlight struct {
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
Section string `json:"section"`
|
||||
Reference string `json:"reference"`
|
||||
}
|
||||
|
||||
// Fields of the Service.
|
||||
func (Service) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("name").NotEmpty().Unique().StructTag(`json:"name"`),
|
||||
field.String("description").NotEmpty().StructTag(`json:"description"`),
|
||||
field.String("logo_url").NotEmpty().StructTag(`json:"logo_url"`),
|
||||
field.String("referral").Default("").StructTag(`json:"referral"`),
|
||||
field.String("listing_comment").Default("").StructTag(`json:"listing_comment"`),
|
||||
field.String("tags").StructTag(`json:"tags"`),
|
||||
field.Strings("urls").StructTag(`json:"urls"`),
|
||||
field.Strings("tos_urls").StructTag(`json:"tos_urls"`),
|
||||
field.Strings("onion_urls").StructTag(`json:"onion_urls"`),
|
||||
field.Strings("attributes").Default([]string{}).StructTag(`json:"attributes"`),
|
||||
field.Int("kyc_level").Default(0).StructTag(`json:"kyc_level"`),
|
||||
field.Bool("pending").Default(true).StructTag(`json:"pending"`),
|
||||
field.Bool("listed").Default(false).StructTag(`json:"listed"`),
|
||||
field.Bool("verified").Default(false).StructTag(`json:"verified"`),
|
||||
field.Bool("xmr").Default(false).StructTag(`json:"xmr"`),
|
||||
field.Bool("btc").Default(false).StructTag(`json:"btc"`),
|
||||
field.Bool("lightning").Default(false).StructTag(`json:"lightning"`),
|
||||
field.Bool("fiat").Default(false).StructTag(`json:"fiat"`),
|
||||
field.Bool("cash").Default(false).StructTag(`json:"cash"`),
|
||||
field.Enum("type").Values("exchange", "service", "wallet").StructTag(`json:"type"`).Default("service"),
|
||||
field.Time("created_at").Default(time.Now).Immutable().StructTag(`json:"created_at"`),
|
||||
field.JSON("tos_highlights", &[]TosHighlight{}).Optional().StructTag(`json:"tos_highlights"`),
|
||||
}
|
||||
}
|
40
src/go.mod
Normal file
40
src/go.mod
Normal file
@ -0,0 +1,40 @@
|
||||
module pluja.dev/kycnot.me
|
||||
|
||||
go 1.21.3
|
||||
|
||||
require (
|
||||
entgo.io/ent v0.12.4
|
||||
github.com/allegro/bigcache/v3 v3.1.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/gofiber/fiber/v2 v2.50.0
|
||||
github.com/gofiber/template/html/v2 v2.0.5
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/rs/zerolog v1.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/gofiber/template v1.8.2 // indirect
|
||||
github.com/gofiber/utils v1.1.0 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
)
|
110
src/go.sum
Normal file
110
src/go.sum
Normal file
@ -0,0 +1,110 @@
|
||||
ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935 h1:JnYs/y8RJ3+MiIUp+3RgyyeO48VHLAZimqiaZYnMKk8=
|
||||
ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935/go.mod h1:isZrlzJ5cpoCoKFoY9knZug7Lq4pP1cm8g3XciLZ0Pw=
|
||||
entgo.io/ent v0.12.4 h1:LddPnAyxls/O7DTXZvUGDj0NZIdGSu317+aoNLJWbD8=
|
||||
entgo.io/ent v0.12.4/go.mod h1:Y3JVAjtlIk8xVZYSn3t3mf8xlZIn5SAOXZQxD6kKI+Q=
|
||||
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/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
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.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
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/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-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/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/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
|
||||
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
|
||||
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
|
||||
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20=
|
||||
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM=
|
||||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||
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/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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 v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
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 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
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/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
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/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
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/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
65
src/main.go
Normal file
65
src/main.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Flags
|
||||
debug := flag.Bool("debug", false, "sets log level to debug")
|
||||
dev := flag.Bool("dev", false, "sets dev mode")
|
||||
nocache := flag.Bool("nocache", false, "disables cache")
|
||||
flag.Parse()
|
||||
|
||||
// Flags
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
if *debug {
|
||||
log.Printf("Debug mode enabled")
|
||||
log.Logger = log.Output(
|
||||
zerolog.ConsoleWriter{
|
||||
Out: os.Stdout,
|
||||
TimeFormat: "02.01.2006 15:04:05",
|
||||
},
|
||||
).With().Caller().Logger()
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
if *dev {
|
||||
log.Logger = log.Output(
|
||||
zerolog.ConsoleWriter{
|
||||
Out: os.Stdout,
|
||||
TimeFormat: "02.01.2006 15:04:05",
|
||||
},
|
||||
).With().Caller().Logger()
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
os.Setenv("DEV_MODE", "true")
|
||||
os.Setenv("SCRAPER", "false")
|
||||
log.Printf("DEV mode enabled")
|
||||
}
|
||||
if *nocache {
|
||||
log.Printf("Cache disabled")
|
||||
os.Setenv("CACHE", "false")
|
||||
}
|
||||
|
||||
// Load .env file
|
||||
log.Info().Msg("Loading .env file.")
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Info().Msg("No .env file found, using environment variables")
|
||||
}
|
||||
|
||||
// Database init
|
||||
log.Info().Msg("Initializing database.")
|
||||
//database.InitDb()
|
||||
//defer database.Close()
|
||||
|
||||
// Server init
|
||||
log.Info().Msg("Initializing server.")
|
||||
//server := server.NewServer(os.Getenv("LISTEN_ADDR"))
|
||||
//server.Run()
|
||||
}
|
112
src/pow/pow.go
Normal file
112
src/pow/pow.go
Normal file
@ -0,0 +1,112 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/allegro/bigcache/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type PowChallenger struct {
|
||||
IncreaseEveryChallenges int
|
||||
Cache *bigcache.BigCache
|
||||
}
|
||||
|
||||
func (p *PowChallenger) Init() {
|
||||
// Parse the environment variable once, at initialization
|
||||
log.Printf("Initializing PoW challenger.")
|
||||
if iec := os.Getenv("POW_INCREASE_EVERY_CHALLENGES"); iec != "" {
|
||||
if parsed, err := strconv.Atoi(iec); err == nil {
|
||||
p.IncreaseEveryChallenges = parsed
|
||||
}
|
||||
}
|
||||
p.IncreaseEveryChallenges = 20
|
||||
|
||||
// Init cache
|
||||
cache, _ := bigcache.New(context.Background(), bigcache.Config{
|
||||
Shards: 1024,
|
||||
LifeWindow: 10 * time.Minute,
|
||||
CleanWindow: 2 * time.Minute,
|
||||
MaxEntriesInWindow: 1000 * 10 * 60,
|
||||
MaxEntrySize: 500,
|
||||
Verbose: false,
|
||||
HardMaxCacheSize: 0,
|
||||
})
|
||||
p.Cache = cache
|
||||
}
|
||||
|
||||
// GenerateChallenge generates a new challenge for the client.
|
||||
func (p *PowChallenger) PowGenerateChallenge(length int) (challenge, id string, difficulty int, err error) {
|
||||
bytes := make([]byte, length/2) // since hex encoding doubles the length
|
||||
_, err = rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
id = uuid.New().String()
|
||||
challenge = hex.EncodeToString(bytes)
|
||||
difficulty, err = strconv.Atoi(os.Getenv("POW_DIFFICULTY"))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not get difficulty from environment")
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// Increase the difficulty by 1 every N challenges in a 10 minute period
|
||||
count := p.Cache.Len()
|
||||
if count%p.IncreaseEveryChallenges == 0 && count != 0 {
|
||||
difficulty++
|
||||
}
|
||||
|
||||
log.Printf("Generated challenge %v with difficulty %v", id, difficulty)
|
||||
|
||||
// Store the challenge in a temporary cache
|
||||
err = p.Cache.Set(fmt.Sprintf("pow-%v-c", id), bytes)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not set challenge in cache")
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// Store the challenge in a temporary cache
|
||||
difficultyBytes := make([]byte, 1)
|
||||
difficultyBytes[0] = byte(difficulty)
|
||||
err = p.Cache.Set(fmt.Sprintf("pow-%v-d", id), difficultyBytes)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not set difficulty in cache")
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyProof verifies the client's proof of work.
|
||||
func (p *PowChallenger) PowVerifyProof(id, nonce string) bool {
|
||||
// Get the challenge from the cache
|
||||
challengeBytes, err := p.Cache.Get(fmt.Sprintf("pow-%v-c", id))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not get challenge from cache")
|
||||
return false
|
||||
}
|
||||
challenge := hex.EncodeToString(challengeBytes)
|
||||
|
||||
// Get the difficulty from the cache
|
||||
difficultyBytes, err := p.Cache.Get(fmt.Sprintf("pow-%v-d", id))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not get difficulty from cache")
|
||||
return false
|
||||
}
|
||||
difficulty := int(difficultyBytes[0])
|
||||
|
||||
candidate := fmt.Sprintf("%s:%s", challenge, nonce)
|
||||
hash := sha256.Sum256([]byte(candidate))
|
||||
hashHex := hex.EncodeToString(hash[:])
|
||||
|
||||
return strings.HasPrefix(hashHex, strings.Repeat("0", difficulty))
|
||||
}
|
19
src/server/handlers_api.go
Normal file
19
src/server/handlers_api.go
Normal file
@ -0,0 +1,19 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func (s *Server) handleVerifyPow(c *fiber.Ctx) error {
|
||||
// Get id, nonce and proof from the request
|
||||
id := c.Params("id")
|
||||
nonce := c.Params("nonce")
|
||||
|
||||
// Verify the proof of work
|
||||
valid := s.PowChallenger.PowVerifyProof(id, nonce)
|
||||
|
||||
// Return the result
|
||||
return c.JSON(fiber.Map{
|
||||
"valid": valid,
|
||||
})
|
||||
}
|
193
src/server/handlers_web.go
Normal file
193
src/server/handlers_web.go
Normal file
@ -0,0 +1,193 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"pluja.dev/kycnot.me/database"
|
||||
"pluja.dev/kycnot.me/ent/service"
|
||||
"pluja.dev/kycnot.me/utils"
|
||||
)
|
||||
|
||||
func (s *Server) handleIndex(c *fiber.Ctx) error {
|
||||
nokycPf := []string{
|
||||
"KYC-Free Crypto Freedom.",
|
||||
"Goodbye KYC, hello privacy.",
|
||||
"KYC is a scam. Don't fall for it.",
|
||||
}
|
||||
|
||||
t := c.Query("t", "exchange")
|
||||
q := c.Query("q", "")
|
||||
|
||||
// Currencies
|
||||
xmr := c.Query("xmr", "")
|
||||
btc := c.Query("btc", "")
|
||||
ln := c.Query("ln", "")
|
||||
cash := c.Query("cash", "")
|
||||
fiat := c.Query("fiat", "")
|
||||
|
||||
// Query all services from the database and filter them by the query and the currencies
|
||||
queryBuilder := database.Client.Service.Query().Where(service.Listed(true), service.Pending(false))
|
||||
|
||||
// Apply the type filter if present
|
||||
if t != "" && t != "all" {
|
||||
log.Printf("Type: %v", t)
|
||||
queryBuilder = queryBuilder.Where(service.TypeEQ(service.Type(t)))
|
||||
}
|
||||
|
||||
// Apply the query text filter if present
|
||||
if q != "" {
|
||||
log.Printf("Query: %v", q)
|
||||
queryBuilder = queryBuilder.Where(
|
||||
service.Or(
|
||||
service.NameContains(q),
|
||||
service.DescriptionContains(q),
|
||||
service.TagsContains(q),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Apply the currency filters if present
|
||||
if xmr != "" {
|
||||
queryBuilder = queryBuilder.Where(service.XmrEQ(xmr == "on"))
|
||||
}
|
||||
if btc != "" {
|
||||
queryBuilder = queryBuilder.Where(service.BtcEQ(btc == "on"))
|
||||
}
|
||||
if ln != "" {
|
||||
queryBuilder = queryBuilder.Where(service.LightningEQ(ln == "on"))
|
||||
}
|
||||
if cash != "" {
|
||||
queryBuilder = queryBuilder.Where(service.CashEQ(cash == "on"))
|
||||
}
|
||||
if fiat != "" {
|
||||
queryBuilder = queryBuilder.Where(service.FiatEQ(fiat == "on"))
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
services, err := queryBuilder.All(context.Background())
|
||||
if err != nil {
|
||||
// Handle error (e.g., log it and return a generic error message to the client)
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
|
||||
return c.Render("index", fiber.Map{
|
||||
"Title": "Home",
|
||||
"Filters": map[string]string{
|
||||
"Type": t,
|
||||
"Query": q,
|
||||
"Xmr": xmr,
|
||||
"Btc": btc,
|
||||
"Ln": ln,
|
||||
"Cash": cash,
|
||||
"Fiat": fiat,
|
||||
},
|
||||
"Current": "index",
|
||||
"Services": services,
|
||||
"RandomPitch": nokycPf[rand.Intn(len(nokycPf))],
|
||||
}, "base")
|
||||
}
|
||||
|
||||
func (s *Server) handleService(c *fiber.Ctx) error {
|
||||
serviceName := strings.ToLower(c.Params("name"))
|
||||
|
||||
// Get service from database by name
|
||||
service, err := database.Client.Service.Query().Where(service.NameEQ(serviceName)).First(context.Background())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
|
||||
log.Printf("Service: %v", serviceName)
|
||||
return c.Render("service", fiber.Map{
|
||||
"Title": fmt.Sprintf("%v | Service", serviceName),
|
||||
"Service": service,
|
||||
}, "base")
|
||||
}
|
||||
|
||||
func (s *Server) handleRequestServiceForm(c *fiber.Ctx) error {
|
||||
challenge, id, difficulty, err := s.PowChallenger.PowGenerateChallenge(16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Render("request_service", fiber.Map{
|
||||
"Title": "Request a service",
|
||||
"Current": "request",
|
||||
"Pow": fiber.Map{
|
||||
"Challenge": challenge,
|
||||
"Difficulty": difficulty,
|
||||
"Id": id,
|
||||
},
|
||||
"Error": c.Query("error", ""),
|
||||
}, "base")
|
||||
}
|
||||
|
||||
func (s *Server) handleRequestServicePostForm(c *fiber.Ctx) error {
|
||||
nonce, id := c.FormValue("pow-nonce"), c.FormValue("pow-id")
|
||||
log.Printf("Nonce: %v, ID: %v", nonce, id)
|
||||
if !s.PowChallenger.PowVerifyProof(id, nonce) {
|
||||
return c.Redirect("/request/service?error=invalid-captcha")
|
||||
}
|
||||
|
||||
// KYC Level
|
||||
log.Printf("KYC Level: %v", c.FormValue("kyc_level"))
|
||||
klInt, err := strconv.Atoi(c.FormValue("kyc_level"))
|
||||
log.Printf("KYC Level Int: %v", klInt)
|
||||
if err != nil || klInt < 0 || klInt >= 4 {
|
||||
log.Error().Err(err).Msgf("Invalid KYC Level value: %v", c.FormValue("kyc_level"))
|
||||
return c.Redirect("/request/service?error=invalid-kyc-level")
|
||||
}
|
||||
|
||||
var serviceType service.Type
|
||||
switch c.FormValue("type") {
|
||||
case "exchange":
|
||||
serviceType = service.TypeExchange
|
||||
case "service":
|
||||
serviceType = service.TypeService
|
||||
case "wallet":
|
||||
serviceType = service.TypeWallet
|
||||
default:
|
||||
return c.Redirect("/request/service?error=invalid-service-type")
|
||||
}
|
||||
|
||||
// Parse tags
|
||||
tags := strings.ReplaceAll(c.FormValue("tags"), " ", ",")
|
||||
// Remove trailing commas
|
||||
tags = strings.TrimSuffix(tags, ",")
|
||||
// Remove duplicate commas
|
||||
tags = strings.ReplaceAll(tags, ",,", ",")
|
||||
// Remove leading commas
|
||||
tags = strings.TrimPrefix(tags, ",")
|
||||
// Convert to lowercase
|
||||
tags = strings.ToLower(tags)
|
||||
|
||||
_, err = database.Client.Service.Create().
|
||||
SetName(strings.ToLower(c.FormValue("name"))).
|
||||
SetDescription(c.FormValue("description")).
|
||||
SetType(serviceType).
|
||||
SetLogoURL(utils.UrlParser(c.FormValue("logo_url"))).
|
||||
SetUrls(utils.UrlListParser(c.FormValue("urls"))).
|
||||
SetTosUrls(utils.UrlListParser(c.FormValue("tos_urls"))).
|
||||
SetOnionUrls(utils.UrlListParser(c.FormValue("tor_urls"))).
|
||||
SetTags(tags).
|
||||
SetXmr(c.FormValue("xmr") == "on").
|
||||
SetBtc(c.FormValue("btc") == "on").
|
||||
SetLightning(c.FormValue("ln") == "on").
|
||||
SetFiat(c.FormValue("fiat") == "on").
|
||||
SetCash(c.FormValue("cash") == "on").
|
||||
SetKycLevel(klInt).
|
||||
SetPending(true).
|
||||
Save(c.Context())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Could not save service to database")
|
||||
return c.Redirect("/request/service?error=internal-error")
|
||||
}
|
||||
|
||||
return c.Redirect("/request/service?message=success")
|
||||
}
|
91
src/server/server.go
Normal file
91
src/server/server.go
Normal file
@ -0,0 +1,91 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/template/html/v2"
|
||||
|
||||
"pluja.dev/kycnot.me/pow"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ListenAddr string
|
||||
Router *fiber.App
|
||||
PowChallenger *pow.PowChallenger
|
||||
}
|
||||
|
||||
func NewServer(listenAddr string) *Server {
|
||||
// Create a new template engine
|
||||
engine := html.New(path.Join(os.Getenv("ROOT_DIR"), "web/templates"), ".html")
|
||||
if os.Getenv("DEV_MODE") == "true" {
|
||||
engine.Reload(true)
|
||||
}
|
||||
engine.AddFuncMap(
|
||||
map[string]interface{}{
|
||||
"attr": func(s string) template.HTMLAttr {
|
||||
return template.HTMLAttr(s)
|
||||
},
|
||||
"safe": func(s string) template.HTML {
|
||||
return template.HTML(s)
|
||||
},
|
||||
"shortText": func(s string) string {
|
||||
if len(s) > 50 {
|
||||
return strings.TrimSpace(s[:50]) + "..."
|
||||
}
|
||||
return s
|
||||
},
|
||||
},
|
||||
)
|
||||
return &Server{
|
||||
ListenAddr: listenAddr,
|
||||
Router: fiber.New(fiber.Config{
|
||||
JSONEncoder: json.Marshal,
|
||||
JSONDecoder: json.Unmarshal,
|
||||
BodyLimit: 2 * 1024 * 1024, // Increase body limit to 2MB
|
||||
ServerHeader: "None", // Optional, for easier debugging
|
||||
Views: engine,
|
||||
}),
|
||||
PowChallenger: &pow.PowChallenger{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
s.SetupMiddleware()
|
||||
s.RegisterRoutes()
|
||||
s.PowChallenger.Init()
|
||||
s.Router.Listen(s.ListenAddr)
|
||||
}
|
||||
|
||||
func (s *Server) SetupMiddleware() {
|
||||
s.Router.Use(cors.New())
|
||||
|
||||
if os.Getenv("DEV_MODE") == "true" {
|
||||
s.Router.Use(logger.New(logger.Config{
|
||||
Format: "${time} | ${method} ${status} ${path} ${latency}\n",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterRoutes() {
|
||||
// Static routes
|
||||
s.Router.Static("/static", path.Join(os.Getenv("ROOT_DIR"), "/web/static"), fiber.Static{
|
||||
Compress: true,
|
||||
ByteRange: true,
|
||||
})
|
||||
|
||||
// Register HTTP route for getting initial state.
|
||||
s.Router.Get("/", s.handleIndex)
|
||||
s.Router.Get("/service/:name", s.handleService)
|
||||
s.Router.Get("/request/service", s.handleRequestServiceForm)
|
||||
s.Router.Post("/request/service", s.handleRequestServicePostForm)
|
||||
|
||||
// Register API routes
|
||||
s.Router.Get("/api/pow/verify/:id/:nonce", s.handleVerifyPow)
|
||||
}
|
26
src/utils/parsers.go
Normal file
26
src/utils/parsers.go
Normal file
@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import "strings"
|
||||
|
||||
func UrlParser(url string) string {
|
||||
url = strings.ReplaceAll(url, " ", "")
|
||||
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = "https://" + url
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func UrlListParser(urls string) []string {
|
||||
url_list := strings.Split(strings.ReplaceAll(urls, " ", ""), ",")
|
||||
|
||||
// Check all urls for http:// or https://, if not present, add it
|
||||
for i, url := range url_list {
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url_list[i] = "https://" + url
|
||||
}
|
||||
}
|
||||
|
||||
return url_list
|
||||
}
|
BIN
src/web/static/assets/favicon.webp
Normal file
BIN
src/web/static/assets/favicon.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
src/web/static/assets/gradient.jpg
Normal file
BIN
src/web/static/assets/gradient.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 421 KiB |
BIN
src/web/static/assets/gradient.webp
Normal file
BIN
src/web/static/assets/gradient.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 277 KiB |
BIN
src/web/static/assets/gradient2.jpg
Normal file
BIN
src/web/static/assets/gradient2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 MiB |
1
src/web/static/assets/logo_wide.svg
Normal file
1
src/web/static/assets/logo_wide.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 31 KiB |
1
src/web/static/assets/spinner.svg
Normal file
1
src/web/static/assets/spinner.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
After Width: | Height: | Size: 400 B |
3
src/web/static/css/input.css
Normal file
3
src/web/static/css/input.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
1
src/web/static/disgus/disgus.css
Normal file
1
src/web/static/disgus/disgus.css
Normal file
File diff suppressed because one or more lines are too long
2095
src/web/static/disgus/index.js
Normal file
2095
src/web/static/disgus/index.js
Normal file
File diff suppressed because one or more lines are too long
2095
src/web/static/img/disgus/index.js
Normal file
2095
src/web/static/img/disgus/index.js
Normal file
File diff suppressed because one or more lines are too long
69
src/web/static/js/pow.js
Normal file
69
src/web/static/js/pow.js
Normal file
@ -0,0 +1,69 @@
|
||||
async function computeProof(challenge, difficulty) {
|
||||
let nonce = 0;
|
||||
const leadingZeros = '0'.repeat(difficulty);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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/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!';
|
||||
}
|
||||
});
|
||||
}
|
109
src/web/templates/base.html
Normal file
109
src/web/templates/base.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!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 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="KYCNOT.ME" />
|
||||
<meta property="og:description"
|
||||
content="Find 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/img/logos/banner.webp" />
|
||||
<meta property="thumbnail" content="https://kycnot.me/static/img/logos/banner.webp">
|
||||
<meta name="twitter:image" content="https://kycnot.me/static/img/logos/banner.webp">
|
||||
<meta property="og:url" content="https://kycnot.me" />
|
||||
<link rel='stylesheet' href='/static/css/style.css'>
|
||||
<link rel="shortcut icon" href="/static/assets/favicon.webp" type="image/x-icon">
|
||||
<link rel="me" href="https://fosstodon.org/@kycnotme">
|
||||
<!-- PROVIDE the pubkey so it can be tagged for responses/alerts -->
|
||||
<meta property="nostr:pubkey" content="npub1tuta00sz4wvvzymqcfq42cqhxal6puqpylxs4yf0z28z3ryvfh9qkqmv92" />
|
||||
<link rel="canonical" href="https://testing.com/testing-nostr-comments" />
|
||||
<!-- CAN provide multiple relays -->
|
||||
<meta property="nostr:relay" content="wss://relay.damus.io" />
|
||||
<!-- MUST PROVIDE CSS -->
|
||||
<link rel="stylesheet" href="/static/disgus/disgus.css">
|
||||
<title>{{.Title}} - KYCnot.me</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.capitalize-first::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="h-full font-mono text-gray-200 bg-fixed bg-black bg-center bg-cover" style="background-image: url('/static/assets/gradient.webp');">
|
||||
{{if .Error}}
|
||||
<div class="px-4 py-3 text-neutral-100 bg-error">
|
||||
<p class="text-sm font-bold text-center">
|
||||
{{ .Error }}
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Message}}
|
||||
<div class="px-4 py-3 text-neutral-100 bg-info">
|
||||
<p class="text-sm font-bold text-center">
|
||||
{{ .Message }}
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Nav Bar -->
|
||||
<nav class="bg-none">
|
||||
<div class="max-w-6xl px-4 mx-auto">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex flex-col items-center justify-center md:flex-row space-x-7">
|
||||
<!-- Logo -->
|
||||
<a href="/" class="block">
|
||||
<img src="/static/assets/logo_wide.svg" alt="Logo" class="w-3/5 pt-2 mr-2 md:w-1/4" />
|
||||
</a>
|
||||
|
||||
<!-- Primary Navbar items -->
|
||||
<div class="flex items-center space-x-1 md:space-x-2">
|
||||
<a
|
||||
href="/"
|
||||
class="px-1 py-1 text-xs 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-xs 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=""
|
||||
class="px-1 py-1 text-xs 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=""
|
||||
class="px-1 py-1 text-xs text-gray-500 transition duration-300 md:text-base hover:text-lime-500"
|
||||
>Support</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!---->
|
||||
|
||||
|
||||
{{embed}}
|
||||
</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>
|
48
src/web/templates/components/kyc_level.html
Normal file
48
src/web/templates/components/kyc_level.html
Normal file
@ -0,0 +1,48 @@
|
||||
<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 do not mention that it will ever request the user for a KYC verification.</p>
|
||||
{{else if eq . 1}}
|
||||
<p class="mt-2 text-sm">KYC is not mentioned, but this service reserves the right to share data with representatives of the authorities, block funds or reject transactions.</p>
|
||||
{{else if eq . 2}}
|
||||
<p class="mt-2 text-sm">The exchange may request KYC from any user at any time, 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 mandatory to use some features. A non-KYCed user can be required to verify their identity at any time or any moment for any reason.</p>
|
||||
{{end}}
|
||||
</div>
|
35
src/web/templates/components/point.html
Normal file
35
src/web/templates/components/point.html
Normal file
@ -0,0 +1,35 @@
|
||||
<a href="/point/{{.ID}}" class="block py-1.5 px-3 text-center text-sm border rounded-lg transition duration-300
|
||||
{{if eq "good" .Type }}border-green-900 bg-green-900/30 hover:bg-green-900/50 hover:border-green-700
|
||||
{{else if eq "warning" .Type }}border-amber-900 bg-amber-900/30 hover:bg-amber-900/50 hover:border-amber-700
|
||||
{{else if eq "bad" .Type }}border-red-900 bg-red-900/30 hover:bg-red-900/50 hover:border-red-700
|
||||
{{else}}border-info-900 bg-info-900/30 hover:bg-info-900/50 hover:border-info-700
|
||||
{{end}}">
|
||||
|
||||
<h3 class="flex items-center justify-center space-x-2">
|
||||
<!-- Icon -->
|
||||
{{if eq "good" .Type }}
|
||||
<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 "warning" .Type }}
|
||||
<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" .Type }}
|
||||
<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 info-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}}
|
||||
<!-- End icon -->
|
||||
|
||||
<span class="capitalize-first">{{.Title}}</span>
|
||||
</h3>
|
||||
</a>
|
35
src/web/templates/components/service_card.html
Normal file
35
src/web/templates/components/service_card.html
Normal file
@ -0,0 +1,35 @@
|
||||
{{/* service_card.html */}}
|
||||
<a href="/service/{{.Name}}">
|
||||
<div class="p-4 m-2 transition duration-500 bg-gray-700 border border-transparent rounded-lg shadow-lg backdrop-blur-md bg-opacity-30 hover:border-lime-600 min-w-32">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<img src="{{.LogoURL}}" alt="{{.Name}} logo" class="w-12 h-12 mr-4 rounded-full">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex flex-col text-xl font-bold text-white">
|
||||
<div class="flex items-center justify-between space-x-3">
|
||||
<span class="flex items-center justify-center capitalize">
|
||||
{{if .Verified}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mr-1 {{if .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>
|
||||
{{end}}
|
||||
{{.Name}}
|
||||
</span>
|
||||
<span class="bg-green-500 backdrop-blur-md bg-opacity-70 font-bold rounded-lg py-0.5 px-2 text-sm">
|
||||
10
|
||||
</span>
|
||||
</div>
|
||||
<span class="pr-1 mt-2 text-xs font-normal text-white/60 text-opacity-60">{{shortText .Description}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pt-3 text-xs">
|
||||
<span class="px-2 py-1 mr-1 font-bold uppercase border text-[10px] rounded text-white/70 bg-zinc-900 border-zinc-700">
|
||||
{{.Type}}
|
||||
</span>
|
||||
{{template "components/service_icons" .}}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
59
src/web/templates/components/service_icons.html
Normal file
59
src/web/templates/components/service_icons.html
Normal file
@ -0,0 +1,59 @@
|
||||
<div class="grid grid-cols-5">
|
||||
|
||||
<!-- Monero -->
|
||||
<span class="px-2 py-1 m-1 text-center text-white border rounded 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 class="px-2 py-1 m-1 text-center text-white border rounded 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 class="px-2 py-1 m-1 text-center text-white border rounded 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 class="px-2 py-1 m-1 text-center text-white border rounded 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 class="px-2 py-1 m-1 text-center text-white border rounded 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>
|
||||
</div>
|
7
src/web/templates/components/tos_check.html
Normal file
7
src/web/templates/components/tos_check.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="px-3 py-2 mt-2 border rounded-md border-amber-900 bg-amber-900/30">
|
||||
<h3 class="font-bold uppercase">{{.Title}}</h3>
|
||||
<p class="my-1.5 text-sm">{{.Text}}</p>
|
||||
{{if .Section}}
|
||||
<p class="text-xs text-gray-400 text-opacity-40">ToS Section: <span>{{.Section}}</span></p>
|
||||
{{end}}
|
||||
</div>
|
91
src/web/templates/index.html
Normal file
91
src/web/templates/index.html
Normal file
@ -0,0 +1,91 @@
|
||||
<main class="py-4 text-lime-500">
|
||||
<div class="px-4 py-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>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="text-center text-gray-500">
|
||||
<a href="/">
|
||||
<span class="p-1.5 rounded-xl backdrop-blur-md bg-opacity-70 {{if eq "" .Filters.Type}}bg-lime-500 text-zinc-900 {{else}} border border-gray-500 {{end}} font-bold text-xs">Exchanges</span>
|
||||
</a>
|
||||
<a href="/?t=service">
|
||||
<span class="p-1.5 rounded-xl backdrop-blur-md bg-opacity-70 {{if eq "service" .Filters.Type}}bg-lime-500 text-zinc-900 {{else}} border border-gray-500 {{end}} font-bold text-xs">Services</span>
|
||||
</a>
|
||||
<a href="/?t=all">
|
||||
<span class="p-1.5 rounded-xl backdrop-blur-md bg-opacity-70 {{if eq "all" .Filters.Type}}bg-lime-500 text-zinc-900 {{else}} border border-gray-500 {{end}} font-bold text-xs">All</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form action="/">
|
||||
<input type="hidden" name="t" id="t" value="{{.Filters.Type}}"/>
|
||||
<div class="flex items-center justify-center mt-6">
|
||||
<input class="text-xs bg-transparent border rounded-lg border-lime-600 text-lime-600" placeholder="Search anything..." value="{{.Filters.Query}}" type="text" name="q" id="q">
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center pt-4 space-x-2">
|
||||
<span class="flex flex-col items-center justify-center">
|
||||
<label class="text-xs uppercase" for="btc">btc</label>
|
||||
<input
|
||||
class="bg-transparent rounded-full outline-none appearance-none 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">xmr</label>
|
||||
<input
|
||||
class="bg-transparent rounded-full outline-none appearance-none 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">ln</label>
|
||||
<input
|
||||
class="bg-transparent rounded-full outline-none appearance-none 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="bg-transparent rounded-full outline-none appearance-none 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="bg-transparent rounded-full outline-none appearance-none focus:ring-0 border-lime-500 text-lime-500"
|
||||
type="checkbox"
|
||||
{{if eq "on" .Filters.Cash}}checked{{end}}
|
||||
name="cash"
|
||||
id="cash"
|
||||
>
|
||||
</span>
|
||||
<button class="p-1 text-xs font-bold uppercase border rounded-lg bg-none border-lime-500" type="submit">filter</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- End Filters -->
|
||||
</main>
|
||||
|
||||
<!-- Services list -->
|
||||
<section class="flex items-center justify-center px-1 mt-4">
|
||||
<div class="grid max-w-6xl grid-cols-1 md:grid-cols-3 lg:grid-cols-3">
|
||||
{{range .Services}}
|
||||
{{template "components/service_card" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
149
src/web/templates/request_service.html
Normal file
149
src/web/templates/request_service.html
Normal file
@ -0,0 +1,149 @@
|
||||
<section class="flex flex-col items-center w-full py-8">
|
||||
<h2 class="py-3 text-2xl font-bold">Request a service</h2>
|
||||
|
||||
<form action="/request/service" method="POST" class="max-w-lg space-y-4 font-mono">
|
||||
<div class="flex flex-col">
|
||||
<select name="type" id="type" required class="p-2 text-white border rounded-lg bg-opacity-30 bg-zinc-900 border-white/30">
|
||||
<option value="def" selected disabled>Select an option</option>
|
||||
<option value="exchange">exchange</option>
|
||||
<option value="service">service</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label for="name">Service Name</label>
|
||||
<input class="p-2 text-white border rounded-lg bg-opacity-30 bg-zinc-900 border-white/30" type="text" name="name" id="name" required>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label for="description">Description</label>
|
||||
<textarea class="p-2 font-mono text-white border rounded-lg bg-opacity-30 bg-zinc-900 border-white/30" name="description" id="description" cols="30" rows="5" required maxlength="500"></textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label 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 bg-opacity-30 bg-zinc-900 border-white/30" type="text" name="urls" id="urls" required placeholder="Awesome Exchange">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label for="logo_url">Logo URL</label>
|
||||
<input class="p-2 text-white border rounded-lg 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 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 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 for="tor_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 bg-opacity-30 bg-zinc-900 border-white/30" type="text" name="tor_urls" id="tor_urls" placeholder="http://zbiowkw...onion">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label 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 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>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 transaccion 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>
|
||||
<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>
|
||||
startPow();
|
||||
</script>
|
159
src/web/templates/service.html
Normal file
159
src/web/templates/service.html
Normal file
@ -0,0 +1,159 @@
|
||||
<section class="pt-14">
|
||||
<div class="flex items-center justify-center space-x-4">
|
||||
<img class="w-16 h-16 rounded-full" src="{{.Service.LogoURL}}" alt="">
|
||||
<h1 class="text-2xl font-bold capitalize-first">{{.Service.Name}}</h1>
|
||||
</div>
|
||||
<!-- Links -->
|
||||
<div class="flex items-center justify-center py-4 mt-2 text-sm md:text-md">
|
||||
<div class="flex items-center justify-center">
|
||||
<a href="{{index .Service.Urls 0}}" target="_blank" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-white transition duration-300 border rounded bg-zinc-900 border-zinc-700 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 (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-white transition duration-300 border rounded bg-zinc-900 border-zinc-700 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}}
|
||||
{{if ne .Service.Referral ""}}
|
||||
<a href="#" target="_blank" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-white transition duration-300 border rounded bg-zinc-900 border-zinc-700 hover:border-lime-600 hover:text-lime-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 tabler-users" 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 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"></path>
|
||||
<path d="M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||||
<path d="M21 21v-2a4 4 0 0 0 -3 -3.85"></path>
|
||||
</svg>
|
||||
<span>Referral</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{if ne (len .Service.OnionUrls) 0}}
|
||||
<a href="#" target="_blank" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center text-purple-600 transition duration-300 border border-purple-900 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-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>
|
||||
<span>Tor</span>
|
||||
</a>
|
||||
{{end}}
|
||||
<a href="#discuss" class="flex items-center justify-center px-2 py-1 m-1 space-x-2 text-center transition duration-300 border rounded text-sky-400 border-sky-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 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>
|
||||
</div>
|
||||
|
||||
<!-- Icons -->
|
||||
<div class="flex items-center justify-center text-xs">
|
||||
{{template "components/service_icons" .Service}}
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="flex items-center justify-center p-4">
|
||||
<p class="max-w-lg p-4 text-sm border rounded-lg md:text-base border-white/20 bg-gray-400/10">
|
||||
{{if ne .Service.Description ""}}
|
||||
{{.Service.Description}}
|
||||
{{else}}
|
||||
This {{.Service.Type}} does not have a description.
|
||||
{{end}}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- KYC Level -->
|
||||
<section class="flex items-center justify-center px-6">
|
||||
{{template "components/kyc_level" .Service.KycLevel}}
|
||||
</section>
|
||||
|
||||
<!-- Points -->
|
||||
<section class="flex flex-col items-center justify-center p-8">
|
||||
<div class="grid grid-cols-1 gap-2 md:grid-cols-2">
|
||||
{{range .Points}}
|
||||
{{template "components/point" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Notes -->
|
||||
<section class="flex flex-col items-center justify-center px-8 mt-4 mb-8">
|
||||
<h2 class="mb-0.5 text-xl font-bold text-center capitalize">Additional info</h2>
|
||||
<div class="max-w-lg space-y-1.5">
|
||||
<p class="block font-bold py-1.5 px-3 text-center text-sm border rounded-lg transition duration-300 border-white/30 bg-white/10 hover:bg-white/20 hover:border-white">
|
||||
* Lorem ipsum dolor sit amet.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ToS Checker -->
|
||||
<section class="flex flex-col items-center justify-center p-8 pb-4 border-t-2 border-opacity-50 border-dashed border-t-lime-400/10" id="tos">
|
||||
|
||||
<a href="/about#tos-checker" class="text-xl font-bold">
|
||||
Automated ToS Checker
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4 question-mark" 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 8a3.5 3 0 0 1 3.5 -3h1a3.5 3 0 0 1 3.5 3a3 3 0 0 1 -2 3a3 4 0 0 0 -2 4"></path>
|
||||
<path d="M12 19l0 .01"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<p class="max-w-sm text-xs text-center text-gray-300 text-opacity-75">Automated, ai-driven weekly ToS checks.</p>
|
||||
<p class="max-w-sm mt-1 text-xs text-center text-gray-400 text-opacity-75">Last Check: 2023-10-18</p>
|
||||
|
||||
{{range .Service.TosHighlights}}
|
||||
<div class="max-w-lg">
|
||||
{{template "components/tos_check" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
<!-- Comments -->
|
||||
<section class="flex flex-col items-center justify-center px-8 pt-4 pb-8" id="discuss">
|
||||
<a href="https://usenostr.org" class="text-xl font-bold">
|
||||
Nostr Comments
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4 question-mark" 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 8a3.5 3 0 0 1 3.5 -3h1a3.5 3 0 0 1 3.5 3a3 3 0 0 1 -2 3a3 4 0 0 0 -2 4"></path>
|
||||
<path d="M12 19l0 .01"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<h2 class="text-xl font-bold"></h2>
|
||||
<p class="max-w-sm pb-4 mt-1 text-xs text-center text-gray-300 text-opacity-75">
|
||||
Beware of fake reviews and accounts. Verify user profiles and conduct your own research.
|
||||
</p>
|
||||
<!-- div with the ID disgus where you would like to display the comments & form -->
|
||||
<div id="disgus" class="max-w-xl">
|
||||
<noscript>
|
||||
<p class="font-bold text-center text-yellow-300">
|
||||
You must have JavaScript enabled to see and use comments. It just will load a 200kb script to interact with Nostr. The source code is available <a target="_blank" class="underline" href="https://github.com/carlitoplatanito/disgus">here</a>.
|
||||
</p>
|
||||
</noscript>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- this can go at the end of the body -->
|
||||
<script type="module" src="/static/disgus/index.js" async></script>
|
12
tailwind.config.js
Normal file
12
tailwind.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/web/**/*.{html,js}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user