Skip to content

Commit

Permalink
Add HTTP authentication (#17)
Browse files Browse the repository at this point in the history
* improve config & add authentication

* update .env.example
  • Loading branch information
utkuufuk authored Apr 26, 2022
1 parent a3bc054 commit 6ab8545
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 69 deletions.
7 changes: 4 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
APP_ENV=production
SECRET=xxxxxxxxxxxxxxxxxxxxxxxxx
HTTP_PORT=XXXX
SPREADSHEET_ID=xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxx
TIMEZONE_LOCATION=Europe/Istanbul

TELEGRAM_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=xxxxxxxxxx

TIMEZONE_LOCATION=Europe/Istanbul

GSHEETS_CLIENT_ID=xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GSHEETS_CLIENT_SECRET=xxxxxxxx-xxxxxxxxxxxxxxx
GSHEETS_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GSHEETS_REFRESH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SPREADSHEET_ID=xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxx
24 changes: 20 additions & 4 deletions cmd/habit-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,40 @@ import (
)

var (
cfg config.ServerConfig
client habit.Client
)

func main() {
func init() {
var err error
client, err = habit.GetClient(context.Background())
cfg, err = config.ParseServerConfig()
if err != nil {
logger.Error("Failed to parse server config: %v", err)
os.Exit(1)
}

client, err = habit.GetClient(context.Background(), cfg.GoogleSheets)
if err != nil {
logger.Error("Could not create gsheets client for Habit Service: %v", err)
os.Exit(1)
}
}

func main() {
http.HandleFunc("/entrello", handleEntrelloRequest)
http.ListenAndServe(fmt.Sprintf(":%d", config.HttpPort), nil)
http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil)
}

func handleEntrelloRequest(w http.ResponseWriter, req *http.Request) {
if cfg.Secret != "" && req.Header.Get("X-Api-Key") != cfg.Secret {
w.WriteHeader(http.StatusUnauthorized)
return
}

if req.Method == http.MethodGet {
action := service.FetchHabitsAsTrelloCardsAction{}
action := service.FetchHabitsAsTrelloCardsAction{
TimezoneLocation: cfg.TimezoneLocation,
}
cards, err := action.Run(req.Context(), client)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Expand Down
17 changes: 15 additions & 2 deletions cmd/progress-report/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,33 @@ import (
"context"
"os"

"github.com/utkuufuk/habit-service/internal/config"
"github.com/utkuufuk/habit-service/internal/habit"
"github.com/utkuufuk/habit-service/internal/logger"
"github.com/utkuufuk/habit-service/internal/service"
)

func main() {
cfg, err := config.ParseProgressReportConfig()
if err != nil {
logger.Error("Failed to parse server config: %v", err)
os.Exit(1)
}

ctx := context.Background()
client, err := habit.GetClient(ctx)
client, err := habit.GetClient(ctx, cfg.GoogleSheets)
if err != nil {
logger.Error("Could not create gsheets client for Habit Service: %v", err)
os.Exit(1)
}

_, err = service.ReportProgressAction{}.Run(ctx, client)
action := service.ReportProgressAction{
TelegramChatId: cfg.TelegramChatId,
TelegramToken: cfg.TelegramToken,
TimezoneLocation: cfg.TimezoneLocation,
}

_, err = action.Run(ctx, client)
if err != nil {
logger.Error("Could not run Glados command: %v", err)
os.Exit(1)
Expand Down
5 changes: 3 additions & 2 deletions cmd/score-update/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (
)

func main() {
client, err := habit.GetClient(context.Background())
loc, cfg := config.ParseCommonConfig()
client, err := habit.GetClient(context.Background(), cfg)
if err != nil {
logger.Error("Could not create gsheets client for Habit Service: %v", err)
os.Exit(1)
}

now := time.Now().In(config.TimezoneLocation)
now := time.Now().In(loc)
habits, err := client.FetchHabits(now)
if err != nil {
logger.Error("could not fetch habits: %v", err)
Expand Down
96 changes: 55 additions & 41 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,78 @@ import (
"github.com/utkuufuk/habit-service/internal/logger"
)

var (
AppEnv string
HttpPort int
TimezoneLocation *time.Location
SpreadsheetId string
type GoogleSheetsConfig struct {
GoogleClientId string
GoogleClientSecret string
GoogleAccessToken string
GoogleRefreshToken string
TelegramChatId int64
TelegramToken string
)

func init() {
godotenv.Load()
SpreadsheetId string
}

AppEnv = os.Getenv("APP_ENV")
TelegramToken = os.Getenv("TELEGRAM_TOKEN")
SpreadsheetId = os.Getenv("SPREADSHEET_ID")
GoogleClientId = os.Getenv("GSHEETS_CLIENT_ID")
GoogleClientSecret = os.Getenv("GSHEETS_CLIENT_SECRET")
GoogleAccessToken = os.Getenv("GSHEETS_ACCESS_TOKEN")
GoogleRefreshToken = os.Getenv("GSHEETS_REFRESH_TOKEN")
type ServerConfig struct {
TimezoneLocation *time.Location
GoogleSheets GoogleSheetsConfig
Port int
Secret string
}

httpPort := os.Getenv("PORT")
if httpPort == "" {
httpPort = os.Getenv("HTTP_PORT")
}
type ProgressReportConfig struct {
TimezoneLocation *time.Location
GoogleSheets GoogleSheetsConfig
TelegramChatId int64
TelegramToken string
}

port, err := strconv.Atoi(httpPort)
if err != nil {
logger.Error("PORT or HTTP_PORT not set")
func ParseCommonConfig() (loc *time.Location, cfg GoogleSheetsConfig) {
godotenv.Load()

if AppEnv == "production" {
os.Exit(1)
}
}
cfg.SpreadsheetId = os.Getenv("SPREADSHEET_ID")
cfg.GoogleClientId = os.Getenv("GSHEETS_CLIENT_ID")
cfg.GoogleClientSecret = os.Getenv("GSHEETS_CLIENT_SECRET")
cfg.GoogleAccessToken = os.Getenv("GSHEETS_ACCESS_TOKEN")
cfg.GoogleRefreshToken = os.Getenv("GSHEETS_REFRESH_TOKEN")

location, err := time.LoadLocation(os.Getenv("TIMEZONE_LOCATION"))
loc, err := time.LoadLocation(os.Getenv("TIMEZONE_LOCATION"))
if err != nil {
fmt.Printf(
logger.Warn(
"Invalid timezone location: '%s', falling back to UTC: %v\n",
location,
os.Getenv("TIMEZONE_LOCATION"),
err,
)
location, _ = time.LoadLocation("UTC")
loc, _ = time.LoadLocation("UTC")
}

chatId, err := strconv.ParseInt(os.Getenv("TELEGRAM_CHAT_ID"), 10, 64)
return loc, cfg
}

func ParseServerConfig() (cfg ServerConfig, err error) {
loc, common := ParseCommonConfig()

port, err := strconv.Atoi(os.Getenv("PORT"))
if err != nil {
logger.Error("Invalid Telegram Chat ID")
return cfg, fmt.Errorf("PORT not set")
}

if AppEnv == "production" {
os.Exit(1)
}
return ServerConfig{
loc,
common,
port,
os.Getenv("SECRET"),
}, nil
}

func ParseProgressReportConfig() (cfg ProgressReportConfig, err error) {
loc, common := ParseCommonConfig()

chatId, err := strconv.ParseInt(os.Getenv("TELEGRAM_CHAT_ID"), 10, 64)
if err != nil {
return cfg, fmt.Errorf("Invalid Telegram Chat ID")
}

HttpPort = port
TimezoneLocation = location
TelegramChatId = chatId
return ProgressReportConfig{
loc,
common,
chatId,
os.Getenv("TELEGRAM_TOKEN"),
}, nil
}
6 changes: 3 additions & 3 deletions internal/habit/habit.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ type cell struct {
row int
}

func GetClient(ctx context.Context) (client Client, err error) {
service, err := initializeService(ctx)
func GetClient(ctx context.Context, cfg config.GoogleSheetsConfig) (client Client, err error) {
service, err := initService(ctx, cfg)
if err != nil {
return client, fmt.Errorf("could not initialize gsheets service: %w", err)
}
return Client{config.SpreadsheetId, service.Spreadsheets.Values}, nil
return Client{cfg.SpreadsheetId, service.Spreadsheets.Values}, nil
}

// FetchHabits retrieves the state of today's habits from the spreadsheet
Expand Down
10 changes: 5 additions & 5 deletions internal/habit/auth.go → internal/habit/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ const (
tokenUrl = "https://oauth2.googleapis.com/token"
)

func initializeService(ctx context.Context) (service *sheets.Service, err error) {
func initService(ctx context.Context, cfg config.GoogleSheetsConfig) (service *sheets.Service, err error) {
auth := &oauth2.Config{
ClientID: config.GoogleClientId,
ClientSecret: config.GoogleClientSecret,
ClientID: cfg.GoogleClientId,
ClientSecret: cfg.GoogleClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: authUrl,
TokenURL: tokenUrl,
Expand All @@ -30,9 +30,9 @@ func initializeService(ctx context.Context) (service *sheets.Service, err error)
}

token := &oauth2.Token{
AccessToken: config.GoogleAccessToken,
AccessToken: cfg.GoogleAccessToken,
TokenType: "Bearer",
RefreshToken: config.GoogleRefreshToken,
RefreshToken: cfg.GoogleRefreshToken,
Expiry: time.Now(),
}
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions internal/service/entrello_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import (
"time"

"github.com/utkuufuk/entrello/pkg/trello"
"github.com/utkuufuk/habit-service/internal/config"
"github.com/utkuufuk/habit-service/internal/entrello"
"github.com/utkuufuk/habit-service/internal/habit"
)

type FetchHabitsAsTrelloCardsAction struct{}
type FetchHabitsAsTrelloCardsAction struct {
TimezoneLocation *time.Location
}

func (a FetchHabitsAsTrelloCardsAction) Run(ctx context.Context, client habit.Client) ([]trello.Card, error) {
now := time.Now().In(config.TimezoneLocation)
now := time.Now().In(a.TimezoneLocation)

habits, err := client.FetchHabits(now)
if err != nil {
Expand Down
15 changes: 9 additions & 6 deletions internal/service/progress_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ import (
"time"

tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/utkuufuk/habit-service/internal/config"
"github.com/utkuufuk/habit-service/internal/habit"
"github.com/utkuufuk/habit-service/internal/tableimage"
)

type ReportProgressAction struct{}
type ReportProgressAction struct {
TelegramChatId int64
TelegramToken string
TimezoneLocation *time.Location
}

func (a ReportProgressAction) Run(ctx context.Context, client habit.Client) (string, error) {
now := time.Now().In(config.TimezoneLocation)
now := time.Now().In(a.TimezoneLocation)
currentHabits, err := client.FetchHabits(now)
if err != nil {
return "", fmt.Errorf("could not fetch this month's habits: %w\n", err)
}

year, month, _ := now.Date()
lastMonth := time.Date(year, month, 1, 0, 0, 0, 0, config.TimezoneLocation).Add(-time.Nanosecond)
lastMonth := time.Date(year, month, 1, 0, 0, 0, 0, a.TimezoneLocation).Add(-time.Nanosecond)
previousHabits, err := client.FetchHabits(lastMonth)
if err != nil {
return "", fmt.Errorf("could not fetch habits from last month: %w\n", err)
Expand All @@ -46,7 +49,7 @@ func (a ReportProgressAction) Run(ctx context.Context, client habit.Client) (str
}

func (a ReportProgressAction) sendProgressReport(path string) error {
bot, err := tgbotapi.NewBotAPI(config.TelegramToken)
bot, err := tgbotapi.NewBotAPI(a.TelegramToken)
if err != nil {
return fmt.Errorf("could not initialize Telegram bot client: %w", err)
}
Expand All @@ -56,7 +59,7 @@ func (a ReportProgressAction) sendProgressReport(path string) error {
return fmt.Errorf("could not read progress report image '%s': %w", path, err)
}

_, err = bot.Send(tgbotapi.NewPhotoUpload(config.TelegramChatId, tgbotapi.FileBytes{
_, err = bot.Send(tgbotapi.NewPhotoUpload(a.TelegramChatId, tgbotapi.FileBytes{
Name: "picture",
Bytes: photoBytes,
}))
Expand Down

0 comments on commit 6ab8545

Please sign in to comment.