package main import ( "context" "encoding/json" "flag" "fmt" "log" "os" "regexp" "strings" "time" "git.sr.ht/~adnano/go-gemini" "git.sr.ht/~adnano/go-gemini/certificate" ) var ( hostname string port string fileSrvHost string fileSrvPort string certificatePath string ) func init() { flag.StringVar(&hostname, "hostname", "", "capsule hostname") flag.StringVar(&port, "port", "1965", "capsule port") flag.StringVar(&certificatePath, "certificatePath", "", "capsule certificate path") flag.StringVar(&fileSrvHost, "fileSrvHost", "", "file push server host") flag.StringVar(&fileSrvPort, "fileSrvPort", "", "file push server port") flag.Parse() } func main() { certificates := &certificate.Store{} certificates.Register(hostname) if err := certificates.Load(certificatePath); err != nil { panic(err) } mux := &gemini.Mux{} mux.HandleFunc("/favicon.txt", processFavicon) // security // feed mux.HandleFunc("/", process) server := &gemini.Server{ Addr: ":" + port, Handler: mux, ReadTimeout: 30 * time.Second, WriteTimeout: 1 * time.Minute, GetCertificate: certificates.Get, } ctx := context.Background() if err := server.ListenAndServe(ctx); err != nil { log.Fatal(err) } } func processFavicon(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { w.SetMediaType("text/plain") w.Write([]byte("\U0001F31B")) } func process(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { log.Println("-> " + r.URL.Path) lang := regexp.MustCompile(`^/(sgs|en)`).FindString(r.URL.Path) if lang != "" { lang = lang[1:] } switch { case "/" == r.URL.Path: w.WriteHeader(gemini.StatusPermanentRedirect, "/sgs") case regexp.MustCompile(`^/(sgs|en)$`).MatchString(r.URL.Path): renderIndex(lang, w) case regexp.MustCompile(`^/(sgs|en)/a$`).MatchString(r.URL.Path): renderAbout(lang, w) default: w.WriteHeader(gemini.StatusNotFound, "Out of space") } } func renderAbout(lang string, w gemini.ResponseWriter) { w.SetMediaType("text/gemini") content, err := os.ReadFile(fmt.Sprintf("templates/%s/about.gmi", lang)) if err != nil { w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") return } _, err = w.Write(content) if err != nil { w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") return } } func renderIndex(lang string, w gemini.ResponseWriter) { w.SetMediaType("text/gemini") content, err := os.ReadFile(fmt.Sprintf("templates/%s/index.gmi", lang)) if err != nil { w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") return } page := string(content) summery := GetSummery() fmt.Println(summery) tree := TSummery{} err = json.Unmarshal([]byte(summery), &tree) if err != nil { w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") return } fmt.Println(tree) categories := "" for c, count := range tree.Categories { categories = categories + fmt.Sprintf("=> /%s/f/%s %s (%d)\n", lang, c, c, count) } page = strings.Replace(page, "{{categories}}", categories, 1) tags := "" for t, count := range tree.Tags { tags = tags + fmt.Sprintf("=> /%s/t/%s %s (%d)\n", lang, t, t, count) } page = strings.Replace(page, "{{tags}}", tags, 1) lastFiles := "" for _, f := range GetLastFiles(tree.Files) { lastFiles = lastFiles + fmt.Sprintf( "=> /%s/f/%s/%s/%s %s (%s)\n", lang, strings.Join(f.Category, "/"), f.Id, strings.Replace(f.Name, ".md", ".gmi", 1), f.Description, f.Created, ) } page = strings.Replace(page, "{{last_posts}}", lastFiles, 1) _, err = w.Write([]byte(page)) if err != nil { w.WriteHeader(gemini.StatusTemporaryFailure, "Internal server error") return } }