rename: kafka → samsa
Full project rename from kafka to samsa (after Gregor Samsa, who woke one morning from uneasy dreams to find himself transformed). - Module: github.com/metamorphosis-dev/kafka → samsa - Binary: cmd/kafka/ → cmd/samsa/ - CSS: kafka.css → samsa.css - UI: all 'kafka' product names, titles, localStorage keys → samsa - localStorage keys: kafka-theme → samsa-theme, kafka-engines → samsa-engines - OpenSearch: ShortName, LongName, description, URLs updated - AGPL headers: 'kafka' → 'samsa' - Docs, configs, examples updated - Cache key prefix: kafka: → samsa:
This commit is contained in:
parent
c91908a427
commit
8e9aae062b
70 changed files with 185 additions and 184 deletions
|
|
@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
kafka is a privacy-respecting metasearch engine written in Go. It provides a SearXNG-compatible `/search` API and an HTML frontend (HTMX + Go templates). 9 engines are implemented natively in Go; unlisted engines can be proxied to an upstream metasearch instance. Responses from multiple engines are merged into a single JSON/CSV/RSS/HTML response.
|
samsa is a privacy-respecting metasearch engine written in Go. It provides a SearXNG-compatible `/search` API and an HTML frontend (HTMX + Go templates). 9 engines are implemented natively in Go; unlisted engines can be proxied to an upstream metasearch instance. Responses from multiple engines are merged into a single JSON/CSV/RSS/HTML response.
|
||||||
|
|
||||||
## Build & Run Commands
|
## Build & Run Commands
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ go test -run TestWikipedia ./internal/engines/
|
||||||
go test -v ./internal/engines/
|
go test -v ./internal/engines/
|
||||||
|
|
||||||
# Run the server (requires config.toml)
|
# Run the server (requires config.toml)
|
||||||
go run ./cmd/kafka -config config.toml
|
go run ./cmd/samsa -config config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
There is no Makefile. There is no linter configured.
|
There is no Makefile. There is no linter configured.
|
||||||
|
|
@ -43,7 +43,7 @@ There is no Makefile. There is no linter configured.
|
||||||
- `internal/cache` — Valkey/Redis-backed cache with SHA-256 cache keys. No-op if unconfigured.
|
- `internal/cache` — Valkey/Redis-backed cache with SHA-256 cache keys. No-op if unconfigured.
|
||||||
- `internal/middleware` — Three rate limiters (per-IP sliding window, burst+sustained, global) and CORS. All disabled by default.
|
- `internal/middleware` — Three rate limiters (per-IP sliding window, burst+sustained, global) and CORS. All disabled by default.
|
||||||
- `internal/views` — HTML templates and static files embedded via `//go:embed`. Renders full pages or HTMX fragments. Templates: `base.html`, `index.html`, `results.html`, `results_inner.html`, `result_item.html`.
|
- `internal/views` — HTML templates and static files embedded via `//go:embed`. Renders full pages or HTMX fragments. Templates: `base.html`, `index.html`, `results.html`, `results_inner.html`, `result_item.html`.
|
||||||
- `cmd/kafka` — Entry point. Loads TOML config, seeds env vars for engine code, wires up middleware chain, starts HTTP server.
|
- `cmd/samsa` — Entry point. Loads TOML config, seeds env vars for engine code, wires up middleware chain, starts HTTP server.
|
||||||
|
|
||||||
**Engine interface** (`internal/engines/engine.go`):
|
**Engine interface** (`internal/engines/engine.go`):
|
||||||
```go
|
```go
|
||||||
|
|
@ -66,7 +66,7 @@ Config is loaded from `config.toml` (see `config.example.toml`). All fields can
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- Module path: `github.com/metamorphosis-dev/kafka`
|
- Module path: `github.com/metamorphosis-dev/samsa`
|
||||||
- Tests use shared mock helpers in `internal/engines/http_mock_test.go` (`roundTripperFunc`, `httpResponse`)
|
- Tests use shared mock helpers in `internal/engines/http_mock_test.go` (`roundTripperFunc`, `httpResponse`)
|
||||||
- Engine implementations are single files under `internal/engines/` (e.g., `wikipedia.go`, `duckduckgo.go`)
|
- Engine implementations are single files under `internal/engines/` (e.g., `wikipedia.go`, `duckduckgo.go`)
|
||||||
- Response merging de-duplicates by `engine|title|url` key; suggestions/corrections are merged as sets
|
- Response merging de-duplicates by `engine|title|url` key; suggestions/corrections are merged as sets
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
# kafka
|
# samsa
|
||||||
|
|
||||||
A privacy-respecting, open metasearch engine written in Go. SearXNG-compatible API with an HTML frontend, designed to be fast, lightweight, and deployable anywhere.
|
A privacy-respecting, open metasearch engine written in Go. SearXNG-compatible API with an HTML frontend, designed to be fast, lightweight, and deployable anywhere.
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ A privacy-respecting, open metasearch engine written in Go. SearXNG-compatible A
|
||||||
- **Valkey cache** — optional Redis-compatible caching with configurable TTL
|
- **Valkey cache** — optional Redis-compatible caching with configurable TTL
|
||||||
- **Rate limiting** — three layers: per-IP, burst, and global (all disabled by default)
|
- **Rate limiting** — three layers: per-IP, burst, and global (all disabled by default)
|
||||||
- **CORS** — configurable origins for browser-based clients
|
- **CORS** — configurable origins for browser-based clients
|
||||||
- **OpenSearch** — browsers can add kafka as a search engine from the address bar
|
- **OpenSearch** — browsers can add samsa as a search engine from the address bar
|
||||||
- **Graceful degradation** — individual engine failures don't kill the whole search
|
- **Graceful degradation** — individual engine failures don't kill the whole search
|
||||||
- **Docker** — multi-stage build, ~20MB runtime image
|
- **Docker** — multi-stage build, ~20MB runtime image
|
||||||
- **NixOS** — native NixOS module with systemd service
|
- **NixOS** — native NixOS module with systemd service
|
||||||
|
|
@ -23,9 +23,9 @@ A privacy-respecting, open metasearch engine written in Go. SearXNG-compatible A
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.ashisgreat.xyz/penal-colony/gosearch.git
|
git clone https://git.ashisgreat.xyz/penal-colony/gosearch.git
|
||||||
cd kafka
|
cd samsa
|
||||||
go build ./cmd/kafka
|
go build ./cmd/samsa
|
||||||
./kafka -config config.toml
|
./samsa -config config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose
|
### Docker Compose
|
||||||
|
|
@ -41,28 +41,28 @@ docker compose up -d
|
||||||
Add to your flake inputs:
|
Add to your flake inputs:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
inputs.kafka.url = "git+https://git.ashisgreat.xyz/penal-colony/gosearch.git";
|
inputs.samsa.url = "git+https://git.ashisgreat.xyz/penal-colony/gosearch.git";
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable in your configuration:
|
Enable in your configuration:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
imports = [ inputs.kafka.nixosModules.default ];
|
imports = [ inputs.samsa.nixosModules.default ];
|
||||||
|
|
||||||
services.kafka = {
|
services.samsa = {
|
||||||
enable = true;
|
enable = true;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
baseUrl = "https://search.example.com";
|
baseUrl = "https://search.example.com";
|
||||||
# config = "/etc/kafka/config.toml"; # default
|
# config = "/etc/samsa/config.toml"; # default
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Write your config:
|
Write your config:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /etc/kafka
|
sudo mkdir -p /etc/samsa
|
||||||
sudo cp config.example.toml /etc/kafka/config.toml
|
sudo cp config.example.toml /etc/samsa/config.toml
|
||||||
sudo $EDITOR /etc/kafka/config.toml
|
sudo $EDITOR /etc/samsa/config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
Deploy:
|
Deploy:
|
||||||
|
|
@ -76,7 +76,7 @@ sudo nixos-rebuild switch --flake .#
|
||||||
```bash
|
```bash
|
||||||
nix develop
|
nix develop
|
||||||
go test ./...
|
go test ./...
|
||||||
go run ./cmd/kafka -config config.toml
|
go run ./cmd/samsa -config config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -25,13 +25,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/autocomplete"
|
"github.com/metamorphosis-dev/samsa/internal/autocomplete"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/cache"
|
"github.com/metamorphosis-dev/samsa/internal/cache"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/config"
|
"github.com/metamorphosis-dev/samsa/internal/config"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/httpapi"
|
"github.com/metamorphosis-dev/samsa/internal/httpapi"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/middleware"
|
"github.com/metamorphosis-dev/samsa/internal/middleware"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/search"
|
"github.com/metamorphosis-dev/samsa/internal/search"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/views"
|
"github.com/metamorphosis-dev/samsa/internal/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -127,7 +127,7 @@ func main() {
|
||||||
}, logger)(handler)
|
}, logger)(handler)
|
||||||
|
|
||||||
addr := fmt.Sprintf(":%d", cfg.Server.Port)
|
addr := fmt.Sprintf(":%d", cfg.Server.Port)
|
||||||
logger.Info("kafka starting",
|
logger.Info("samsa starting",
|
||||||
"addr", addr,
|
"addr", addr,
|
||||||
"cache", searchCache.Enabled(),
|
"cache", searchCache.Enabled(),
|
||||||
"rate_limit", cfg.RateLimit.Requests > 0,
|
"rate_limit", cfg.RateLimit.Requests > 0,
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# kafka configuration
|
# samsa configuration
|
||||||
# Copy to config.toml and adjust as needed.
|
# Copy to config.toml and adjust as needed.
|
||||||
# Environment variables are used as fallbacks when a config field is empty/unset.
|
# Environment variables are used as fallbacks when a config field is empty/unset.
|
||||||
|
|
||||||
|
|
@ -10,13 +10,13 @@ port = 8080
|
||||||
http_timeout = "10s"
|
http_timeout = "10s"
|
||||||
|
|
||||||
# Public base URL for OpenSearch XML (env: BASE_URL)
|
# Public base URL for OpenSearch XML (env: BASE_URL)
|
||||||
# Set this so browsers can add kafka as a search engine.
|
# Set this so browsers can add samsa as a search engine.
|
||||||
# Example: "https://search.example.com"
|
# Example: "https://search.example.com"
|
||||||
base_url = ""
|
base_url = ""
|
||||||
|
|
||||||
# Link to the source code (shown in footer as "Source" link)
|
# Link to the source code (shown in footer as "Source" link)
|
||||||
# Defaults to the upstream kafka repo if not set.
|
# Defaults to the upstream samsa repo if not set.
|
||||||
# Example: "https://git.example.com/my-kafka-fork"
|
# Example: "https://git.example.com/my-samsa-fork"
|
||||||
source_url = ""
|
source_url = ""
|
||||||
|
|
||||||
[upstream]
|
[upstream]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
**Goal:** Redesign the kafka frontend to match Brave Search's layout: three-column results page, category tiles on homepage, and a hybrid preferences system with full-page `/preferences` route.
|
**Goal:** Redesign the samsa frontend to match Brave Search's layout: three-column results page, category tiles on homepage, and a hybrid preferences system with full-page `/preferences` route.
|
||||||
|
|
||||||
**Architecture:** CSS Grid for page-level layouts (three-column results, two-column preferences). JavaScript popover for quick settings (theme + engines only). Server-rendered full preferences page with localStorage persistence. Category tiles are static links with category query params.
|
**Architecture:** CSS Grid for page-level layouts (three-column results, two-column preferences). JavaScript popover for quick settings (theme + engines only). Server-rendered full preferences page with localStorage persistence. Category tiles are static links with category query params.
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
| File | Responsibility |
|
| File | Responsibility |
|
||||||
|------|----------------|
|
|------|----------------|
|
||||||
| `internal/views/static/css/kafka.css` | Add layout grids, category tiles, sidebar styles, mobile breakpoints |
|
| `internal/views/static/css/samsa.css` | Add layout grids, category tiles, sidebar styles, mobile breakpoints |
|
||||||
| `internal/views/templates/index.html` | Add category tiles below search box |
|
| `internal/views/templates/index.html` | Add category tiles below search box |
|
||||||
| `internal/views/templates/results.html` | Add left sidebar, restructure for three-column grid |
|
| `internal/views/templates/results.html` | Add left sidebar, restructure for three-column grid |
|
||||||
| `internal/views/templates/preferences.html` | **New** — full preferences page with nav |
|
| `internal/views/templates/preferences.html` | **New** — full preferences page with nav |
|
||||||
|
|
@ -30,11 +30,11 @@
|
||||||
### Task 1: Add CSS Grid Layouts and Breakpoints
|
### Task 1: Add CSS Grid Layouts and Breakpoints
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Modify: `internal/views/static/css/kafka.css`
|
- Modify: `internal/views/static/css/samsa.css`
|
||||||
|
|
||||||
- [ ] **Step 1: Add three-column results layout CSS**
|
- [ ] **Step 1: Add three-column results layout CSS**
|
||||||
|
|
||||||
Append to end of `kafka.css`, before the `@media print` block:
|
Append to end of `samsa.css`, before the `@media print` block:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|
@ -364,7 +364,7 @@ Note: CSS is embedded as static files and not processed by the Go compiler. CSS
|
||||||
- [ ] **Step 7: Commit**
|
- [ ] **Step 7: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add internal/views/static/css/kafka.css
|
git add internal/views/static/css/samsa.css
|
||||||
git commit -m "feat(frontend): add CSS layout framework for three-column results and preferences page"
|
git commit -m "feat(frontend): add CSS layout framework for three-column results and preferences page"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -895,7 +895,7 @@ git commit -m "feat(frontend): add category tiles to homepage"
|
||||||
|
|
||||||
- [ ] **Step 2: Add preferences section CSS styles**
|
- [ ] **Step 2: Add preferences section CSS styles**
|
||||||
|
|
||||||
Append to `kafka.css`:
|
Append to `samsa.css`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|
@ -1010,7 +1010,7 @@ func RenderPreferences(w http.ResponseWriter, sourceURL string) error {
|
||||||
- [ ] **Step 4: Commit**
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add internal/views/templates/preferences.html internal/views/static/css/kafka.css internal/views/views.go
|
git add internal/views/templates/preferences.html internal/views/static/css/samsa.css internal/views/views.go
|
||||||
git commit -m "feat(frontend): add preferences page template and styles"
|
git commit -m "feat(frontend): add preferences page template and styles"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1020,7 +1020,7 @@ git commit -m "feat(frontend): add preferences page template and styles"
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Modify: `internal/httpapi/handlers.go`
|
- Modify: `internal/httpapi/handlers.go`
|
||||||
- Modify: `cmd/kafka/main.go`
|
- Modify: `cmd/samsa/main.go`
|
||||||
|
|
||||||
- [ ] **Step 1: Add GET and POST handlers for /preferences**
|
- [ ] **Step 1: Add GET and POST handlers for /preferences**
|
||||||
|
|
||||||
|
|
@ -1053,7 +1053,7 @@ func (h *Handler) PreferencesPOST(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
- [ ] **Step 2: Register the route in main**
|
- [ ] **Step 2: Register the route in main**
|
||||||
|
|
||||||
Find where routes are registered (likely in `cmd/kafka/main.go`) and add:
|
Find where routes are registered (likely in `cmd/samsa/main.go`) and add:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
mux.HandleFunc("GET /preferences", handler.Preferences)
|
mux.HandleFunc("GET /preferences", handler.Preferences)
|
||||||
|
|
@ -1068,7 +1068,7 @@ Expected: No errors
|
||||||
- [ ] **Step 4: Commit**
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add internal/httpapi/handlers.go cmd/kafka/main.go
|
git add internal/httpapi/handlers.go cmd/samsa/main.go
|
||||||
git commit -m "feat: add GET and POST /preferences route"
|
git commit -m "feat: add GET and POST /preferences route"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1192,7 +1192,7 @@ git commit -m "fix(frontend): add HTMX filter submission"
|
||||||
### Task 8: Final Mobile Responsiveness Audit
|
### Task 8: Final Mobile Responsiveness Audit
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Review: `internal/views/static/css/kafka.css`
|
- Review: `internal/views/static/css/samsa.css`
|
||||||
|
|
||||||
- [ ] **Step 1: Test all breakpoints manually**
|
- [ ] **Step 1: Test all breakpoints manually**
|
||||||
|
|
||||||
|
|
@ -1201,7 +1201,7 @@ git commit -m "fix(frontend): add HTMX filter submission"
|
||||||
- [ ] **Step 3: Commit any fixes**
|
- [ ] **Step 3: Commit any fixes**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add internal/views/static/css/kafka.css
|
git add internal/views/static/css/samsa.css
|
||||||
git commit -m "fix(frontend): improve mobile responsiveness"
|
git commit -m "fix(frontend): improve mobile responsiveness"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1211,7 +1211,7 @@ git commit -m "fix(frontend): improve mobile responsiveness"
|
||||||
|
|
||||||
| Phase | Task | Files |
|
| Phase | Task | Files |
|
||||||
|-------|------|-------|
|
|-------|------|-------|
|
||||||
| 1 | CSS Layout Framework | `kafka.css` |
|
| 1 | CSS Layout Framework | `samsa.css` |
|
||||||
| 2 | Results Three-Column | `results.html`, `views.go` |
|
| 2 | Results Three-Column | `results.html`, `views.go` |
|
||||||
| 3 | Homepage Tiles | `index.html` |
|
| 3 | Homepage Tiles | `index.html` |
|
||||||
| 4 | Preferences Page | `preferences.html` (new), `handlers.go`, `settings.js` |
|
| 4 | Preferences Page | `preferences.html` (new), `handlers.go`, `settings.js` |
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
**Goal:** A preferences popover panel (top-right on desktop, bottom sheet on mobile) that lets users set theme, enabled engines, safe search, and default format. All changes auto-save to `localStorage` and apply immediately to the DOM.
|
**Goal:** A preferences popover panel (top-right on desktop, bottom sheet on mobile) that lets users set theme, enabled engines, safe search, and default format. All changes auto-save to `localStorage` and apply immediately to the DOM.
|
||||||
|
|
||||||
**Architecture:** Pure client-side JS + CSS added alongside existing templates. No Go changes. Settings persist via `localStorage` key `kafka_prefs`. Theme applies via `data-theme` attribute on `<html>`.
|
**Architecture:** Pure client-side JS + CSS added alongside existing templates. No Go changes. Settings persist via `localStorage` key `samsa_prefs`. Theme applies via `data-theme` attribute on `<html>`.
|
||||||
|
|
||||||
**Tech Stack:** Vanilla JS (no framework), existing `kafka.css` custom properties, HTMX for search.
|
**Tech Stack:** Vanilla JS (no framework), existing `samsa.css` custom properties, HTMX for search.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
| Action | File |
|
| Action | File |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| Create | `internal/views/static/js/settings.js` |
|
| Create | `internal/views/static/js/settings.js` |
|
||||||
| Modify | `internal/views/static/css/kafka.css` |
|
| Modify | `internal/views/static/css/samsa.css` |
|
||||||
| Modify | `internal/views/templates/base.html` |
|
| Modify | `internal/views/templates/base.html` |
|
||||||
| Modify | `internal/views/templates/index.html` |
|
| Modify | `internal/views/templates/index.html` |
|
||||||
| Modify | `internal/views/templates/results.html` |
|
| Modify | `internal/views/templates/results.html` |
|
||||||
|
|
@ -28,11 +28,11 @@
|
||||||
## Task 1: CSS — Popover, toggles, bottom sheet
|
## Task 1: CSS — Popover, toggles, bottom sheet
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- Modify: `internal/views/static/css/kafka.css`
|
- Modify: `internal/views/static/css/samsa.css`
|
||||||
|
|
||||||
- [ ] **Step 1: Add CSS for popover, triggers, toggles, bottom sheet**
|
- [ ] **Step 1: Add CSS for popover, triggers, toggles, bottom sheet**
|
||||||
|
|
||||||
Append the following to `kafka.css`:
|
Append the following to `samsa.css`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* ============================================
|
/* ============================================
|
||||||
|
|
@ -305,7 +305,7 @@ Expected: all pass
|
||||||
- [ ] **Step 3: Commit**
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git add internal/views/static/css/kafka.css
|
git add internal/views/static/css/samsa.css
|
||||||
git commit -m "feat(settings): add popover, toggle, and bottom-sheet CSS"
|
git commit -m "feat(settings): add popover, toggle, and bottom-sheet CSS"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -335,7 +335,7 @@ var DEFAULT_PREFS = {
|
||||||
format: 'html'
|
format: 'html'
|
||||||
};
|
};
|
||||||
|
|
||||||
var STORAGE_KEY = 'kafka_prefs';
|
var STORAGE_KEY = 'samsa_prefs';
|
||||||
|
|
||||||
// ── Persistence ──────────────────────────────────────────────────────────────
|
// ── Persistence ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -619,7 +619,7 @@ In `base.html`, update the `<body>` to:
|
||||||
<body class="{{if .Query}}search_on_results{{end}}">
|
<body class="{{if .Query}}search_on_results{{end}}">
|
||||||
{{if .ShowHeader}}
|
{{if .ShowHeader}}
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<span class="site-title">kafka</span>
|
<span class="site-title">samsa</span>
|
||||||
<!-- Desktop trigger (hidden on mobile) -->
|
<!-- Desktop trigger (hidden on mobile) -->
|
||||||
<button id="settings-trigger" class="settings-trigger settings-trigger-desktop"
|
<button id="settings-trigger" class="settings-trigger settings-trigger-desktop"
|
||||||
aria-label="Preferences" aria-expanded="false" aria-controls="settings-popover">⚙</button>
|
aria-label="Preferences" aria-expanded="false" aria-controls="settings-popover">⚙</button>
|
||||||
|
|
@ -633,7 +633,7 @@ In `base.html`, update the `<body>` to:
|
||||||
{{template "content" .}}
|
{{template "content" .}}
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<p>Powered by <a href="https://git.ashisgreat.xyz/penal-colony/kafka">kafka</a> — a privacy-respecting, open metasearch engine</p>
|
<p>Powered by <a href="https://git.ashisgreat.xyz/penal-colony/samsa">samsa</a> — a privacy-respecting, open metasearch engine</p>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="/static/js/settings.js"></script>
|
<script src="/static/js/settings.js"></script>
|
||||||
<div id="settings-popover" data-open="false" role="dialog" aria-label="Preferences" aria-modal="true">
|
<div id="settings-popover" data-open="false" role="dialog" aria-label="Preferences" aria-modal="true">
|
||||||
|
|
@ -699,7 +699,7 @@ The `value` is populated by `syncEngineInput(prefs)` on page load. When the form
|
||||||
|
|
||||||
- [ ] **Step 2: Verify existing search works**
|
- [ ] **Step 2: Verify existing search works**
|
||||||
|
|
||||||
Run: `go run ./cmd/kafka -config config.toml`
|
Run: `go run ./cmd/samsa -config config.toml`
|
||||||
Open: `http://localhost:8080`
|
Open: `http://localhost:8080`
|
||||||
Search for "golang" — results should appear as normal.
|
Search for "golang" — results should appear as normal.
|
||||||
|
|
||||||
|
|
@ -716,7 +716,7 @@ git commit -m "feat(settings): add hidden engines input to search forms"
|
||||||
|
|
||||||
- [ ] **Step 1: Start server**
|
- [ ] **Step 1: Start server**
|
||||||
|
|
||||||
Run: `go run ./cmd/kafka -config config.toml`
|
Run: `go run ./cmd/samsa -config config.toml`
|
||||||
Open: `http://localhost:8080`
|
Open: `http://localhost:8080`
|
||||||
|
|
||||||
- [ ] **Step 2: Verify gear icon and panel**
|
- [ ] **Step 2: Verify gear icon and panel**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Redesign the kafka frontend to match Brave Search's clean, functional aesthetic with emphasis on layout changes: three-column results page, category tiles on homepage, and a hybrid preferences system with full-page `/preferences` route.
|
Redesign the samsa frontend to match Brave Search's clean, functional aesthetic with emphasis on layout changes: three-column results page, category tiles on homepage, and a hybrid preferences system with full-page `/preferences` route.
|
||||||
|
|
||||||
## Design Principles
|
## Design Principles
|
||||||
|
|
||||||
|
|
@ -302,7 +302,7 @@ On mobile (< 768px):
|
||||||
| `internal/views/templates/results.html` | Add left sidebar, restructure for three columns |
|
| `internal/views/templates/results.html` | Add left sidebar, restructure for three columns |
|
||||||
| `internal/views/templates/base.html` | Minimal changes (no structural changes needed) |
|
| `internal/views/templates/base.html` | Minimal changes (no structural changes needed) |
|
||||||
| `internal/views/templates/preferences.html` | **New** — full preferences page |
|
| `internal/views/templates/preferences.html` | **New** — full preferences page |
|
||||||
| `internal/views/static/css/kafka.css` | Add layout grids, category tiles, sidebar styles, sticky positioning, mobile breakpoints |
|
| `internal/views/static/css/samsa.css` | Add layout grids, category tiles, sidebar styles, sticky positioning, mobile breakpoints |
|
||||||
| `internal/views/static/js/settings.js` | Reduce popover to theme + engines; add preferences page JS |
|
| `internal/views/static/js/settings.js` | Reduce popover to theme + engines; add preferences page JS |
|
||||||
| `internal/httpapi/httpapi.go` | Add `/preferences` route (GET + POST) |
|
| `internal/httpapi/httpapi.go` | Add `/preferences` route (GET + POST) |
|
||||||
| `internal/views/views.go` | Add preferences template rendering |
|
| `internal/views/views.go` | Add preferences template rendering |
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Settings UI Design — kafka
|
# Settings UI Design — samsa
|
||||||
|
|
||||||
**Date:** 2026-03-22
|
**Date:** 2026-03-22
|
||||||
**Status:** Approved
|
**Status:** Approved
|
||||||
|
|
@ -61,10 +61,10 @@ const DEFAULT_PREFS = {
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Written on every interaction
|
// Written on every interaction
|
||||||
localStorage.setItem('kafka_prefs', JSON.stringify({ ... }));
|
localStorage.setItem('samsa_prefs', JSON.stringify({ ... }));
|
||||||
|
|
||||||
// Read on page load — merge with DEFAULT_PREFS
|
// Read on page load — merge with DEFAULT_PREFS
|
||||||
const saved = JSON.parse(localStorage.getItem('kafka_prefs') || '{}');
|
const saved = JSON.parse(localStorage.getItem('samsa_prefs') || '{}');
|
||||||
const prefs = { ...DEFAULT_PREFS, ...saved };
|
const prefs = { ...DEFAULT_PREFS, ...saved };
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -76,5 +76,5 @@ const prefs = { ...DEFAULT_PREFS, ...saved };
|
||||||
## Applied to Existing Code
|
## Applied to Existing Code
|
||||||
|
|
||||||
- `base.html` — add gear button in header, panel markup at end of `<body>`
|
- `base.html` — add gear button in header, panel markup at end of `<body>`
|
||||||
- `kafka.css` — popover styles, toggle switch styles, bottom sheet styles for mobile
|
- `samsa.css` — popover styles, toggle switch styles, bottom sheet styles for mobile
|
||||||
- `settings.js` — localStorage read/write, theme application, panel toggle, aria attributes, focus trap
|
- `settings.js` — localStorage read/write, theme application, panel toggle, aria attributes, focus trap
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -1,4 +1,4 @@
|
||||||
module github.com/metamorphosis-dev/kafka
|
module github.com/metamorphosis-dev/samsa
|
||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -102,7 +102,7 @@ func (s *Service) wikipediaSuggestions(ctx context.Context, query string) ([]str
|
||||||
}
|
}
|
||||||
req.Header.Set(
|
req.Header.Set(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
"gosearch-go/0.1 (compatible; +https://github.com/metamorphosis-dev/kafka)",
|
"gosearch-go/0.1 (compatible; +https://github.com/metamorphosis-dev/samsa)",
|
||||||
)
|
)
|
||||||
|
|
||||||
resp, err := s.http.Do(req)
|
resp, err := s.http.Do(req)
|
||||||
|
|
|
||||||
10
internal/cache/cache.go
vendored
10
internal/cache/cache.go
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ func (c *Cache) Get(ctx context.Context, key string) (contracts.SearchResponse,
|
||||||
return contracts.SearchResponse{}, false
|
return contracts.SearchResponse{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
fullKey := "kafka:" + key
|
fullKey := "samsa:" + key
|
||||||
|
|
||||||
data, err := c.client.Get(ctx, fullKey).Bytes()
|
data, err := c.client.Get(ctx, fullKey).Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -129,7 +129,7 @@ func (c *Cache) Set(ctx context.Context, key string, resp contracts.SearchRespon
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fullKey := "kafka:" + key
|
fullKey := "samsa:" + key
|
||||||
if err := c.client.Set(ctx, fullKey, data, c.ttl).Err(); err != nil {
|
if err := c.client.Set(ctx, fullKey, data, c.ttl).Err(); err != nil {
|
||||||
c.logger.Warn("cache set failed", "key", fullKey, "error", err)
|
c.logger.Warn("cache set failed", "key", fullKey, "error", err)
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@ func (c *Cache) Invalidate(ctx context.Context, key string) {
|
||||||
if !c.Enabled() {
|
if !c.Enabled() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fullKey := "kafka:" + key
|
fullKey := "samsa:" + key
|
||||||
c.client.Del(ctx, fullKey)
|
c.client.Del(ctx, fullKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
internal/cache/cache_test.go
vendored
6
internal/cache/cache_test.go
vendored
|
|
@ -3,13 +3,13 @@ package cache
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKey_Deterministic(t *testing.T) {
|
func TestKey_Deterministic(t *testing.T) {
|
||||||
req := contracts.SearchRequest{
|
req := contracts.SearchRequest{
|
||||||
Format: contracts.FormatJSON,
|
Format: contracts.FormatJSON,
|
||||||
Query: "kafka metamorphosis",
|
Query: "samsa metamorphosis",
|
||||||
Pageno: 1,
|
Pageno: 1,
|
||||||
Safesearch: 0,
|
Safesearch: 0,
|
||||||
Language: "auto",
|
Language: "auto",
|
||||||
|
|
@ -29,7 +29,7 @@ func TestKey_Deterministic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKey_DifferentQueries(t *testing.T) {
|
func TestKey_DifferentQueries(t *testing.T) {
|
||||||
reqA := contracts.SearchRequest{Query: "kafka", Format: contracts.FormatJSON}
|
reqA := contracts.SearchRequest{Query: "samsa", Format: contracts.FormatJSON}
|
||||||
reqB := contracts.SearchRequest{Query: "orwell", Format: contracts.FormatJSON}
|
reqB := contracts.SearchRequest{Query: "orwell", Format: contracts.FormatJSON}
|
||||||
|
|
||||||
if Key(reqA) == Key(reqB) {
|
if Key(reqA) == Key(reqB) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -23,10 +23,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/util"
|
"github.com/metamorphosis-dev/samsa/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the top-level configuration for the kafka service.
|
// Config is the top-level configuration for the samsa service.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Server ServerConfig `toml:"server"`
|
Server ServerConfig `toml:"server"`
|
||||||
Upstream UpstreamConfig `toml:"upstream"`
|
Upstream UpstreamConfig `toml:"upstream"`
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArxivEngine_Search(t *testing.T) {
|
func TestArxivEngine_Search(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BingEngine searches Bing via the public Bing API.
|
// BingEngine searches Bing via the public Bing API.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BingImagesEngine searches Bing Images via their public RSS endpoint.
|
// BingImagesEngine searches Bing Images via their public RSS endpoint.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBingEngine_EmptyQuery(t *testing.T) {
|
func TestBingEngine_EmptyQuery(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BraveEngine struct {
|
type BraveEngine struct {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BraveEngine implements the Brave Web Search API.
|
// BraveEngine implements the Brave Web Search API.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBraveEngine_GatingAndHeader(t *testing.T) {
|
func TestBraveEngine_GatingAndHeader(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CrossrefEngine struct {
|
type CrossrefEngine struct {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCrossrefEngine_Search(t *testing.T) {
|
func TestCrossrefEngine_Search(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DuckDuckGoImagesEngine searches DuckDuckGo Images via their vql API.
|
// DuckDuckGoImagesEngine searches DuckDuckGo Images via their vql API.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DuckDuckGoEngine searches DuckDuckGo's Lite/HTML endpoint.
|
// DuckDuckGoEngine searches DuckDuckGo's Lite/HTML endpoint.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseDuckDuckGoHTML parses DuckDuckGo Lite's HTML response for search results.
|
// parseDuckDuckGoHTML parses DuckDuckGo Lite's HTML response for search results.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDuckDuckGoEngine_EmptyQuery(t *testing.T) {
|
func TestDuckDuckGoEngine_EmptyQuery(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -19,7 +19,7 @@ package engines
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Engine is a Go-native implementation of a search engine.
|
// Engine is a Go-native implementation of a search engine.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/config"
|
"github.com/metamorphosis-dev/samsa/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDefaultPortedEngines returns the Go-native engine registry.
|
// NewDefaultPortedEngines returns the Go-native engine registry.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitHubEngine searches GitHub repositories and code via the public search API.
|
// GitHubEngine searches GitHub repositories and code via the public search API.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGitHubEngine_EmptyQuery(t *testing.T) {
|
func TestGitHubEngine_EmptyQuery(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -25,13 +25,13 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// googleUserAgent is an honest User-Agent identifying the metasearch engine.
|
// googleUserAgent is an honest User-Agent identifying the metasearch engine.
|
||||||
// Using a spoofed GSA User-Agent violates Google's Terms of Service and
|
// Using a spoofed GSA User-Agent violates Google's Terms of Service and
|
||||||
// risks permanent IP blocking.
|
// risks permanent IP blocking.
|
||||||
var googleUserAgent = "Kafka/0.1 (compatible; +https://github.com/metamorphosis-dev/kafka)"
|
var googleUserAgent = "Kafka/0.1 (compatible; +https://github.com/metamorphosis-dev/samsa)"
|
||||||
|
|
||||||
type GoogleEngine struct {
|
type GoogleEngine struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultPortedEngines = []string{
|
var defaultPortedEngines = []string{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QwantImagesEngine searches Qwant Images via the v3 search API.
|
// QwantImagesEngine searches Qwant Images via the v3 search API.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQwantEngine_WebLite(t *testing.T) {
|
func TestQwantEngine_WebLite(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQwantEngine_Web(t *testing.T) {
|
func TestQwantEngine_Web(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RedditEngine searches Reddit posts via the public JSON API.
|
// RedditEngine searches Reddit posts via the public JSON API.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedditEngine_EmptyQuery(t *testing.T) {
|
func TestRedditEngine_EmptyQuery(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
const stackOverflowAPIBase = "https://api.stackexchange.com/2.3"
|
const stackOverflowAPIBase = "https://api.stackexchange.com/2.3"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStackOverflow_Name(t *testing.T) {
|
func TestStackOverflow_Name(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WikipediaEngine struct {
|
type WikipediaEngine struct {
|
||||||
|
|
@ -108,7 +108,7 @@ func (e *WikipediaEngine) Search(ctx context.Context, req contracts.SearchReques
|
||||||
// Wikimedia APIs require a descriptive User-Agent.
|
// Wikimedia APIs require a descriptive User-Agent.
|
||||||
httpReq.Header.Set(
|
httpReq.Header.Set(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
"gosearch-go/0.1 (compatible; +https://github.com/metamorphosis-dev/kafka)",
|
"gosearch-go/0.1 (compatible; +https://github.com/metamorphosis-dev/samsa)",
|
||||||
)
|
)
|
||||||
// Best-effort: hint content language.
|
// Best-effort: hint content language.
|
||||||
if req.Language != "" && req.Language != "auto" {
|
if req.Language != "" && req.Language != "auto" {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWikipediaEngine_Search(t *testing.T) {
|
func TestWikipediaEngine_Search(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YouTubeEngine struct {
|
type YouTubeEngine struct {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -22,9 +22,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/search"
|
"github.com/metamorphosis-dev/samsa/internal/search"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/views"
|
"github.com/metamorphosis-dev/samsa/internal/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -24,9 +24,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/httpapi"
|
"github.com/metamorphosis-dev/samsa/internal/httpapi"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/search"
|
"github.com/metamorphosis-dev/samsa/internal/search"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mockUpstreamHandler returns controlled JSON responses.
|
// mockUpstreamHandler returns controlled JSON responses.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MergeResponses merges multiple compatible JSON responses.
|
// MergeResponses merges multiple compatible JSON responses.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergeResponses_DedupResultsAndSets(t *testing.T) {
|
func TestMergeResponses_DedupResultsAndSets(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -127,7 +127,7 @@ func writeCSV(w http.ResponseWriter, resp SearchResponse) error {
|
||||||
|
|
||||||
func writeRSS(w http.ResponseWriter, resp SearchResponse) error {
|
func writeRSS(w http.ResponseWriter, resp SearchResponse) error {
|
||||||
q := resp.Query
|
q := resp.Query
|
||||||
escapedTitle := xmlEscape("kafka search: " + q)
|
escapedTitle := xmlEscape("samsa search: " + q)
|
||||||
escapedDesc := xmlEscape("Search results for \"" + q + "\" - kafka")
|
escapedDesc := xmlEscape("Search results for \"" + q + "\" - kafka")
|
||||||
escapedQueryTerms := xmlEscape(q)
|
escapedQueryTerms := xmlEscape(q)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -22,11 +22,11 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/cache"
|
"github.com/metamorphosis-dev/samsa/internal/cache"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/config"
|
"github.com/metamorphosis-dev/samsa/internal/config"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/engines"
|
"github.com/metamorphosis-dev/samsa/internal/engines"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/upstream"
|
"github.com/metamorphosis-dev/samsa/internal/upstream"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/engines"
|
"github.com/metamorphosis-dev/samsa/internal/engines"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mockEngine is a test engine that returns a predefined response or error.
|
// mockEngine is a test engine that returns a predefined response or error.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package search
|
package search
|
||||||
|
|
||||||
import "github.com/metamorphosis-dev/kafka/internal/contracts"
|
import "github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
|
|
||||||
// Re-export the JSON contract types so the rest of the code can stay in the
|
// Re-export the JSON contract types so the rest of the code can stay in the
|
||||||
// `internal/search` namespace without creating an import cycle.
|
// `internal/search` namespace without creating an import cycle.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="referrer" content="no-referrer">
|
<meta name="referrer" content="no-referrer">
|
||||||
<meta name="robots" content="noarchive">
|
<meta name="robots" content="noarchive">
|
||||||
<meta name="description" content="kafka — a privacy-respecting, open metasearch engine">
|
<meta name="description" content="samsa — a privacy-respecting, open metasearch engine">
|
||||||
<title>{{if .Query}}{{.Query}} — {{end}}kafka</title>
|
<title>{{if .Query}}{{.Query}} — {{end}}samsa</title>
|
||||||
<link rel="stylesheet" href="/static/css/kafka.css">
|
<link rel="stylesheet" href="/static/css/samsa.css">
|
||||||
<link rel="icon" href="/static/img/favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="/static/img/favicon.svg" type="image/svg+xml">
|
||||||
<link title="kafka" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml">
|
<link title="samsa" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml">
|
||||||
<script>var s=document.documentElement;s.setAttribute('data-theme',localStorage.getItem('kafka-theme')||'light');</script>
|
<meta name="samsa" content="samsa">
|
||||||
|
<script>var s=document.documentElement;s.setAttribute('data-theme',localStorage.getItem('samsa-theme')||'light');</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
<circle cx="11" cy="11" r="8"/>
|
<circle cx="11" cy="11" r="8"/>
|
||||||
<path d="m21 21-4.35-4.35"/>
|
<path d="m21 21-4.35-4.35"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="site-name">kafka</span>
|
<span class="site-name">samsa</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/preferences" class="settings-link" title="Preferences">
|
<a href="/preferences" class="settings-link" title="Preferences">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|
@ -35,7 +36,7 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Powered by <a href="https://git.ashisgreat.xyz/penal-colony/kafka">kafka</a> — a privacy-respecting, open metasearch engine{{if .SourceURL}} · <a href="{{.SourceURL}}">Source</a>{{end}} · <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPLv3</a></p>
|
<p>Powered by <a href="https://git.ashisgreat.xyz/penal-colony/samsa">samsa</a> — a privacy-respecting, open metasearch engine{{if .SourceURL}} · <a href="{{.SourceURL}}">Source</a>{{end}} · <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPLv3</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<circle cx="11" cy="11" r="8"/>
|
<circle cx="11" cy="11" r="8"/>
|
||||||
<path d="m21 21-4.35-4.35"/>
|
<path d="m21 21-4.35-4.35"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="home-logo-text">kafka</span>
|
<span class="home-logo-text">samsa</span>
|
||||||
</a>
|
</a>
|
||||||
<p class="home-tagline">Private meta-search, powered by open source.</p>
|
<p class="home-tagline">Private meta-search, powered by open source.</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<ShortName>kafka</ShortName>
|
<ShortName>samsa</ShortName>
|
||||||
<Description>A privacy-respecting, open metasearch engine</Description>
|
<Description>A privacy-respecting, open metasearch engine</Description>
|
||||||
<InputEncoding>UTF-8</InputEncoding>
|
<InputEncoding>UTF-8</InputEncoding>
|
||||||
<OutputEncoding>UTF-8</OutputEncoding>
|
<OutputEncoding>UTF-8</OutputEncoding>
|
||||||
<LongName>kafka — Privacy-respecting metasearch</LongName>
|
<LongName>samsa — Privacy-respecting metasearch</LongName>
|
||||||
<Image width="16" height="16" type="image/svg+xml">/static/img/favicon.svg</Image>
|
<Image width="16" height="16" type="image/svg+xml">/static/img/favicon.svg</Image>
|
||||||
<Contact>https://git.ashisgreat.xyz/penal-colony/kafka</Contact>
|
<Contact>https://git.ashisgreat.xyz/penal-colony/samsa</Contact>
|
||||||
<Url type="text/html" method="GET" template="{baseUrl}/search?q={searchTerms}&format=html">
|
<Url type="text/html" method="GET" template="{baseUrl}/search?q={searchTerms}&format=html">
|
||||||
<Param name="pageno" value="{startPage?}" />
|
<Param name="pageno" value="{startPage?}" />
|
||||||
<Param name="language" value="{language?}" />
|
<Param name="language" value="{language?}" />
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
var themeButtons = document.querySelectorAll('.theme-btn');
|
var themeButtons = document.querySelectorAll('.theme-btn');
|
||||||
|
|
||||||
// Load saved theme
|
// Load saved theme
|
||||||
var savedTheme = localStorage.getItem('kafka-theme') || 'light';
|
var savedTheme = localStorage.getItem('samsa-theme') || 'light';
|
||||||
themeInput.value = savedTheme;
|
themeInput.value = savedTheme;
|
||||||
document.querySelectorAll('.theme-btn').forEach(function(btn) {
|
document.querySelectorAll('.theme-btn').forEach(function(btn) {
|
||||||
btn.classList.toggle('active', btn.dataset.theme === savedTheme);
|
btn.classList.toggle('active', btn.dataset.theme === savedTheme);
|
||||||
|
|
@ -116,12 +116,12 @@
|
||||||
themeButtons.forEach(function(b) { b.classList.remove('active'); });
|
themeButtons.forEach(function(b) { b.classList.remove('active'); });
|
||||||
btn.classList.add('active');
|
btn.classList.add('active');
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
localStorage.setItem('kafka-theme', theme);
|
localStorage.setItem('samsa-theme', theme);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load saved engine preferences
|
// Load saved engine preferences
|
||||||
var savedEngines = JSON.parse(localStorage.getItem('kafka-engines') || 'null');
|
var savedEngines = JSON.parse(localStorage.getItem('samsa-engines') || 'null');
|
||||||
if (savedEngines) {
|
if (savedEngines) {
|
||||||
savedEngines.forEach(function(engine) {
|
savedEngines.forEach(function(engine) {
|
||||||
var checkbox = document.querySelector('input[name="engine"][value="' + engine.id + '"]');
|
var checkbox = document.querySelector('input[name="engine"][value="' + engine.id + '"]');
|
||||||
|
|
@ -135,7 +135,7 @@
|
||||||
document.querySelectorAll('input[name="engine"]').forEach(function(cb) {
|
document.querySelectorAll('input[name="engine"]').forEach(function(cb) {
|
||||||
engines.push({ id: cb.value, enabled: cb.checked });
|
engines.push({ id: cb.value, enabled: cb.checked });
|
||||||
});
|
});
|
||||||
localStorage.setItem('kafka-engines', JSON.stringify(engines));
|
localStorage.setItem('samsa-engines', JSON.stringify(engines));
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<circle cx="11" cy="11" r="8"/>
|
<circle cx="11" cy="11" r="8"/>
|
||||||
<path d="m21 21-4.35-4.35"/>
|
<path d="m21 21-4.35-4.35"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span>kafka</span>
|
<span>samsa</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<form class="header-search" method="GET" action="/search" role="search">
|
<form class="header-search" method="GET" action="/search" role="search">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// kafka — a privacy-respecting metasearch engine
|
// samsa — a privacy-respecting metasearch engine
|
||||||
// Copyright (C) 2026-present metamorphosis-dev
|
// Copyright (C) 2026-present metamorphosis-dev
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -26,8 +26,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
"github.com/metamorphosis-dev/kafka/internal/util"
|
"github.com/metamorphosis-dev/samsa/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed all:templates
|
//go:embed all:templates
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package views
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metamorphosis-dev/kafka/internal/contracts"
|
"github.com/metamorphosis-dev/samsa/internal/contracts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockSearchResponse(query string, numResults int) contracts.SearchResponse {
|
func mockSearchResponse(query string, numResults int) contracts.SearchResponse {
|
||||||
|
|
@ -36,10 +36,10 @@ func mockEmptyResponse() contracts.SearchResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromResponse_Basic(t *testing.T) {
|
func TestFromResponse_Basic(t *testing.T) {
|
||||||
resp := mockSearchResponse("kafka trial", 42)
|
resp := mockSearchResponse("samsa trial", 42)
|
||||||
data := FromResponse(resp, "kafka trial", 1, "", "", "")
|
data := FromResponse(resp, "samsa trial", 1, "", "", "")
|
||||||
|
|
||||||
if data.Query != "kafka trial" {
|
if data.Query != "samsa trial" {
|
||||||
t.Errorf("expected query 'kafka trial', got %q", data.Query)
|
t.Errorf("expected query 'kafka trial', got %q", data.Query)
|
||||||
}
|
}
|
||||||
if data.NumberOfResults != 42 {
|
if data.NumberOfResults != 42 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue