basic app

This commit is contained in:
rayaman 2025-06-22 22:09:21 -04:00
parent 1892d3d83e
commit e95ab1fb73
12 changed files with 501 additions and 1 deletions

3
.gitignore vendored
View File

@ -7,6 +7,7 @@
*.dll *.dll
*.so *.so
*.dylib *.dylib
**.db
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test
@ -29,4 +30,4 @@ go.work.sum
# Editor/IDE # Editor/IDE
# .idea/ # .idea/
# .vscode/ .vscode/

114
app/backend/binary.go Normal file
View File

@ -0,0 +1,114 @@
package backend
import (
"binaryserver/app/db"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
var browser *rod.Browser
func asciiToBinary(text string) string {
var result []string
for _, char := range text {
binary := fmt.Sprintf("%08b", char) // 8-bit binary format
result = append(result, binary)
}
return strings.Join(result, " ")
}
func binaryToASCII(binary string) (string, error) {
// Ensure the length of the binary string is a multiple of 8
if len(binary)%8 != 0 {
return "", fmt.Errorf("binary string length must be a multiple of 8")
}
var sb strings.Builder
for i := 0; i < len(binary); i += 8 {
byteStr := binary[i : i+8]
b, err := strconv.ParseUint(byteStr, 2, 8)
if err != nil {
return "", fmt.Errorf("invalid binary sequence at position %d: %v", i, err)
}
sb.WriteByte(byte(b))
}
return sb.String(), nil
}
func instagramHandleExists(username string, bypass ...bool) string {
records, _ := db.GetRecords(db.QueryOptions{Where: fmt.Sprintf(`handle='%v'`, username)})
if len(records) > 0 && len(bypass) == 0 {
return records[0].URL
}
url := fmt.Sprintf("https://www.instagram.com/%s/", username)
page := browser.MustPage(url)
// Wait until body is loaded
page.MustWaitLoad().MustWaitIdle()
body := page.MustElement("body").MustText()
record := db.NewRecord(username, url)
// Instagram shows this for non-existent users
if strings.Contains(body, "Sorry, this page isn't available") {
record.URL = ""
db.SaveRecord(record)
return ""
}
db.SaveRecord(record)
return url
}
func BinaryHandler(w http.ResponseWriter, req *http.Request) {
binaryInput := strings.ReplaceAll(req.URL.Query().Get("binary"), " ", "")
var text string
var handle string
var err error
if binaryInput == "" {
asciiInput := req.URL.Query().Get("text")
if asciiInput == "" {
fmt.Fprintf(w, "Could not encode or decode!\n")
return
}
text = asciiToBinary(asciiInput)
fmt.Fprintf(w, "%v\n", GetFancyResponse(`<center><p>`+text+`</p></center>`, ""))
return
} else {
text, err = binaryToASCII(binaryInput)
text = strings.ReplaceAll(text, "@", "")
handle = text
}
if err != nil {
fmt.Fprintf(w, "Could not decode!\n")
return
}
var url string
if url = instagramHandleExists(text); url != "" {
text = `<center><a href="` + url + `">` + text + `</a></center>`
} else {
text = `<center><p>` + text + `</p></center>`
}
if url == "" {
handle = ""
}
fmt.Fprintf(w, "%v\n", GetFancyResponse(text, handle))
}
func init() {
path, _ := launcher.New().
Headless(true).
// Uncomment below to see browser
// Set("headless", "false").
Launch()
browser = rod.New().ControlURL(path).MustConnect()
}

51
app/backend/page.go Normal file
View File

@ -0,0 +1,51 @@
package backend
func GetFancyResponse(input, handle string) string {
var hasHandle string
if handle != "" {
hasHandle = `<a class="bottom-right" href="/report?handle=` + handle + `">Report Broken Link</a>`
}
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Decoded Binary</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color:rgb(19, 31, 49);
margin: 40px;
color: #333;
}
.fancy-box {
background-color: #000000;
border: 2px solid #4a90e2;
border-radius: 12px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.bottom-right {
position: absolute;
bottom: 10px;
right: 10px;
margin: 0;
}
p {
color: #ffffff;
font-size: 1.1em;
}
</style>
</head>
<body>
<div class="fancy-box">
` + input + `
` + hasHandle + `
</div>
</body>
</html>
`
}

20
app/backend/report.go Normal file
View File

@ -0,0 +1,20 @@
package backend
import (
"binaryserver/app/db"
"fmt"
"net/http"
)
func ReportHandler(w http.ResponseWriter, req *http.Request) {
handle := req.URL.Query().Get("handle")
records, _ := db.GetRecords(db.QueryOptions{Where: fmt.Sprintf(`handle='%v'`, handle)})
if len(records) > 0 {
if result := instagramHandleExists(records[0].Handle, true); result == "" && records[0].URL != "" {
fmt.Fprintf(w, "Link does seem to be bad. Fixing...\n")
db.DeleteRecord(records[0])
} else {
fmt.Fprintf(w, "Link seems to be active and working!\n")
}
}
}

39
app/db/db.go Normal file
View File

@ -0,0 +1,39 @@
package db
import (
"database/sql"
_ "modernc.org/sqlite"
)
var conn *sql.DB
func HandleConn(testing ...bool) *sql.DB {
if len(testing) > 0 {
conn, _ = sql.Open("sqlite", "file::memory:?cache=shared")
buildSchema()
return conn
}
if conn == nil {
conn, _ = sql.Open("sqlite", "records.db")
buildSchema()
return conn
}
return conn
}
func buildSchema() error {
c := HandleConn()
_, err := c.Exec(`
CREATE TABLE IF NOT EXISTS records
(
UUID TEXT NOT NULL,
Handle TEXT NOT NULL UNIQUE,
URL TEXT NOT NULL,
PRIMARY KEY (UUID)
);
`)
return err
}

77
app/db/record.go Normal file
View File

@ -0,0 +1,77 @@
package db
import (
"binaryserver/app/utils"
"github.com/google/uuid"
)
type Record struct {
UUID uuid.UUID `json:"uuid"`
Handle string `json:"handle"`
URL string `json:"url"`
}
func NewRecord(handle, url string) Record {
return Record{
UUID: utils.GetUUID(),
Handle: handle,
URL: url,
}
}
func SaveRecord(record Record) error {
conn := HandleConn()
stmt, err := conn.Prepare(`INSERT INTO records
VALUES(?, ?, ?)`)
if err != nil {
return err
}
_, err = stmt.Exec(record.UUID.String(), record.Handle, record.URL)
if err != nil {
return err
}
defer stmt.Close()
return nil
}
func GetRecords(opts QueryOptions) ([]Record, error) {
conn := HandleConn()
var records []Record
stmt, err := opts.Build(conn, "records")
if err != nil {
return nil, err
}
row, err := stmt.Query()
stmt.Close()
if err != nil {
return nil, err
}
defer row.Close()
for {
if err != nil {
return nil, err
}
if !row.Next() {
break
}
record := Record{}
row.Scan(&record.UUID, &record.Handle, &record.URL)
records = append(records, record)
}
return records, nil
}
func DeleteRecord(record Record) error {
stmt, err := conn.Prepare(`DELETE FROM records
WHERE UUID = ?`)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(record.UUID.String())
if err != nil {
return err
}
return nil
}

18
app/db/record_test.go Normal file
View File

@ -0,0 +1,18 @@
package db
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRecords(t *testing.T) {
HandleConn(true)
rec := NewRecord("test", "https://instagram.com/handle")
err := SaveRecord(rec)
assert.Nil(t, err)
records, err := GetRecords(QueryOptions{Where: `handle='test'`})
assert.Nil(t, err)
assert.Greater(t, len(records), 0)
assert.Equal(t, "https://instagram.com/handle", records[0].URL)
}

50
app/db/types.go Normal file
View File

@ -0,0 +1,50 @@
package db
import (
"database/sql"
"fmt"
"strings"
)
type QueryOptions struct {
Select []string
Where string
OrderBy string
Limit uint
Offset uint
}
func (o *QueryOptions) Build(conn *sql.DB, table string) (*sql.Stmt, error) {
sel := "*"
where := ""
orderby := ""
limit := ""
if o.Select != nil {
sel = strings.Join(o.Select, ", ")
}
if o.Where != "" {
where = fmt.Sprintf(
`WHERE
%v`, o.Where)
}
if o.OrderBy != "" {
orderby = fmt.Sprintf(
`ORDER BY
%v`, o.OrderBy)
}
if o.Limit != 0 && o.Offset != 0 {
limit = fmt.Sprintf(
`LIMIT %v OFFSET %v;`, o.Limit, o.Offset)
} else if o.Limit != 0 {
limit = fmt.Sprintf(
`LIMIT %v`, o.Limit)
}
return conn.Prepare(fmt.Sprintf(
`SELECT
%v
FROM
%v
%v
%v
%v;`, sel, table, where, orderby, limit))
}

12
app/utils/utils.go Normal file
View File

@ -0,0 +1,12 @@
package utils
import "github.com/google/uuid"
func GetUUID() uuid.UUID {
uuid.SetClockSequence(uuid.ClockSequence())
if uid, err := uuid.NewV7(); err == nil {
return uid
}
uid, _ := uuid.NewUUID()
return uid
}

31
go.mod Normal file
View File

@ -0,0 +1,31 @@
module binaryserver
go 1.24.2
require (
github.com/go-rod/rod v0.116.2
github.com/google/uuid v1.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/stretchr/testify v1.10.0
modernc.org/sqlite v1.38.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/ysmood/fetchup v0.2.3 // indirect
github.com/ysmood/goob v0.4.0 // indirect
github.com/ysmood/got v0.40.0 // indirect
github.com/ysmood/gson v0.7.3 // indirect
github.com/ysmood/leakless v0.9.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sys v0.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.65.10 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)

75
go.sum Normal file
View File

@ -0,0 +1,75 @@
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg=
github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=
github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=
github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY=
github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

12
main.go Normal file
View File

@ -0,0 +1,12 @@
package main
import (
"binaryserver/app/backend"
"net/http"
)
func main() {
http.HandleFunc("/report", backend.ReportHandler)
http.HandleFunc("/", backend.BinaryHandler)
http.ListenAndServe(":8080", nil)
}