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:
76
cmd/eng-pad-server/init.go
Normal file
76
cmd/eng-pad-server/init.go
Normal 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
|
||||
}
|
||||
15
cmd/eng-pad-server/main.go
Normal file
15
cmd/eng-pad-server/main.go
Normal 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)
|
||||
}
|
||||
}
|
||||
14
cmd/eng-pad-server/root.go
Normal file
14
cmd/eng-pad-server/root.go
Normal 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")
|
||||
}
|
||||
50
cmd/eng-pad-server/snapshot.go
Normal file
50
cmd/eng-pad-server/snapshot.go
Normal 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
|
||||
}
|
||||
47
cmd/eng-pad-server/status.go
Normal file
47
cmd/eng-pad-server/status.go
Normal 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(¬ebookCount)
|
||||
_ = 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
|
||||
}
|
||||
Reference in New Issue
Block a user