add search

This commit is contained in:
eyedeekay
2025-03-17 19:52:56 -04:00
parent 1d63691280
commit e07b0f6645
7 changed files with 260 additions and 74 deletions

70
add.go Normal file
View File

@ -0,0 +1,70 @@
package jumpserver
import (
"net/http"
"time"
"github.com/go-i2p/i2pkeys"
)
func (j *JumpServer) handleAdd(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
err := templates.ExecuteTemplate(w, "add.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}
if r.Method == "POST" {
err := r.ParseForm()
if err != nil {
http.Error(w, "Failed to parse form", http.StatusBadRequest)
return
}
// Extract form data
hostname := r.Form.Get("hostname")
destination := r.Form.Get("destination")
regType := r.Form.Get("type")
name := r.Form.Get("name")
description := r.Form.Get("description")
tags := r.Form.Get("tags")
// Validate hostname
if hostname == "" {
http.Error(w, "Hostname is required", http.StatusBadRequest)
return
}
// Parse I2P address
addr, err := i2pkeys.NewI2PAddrFromString(destination)
if err != nil {
http.Error(w, "Invalid I2P destination", http.StatusBadRequest)
return
}
// Create new hostname entry
host := &Hostname{
I2PAddr: &addr,
Time: time.Now(),
Registrant: Registrant{
Type: regType,
Name: name,
Description: description,
Tags: splitTags(tags),
},
Hostname: hostname,
}
// Add to jump server
j.AddHostname(host)
// Redirect to index page
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}

View File

@ -4,9 +4,6 @@ import (
"html/template"
"net/http"
"path/filepath"
"time"
"github.com/go-i2p/i2pkeys"
)
var templates *template.Template
@ -28,6 +25,8 @@ func (j *JumpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
case "/add", "/add.html":
j.handleAdd(w, r)
case "/search":
j.handleSearch(w, r)
case "/all-hosts.txt":
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(j.HostsFile()))
@ -39,73 +38,3 @@ func (j *JumpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
}
}
func (j *JumpServer) handleAdd(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
err := templates.ExecuteTemplate(w, "add.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}
if r.Method == "POST" {
err := r.ParseForm()
if err != nil {
http.Error(w, "Failed to parse form", http.StatusBadRequest)
return
}
// Extract form data
hostname := r.Form.Get("hostname")
destination := r.Form.Get("destination")
regType := r.Form.Get("type")
name := r.Form.Get("name")
description := r.Form.Get("description")
tags := r.Form.Get("tags")
// Validate hostname
if hostname == "" {
http.Error(w, "Hostname is required", http.StatusBadRequest)
return
}
// Parse I2P address
addr, err := i2pkeys.NewI2PAddrFromString(destination)
if err != nil {
http.Error(w, "Invalid I2P destination", http.StatusBadRequest)
return
}
// Create new hostname entry
host := &Hostname{
I2PAddr: &addr,
Time: time.Now(),
Registrant: Registrant{
Type: regType,
Name: name,
Description: description,
Tags: splitTags(tags),
},
Hostname: hostname,
}
// Add to jump server
j.AddHostname(host)
// Redirect to index page
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
func (j *JumpServer) HostsFile() string {
var hosts string
for _, host := range j.Hostnames {
hosts += host.Hostname + "=" + host.Base64() + "\n"
}
return hosts
}

9
hosts.go Normal file
View File

@ -0,0 +1,9 @@
package jumpserver
func (j *JumpServer) HostsFile() string {
var hosts string
for _, host := range j.Hostnames {
hosts += host.Hostname + "=" + host.Base64() + "\n"
}
return hosts
}

79
search.go Normal file
View File

@ -0,0 +1,79 @@
package jumpserver
import "net/http"
type SearchPage struct {
Query string
Field string
Results SearchResults
}
func (j *JumpServer) handleSearch(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
query := r.URL.Query().Get("q")
field := r.URL.Query().Get("field")
var results SearchResults
if query != "" {
// Handle field-specific searches
switch field {
case "hostname":
results = j.searchByField(query, field, func(h *Hostname) bool {
return h.HostSearch(query)
})
case "address":
results = j.searchByField(query, field, func(h *Hostname) bool {
return h.AddrSearch(query)
})
case "registrant":
results = j.searchByField(query, field, func(h *Hostname) bool {
return h.Registrant.RegistrarSearch(query)
})
case "description":
results = j.searchByField(query, field, func(h *Hostname) bool {
return h.Registrant.TextSearch(query)
})
case "tags":
results = j.searchByField(query, field, func(h *Hostname) bool {
return h.Registrant.HasTag(query)
})
default:
// Default to full search across all fields
results = j.Search(query)
}
}
page := &SearchPage{
Query: query,
Field: field,
Results: results,
}
err := templates.ExecuteTemplate(w, "search.html", page)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// Helper function to search by specific field
func (j *JumpServer) searchByField(query string, field string, matcher func(*Hostname) bool) SearchResults {
var results SearchResults
for _, host := range j.Hostnames {
if matcher(host) {
results = append(results, &SearchResult{
Hostname: host,
Host: field == "hostname",
Addr: field == "address",
Registrar: field == "registrant",
Text: field == "description",
Tag: field == "tags",
})
}
}
return results
}

View File

@ -11,7 +11,11 @@ pl/add.html
<header>
<h1>Add Host to Jump Server</h1>
</header>
<div id="index-links">
<a href="/add">Add Host</a>
<a href="/search">Search</a>
<a href="/">Home</a>
</div>
<main>
<section id="rules">
<h2>Rules for Adding Hosts</h2>

View File

@ -19,6 +19,11 @@
<p>Welcome to the I2P Jump Server. This service helps you discover and navigate I2P services.</p>
{{end}}
</section>
<div id="index-links">
<a href="/add">Add Host</a>
<a href="/search">Search</a>
<a href="/">Home</a>
</div>
<section id="hostname-list">
<h2>Registered Hostnames</h2>

90
tpl/search.html Normal file
View File

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search - I2P Jump Server</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header>
<h1>Search I2P Jump Server</h1>
</header>
<div id="index-links">
<a href="/add">Add Host</a>
<a href="/search">Search</a>
<a href="/">Home</a>
</div>
<main>
<section id="search-form">
<form method="GET" action="/search">
<div class="form-group">
<label for="q">Search Query:</label>
<input type="text" id="q" name="q" value="{{.Query}}"
placeholder="Enter search terms...">
</div>
<div class="form-group">
<label for="field">Search In:</label>
<select id="field" name="field">
<option value="all" {{if eq .Field "all"}}selected{{end}}>All Fields</option>
<option value="hostname" {{if eq .Field "hostname"}}selected{{end}}>Hostname</option>
<option value="address" {{if eq .Field "address"}}selected{{end}}>I2P Address</option>
<option value="registrant" {{if eq .Field "registrant"}}selected{{end}}>Registrant</option>
<option value="description" {{if eq .Field "description"}}selected{{end}}>Description</option>
<option value="tags" {{if eq .Field "tags"}}selected{{end}}>Tags</option>
</select>
</div>
<button type="submit">Search</button>
</form>
</section>
{{if .Results}}
<section id="search-results">
<h2>Search Results</h2>
<table>
<thead>
<tr>
<th>Hostname</th>
<th>Address</th>
<th>Type</th>
<th>Registrant</th>
<th>Description</th>
<th>Tags</th>
<th>Match Type</th>
</tr>
</thead>
<tbody>
{{range .Results}}
<tr>
<td>{{.Hostname.Hostname}}</td>
<td>{{.Hostname.I2PAddr.Base32}}</td>
<td>{{.Hostname.Registrant.Type}}</td>
<td>{{.Hostname.Registrant.Name}}</td>
<td>{{.Hostname.Registrant.Description}}</td>
<td>
{{range .Hostname.Registrant.Tags}}
<span class="tag">{{.}}</span>
{{end}}
</td>
<td>
{{if .Host}}Hostname{{end}}
{{if .Addr}}Address{{end}}
{{if .Registrar}}Registrant{{end}}
{{if .Text}}Description{{end}}
{{if .Tag}}Tags{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</section>
{{else if .Query}}
<p>No results found for "{{.Query}}"</p>
{{end}}
</main>
<script src="/static/script.js"></script>
</body>
</html>