all repos — artbound-go @ 7434a4e2029cf4a5cb865af295d9c14ff3f23181

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

api.go (view raw)

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