
Presented by: AlexTLDR

package main
import "fmt"
func main() {
message := "Hello, pizza lovers!"
fmt.Println(message)
}
mux := http.NewServeMux()db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return err
}
defer db.Close()func Initialize() (*OAuthConfig, error) {
// Load .env file
if err := godotenv.Load(); err != nil {
return nil, fmt.Errorf("error loading .env file: %w", err)
}
// Get Google OAuth credentials
clientID := os.Getenv("GOOGLE_CLIENT_ID")
clientSecret := os.Getenv("GOOGLE_CLIENT_SECRET")
redirectURL := os.Getenv("GOOGLE_REDIRECT_URL")
if clientID == "" || clientSecret == "" || redirectURL == "" {
return nil, errors.New("missing Google OAuth configuration in .env file")
}
// Get allowed emails
allowedEmailsStr := os.Getenv("ALLOWED_EMAILS")
allowedEmails := strings.Split(allowedEmailsStr, ",")
for i := range allowedEmails {
allowedEmails[i] = strings.TrimSpace(allowedEmails[i])
}
if len(allowedEmails) == 0 || (len(allowedEmails) == 1 && allowedEmails[0] == "") {
return nil, errors.New("no allowed email addresses configured in .env file")
}
// Create OAuth config
oauthConfig := &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: redirectURL,
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
Endpoint: google.Endpoint,
}
return &OAuthConfig{
GoogleOAuthConfig: oauthConfig,
AllowedEmails: allowedEmails,
}, nil
}func SetSecureSessionCookie(w http.ResponseWriter, email string) {
// Current timestamp for the cookie
now := time.Now()
expires := now.Add(SessionDuration)
// Create the cookie payload: email|expiration_timestamp
expiresStr := strconv.FormatInt(expires.Unix(), 10)
payload := fmt.Sprintf("%s|%s", email, expiresStr)
// Create HMAC signature
h := hmac.New(sha256.New, cookieSecret)
h.Write([]byte(payload))
signature := h.Sum(nil)
// Encode the payload and signature for the cookie
encodedPayload := base64.URLEncoding.EncodeToString([]byte(payload))
encodedSignature := base64.URLEncoding.EncodeToString(signature)
// Final cookie value: base64(payload).base64(signature)
cookieValue := fmt.Sprintf("%s.%s", encodedPayload, encodedSignature)
// Set the cookie
cookie := http.Cookie{
Name: SessionCookieName,
Value: cookieValue,
Path: "/",
HttpOnly: true,
Secure: true, // Set to true in production
SameSite: http.SameSiteLaxMode,
MaxAge: int(SessionDuration.Seconds()),
Expires: expires,
}
http.SetCookie(w, &cookie)
log.Printf("Set secure session cookie for %s, expires: %s", email, expires.Format(time.RFC3339))
}var imageURL string
file, header, err := r.FormFile("image_upload")
if err == nil {
defer file.Close()
// Create unique filename based on timestamp
timestamp := time.Now().Unix()
filename := fmt.Sprintf("%d_%s", timestamp, header.Filename)
// Ensure filename contains only valid characters
filename = strings.ReplaceAll(filename, " ", "-")
// Save the file
filePath := filepath.Join("static", "images", "menu", filename)
dst, err := os.Create(filePath)
if err != nil {
m.adminError(w, r, err, http.StatusInternalServerError, "CreateMenuItem - saving image")
return
}
defer dst.Close()
// Copy the file content
_, err = dst.ReadFrom(file)
if err != nil {
m.adminError(w, r, err, http.StatusInternalServerError, "CreateMenuItem - saving image")
return
}
// Set the image URL
imageURL = "/" + filePath // Add leading slash for web URLs
} var imageURL string
file, header, err := r.FormFile("image_upload")
if err == nil {
defer file.Close()
// Validate that the file is an image
if !m.isValidImageExtension(header.Filename) {
m.clientError(w, http.StatusBadRequest, "Invalid file type. Only image files (jpg, jpeg, png, gif, webp, bmp, svg) are allowed.")
return
}
// Create a completely random filename with timestamp prefix
timestamp := time.Now().Unix()
extension := filepath.Ext(header.Filename) // Get the file extension
randomName := fmt.Sprintf("%d_%s%s", timestamp, uuid.New().String(), extension)
// Save the file
filePath := filepath.Join("static", "images", "menu", randomName)
dst, err := os.Create(filePath)
if err != nil {
m.adminError(w, r, err, http.StatusInternalServerError, "CreateMenuItem - saving image")
return
}
defer dst.Close()
// Copy the file content
_, err = dst.ReadFrom(file)
if err != nil {
m.adminError(w, r, err, http.StatusInternalServerError, "CreateMenuItem - saving image")
return
}
// Set the image URL
imageURL = "/" + filePath // Add leading slash for web URLs
}templates := map[string]*template.Template{
"index.html": template.Must(template.New("index.html").Funcs(funcMap).ParseFiles("templates/index.html", "templates/header.html", "templates/footer.html", "templates/category-nav.html")),
"login.html": template.Must(template.New("login.html").Funcs(funcMap).ParseFiles("templates/login.html")),
"admin-dashboard.html": template.Must(template.New("admin-dashboard.html").Funcs(funcMap).ParseFiles("templates/admin-dashboard.html")),
"menu-form.html": template.Must(template.New("menu-form.html").Funcs(funcMap).ParseFiles("templates/menu-form.html")),
}-- +goose Up
-- SQL in this section is executed when the migration is applied.
CREATE TABLE menu_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
small_price REAL,
category TEXT NOT NULL,
image_url TEXT, -- Stores the relative path like /static/images/menu/image.jpg
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- +goose Down
-- SQL in this section is executed when the migration is rolled back.
DROP TABLE users;
DROP TABLE menu_items;- auth: Google OAuth implementation
- handlers: HTTP request handlers
- middleware: Authentication and session middleware
- models: Data models and database interactions- database/sql: Core database operations
- net/http: HTTP server and client operations
- html/template: HTML templating
- time: Time handling and formatting
- context: Request context management
- encoding/json: JSON encoding/decoding
- encoding/base64: Base64 encoding/decoding for cookies
- crypto/hmac: HMAC signatures for secure cookies
- crypto/sha256: SHA256 hashing for HMAC
- crypto/rand: Secure random number generation
- path/filepath: File path operations
- strings: String manipulation
- log: Logging
- os: File manipulation and operations- github.com/joho/godotenv: For loading environment variables from .env files
- github.com/mattn/go-sqlite3: SQLite3 driver for Go
- github.com/pressly/goose/v3: Database migration tool
- golang.org/x/oauth2: Google OAuth2 integration
- tailwindcss: Frontend CSS framework (not Go related)- Docker support via Dockerfile and docker-compose.yml
- Environment configuration via .env files
- Database migrations for setting up the initial schema and seed data
- Using for hosting a GCP VM via Compute Engine
- Caddy for HTTPS cert and routingPresentation Slides

Pizzeria Website
