all repos — artbound-go @ 79e63cd2787ece34f59ee8d885803aefcccf2945

The official administration panel for ArtBound, by EarthBound Café.

cache/api.go (view raw)

  1package cache
  2
  3import (
  4	"context"
  5	"encoding/json"
  6	"fmt"
  7	"io"
  8	"log"
  9	"net/http"
 10	"os"
 11
 12	"path/filepath"
 13	"time"
 14
 15	"golang.org/x/oauth2"
 16	"golang.org/x/oauth2/google"
 17	"google.golang.org/api/drive/v3"
 18	"google.golang.org/api/option"
 19	"google.golang.org/api/sheets/v4"
 20)
 21
 22type GoogleAPI struct {
 23	Ctx              context.Context
 24	Client           *http.Client
 25	spreadsheetId    string
 26	spreadsheetRange string
 27}
 28
 29type Entry struct {
 30	FileID   string `json:"id"`
 31	Month    string `json:"date"`
 32	Name     string `json:"name"`
 33	FilePath string `json:"content"`
 34}
 35
 36// Retrieve a token, saves the token, then returns the generated client.
 37func getClient(config *oauth2.Config) *http.Client {
 38	// The file token.json stores the user's access and refresh tokens, and is
 39	// created automatically when the authorization flow completes for the first
 40	// time.
 41	tokFile := "token.json"
 42	tok, err := tokenFromFile(tokFile)
 43	if err != nil {
 44		tok = getTokenFromWeb(config)
 45		saveToken(tokFile, tok)
 46	}
 47	return config.Client(context.Background(), tok)
 48}
 49
 50// Request a token from the web, then returns the retrieved token.
 51func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
 52	authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
 53	fmt.Printf("Go to the following link in your browser then type the "+
 54		"authorization code: \n%v\n", authURL)
 55
 56	var authCode string
 57	if _, err := fmt.Scan(&authCode); err != nil {
 58		log.Fatalf("Unable to read authorization code %v", err)
 59	}
 60
 61	tok, err := config.Exchange(context.TODO(), authCode)
 62	if err != nil {
 63		log.Fatalf("Unable to retrieve token from web %v", err)
 64	}
 65	return tok
 66}
 67
 68// Retrieves a token from a local file.
 69func tokenFromFile(file string) (*oauth2.Token, error) {
 70	f, err := os.Open(file)
 71	if err != nil {
 72		return nil, err
 73	}
 74	defer f.Close()
 75	tok := &oauth2.Token{}
 76	err = json.NewDecoder(f).Decode(tok)
 77	return tok, err
 78}
 79
 80// Saves a token to a file path.
 81func saveToken(path string, token *oauth2.Token) {
 82	fmt.Printf("Saving credential file to: %s\n", path)
 83	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
 84	if err != nil {
 85		log.Fatalf("Unable to cache oauth token: %v", err)
 86	}
 87	defer f.Close()
 88	json.NewEncoder(f).Encode(token)
 89}
 90
 91func initGoogleAPI(spreadsheetId string, spreadsheetRange string) *GoogleAPI {
 92	ctx := context.Background()
 93	b, err := os.ReadFile("credentials.json")
 94	if err != nil {
 95		log.Fatalf("Unable to read client secret file: %v", err)
 96	}
 97
 98	// If modifying these scopes, delete your previously saved token.json.
 99	config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope, sheets.SpreadsheetsReadonlyScope)
100	if err != nil {
101		log.Fatalf("Unable to parse client secret file to config: %v", err)
102	}
103	client := getClient(config)
104	return &GoogleAPI{ctx, client, spreadsheetId, spreadsheetRange}
105}
106
107func getEntries(googleApi *GoogleAPI) ([]Entry, error) {
108	srv, err := sheets.NewService(googleApi.Ctx, option.WithHTTPClient(googleApi.Client))
109	if err != nil {
110		log.Fatalf("Unable to retrieve Sheets client: %v", err)
111		return nil, err
112	}
113
114	resp, err := srv.Spreadsheets.Values.Get(googleApi.spreadsheetId, googleApi.spreadsheetRange).Do()
115	if err != nil {
116		log.Fatalf("Unable to retrieve data from sheet: %v", err)
117		return nil, err
118	}
119
120	values := resp.Values
121	rows := make([]Entry, len(values))
122	for i, row := range values {
123		dateString := row[0].(string)
124		date, err := time.Parse("02/01/2006 15.04.05", dateString)
125		if err != nil {
126			log.Println("Error while parsing the following time and date string:", dateString)
127			date = time.Now()
128		}
129		rows[i] = Entry{row[3].(string)[33:], date.Format("2006-01"), row[1].(string), ""}
130	}
131	return rows, nil
132}
133
134func getFile(googleApi *GoogleAPI, fileID string, cachePath string) (string, error) {
135	srv, err := drive.NewService(googleApi.Ctx, option.WithHTTPClient(googleApi.Client))
136	if err != nil {
137		log.Fatalf("Unable to retrieve Drive client: %v", err)
138	}
139
140	fileInfo, err := srv.Files.Get(fileID).Fields("name").Do()
141	if err != nil {
142		return "", err
143	}
144	fileExt := filepath.Ext(fileInfo.Name)
145
146	fileName := fileID + fileExt
147	filePath := filepath.Join(cachePath, fileName)
148	out, err := os.Create(filePath)
149	if err != nil {
150		return "", err
151	}
152	defer out.Close()
153
154	res, err := srv.Files.Get(fileID).Download()
155	if err != nil {
156		return "", err
157	}
158	defer res.Body.Close()
159
160	_, err = io.Copy(out, res.Body)
161	if err != nil {
162		return "", err
163	}
164
165	return fileExt, nil
166}