summaryrefslogtreecommitdiffstats
path: root/tools/notes/notes.go
diff options
context:
space:
mode:
Diffstat (limited to 'tools/notes/notes.go')
-rw-r--r--tools/notes/notes.go351
1 files changed, 0 insertions, 351 deletions
diff --git a/tools/notes/notes.go b/tools/notes/notes.go
deleted file mode 100644
index 7714a89..0000000
--- a/tools/notes/notes.go
+++ /dev/null
@@ -1,351 +0,0 @@
-package main
-
-import (
- "bytes"
- "context"
- "encoding/gob"
- "errors"
- "flag"
- "fmt"
- "io"
- "net/http"
- "os"
- "path/filepath"
- "time"
-
- "github.com/google/subcommands"
- "gitlab.com/rhogenson/roseh.moe/internal/api"
- "golang.org/x/term"
-)
-
-var serverURL = flag.String("url", "https://roseh.moe", "server url")
-
-const tokenCache = "/dev/shm/roseh.moe-upload-token"
-
-var errUnavailable = errors.New("unavailable")
-
-func readGobResp[Response any](req *http.Request) (*Response, error) {
- httpResp, err := http.DefaultClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("%w: read gob response: http: %s", errUnavailable, err)
- }
- defer httpResp.Body.Close()
- if httpResp.StatusCode != http.StatusOK {
- body, _ := io.ReadAll(httpResp.Body)
- return nil, fmt.Errorf("read gob response: error status: %s\n%s", httpResp.Status, body)
- }
- apiResp := new(api.Response)
- if err := gob.NewDecoder(httpResp.Body).Decode(apiResp); err != nil {
- return nil, fmt.Errorf("read gob response: decode response: %s", err)
- }
- if apiResp.Status != api.Ok {
- return nil, fmt.Errorf("read gob response: api error: %s: %s", apiResp.Status, apiResp.Err)
- }
- resp, ok := apiResp.Ok.(*Response)
- if !ok {
- return nil, fmt.Errorf("read gob response: invalid response")
- }
- return resp, nil
-}
-
-func login(ctx context.Context) (*api.LoginResponse, error) {
- fmt.Fprint(os.Stderr, "Enter password:")
- password, err := term.ReadPassword(int(os.Stdin.Fd()))
- fmt.Fprintln(os.Stderr)
- if err != nil {
- return nil, err
- }
- req, err := http.NewRequestWithContext(ctx, "POST", *serverURL+"/api/login", nil)
- if err != nil {
- return nil, err
- }
- req.Header.Set("Roseh-Password", string(password))
- return readGobResp[api.LoginResponse](req)
-}
-
-func loadToken(ctx context.Context) (string, error) {
- token, err := os.ReadFile(tokenCache)
- if err != nil {
- if errors.Is(err, os.ErrNotExist) {
- token, err := login(ctx)
- if err != nil {
- return "", err
- }
- os.WriteFile(tokenCache, []byte(token.Token), 0600)
- return token.Token, nil
- }
- return "", err
- }
- return string(token), nil
-}
-
-func gobReq(ctx context.Context, method, url string, req any) (*http.Request, error) {
- buf := new(bytes.Buffer)
- if err := gob.NewEncoder(buf).Encode(req); err != nil {
- return nil, err
- }
- httpReq, err := http.NewRequestWithContext(ctx, method, url, buf)
- if err != nil {
- return nil, err
- }
- token, err := loadToken(ctx)
- if err != nil {
- return nil, err
- }
- httpReq.Header.Set("Roseh-Token", token)
- return httpReq, nil
-}
-
-func gobReqResp[Response any](ctx context.Context, method, url string, req any) (*Response, error) {
- httpReq, err := gobReq(ctx, method, url, req)
- if err != nil {
- return nil, err
- }
- return readGobResp[Response](httpReq)
-}
-
-func listNotes(ctx context.Context, req *api.ListNotesRequest) (*api.ListNotesResponse, error) {
- return gobReqResp[api.ListNotesResponse](ctx, "GET", *serverURL+"/api/list-notes", req)
-}
-
-func createNote(ctx context.Context, req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) {
- return gobReqResp[api.CreateNoteResponse](ctx, "POST", *serverURL+"/api/create-note", req)
-}
-
-func readNote(ctx context.Context, req *api.ReadNoteRequest) (*gob.Decoder, func() error, error) {
- httpReq, err := gobReq(ctx, "GET", *serverURL+"/api/read-note", req)
- if err != nil {
- return nil, nil, err
- }
- resp, err := http.DefaultClient.Do(httpReq)
- if err != nil {
- return nil, nil, err
- }
- if resp.StatusCode != http.StatusOK {
- body, _ := io.ReadAll(resp.Body)
- resp.Body.Close()
- return nil, nil, fmt.Errorf("error status: %s\n%s", resp.Status, body)
- }
- return gob.NewDecoder(resp.Body), resp.Body.Close, nil
-}
-
-type loginCommand struct{}
-
-func (*loginCommand) Name() string {
- return "login"
-}
-
-func (*loginCommand) Synopsis() string {
- return "/api/login"
-}
-
-func (*loginCommand) Usage() string {
- return "makes a request to /api/login for debugging"
-}
-
-func (*loginCommand) SetFlags(*flag.FlagSet) {}
-
-func (*loginCommand) login(ctx context.Context) error {
- token, err := login(ctx)
- if err != nil {
- return err
- }
- return os.WriteFile(tokenCache, []byte(token.Token), 0600)
-}
-
-func (c *loginCommand) Execute(ctx context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus {
- if err := c.login(ctx); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-type listCommand struct{}
-
-func (*listCommand) Name() string {
- return "list"
-}
-
-func (*listCommand) Synopsis() string {
- return "list notes"
-}
-
-func (*listCommand) Usage() string {
- return "list all the notes"
-}
-
-func (*listCommand) SetFlags(*flag.FlagSet) {}
-
-func (*listCommand) list(ctx context.Context) error {
- resp, err := listNotes(ctx, &api.ListNotesRequest{})
- if err != nil {
- return err
- }
- for _, note := range resp.Notes {
- fmt.Println(note)
- }
- return nil
-}
-
-func (c *listCommand) Execute(ctx context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus {
- if err := c.list(ctx); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-type newCommand struct{}
-
-func (*newCommand) Name() string {
- return "new"
-}
-
-func (*newCommand) Synopsis() string {
- return "create note"
-}
-
-func (*newCommand) Usage() string {
- return "upload a new note"
-}
-
-func (*newCommand) SetFlags(*flag.FlagSet) {}
-
-type createNoteRequestStreamWriter struct {
- ctx context.Context
- fileName string
- continuationToken []byte
-}
-
-func (w *createNoteRequestStreamWriter) Write(buf []byte) (int, error) {
- var (
- resp *api.CreateNoteResponse
- err error
- )
- delay := time.Second
- for i := 0; i < 3; i++ {
- resp, err = createNote(w.ctx, &api.CreateNoteRequest{
- ContinuationToken: w.continuationToken,
- FileName: w.fileName,
- Chunk: buf,
- More: true,
- })
- if !errors.Is(err, errUnavailable) {
- break
- }
- fmt.Fprintf(os.Stderr, "Warning: %s (retrying)\n", err)
- select {
- case <-time.After(delay):
- case <-w.ctx.Done():
- return 0, w.ctx.Err()
- }
- delay *= 2
- }
- if err != nil {
- return 0, err
- }
- w.fileName = ""
- w.continuationToken = resp.ContinuationToken
- return len(buf), nil
-}
-
-func (*newCommand) new(ctx context.Context, fileName string) error {
- f, err := os.Open(fileName)
- if err != nil {
- return err
- }
- defer f.Close()
- streamWriter := &createNoteRequestStreamWriter{ctx: ctx, fileName: filepath.Base(fileName)}
- if _, err := io.Copy(streamWriter, f); err != nil {
- return err
- }
- resp, err := createNote(ctx, &api.CreateNoteRequest{ContinuationToken: streamWriter.continuationToken})
- if err != nil {
- return err
- }
- fmt.Println(resp.Name)
- return nil
-}
-
-func (c *newCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...any) subcommands.ExitStatus {
- args := fs.Args()
- if len(args) != 1 {
- fmt.Fprintln(os.Stderr, "usage error")
- return subcommands.ExitUsageError
- }
- if err := c.new(ctx, args[0]); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-type readCommand struct{}
-
-func (*readCommand) Name() string {
- return "read"
-}
-
-func (*readCommand) Synopsis() string {
- return "read a note"
-}
-
-func (*readCommand) Usage() string {
- return "read a note by name"
-}
-
-func (*readCommand) SetFlags(*flag.FlagSet) {}
-
-func (*readCommand) read(ctx context.Context, key string) error {
- decoder, close, err := readNote(ctx, &api.ReadNoteRequest{Note: key})
- if err != nil {
- return err
- }
- defer close()
- for {
- apiResp := new(api.Response)
- if err := decoder.Decode(apiResp); err != nil {
- if errors.Is(err, io.EOF) {
- break
- }
- return err
- }
- if apiResp.Status != api.Ok {
- return fmt.Errorf("%s: %s", apiResp.Status, apiResp.Err)
- }
- resp, ok := apiResp.Ok.(*api.ReadNoteResponseStream)
- if !ok {
- return fmt.Errorf("invalid response")
- }
- fmt.Printf("%s", resp.Chunk)
- }
- return nil
-}
-
-func (c *readCommand) Execute(ctx context.Context, fs *flag.FlagSet, _ ...any) subcommands.ExitStatus {
- args := fs.Args()
- if len(args) != 1 {
- fmt.Fprintln(os.Stderr, "usage error")
- return subcommands.ExitUsageError
- }
- if err := c.read(ctx, args[0]); err != nil {
- fmt.Fprintln(os.Stderr, err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-func main() {
- subcommands.ImportantFlag("url")
- subcommands.Register(subcommands.HelpCommand(), "")
- subcommands.Register(subcommands.FlagsCommand(), "")
- subcommands.Register(subcommands.CommandsCommand(), "")
- subcommands.Register(&loginCommand{}, "")
- subcommands.Register(&listCommand{}, "")
- subcommands.Register(&newCommand{}, "")
- subcommands.Register(&readCommand{}, "")
-
- flag.Parse()
- os.Exit(int(subcommands.Execute(context.Background())))
-}