mirror of
https://github.com/go-i2p/go-jump-addr.git
synced 2025-07-12 19:04:51 -04:00
add search
This commit is contained in:
70
add.go
Normal file
70
add.go
Normal 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)
|
||||
}
|
75
handlers.go
75
handlers.go
@ -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
9
hosts.go
Normal 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
79
search.go
Normal 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
|
||||
}
|
@ -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>
|
||||
|
@ -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
90
tpl/search.html
Normal 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>
|
Reference in New Issue
Block a user