feat: initialize project structure with SQLite database, crawler framework, and LLM-based JD analysis integration

This commit is contained in:
2026-08-12 01:13:53 -07:00
commit 51b9468737
27 changed files with 3422 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
package main
import (
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"github.com/hireme/hireme/internal/analyzer"
"github.com/hireme/hireme/internal/config"
"github.com/hireme/hireme/internal/db"
"github.com/hireme/hireme/internal/service"
"github.com/hireme/hireme/internal/ui"
"github.com/spf13/cobra"
)
var cfg *config.Config
func newRootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "hireme",
Short: "Hireme (GJCC) - High Performance Job Application Command Center",
Long: `Hireme is a high-performance single-binary job application and tracking command center written in Go.`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg.Normalize()
log.Println("==========================================================")
log.Println("🚀 Starting Hireme (GJCC) Job Application Command Center")
log.Println("==========================================================")
log.Printf("[Config] Port: %s | DB Path: %s | Target Config: %s | Active LLM: %s",
cfg.Port, cfg.DBPath, cfg.TargetConfig, cfg.LLMProvider)
// Ensure DB directory exists
dbDir := filepath.Dir(cfg.DBPath)
if dbDir != "." && dbDir != "" {
_ = os.MkdirAll(dbDir, 0755)
}
// Initialize Database
database, err := db.InitDB(cfg.DBPath)
if err != nil {
return fmt.Errorf("fatal DB error: %w", err)
}
defer database.Close()
// Initialize LLM Analyzer
llmAnalyzer, err := analyzer.NewAnalyzer(cfg)
if err != nil {
return fmt.Errorf("fatal Analyzer setup error: %w", err)
}
// Initialize Pipeline Service
pipeline := service.NewPipelineService(database, llmAnalyzer)
// Load targets from YAML config if database is empty
if err := pipeline.LoadTargetsFromYAML(cfg.TargetConfig); err != nil {
log.Printf("[Main] Target YAML loading status: %v", err)
}
// Setup UI Server & Routes
server := ui.NewServer(cfg, database, pipeline)
mux := http.NewServeMux()
server.RegisterRoutes(mux)
addr := fmt.Sprintf(":%s", cfg.Port)
log.Printf("✨ Hireme Command Center is live at http://localhost:%s", cfg.Port)
return http.ListenAndServe(addr, mux)
},
}
cfg = config.RegisterFlags(cmd.Flags())
return cmd
}
func main() {
rootCmd := newRootCmd()
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
+109
View File
@@ -0,0 +1,109 @@
package main
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/hireme/hireme/internal/analyzer"
"github.com/hireme/hireme/internal/config"
"github.com/hireme/hireme/internal/db"
"github.com/hireme/hireme/internal/service"
"github.com/hireme/hireme/internal/ui"
)
func TestFullApplicationIntegration(t *testing.T) {
tempDir := t.TempDir()
dbPath := filepath.Join(tempDir, "test_hireme.db")
yamlPath := filepath.Join(tempDir, "test_targets.yaml")
yamlContent := `targets:
- name: "Google Software Engineer (Seattle)"
target_url: "https://www.google.com/about/careers/applications/jobs/results/?employment_type=FULL_TIME&q=%22Software%20Engineer%22&location=Seattle%2C%20WA%2C%20USA&target_level=MID"
platform: "google_jobs"
is_active: true
`
if err := os.WriteFile(yamlPath, []byte(yamlContent), 0644); err != nil {
t.Fatalf("failed to create test targets.yaml: %v", err)
}
cfg := &config.Config{
Port: "8080",
DBPath: dbPath,
TargetConfig: yamlPath,
LLMProvider: "mock",
}
database, err := db.InitDB(cfg.DBPath)
if err != nil {
t.Fatalf("InitDB failed: %v", err)
}
defer database.Close()
llmAnalyzer, err := analyzer.NewAnalyzer(cfg)
if err != nil {
t.Fatalf("NewAnalyzer failed: %v", err)
}
pipeline := service.NewPipelineService(database, llmAnalyzer)
// Verify target loading from YAML into SQLite
if err := pipeline.LoadTargetsFromYAML(cfg.TargetConfig); err != nil {
t.Fatalf("LoadTargetsFromYAML failed: %v", err)
}
targets, err := database.GetAllTargets()
if err != nil {
t.Fatalf("GetAllTargets failed: %v", err)
}
if len(targets) != 1 {
t.Errorf("expected 1 target from YAML, got %d", len(targets))
}
// Verify jobs start empty on clean SQLite DB startup (0 jobs)
jobs, err := database.GetJobsWithAnalysis()
if err != nil {
t.Fatalf("GetJobsWithAnalysis failed: %v", err)
}
if len(jobs) != 0 {
t.Errorf("expected 0 jobs on clean startup, got %d", len(jobs))
}
// Setup UI Server & routes
server := ui.NewServer(cfg, database, pipeline)
mux := http.NewServeMux()
server.RegisterRoutes(mux)
// Test GET /
reqRoot := httptest.NewRequest("GET", "/", nil)
wRoot := httptest.NewRecorder()
mux.ServeHTTP(wRoot, reqRoot)
if wRoot.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /, got %d", wRoot.Code)
}
if !strings.Contains(wRoot.Body.String(), "Hireme") {
t.Errorf("expected homepage HTML to contain 'Hireme'")
}
// Test GET /targets
reqTargets := httptest.NewRequest("GET", "/targets", nil)
wTargets := httptest.NewRecorder()
mux.ServeHTTP(wTargets, reqTargets)
if wTargets.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /targets, got %d", wTargets.Code)
}
// Test GET /jobs
reqJobs := httptest.NewRequest("GET", "/jobs", nil)
wJobs := httptest.NewRecorder()
mux.ServeHTTP(wJobs, reqJobs)
if wJobs.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /jobs, got %d", wJobs.Code)
}
}