parent
149a8c3c53
commit
3ba32b5dbf
1 changed files with 150 additions and 0 deletions
@ -0,0 +1,150 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"fmt" |
||||
"log" |
||||
"net/http" |
||||
"strings" |
||||
"sync/atomic" |
||||
) |
||||
|
||||
var ( |
||||
// requestCounter stores the number of HTTP requests (excluding /stats).
|
||||
requestCounter uint64 |
||||
) |
||||
|
||||
// colorComponentKeys defines the names for the color components in their encoding order.
|
||||
// The encoding process described uses 9 components, but output is only for the first 8.
|
||||
var colorComponentKeys = []string{ |
||||
"PRIMARY_COLOR", |
||||
"SECONDARY_COLOR", |
||||
"ROOT_COLOR", |
||||
"TIME_COLOR", |
||||
"ERR_COLOR", |
||||
"SEPARATOR_COLOR", |
||||
"BORDCOL", |
||||
"PATH_COLOR", |
||||
// The 9th component (C9, potentially "Reset") is decoded but not listed for output.
|
||||
} |
||||
|
||||
// decodeAndPrintColorsHandler processes requests with encoded color data.
|
||||
func decodeAndPrintColorsHandler(w http.ResponseWriter, r *http.Request) { |
||||
encodedData := strings.TrimPrefix(r.URL.Path, "/") |
||||
|
||||
// 1. Convert URL-safe Base64 characters back to standard Base64.
|
||||
// The Vue code replaces '+' with '-', '/' with '_', and removes padding.
|
||||
standardBase64 := strings.ReplaceAll(encodedData, "-", "+") |
||||
standardBase64 = strings.ReplaceAll(standardBase64, "_", "/") |
||||
|
||||
// 2. Decode Base64 string to bytes.
|
||||
// RawStdEncoding handles Base64 without padding.
|
||||
// The Vue code generates an 8-character string from 6 bytes, which means no padding.
|
||||
decodedBytes, err := base64.RawStdEncoding.DecodeString(standardBase64) |
||||
if err != nil { |
||||
http.Error(w, fmt.Sprintf("Base64 decoding failed: %v. Input: '%s'", err, encodedData), http.StatusBadRequest) |
||||
return |
||||
} |
||||
|
||||
if len(decodedBytes) != 6 { |
||||
http.Error(w, fmt.Sprintf("Decoded data must be 6 bytes long, got %d bytes from input '%s'", len(decodedBytes), encodedData), http.StatusBadRequest) |
||||
return |
||||
} |
||||
|
||||
// 3. Unpack 6 bytes into nine 5-bit values (C1 to C9).
|
||||
// Based on the Vue frontend's "Byte Packing" description:
|
||||
// Byte 1: C1_b4 C1_b3 C1_b2 C1_b1 C1_b0 C2_b4 C2_b3 C2_b2
|
||||
// Byte 2: C2_b1 C2_b0 C3_b4 C3_b3 C3_b2 C3_b1 C3_b0 C4_b4
|
||||
// Byte 3: C4_b3 C4_b2 C4_b1 C4_b0 C5_b4 C5_b3 C5_b2 C5_b1
|
||||
// Byte 4: C5_b0 C6_b4 C6_b3 C6_b2 C6_b1 C6_b0 C7_b4 C7_b3
|
||||
// Byte 5: C7_b2 C7_b1 C7_b0 C8_b4 C8_b3 C8_b2 C8_b1 C8_b0
|
||||
// Byte 6: C9_b4 C9_b3 C9_b2 C9_b1 C9_b0 0 0 0 (padding)
|
||||
fiveBitValues := make([]byte, 9) |
||||
b := decodedBytes |
||||
|
||||
fiveBitValues[0] = b[0] >> 3 // C1
|
||||
fiveBitValues[1] = ((b[0] & 0x07) << 2) | (b[1] >> 6) // C2
|
||||
fiveBitValues[2] = (b[1] & 0x3E) >> 1 // C3
|
||||
fiveBitValues[3] = ((b[1] & 0x01) << 4) | (b[2] >> 4) // C4
|
||||
fiveBitValues[4] = ((b[2] & 0x0F) << 1) | (b[3] >> 7) // C5
|
||||
fiveBitValues[5] = (b[3] & 0x7C) >> 2 // C6
|
||||
fiveBitValues[6] = ((b[3] & 0x03) << 3) | (b[4] >> 5) // C7
|
||||
fiveBitValues[7] = b[4] & 0x1F // C8
|
||||
fiveBitValues[8] = b[5] >> 3 // C9
|
||||
|
||||
var result strings.Builder |
||||
|
||||
// 4. For each of the first 8 components, generate the bash color code.
|
||||
for i := 0; i < 8; i++ { |
||||
val5bit := fiveBitValues[i] |
||||
|
||||
// Extract attributes from the 5-bit value:
|
||||
// (base_color_0_7 << 2) | (light_bit << 1) | bold_bit
|
||||
baseColor07 := (val5bit >> 2) & 0x07 // 3 MSB for base color (0-7)
|
||||
lightBit := (val5bit >> 1) & 0x01 // 1 bit for light state
|
||||
boldBit := val5bit & 0x01 // 1 bit for bold state
|
||||
|
||||
// Convert to ANSI color codes
|
||||
baseAnsiCode := baseColor07 + 30 // Base colors are 30-37
|
||||
actualAnsiCode := baseAnsiCode |
||||
if lightBit == 1 { |
||||
actualAnsiCode += 60 // Bright colors are 90-97
|
||||
} |
||||
|
||||
styleAttr := 0 // Style: 0 for normal
|
||||
if boldBit == 1 { |
||||
styleAttr = 1 // Style: 1 for bold
|
||||
} |
||||
|
||||
// Format as bash-compatible string: \[\033[<style>;<color>m\]
|
||||
bashColor := fmt.Sprintf(`\[\033[%d;%dm\]`, styleAttr, actualAnsiCode) |
||||
result.WriteString(fmt.Sprintf("%s=%s\n", colorComponentKeys[i], bashColor)) |
||||
} |
||||
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8") |
||||
fmt.Fprint(w, result.String()) |
||||
} |
||||
|
||||
// statsReportHandler serves the HTTP request counter.
|
||||
func statsReportHandler(w http.ResponseWriter, r *http.Request) { |
||||
count := atomic.LoadUint64(&requestCounter) |
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8") |
||||
fmt.Fprintf(w, "%d", count) |
||||
} |
||||
|
||||
// rootPathHandler handles requests to the bare "/" path.
|
||||
func rootPathHandler(w http.ResponseWriter, r *http.Request) { |
||||
http.Error(w, "Please provide an encoded string in the URL path, e.g., /YourEncodedString", http.StatusBadRequest) |
||||
} |
||||
|
||||
// mainRouter is the primary HTTP handler.
|
||||
// It routes requests to appropriate handlers and manages request counting.
|
||||
func mainRouter(w http.ResponseWriter, r *http.Request) { |
||||
// Handle /stats endpoint separately; it does not increment the counter.
|
||||
if r.URL.Path == "/stats" { |
||||
statsReportHandler(w, r) |
||||
return |
||||
} |
||||
|
||||
// For all other paths, increment the request counter.
|
||||
atomic.AddUint64(&requestCounter, 1) |
||||
|
||||
// Handle the root path "/"
|
||||
if r.URL.Path == "/" { |
||||
rootPathHandler(w, r) |
||||
} else { |
||||
// Assume any other path contains encoded data.
|
||||
decodeAndPrintColorsHandler(w, r) |
||||
} |
||||
} |
||||
|
||||
func main() { |
||||
http.HandleFunc("/", mainRouter) |
||||
|
||||
port := "8080" |
||||
log.Printf("🎨 Color Theme Backend starting on port %s (e.g., http://localhost:%s)\n", port, port) |
||||
// Listen on all available network interfaces.
|
||||
if err := http.ListenAndServe(":"+port, nil); err != nil { |
||||
log.Fatalf("❌ Failed to start server: %s\n", err) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue