WIP: more nae nae
This commit is contained in:
		
							parent
							
								
									53190fcfdc
								
							
						
					
					
						commit
						6712864da0
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,7 @@ | ||||
| db.json | ||||
| /cmd/again/again | ||||
| /again | ||||
| 
 | ||||
| # ---> Go | ||||
| # Binaries for programs and plugins | ||||
| *.exe | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								cmd/again/again
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cmd/again/again
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -1,6 +1,7 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| @ -9,14 +10,17 @@ import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	again "git.rootprojects.org/root/go-again" | ||||
| 	"git.rootprojects.org/root/go-again/data/jsondb" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	portEnv := os.Getenv("PORT") | ||||
| 	dbEnv := os.Getenv("DATABASE_URL") | ||||
| 
 | ||||
| 	portInt := flag.Int("port", 0, "port on which to serve http") | ||||
| 	addr := flag.String("addr", "", "address on which to serve http") | ||||
| 	dburl := flag.String("database-url", "", "For example: json://relative-path/db.json or json:///absolute-path/db.json") | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	if "" != portEnv { | ||||
| @ -32,24 +36,86 @@ func main() { | ||||
| 		*portInt = n | ||||
| 	} | ||||
| 	if *portInt < 1024 || *portInt > 65535 { | ||||
| 		log.Fatalf("port should be between 1024 and 65535, not %d.", *portInt) | ||||
| 		log.Fatalf("`port` should be between 1024 and 65535, not %d.", *portInt) | ||||
| 		return | ||||
| 	} | ||||
| 	portEnv = strconv.Itoa(*portInt) | ||||
| 
 | ||||
| 	if "" != dbEnv { | ||||
| 		if "" != *dburl { | ||||
| 			log.Fatal("You may set DATABASE_URL or --database-url, but not both.") | ||||
| 			return | ||||
| 		} | ||||
| 		*dburl = dbEnv | ||||
| 		// TODO parse string? | ||||
| 		// TODO have each connector try in sequence by registering with build tags like go-migrate does? | ||||
| 	} | ||||
| 	if "" == *dburl { | ||||
| 		log.Fatalf("`database-url` must be specified." + | ||||
| 			" Something like --database-url='json:///var/go-again/db.json' should do nicely.") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	db, err := jsondb.Connect(*dburl) | ||||
| 	if nil != err { | ||||
| 		log.Fatalf("Could not connect to database %q: %s", *dburl, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	s := &scheduler{ | ||||
| 		DB: db, | ||||
| 	} | ||||
| 	mux := http.NewServeMux() | ||||
| 	server := &http.Server{ | ||||
| 		Addr:           fmt.Sprintf("%s:%s", *addr, portEnv), | ||||
| 		Handler:        http.HandlerFunc(handleFunc), | ||||
| 		Handler:        mux, | ||||
| 		ReadTimeout:    10 * time.Second, | ||||
| 		WriteTimeout:   10 * time.Second, | ||||
| 		MaxHeaderBytes: 1 << 20, | ||||
| 	} | ||||
| 	//mux.Handle("/api/", http.HandlerFunc(handleFunc)) | ||||
| 	mux.HandleFunc("/api/schedules", s.Handle) | ||||
| 
 | ||||
| 	// TODO Filebox FS | ||||
| 	mux.Handle("/", http.FileServer(http.Dir("./public"))) | ||||
| 
 | ||||
| 	fmt.Println("Listening on", server.Addr) | ||||
| 	log.Fatal(server.ListenAndServe()) | ||||
| } | ||||
| 
 | ||||
| func handleFunc(w http.ResponseWriter, r *http.Request) { | ||||
| 	jsondb.List() | ||||
| 	w.Write([]byte("Hello, World!")) | ||||
| type ScheduleDB interface { | ||||
| 	List() ([]again.Schedule, error) | ||||
| } | ||||
| 
 | ||||
| type scheduler struct { | ||||
| 	DB ScheduleDB | ||||
| } | ||||
| 
 | ||||
| func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) { | ||||
| 	fmt.Println("whatever", r.Method, r.URL) | ||||
| 	switch r.Method { | ||||
| 	case http.MethodGet: | ||||
| 		s.List(w, r) | ||||
| 		return | ||||
| 	case http.MethodPost: | ||||
| 		http.Error(w, "Not Implemented", http.StatusNotImplemented) | ||||
| 		return | ||||
| 	default: | ||||
| 		http.Error(w, "Not Implemented", http.StatusNotImplemented) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *scheduler) List(w http.ResponseWriter, r *http.Request) { | ||||
| 	schedules, err := s.DB.List() | ||||
| 	if nil != err { | ||||
| 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
| 	buf, err := json.Marshal(schedules) | ||||
| 	if nil != err { | ||||
| 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Write(buf) | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,78 @@ | ||||
| package jsondb | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	again "git.rootprojects.org/root/go-again" | ||||
| ) | ||||
| 
 | ||||
| func List() ([]again.Schedule, error) { | ||||
| 	return nil, errors.New("Not Implemented") | ||||
| type JSONDB struct { | ||||
| 	dburl string | ||||
| 	file  *os.File | ||||
| 	json  *dbjson | ||||
| } | ||||
| 
 | ||||
| type dbjson struct { | ||||
| 	Schedules []again.Schedule `json:"schedules"` | ||||
| } | ||||
| 
 | ||||
| func Connect(dburl string) (*JSONDB, error) { | ||||
| 	u, err := url.Parse(dburl) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// json:/abspath/to/db.json | ||||
| 	fmt.Println("url.Opaque:", u.Opaque) | ||||
| 	// json:///abspath/to/db.json | ||||
| 	fmt.Println("url.Path:", u.Path) | ||||
| 	fmt.Println(u) | ||||
| 
 | ||||
| 	path := u.Opaque | ||||
| 	if "" == path { | ||||
| 		path = u.Path | ||||
| 		if "" == path { | ||||
| 			// json:relpath/to/db.json | ||||
| 			// json://relpath/to/db.json | ||||
| 			path = strings.TrimSuffix(u.Host+"/"+u.Path, "/") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700) | ||||
| 	if nil != err { | ||||
| 		return nil, fmt.Errorf("Couldn't open %q: %s", path, err) | ||||
| 	} | ||||
| 
 | ||||
| 	stat, err := f.Stat() | ||||
| 	if 0 == stat.Size() { | ||||
| 		_, err := f.Write([]byte(`{"schedules":[]}`)) | ||||
| 		if nil != err { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700) | ||||
| 		if nil != err { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	decoder := json.NewDecoder(f) | ||||
| 	db := &dbjson{} | ||||
| 	err = decoder.Decode(db) | ||||
| 	if nil != err { | ||||
| 		return nil, fmt.Errorf("Couldn't parse %q as JSON: %s", path, err) | ||||
| 	} | ||||
| 
 | ||||
| 	return &JSONDB{ | ||||
| 		dburl: dburl, | ||||
| 		file:  f, | ||||
| 		json:  db, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (db *JSONDB) List() ([]again.Schedule, error) { | ||||
| 	return db.json.Schedules, nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										9
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| 	<head> | ||||
| 		<title>Go Again</title> | ||||
| 	</head> | ||||
| 	<body> | ||||
| 		<h1>Hello, World!</h1> | ||||
| 	</body> | ||||
| </html> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user