├── go.mod ├── img └── gohome.jpg ├── sample_scripts ├── memory.sh └── cpu.sh ├── templates ├── entrie.txt ├── default.html └── default.css ├── README.md └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module SeungheonOh/GoHome 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /img/gohome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeungheonOh/GoHome/HEAD/img/gohome.jpg -------------------------------------------------------------------------------- /sample_scripts/memory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | free -m | awk 'NR==2{printf "%.2f",$3*100/$2 }' 3 | -------------------------------------------------------------------------------- /sample_scripts/cpu.sh: -------------------------------------------------------------------------------- 1 | top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}' 2 | -------------------------------------------------------------------------------- /templates/entrie.txt: -------------------------------------------------------------------------------- 1 | !GitLab 2 | MyDots Redirect('https://www.github.com/SeungheonOh/Dots') 3 | 4 | !Reddit 5 | *Nix Redirect('https://www.reddit.com/r/unixporn') 6 | 7 | !AnyOtherCustomGroups 8 | a Redirect('https://www.github.com/SeungheonOh/GoHome') 9 | b Redirect('https://www.github.com/SeungheonOh/GoHome') 10 | c Redirect('https://www.github.com/SeungheonOh/GoHome') 11 | 12 | !Application 13 | terminal Launch('st') 14 | htop Launch('st -e htop') 15 | vim Launch('st -e vim') 16 | 17 | +Memory: ${Launch('bash -c ./sample_scripts/memory.sh')}% 18 | -------------------------------------------------------------------------------- /templates/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 20 | New Tab 21 | 22 | 23 | 24 |
25 |
[GoHome ~]$ tree
26 |
27 |

.

28 | 40 |
41 |
42 | {{range .Labels}} 43 | Loading
44 | {{end}} 45 | 46 |
[GoHome ~]$ ddg
47 |
48 |

search:

49 | 50 |
51 |
52 | 53 | 54 | 55 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoHome 2 | Have you guys seen those cool looking single filed homepages on Linux communites? It looks really cool, but it's just a shallow shell 3 | which only can let you go to pre-setted internet pages. GoHome in other hand, can do much more, such as opening applications, 4 | shutting your system down, or fetching your system data to your local homepage. Not only that, GoHome also comes with 5 | easy setup system that does not require any tidious html coding processes, just simple and nice entrie lists-you can customize css as you desire. 6 | 7 | ![](https://github.com/SeungheonOh/GoHome/blob/master/img/gohome.jpg) 8 | Theme from [/u/nytly](https://www.reddit.com/user/nytly/), Thanks! 9 | 10 | # Installation 11 | GoHome is only written with go standard libraries! So you can just download ordinary go compiler, run it! 12 | You can also add gohome to system service so it starts up automatically on your system's startup. 13 | 14 | # Example Entrie File 15 | ``` 16 | !Application 17 | terminal Launch('st') 18 | chrome Launch('google-chrome-stable') 19 | htop Launch('st -e htop') 20 | vim Launch('st -e vim') 21 | code Launch('code') 22 | pipes Launch('st -e pipes.sh') 23 | cowsayToConsole console.log(Launch('cowsay hello world')) 24 | 25 | !Website 26 | reddit Launch('xdg-open https://www.reddit.com') 27 | google Launch('xdg-open https://www.google.com') 28 | ArchWiki Launch('xdg-open https://wiki.archlinux.org') 29 | *nix Launch('xdg-open https://www.reddit.com/r/unixporn') 30 | 31 | !FunStuff 32 | youtube Launch('xdg-open https://www.youtube.com') 33 | DankMemes Launch('xdg-open https://www.reddit.com/r/dankmemes') 34 | MoonBuggy Launch('st -e moon-buggy') 35 | 36 | +CPU: ${Launch('bash -c ./sample_scripts/cpu.sh')}% 37 | +Memory: ${Launch('bash -c ./sample_scripts/memory.sh')}% 38 | 39 | ``` 40 | 41 | # Options 42 | ``` 43 | -c Set CSS file 44 | -t Set Template file 45 | -e Set Entrie file 46 | ``` 47 | 48 | #Inspired by 49 | ![/u/whyvitamins](https://www.reddit.com/r/unixporn/comments/da3lx5/bspwm_black_and_white/) 50 | and many other *nix porn posts! 51 | -------------------------------------------------------------------------------- /templates/default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Iosevka Regular"; 3 | font-weight: "bold" 4 | } 5 | 6 | :root { 7 | --font: "Iosevka Regular"; 8 | --background: #0f0f13; 9 | --foreground: #fffffe; 10 | --pink: #62b3e4; 11 | --red: #f25f4c; 12 | --orange: #ff8906; 13 | --branch: 1px solid #a7a9be; 14 | } 15 | 16 | html { 17 | font-size: 18px; 18 | font-weight: bold; 19 | } 20 | 21 | body { 22 | background: var(--background); 23 | } 24 | 25 | .container { 26 | position: absolute; 27 | top: 50%; 28 | left: 45%; 29 | transform: translate(-50%, -50%); 30 | } 31 | 32 | .prompt { 33 | font-family: var(--font); 34 | font-weight: bold; 35 | color: var(--foreground); 36 | } 37 | 38 | .prompt~.prompt { 39 | padding: 1.5rem 0 0.3125rem; 40 | } 41 | 42 | span { 43 | color: var(--pink); 44 | } 45 | 46 | h1 { 47 | display: inline; 48 | font-family: var(--font); 49 | font-size: 1rem; 50 | font-weight: bold; 51 | color: var(--red); 52 | } 53 | 54 | .tree > ul { 55 | margin: 0; 56 | padding-left: 1rem; 57 | } 58 | 59 | ul { 60 | list-style: none; 61 | padding-left: 2.5rem; 62 | } 63 | 64 | li { 65 | position: relative; 66 | } 67 | 68 | li::before, li::after { 69 | content: ""; 70 | position: absolute; 71 | left: -0.75rem; 72 | } 73 | 74 | li::before { 75 | border-top: var(--branch); 76 | top: 0.75rem; 77 | width: 0.5rem; 78 | } 79 | 80 | li::after { 81 | border-left: var(--branch); 82 | height: 100%; 83 | top: 0.25rem; 84 | } 85 | 86 | li:last-child::after { 87 | height: 0.5rem; 88 | } 89 | 90 | a { 91 | font-family: var(--font); 92 | font-size: 1rem; 93 | font-weight: bold; 94 | color: var(--foreground); 95 | text-decoration: none; 96 | outline: none; 97 | } 98 | 99 | a:hover { 100 | color: var(--background); 101 | background: var(--orange); 102 | } 103 | 104 | form h1 { 105 | padding-left: 0.125rem; 106 | } 107 | 108 | input { 109 | font-family: var(--font); 110 | font-size: 1rem; 111 | font-weight: bold; 112 | color: var(--foreground); 113 | background-color: var(--background); 114 | border: none; 115 | } 116 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "os" 11 | "os/exec" 12 | "strings" 13 | "text/template" 14 | "time" 15 | ) 16 | 17 | type Cmd struct { 18 | Output []byte 19 | Error error 20 | } 21 | 22 | func RunCommand(ch chan<- Cmd, s []string) { 23 | output, err := exec.Command(s[0], s[1:]...).Output() 24 | ch <- Cmd{Output: output, Error: err} 25 | } 26 | 27 | type Entrie struct { 28 | Name string 29 | Command string 30 | } 31 | 32 | type EntrieGroup struct { 33 | SubTitle string 34 | Entries []Entrie 35 | } 36 | 37 | type Label struct { 38 | Prompt string 39 | } 40 | 41 | type HomeMenu struct { 42 | Title string 43 | CSS string 44 | EntrieGroups []EntrieGroup 45 | Labels []Label 46 | } 47 | 48 | var ( 49 | CSSFile string 50 | HTMLTemplate string 51 | EntrieFile string 52 | ServerPort string 53 | NoLog bool 54 | ) 55 | 56 | func init() { 57 | flag.StringVar(&CSSFile, "c", "", "Set CSS") 58 | flag.StringVar(&CSSFile, "css", "./templates/default.css", "Set CSS") 59 | 60 | flag.StringVar(&HTMLTemplate, "t", "", "Set Tamplate") 61 | flag.StringVar(&HTMLTemplate, "tamplate", "./templates/default.html", "Set Tamplate") 62 | 63 | flag.StringVar(&EntrieFile, "e", "", "Set Tamplate") 64 | flag.StringVar(&EntrieFile, "entrie", "./templates/entrie.txt", "Set Entries") 65 | 66 | flag.StringVar(&ServerPort, "p", "", "Set server port") 67 | flag.StringVar(&ServerPort, "port", "8080", "Set server port") 68 | 69 | flag.BoolVar(&NoLog, "n", false, "Disable Log") 70 | flag.BoolVar(&NoLog, "nolog", false, "Disable Log") 71 | } 72 | 73 | func main() { 74 | flag.Parse() 75 | 76 | log.SetFlags(log.Ltime | log.Llongfile) 77 | if NoLog { 78 | log.SetFlags(0) 79 | log.SetOutput(ioutil.Discard) 80 | } 81 | 82 | css, err := ioutil.ReadFile(CSSFile) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | html, err := ioutil.ReadFile(HTMLTemplate) 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | file, err := os.Open(EntrieFile) 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | defer file.Close() 96 | 97 | scanner := bufio.NewScanner(file) 98 | 99 | HomePage := HomeMenu{ 100 | Title: "", 101 | CSS: string(css), 102 | } 103 | 104 | for scanner.Scan() { 105 | line := scanner.Text() 106 | if len(line) <= 0 { 107 | continue 108 | } 109 | 110 | var group EntrieGroup 111 | if line[0] == '!' { 112 | group.SubTitle = line[1:] 113 | for scanner.Scan() { 114 | if len(scanner.Text()) <= 0 || scanner.Text()[0] == '!' { 115 | HomePage.EntrieGroups = append(HomePage.EntrieGroups, group) 116 | break 117 | } 118 | divided := strings.Fields(scanner.Text()) 119 | group.Entries = append(group.Entries, Entrie{divided[0], strings.Join(divided[1:], " ")}) 120 | } 121 | } else if line[0] == '+' { 122 | HomePage.Labels = append(HomePage.Labels, Label{line[1:]}) 123 | } 124 | } 125 | 126 | tmpl, err := template.New("").Parse(string(html)) 127 | if err != nil { 128 | log.Fatal(err) 129 | } 130 | 131 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 132 | w.Header().Set("Access-Control-Allow-Origin", "*") 133 | w.Header().Set("Access-Control-Allow-Methods", "GET,POST") 134 | w.Header().Set("Access-Control-Allow-Headers", "Content-Type") 135 | 136 | tmpl.Execute(w, HomePage) 137 | log.Printf("requested to home") 138 | }) 139 | http.HandleFunc("/run/", func(w http.ResponseWriter, r *http.Request) { 140 | path := r.URL.Path 141 | log.Printf("Command: %s\n", path[len("/run/"):len(path)]) 142 | 143 | c := make(chan Cmd) 144 | 145 | app := strings.Fields(path[len("/run/"):len(path)]) 146 | go RunCommand(c, app) 147 | 148 | var out Cmd 149 | select { 150 | case o := (<-c): 151 | out = o 152 | case <-time.After(300 * time.Millisecond): 153 | return 154 | } 155 | if out.Error != nil { 156 | log.Println(err) 157 | } 158 | 159 | log.Printf("\n%s", string(out.Output)) 160 | 161 | fmt.Fprintf(w, string(out.Output)) 162 | }) 163 | 164 | serverAddress := ":" + ServerPort 165 | log.Fatal(http.ListenAndServe(serverAddress, nil)) 166 | } 167 | --------------------------------------------------------------------------------