Skip to content

Commit

Permalink
restructure project into packages
Browse files Browse the repository at this point in the history
  • Loading branch information
KireinaHoro committed Jan 26, 2018
1 parent 922f57d commit 825660c
Show file tree
Hide file tree
Showing 8 changed files with 759 additions and 687 deletions.
118 changes: 118 additions & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package auth

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"os/user"
"path/filepath"

"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/drive/v3"
)

// getClient uses a Context and Config to retrieve a Token
// then generate a Client. It returns the generated Client.
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
cacheFile, err := tokenCacheFile()
if err != nil {
log.Fatalf("Unable to get path to cached credential file: %v", err)
}
tok, err := tokenFromFile(cacheFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(cacheFile, tok)
}
return config.Client(ctx, tok)
}

// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authURL)

var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code: %v", err)
}

tok, err := config.Exchange(context.Background(), code)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
return tok
}

// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func tokenCacheFile() (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
os.MkdirAll(tokenCacheDir, 0700)
return filepath.Join(tokenCacheDir,
url.QueryEscape("drive-go-quickstart.json")), err
}

// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
t := &oauth2.Token{}
err = json.NewDecoder(f).Decode(t)
return t, err
}

// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", file)
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}

// Authenticate authenticates the application with Google Drive
// server and returns a *drive.Service for further operation.
func Authenticate() *drive.Service {
ctx := context.Background()

usr, err := user.Current()
if err != nil {
log.Fatalf("Unable to get current user: %v", err)
}
path := usr.HomeDir + "/Documents/client_secret.json"
b, err := ioutil.ReadFile(path)
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}

config, err := google.ConfigFromJSON(b, drive.DriveScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(ctx, config)

srv, err := drive.New(client)
if err != nil {
log.Fatalf("Unable to retrieve drive Client: %v", err)
}
return srv
}
116 changes: 116 additions & 0 deletions cmd/drivesync/uploader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package main

import (
"bufio"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"

A "github.com/KireinaHoro/DriveSync/auth"
C "github.com/KireinaHoro/DriveSync/config"
E "github.com/KireinaHoro/DriveSync/errors"
R "github.com/KireinaHoro/DriveSync/remote"
)

// initFlags initializes the command-line arguments.
func initFlags() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "\nUsage: %s [options] ( <target> || -interactive )\n\n",
filepath.Base(os.Args[0]))
flag.PrintDefaults()
}

flag.StringVar(&C.ArchiveRootName, "root", "archive", "name of the archive root")
flag.StringVar(&C.Category, "category", "Uncategorized", "destination category")
flag.BoolVar(&C.ForceRecheck, "recheck", true, "force file checksum recheck")
flag.BoolVar(&C.Interactive, "interactive", false, "work interactively")
flag.BoolVar(&C.Verbose, "verbose", false, "verbose output")
flag.BoolVar(&C.CreateMissing, "create-missing", false, "create category if not exist")

flag.Parse()

C.Target = flag.Arg(0)
}

func main() {
initFlags()

reader := bufio.NewReader(os.Stdin)

if runtime.GOOS == "darwin" {
// set up proxy for run in restricted network environment
proxyUrl, _ := url.Parse("http://127.0.0.1:8001")
http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
}

// we need to do this manually for old runtime
if C.Verbose {
fmt.Println("Procs usable:", runtime.NumCPU())
}
runtime.GOMAXPROCS(runtime.NumCPU())

// authenticate to Google Drive server to get *drive.Service
srv := A.Authenticate()

var (
info os.FileInfo
err error
)

if C.Interactive {
fmt.Print("Enter target to sync, in absolute path: ")
C.Target, err = reader.ReadString('\n')
C.Target = strings.TrimRight(C.Target, "\n")
if err != nil {
log.Fatalf("Failed to scan: %v", err)
}
info, err = os.Stat(C.Target)
if err != nil {
log.Fatalf("Failed to stat target '%s': %v", C.Target, err)
}
fmt.Print("Enter desired category: ")
C.Category, err = reader.ReadString('\n')
C.Category = strings.TrimRight(C.Category, "\n")
if err != nil {
log.Fatalf("Failed to scan: %v", err)
}
} else {
if C.Target == "" {
fmt.Fprintln(os.Stderr, "Please specify target properly.")
flag.Usage()
os.Exit(1)
}
if C.Target[0] != '/' {
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to get current working directory: %v", err)
}
C.Target = filepath.Clean(pwd + "/" + C.Target)
}
info, err = os.Stat(C.Target)
if err != nil {
log.Fatalf("Failed to stat target '%s': %v", C.Target, err)
}
}
if info.IsDir() {
fmt.Printf("Syncing directory '%s'...\n", C.Target)
err = R.SyncDirectory(reader, srv, C.Target, C.Category)
} else {
fmt.Printf("Syncing file '%s'...\n", C.Target)
err = R.SyncFile(reader, srv, C.Target, C.Category)
}
if err != nil {
if _, ok := err.(E.ErrorSetMarkFailed); ok {
log.Printf("Sync succeeded, yet failed to set sync mark: %v", err)
} else {
log.Fatalf("Failed to sync '%s': %v", C.Target, err)
}
}
fmt.Println("Sync succeeded.")
}
32 changes: 32 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package config

import (
U "github.com/KireinaHoro/DriveSync/utils"
)

var (
IgnoreList = map[string]struct{}{
".DS_Store": {},
".localized": {},
".idea": {},
".sync_finished": {},
}
ArchiveRootID string
CategoryIDs = U.NewSafeMap()
)

const (
DriveFolderType = "application/vnd.google-apps.folder"
RetryRatio = 2
RetryStartingRate = 1
)

var (
ArchiveRootName string // "archive"
Target string // ""
Category string // "Uncategorized"
ForceRecheck bool // true
Interactive bool // false
Verbose bool // false
CreateMissing bool // false
)
26 changes: 26 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package errors

type ErrorNotFound string

func (r ErrorNotFound) Error() string {
return string(r)
}

type ErrorAlreadySynced string

func (r ErrorAlreadySynced) Error() string {
return string(r)
}

type ErrorChecksumMismatch string

func (r ErrorChecksumMismatch) Error() string {
return string(r)
}

type ErrorSetMarkFailed string

func (r ErrorSetMarkFailed) Error() string {
return string(r)
}

Loading

0 comments on commit 825660c

Please sign in to comment.