Rename service to EngPadSyncService (buf lint), add java_package, add buf.yaml

- Proto service renamed from EngPadSync to EngPadSyncService per buf
  STANDARD lint rule SERVICE_SUFFIX
- Added java_package and java_multiple_files options for Android client
- Added buf.yaml with STANDARD lint and FILE breaking detection
- Regenerated Go gRPC stubs, updated server references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 21:25:09 -07:00
parent 4dc71703fe
commit 49de9269d6
11 changed files with 509 additions and 170 deletions

View File

@@ -0,0 +1,76 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"git.wntrmute.dev/kyle/eng-pad-server/internal/auth"
"git.wntrmute.dev/kyle/eng-pad-server/internal/config"
"git.wntrmute.dev/kyle/eng-pad-server/internal/db"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize database and create admin user",
RunE: runInit,
}
func init() {
rootCmd.AddCommand(initCmd)
}
func runInit(cmd *cobra.Command, args []string) error {
cfg, err := config.Load(cfgFile)
if err != nil {
return err
}
database, err := db.Open(cfg.Database.Path)
if err != nil {
return err
}
defer func() { _ = database.Close() }()
if err := db.Migrate(database); err != nil {
return fmt.Errorf("migrate: %w", err)
}
fmt.Println("Database migrated.")
reader := bufio.NewReader(os.Stdin)
fmt.Print("Admin username: ")
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)
if username == "" {
return fmt.Errorf("username cannot be empty")
}
fmt.Print("Admin password: ")
passBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
return fmt.Errorf("read password: %w", err)
}
password := strings.TrimSpace(string(passBytes))
if password == "" {
return fmt.Errorf("password cannot be empty")
}
params := auth.Argon2Params{
Memory: cfg.Auth.Argon2Memory,
Time: cfg.Auth.Argon2Time,
Threads: cfg.Auth.Argon2Threads,
}
id, err := auth.CreateUser(database, username, password, params)
if err != nil {
return fmt.Errorf("create user: %w", err)
}
fmt.Printf("User %q created (ID %d).\n", username, id)
return nil
}

View File

@@ -0,0 +1,15 @@
package main
import (
"fmt"
"os"
)
var version = "dev"
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

View File

@@ -0,0 +1,14 @@
package main
import "github.com/spf13/cobra"
var cfgFile string
var rootCmd = &cobra.Command{
Use: "eng-pad-server",
Short: "Engineering notebook sync and viewer",
}
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file path")
}

View File

@@ -0,0 +1,50 @@
package main
import (
"fmt"
"path/filepath"
"strings"
"time"
"git.wntrmute.dev/kyle/eng-pad-server/internal/config"
"git.wntrmute.dev/kyle/eng-pad-server/internal/db"
"github.com/spf13/cobra"
)
var snapshotCmd = &cobra.Command{
Use: "snapshot",
Short: "Create a database backup via VACUUM INTO",
RunE: runSnapshot,
}
func init() {
rootCmd.AddCommand(snapshotCmd)
}
func runSnapshot(cmd *cobra.Command, args []string) error {
cfg, err := config.Load(cfgFile)
if err != nil {
return err
}
database, err := db.Open(cfg.Database.Path)
if err != nil {
return err
}
defer func() { _ = database.Close() }()
dir := filepath.Dir(cfg.Database.Path)
backupDir := filepath.Join(dir, "backups")
ts := time.Now().Format("20060102-150405")
backupPath := filepath.Join(backupDir, fmt.Sprintf("eng-pad-server-%s.db", ts))
// Escape single quotes in the path to prevent SQL injection.
// VACUUM INTO does not support parameter binding.
escapedPath := strings.ReplaceAll(backupPath, "'", "''")
if _, err := database.Exec(fmt.Sprintf("VACUUM INTO '%s'", escapedPath)); err != nil {
return fmt.Errorf("vacuum into: %w", err)
}
fmt.Printf("Backup created: %s\n", backupPath)
return nil
}

View File

@@ -0,0 +1,47 @@
package main
import (
"fmt"
"git.wntrmute.dev/kyle/eng-pad-server/internal/config"
"git.wntrmute.dev/kyle/eng-pad-server/internal/db"
"github.com/spf13/cobra"
)
var statusCmd = &cobra.Command{
Use: "status",
Short: "Check database health",
RunE: runStatus,
}
func init() {
rootCmd.AddCommand(statusCmd)
}
func runStatus(cmd *cobra.Command, args []string) error {
cfg, err := config.Load(cfgFile)
if err != nil {
return err
}
database, err := db.Open(cfg.Database.Path)
if err != nil {
return err
}
defer func() { _ = database.Close() }()
var userCount, notebookCount, pageCount, strokeCount int
_ = database.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
_ = database.QueryRow("SELECT COUNT(*) FROM notebooks").Scan(&notebookCount)
_ = database.QueryRow("SELECT COUNT(*) FROM pages").Scan(&pageCount)
_ = database.QueryRow("SELECT COUNT(*) FROM strokes").Scan(&strokeCount)
fmt.Printf("eng-pad-server %s\n", version)
fmt.Printf(" Database: %s\n", cfg.Database.Path)
fmt.Printf(" Users: %d\n", userCount)
fmt.Printf(" Notebooks: %d\n", notebookCount)
fmt.Printf(" Pages: %d\n", pageCount)
fmt.Printf(" Strokes: %d\n", strokeCount)
return nil
}