hirevec

package module
v0.0.0-...-893fbc6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: Unlicense Imports: 34 Imported by: 0

README

Hirevec

About Hirevec

Hirevec is a modern job recommendation engine that finds jobs and suitable candidates.

Quick start

Start server with:

go run cmd/hvserver/main.go

Ingest some test data with:

go run cmd/hvcli/main.go dev ingest

You are set!

Recommendation Engine

The recommendation engine currently only employs a content-based approach. There are plans to extend it to utilize collaborative filtering with matrix factorization.

Model Features
Variable Meaning
$S_{bm25}$ Normalized BM25/FTS score
$S_{embed}$ Embedding cosine similarity
$S_{rerank}$ Reranker score
$S_{skills}$ Skills overlap
$S_{title}$ Previous-title similarity
$S_{yoe}$ Years-of-experience match
$S_{company}$ Worked for company before
$H_{loc}$ Location match (0/1)
$H_{mode}$ Work mode match (0/1)
$H_{lang}$ Language match (0/1)
$\alpha$ 1 if reranker enabled, else 0
$\beta$ BM25/embedding mixture weight

$Score=\left(0.8\left(\alpha S_{rerank}+(1-\alpha)\left(\beta S_{embed}+(1-\beta)S_{bm25}\right)\right)+0.2\left(0.6S_{skills}+0.25S_{title}+0.1S_{yoe}+0.05S_{company}\right)\right)\times\left(0.2+0.8\left(0.4H_{loc}+0.3H_{mode}+0.3H_{lang}\right)\right)$

Server Features

All currently available server settings are demonstrated in ./.example.env. You can define them in a .env file of a current working directory or via environment variables.

Checklist
  • Developers can enable PostgreSQL.
  • Developers can enable TEI (embeddings and reranker worker).
  • Developers can enable SMTP relay for resetting user password (e-mail users).
  • Developers can enable SSO with Google and Apple.

Misc

Hot-reload server
air \
  --build.cmd "go build -o bin/app cmd/hvserver/main.go" \
  --build.entrypoint "./bin/app" \
  --build.include_ext "go,sql,env,json" \
  --misc.clean_on_exit true \
  --log.main_only true \
  --tmp_dir "bin" \
  --misc.clean_on_exit true \
  --log.silent true \
  --screen.clear_on_rebuild true \
  --color never \
  --screen.keep_scroll false \

Documentation

Overview

Package hirevec implements core server and client.

Package hirevec implements core server and client.

Package hirevec implements core server and client.

Package hirevec implements core server and client.

Index

Constants

View Source
const (
	DefaultCandidatesBatchSize       = 32
	DefaultRecommendationsDailyLimit = 32
	DefaultTopPositions              = 128
)
View Source
const (
	DefaultRequestReadTimeout  = 1000 * time.Millisecond
	DefaultRequestWriteTimeout = 1000 * time.Millisecond
	DefaultGracePeriod         = 5000 * time.Millisecond
)
View Source
const (
	DefaultEmbeddingsJobFrequency      = 1 * time.Hour
	DefaultRecommendationsJobFrequency = 24 * time.Hour
)
View Source
const (
	DefaultPageSize = 32
	MaxPageSize     = 128
)
View Source
const (
	ErrorCodeBearerTokenRequired = "bearer_token_required"
	ErrorCodeInvalidAccessToken  = "invalid_access_token"
	ErrorCodeUnauthorized        = "unauthorized"
)
View Source
const (
	ErrorCodeInvalidRequestBody   = "invalid_request_body"
	ErrorCodeInvalidGrantType     = "invalid_grant_type"
	ErrorCodeRefreshTokenRequired = "refresh_token_required"
	ErrorCodeInvalidRefreshToken  = "invalid_refresh_token"
)
View Source
const (
	ErrorCodePasswordRequired   = "password_required"
	ErrorCodeInvalidCredentials = "invalid_credentials"
)
View Source
const (
	ErrorCodeMissingState          = "missing_state"
	ErrorCodeInvalidState          = "invalid_state"
	ErrorCodeInvalidCSRF           = "invalid_csrf"
	ErrorCodeMissingVerifier       = "missing_verifier"
	ErrorCodeAuthorizationProvider = "authorization_provider_error"
	ErrorCodeMissingCode           = "missing_code"
	ErrorCodeIDTokenRequired       = "id_token_required"
	ErrorCodeInvalidIDToken        = "invalid_id_token"
	ErrorCodeFailedParseClaims     = "failed_parse_claims"
	ErrorCodeUnverifiedEmail       = "unverified_email"
)
View Source
const (
	DefaultUserFullNameMinLength = 2
	DefaultUserFullNameMaxLength = 512
)
View Source
const (
	ErrorCodeRecommendationIDRequired = "recommendation_id_required"
	ErrorCodeRecommendationNotFound   = "recommendation_not_found"
	ErrorCodeReactionExists           = "reaction_exists"
	ErrorCodeInvalidReactionType      = "invalid_reaction_type"
)
View Source
const (
	DefaultUserPasswordMinLength = 8
	DefaultUserPasswordMaxLength = 128
)
View Source
const (
	ErrorCodeEmailRequired             = "email_required"
	ErrorCodeInvalidUserEmailFormat    = "invalid_user_email_format"
	ErrorCodeInvalidUserPasswordFormat = "invalid_user_password_format"
	ErrorCodeInvalidUserFullNameFormat = "invalid_user_full_name_format"
	ErrorCodeUserAlreadyExists         = "user_exists"
)
View Source
const (
	ErrorCodeCandidateAlreadyExists      = "candiate_already_exists"
	ErrorCodeInvalidCandidateAboutFormat = "invalid_candidate_about_format"
)
View Source
const (
	ErrorCodeInternalServerError = "internal_server_error"
	ErrorCodeUserNotFound        = "user_not_found"
	ErrorCodeForbidden           = "forbidden"
)
View Source
const (
	DefaultUserNameMinLength = 4
	DefaultUesrNameMaxLength = 32
)
View Source
const (
	ErrorMessageUserNameWrongSize      = "user_name must be between 4 and 32 characters"
	ErrorMessageUserNameForbiddenChars = "user_name can only contain underscores, latin characters and numbers"
)
View Source
const (
	ErrorCodeResourceTypeMismatch      = "resource_type_mismatch"
	ErrorCodeResourceIDMismatch        = "resource_id_mismatch"
	ErrorCodeInvalidUserUserNameFormat = "invalid_user_user_name_format"
)
View Source
const (
	ErrorCodeMissingRequiredRole = "missing_required_role"
	ErrorCodeCandidateNotFound   = "candidate_not_found"
)
View Source
const (
	DefaultPositionTitleMinLength = 4
	DefaultPositionTitleMaxLength = 64
)
View Source
const (
	DefaultPositionCompanyNameMinLength = 2
	DefaultPositionCompanyNameMaxLength = 512
)
View Source
const (
	ErrorCodeInvalidPositionTitleFormat       = "invalid_position_title_format"
	ErrorCodeInvalidPositionDescriptionFormat = "invalid_position_description_format"
	ErrorCodeInvalidPositionCompanyFormat     = "invalid_position_company_format"
	ErrorCodePositionExists                   = "position_exists"
)
View Source
const (
	ErrorCodePositionIDRequired = "position_id_required"
	ErrorCodePositionNotFound   = "position_not_found"
)
View Source
const (
	EmbeddingStatusPending = "pending"
	EmbeddingStatusDone    = "done"
	EmbeddingStatusFailed  = "failed"
)
View Source
const (
	DefaultRefreshTokenExpiration = 90 * 24 * time.Hour // 90 days
	DefaultAccessTokenExpiration  = 30 * time.Minute    // 30 minutes
	DefaultStateTokenExpiration   = 10 * time.Minute    // 10 minutes
	DefaultVerifierExpiration     = 10 * time.Minute    // 10 minutes
)
View Source
const (
	TokenAudience      = "hirevec.com"
	TokenIssuer        = "hirevec.com"
	StateTokenAudience = "oauth-state"
)
View Source
const (
	DefaultCandidateAboutMaxLength = 1024
)
View Source
const DefaultEmbeddingsBatchSize = 64
View Source
const (
	DefaultLogLevel = slog.LevelWarn
)
View Source
const DefaultMaxRefreshTokensCount = 5
View Source
const (
	DefaultPositionDescriptionMaxLength = 2048
)
View Source
const DefaultProvider = ProviderGoogle
View Source
const Enc = "0123456789abcdefghjkmnpqrstvwxyz"
View Source
const ErrorCodeInvalidProvider = "invalid_provider"
View Source
const ErrorCodeRecruiterExists = "recruiter_exists"
View Source
const ErrorCodeRecruiterNotFound = "recruiter_not_found"
View Source
const ErrorCodeUnsupportedMediaType = "unsupported_media_type"
View Source
const ULIDPrefixCandidate = "can_"
View Source
const ULIDPrefixJTI = "jti_"
View Source
const ULIDPrefixPosition = "pos_"
View Source
const ULIDPrefixRecommendation = "rcm_"
View Source
const ULIDPrefixRecruiter = "rcr_"
View Source
const ULIDPrefixUser = "usr_"

Variables

View Source
var (
	RegexHTMLTag = regexp.MustCompile(`(?s)<[^>]*>`)
	RegexJSTag   = regexp.MustCompile(`(?is)<script.*?>.*?</script>`)
	RegexSQL     = regexp.MustCompile(`(?i)\b(select|insert|update|delete|drop|truncate|alter)\b`)
)
View Source
var (
	ErrTextForbiddenChars = errors.New("text contains forbidden characters")
	ErrTextTooShort       = errors.New("text too short")
	ErrTextTooLong        = errors.New("text too long")
)
View Source
var (
	ErrorMessageUserFullNameWrongSize      = fmt.Sprintf("full_name must be between %v and %v characters", DefaultUserFullNameMinLength, DefaultUserFullNameMaxLength)
	ErrorMessageUserFullNameForbiddenChars = "" /* 143-byte string literal not displayed */
)
View Source
var (
	// UserNameAdjectives is an array for creating random user names, used in conjunction with nouns
	UserNameAdjectives = [...]string{
		"fast",
		"lazy",
		"clever",
		"curious",
		"brave",
		"mighty",
		"silent",
		"noisy",
		"happy",
		"grumpy",
	}

	// UserNameNouns is an array for creating random user names, used in conjunction with adjectives
	UserNameNouns = [...]string{
		"lion",
		"tiger",
		"panda",
		"fox",
		"eagle",
		"shark",
		"wolf",
		"dragon",
		"otter",
		"koala",
	}
)
View Source
var (
	RegexPasswordHasLower   = regexp.MustCompile(`[a-z]`)
	RegexPasswordHasUpper   = regexp.MustCompile(`[A-Z]`)
	RegexPasswordHasDigit   = regexp.MustCompile(`\d`)
	RegexPasswordHasSpecial = regexp.MustCompile(`[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>/?]`)
)
View Source
var (
	ErrPasswordHasNoUpper   = errors.New("password has no upper letter")
	ErrPasswordHasNoDigit   = errors.New("password has no digit")
	ErrPasswordHasNoSpecial = errors.New("password has no special character")
	ErrPasswordHasNoLower   = errors.New("password has no lower letter")
)
View Source
var (
	PathInitMigration          = path.Join("migrations/init.sql")
	PathEmbeddingsMigration    = path.Join("migrations/embeddings.sql")
	PathPostgreSQLFTSMigration = path.Join("migrations/postgresql-fts.sql")
	PathSQLiteFTSMigration     = path.Join("migrations/sqlite-fts.sql")
	PathDevIngestMigration     = path.Join("migrations/dev-ingest.sql")
)
View Source
var (
	ErrUserNoRole   = errors.New("user has no role")
	ErrUserNotFound = errors.New("user not found")
)
View Source
var (
	ErrUserAlreadyExists      = errors.New("user already exists")
	ErrFailedGenerateUserULID = errors.New("failed to generate ULID for user")
)
View Source
var (
	ErrRecommendationAlreadyExists      = errors.New("recommendation already exists")
	ErrFailedGenerateRecommendationULID = errors.New("failed to generate ULID for recommendation")
)
View Source
var (
	ErrRecruiterAlreadyExists      = errors.New("recruiter already exists")
	ErrFailedGenerateRecruiterULID = errors.New("failed to generate ULID for recruiter")
)
View Source
var (
	ErrPositionAlreadyExists      = errors.New("position already exists")
	ErrFailedGeneratePositionULID = errors.New("failed to generate ULID for position")
)
View Source
var (
	ErrCandidateAlreadyExists      = errors.New("candidate already exists")
	ErrFailedGenerateCandidateULID = errors.New("failed to generate ULID for candidate")
)
View Source
var (
	ErrGoogleOIDCConfigNotInitialized = errors.New("Google OIDC configuration is not initialized")
	ErrAppleOIDCConfigNotInitialized  = errors.New("Apple OIDC configuration is not initialized")
)
View Source
var (
	ErrFailedParseClaims = errors.New("failed to parse claims")
	ErrInvalidIDToken    = errors.New("invalid id_token")
)
View Source
var DefaultSQLiteConn = "file:.db?_pragma=foreign_keys(1)&_pragma=busy_timeout(5000)"
View Source
var EmbeddedStatic embed.FS
View Source
var ErrCandidateNotFound = errors.New("candidate not found")
View Source
var ErrEmailNotVerified = errors.New("email not verified")
View Source
var ErrEmbeddingsCountConflict = errors.New("mismatch between count of embedding IDs and embeddings")
View Source
var ErrEmptyCandidateProfile = errors.New("candidate profile is empty")
View Source
var ErrExtraDataDecoded = errors.New("extra data decoded")
View Source
var ErrFailedGenerateJTIULID = errors.New("failed to generate ULID for refresh token (JTI)")
View Source
var ErrFailedShutdownServer = errors.New("failed to shutdown server")
View Source
var ErrIDTokenRequired = errors.New("id_token is required")
View Source
var ErrInvalidProvider = errors.New("invalid provider")
View Source
var ErrMissingDatabaseURL = errors.New("database URL is not set")
View Source
var ErrPositionNotFound = errors.New("position not found")
View Source
var ErrPositionTitleHasURL = errors.New("position title must not contain URLs")
View Source
var ErrReactionAlreadyExists = errors.New("reaction already exists")
View Source
var ErrRecommendationNotFound = errors.New("recommendation not found")
View Source
var ErrRecruiterNotFound = errors.New("recruiter not found")
View Source
var ErrUnknownLogLevel = errors.New("unknown log level")
View Source
var ErrUnsupportedDatabaseProvider = errors.New("unsupported database provider")
View Source
var RegexFullName = regexp.MustCompile(`^[\pL][\pL\s'’-]{2,512}\z`)
View Source
var RegexHasTags = regexp.MustCompile(`<[^>]*>`)
View Source
var RegexTags = regexp.MustCompile(`<[^>]*>`)
View Source
var RegexUrl = regexp.MustCompile(`https?://|www\.`)
View Source
var RegexUserName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)

Functions

func ConnectPostgreSQL

func ConnectPostgreSQL(url string) (*sql.DB, error)

func ConnectSQLite

func ConnectSQLite() (*sql.DB, error)

func CurrentTimestamp

func CurrentTimestamp(offset ...time.Duration) string

func DecodeRequestBody

func DecodeRequestBody[T any](r *http.Request) (T, error)

func EscapeSQLiteFTS

func EscapeSQLiteFTS(query string) string

EscapeSQLiteFTS prevents SQLite SQLITE_ERROR panics if a user types FTS operators (like double quotes, OR, AND, NOT, or asterisks) into their profile bio.

func ExecMigration

func ExecMigration(db *sql.DB, path string) error

func GenerateUserName

func GenerateUserName() (string, error)

func Getenv

func Getenv(key string, defaultValue string) string

func GetenvAndParse

func GetenvAndParse[T any](key string, parser func(string) (T, error), defaultValue T) T

GetenvAndParse parses extracted value of an environment variable and returns a default in case of an error.

func HashPassword

func HashPassword(password string) (string, error)

func InitLogger

func InitLogger(level slog.Level)

func InitPostgreSQL

func InitPostgreSQL(url string) (*sql.DB, error)

func InitSQLite

func InitSQLite() (*sql.DB, error)

func IsValidPassword

func IsValidPassword(passwordHash string, password string) bool

func JSON

func JSON(w http.ResponseWriter, responseBody Envelope, status int)

func LoadOrCreateAsymmetricKey

func LoadOrCreateAsymmetricKey(val string) (paseto.V4AsymmetricSecretKey, error)

func LoadOrCreateSymmetricKey

func LoadOrCreateSymmetricKey(val string) (paseto.V4SymmetricKey, error)

func Loadenv

func Loadenv(path string) error

func MiddlewareChain

func MiddlewareChain(handler http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc

func NormalizeAndValidateCandidateAbout

func NormalizeAndValidateCandidateAbout(about string) (string, error)

func NormalizeAndValidatePositionCompanyName

func NormalizeAndValidatePositionCompanyName(company string) (string, error)

func NormalizeAndValidatePositionDescription

func NormalizeAndValidatePositionDescription(description string) (string, error)

func NormalizeAndValidatePositionTitle

func NormalizeAndValidatePositionTitle(title string) (string, error)

func NormalizeAndValidateUserFullName

func NormalizeAndValidateUserFullName(name string) (string, error)

TODO: Return an array of errors instead of one by one.

func NormalizeAndValidateUserName

func NormalizeAndValidateUserName(userName string) (string, error)

func ParseInt

func ParseInt(value string) (int, error)

func ParseLogLevel

func ParseLogLevel(value string) (slog.Level, error)

func RunAPI

func RunAPI(ctx context.Context, c APIConfig, s Store, v Vault) error

func RunApp

func RunApp(c AppConfig) error

func SqlIn

func SqlIn(column string, n int) string

func UpsertEnvKey

func UpsertEnvKey(filename string, key string, value string) error

func ValidateUserPassword

func ValidateUserPassword(password string) error

Types

type AIConfig

type AIConfig struct {
	UseEmbeddings   bool
	UseReranker     bool
	TEIBaseURL      string
	TEIAPIKey       string
	EmbeddingsModel string
	RerankerModel   string
}

type API

type API struct {
	Store  Store
	Vault  Vault
	Server http.Server
	Mux    *http.ServeMux
}

func NewAPI

func NewAPI(ctx context.Context, c APIConfig, s Store, v Vault) *API

func (*API) CreateAccessToken

func (a *API) CreateAccessToken(w http.ResponseWriter, userID ULID, provider Provider, roles map[Role]ULID)

func (*API) HandlerCreateCandidate

func (a *API) HandlerCreateCandidate() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerCreatePosition

func (a *API) HandlerCreatePosition() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerCreateReaction

func (a *API) HandlerCreateReaction() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerCreateRecruiter

func (a *API) HandlerCreateRecruiter() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerCreateUser

func (a *API) HandlerCreateUser() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerDeleteCandidate

func (a *API) HandlerDeleteCandidate() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerDeletePosition

func (a *API) HandlerDeletePosition() http.HandlerFunc

func (*API) HandlerDeleteRecruiter

func (a *API) HandlerDeleteRecruiter() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerDeleteUser

func (a *API) HandlerDeleteUser() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetCandidate

func (a *API) HandlerGetCandidate() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetMatches

func (a *API) HandlerGetMatches() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetPosition

func (a *API) HandlerGetPosition() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetPositions

func (a *API) HandlerGetPositions() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetReactions

func (a *API) HandlerGetReactions() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetRecommendations

func (a *API) HandlerGetRecommendations() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetRecruiter

func (a *API) HandlerGetRecruiter() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerGetUser

func (a *API) HandlerGetUser() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerHealth

func (a *API) HandlerHealth(w http.ResponseWriter, r *http.Request)

func (*API) HandlerLoginViaEmail

func (a *API) HandlerLoginViaEmail() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerLoginViaProvider

func (a *API) HandlerLoginViaProvider() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerPatchCandidate

func (a *API) HandlerPatchCandidate() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerPatchPosition

func (a *API) HandlerPatchPosition() http.HandlerFunc

func (*API) HandlerPatchUser

func (a *API) HandlerPatchUser() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerRefresh

func (a *API) HandlerRefresh() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) HandlerSSOCallback

func (a *API) HandlerSSOCallback() http.HandlerFunc

TODO: Write integration tests for this handler https://github.com/akvachan/hirevec-core/issues/34

func (*API) MiddlewareAuth

func (a *API) MiddlewareAuth(roles map[Role]bool) Middleware

func (*API) MiddlewareLogging

func (a *API) MiddlewareLogging(next http.HandlerFunc) http.HandlerFunc

func (*API) MiddlewareMaxBytesLimit

func (a *API) MiddlewareMaxBytesLimit(next http.HandlerFunc) http.HandlerFunc

func (*API) MiddlewarePanicRecovery

func (a *API) MiddlewarePanicRecovery(next http.HandlerFunc) http.HandlerFunc

func (*API) PrivateRoute

func (a *API) PrivateRoute(c RouteConfig)

func (*API) PublicRoute

func (a *API) PublicRoute(c RouteConfig)

func (*API) RegisterRoutes

func (a *API) RegisterRoutes()

func (*API) RunEmbeddingsJob

func (a *API) RunEmbeddingsJob(c AIConfig) error

func (*API) RunRecommendationsJob

func (a *API) RunRecommendationsJob(c AIConfig) error

func (*API) WaitAndShutdown

func (a *API) WaitAndShutdown(ctx context.Context, errCh chan error, gracePeriod time.Duration) error

type APIConfig

type APIConfig struct {
	ServerBaseURL               string
	RequestReadTimeout          time.Duration
	RequestWriteTimeout         time.Duration
	GracePeriod                 time.Duration
	UseGoogleSSO                bool
	UseAppleSSO                 bool
	TEIBaseURL                  string
	TEIAPIKey                   string
	EmbeddingsModel             string
	RerankerModel               string
	UseEmbeddings               bool
	UseReranker                 bool
	EmbeddingsJobFrequency      time.Duration
	RecommendationsJobFrequency time.Duration
}

type AccessToken

type AccessToken struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	ExpiresIn   uint32 `json:"expires_in"`
	Scope       string `json:"scope"`
	UserID      ULID   `json:"user_id"`
}

type AccessTokenClaims

type AccessTokenClaims struct {
	UserID   ULID
	Provider Provider
	Roles    map[Role]ULID
}

func GetClaims

func GetClaims(r *http.Request) (AccessTokenClaims, bool)

type AppConfig

type AppConfig struct {
	ServerBaseURL               string
	LogLevel                    slog.Level
	RequestReadTimeout          time.Duration
	RequestWriteTimeout         time.Duration
	GracePeriod                 time.Duration
	EmbeddingsJobFrequency      time.Duration
	RecommendationsJobFrequency time.Duration
	SymmetricKey                string
	AsymmetricKey               string
	AppleClientID               string
	AppleClientSecret           string
	GoogleClientID              string
	GoogleClientSecret          string
	PostgreSQLDatabaseURL       string
	TEIAPIKey                   string
	TEIBaseURL                  string
}

type AppleClaims

type AppleClaims struct {
	Sub            string          `json:"sub"`
	Email          string          `json:"email"`
	EmailVerified  json.RawMessage `json:"email_verified"`
	IsPrivateEmail string          `json:"is_private_email"`
}

type Candidate

type Candidate struct {
	ID                ULID      `json:"id"`
	UserID            ULID      `json:"user_id"`
	About             string    `json:"about"`
	LastRecommendedAt time.Time `json:"last_recommended_at"`
}

type ContextKey

type ContextKey string
const ContextKeyClaims ContextKey = "claims"

type DatabaseProvider

type DatabaseProvider string
const (
	DatabaseProviderPostgreSQL DatabaseProvider = "PostgreSQL"
	DatabaseProviderSQLite     DatabaseProvider = "SQLite"
)

type EmbeddingEntity

type EmbeddingEntity struct {
	Embedding []float32 `json:"embedding"`
}

func CreateEmbeddings

func CreateEmbeddings(aiConfig AIConfig, input []string) ([]EmbeddingEntity, error)

type EmbeddingStatus

type EmbeddingStatus string

type EmbeddingsRequest

type EmbeddingsRequest struct {
	Input []string `json:"input"`
	Model string   `json:"model"`
}

type EmbeddingsResponse

type EmbeddingsResponse struct {
	Data []EmbeddingEntity `json:"data"`
}

type Envelope

type Envelope struct {
	// Data contains any main data to be consumed.
	Data any `json:"data,omitempty"`

	// Errors contains one or more errors when the request fails.
	Errors []Error `json:"errors,omitempty"`

	// Links contains top-level navigation or pagination URLs.
	Links Links `json:"links,omitempty"`

	// Meta contains additional information that is not links or errors or data.
	Meta Meta `json:"meta,omitempty"`
}

Envelope is a wrapper that contains zero or more fields.

type Error

type Error struct {
	// Code is an application-specific error identifier used for programmatic handling.
	Code string `json:"code,omitempty"`

	// Message provides a detailed explanation of the error.
	Message string `json:"detail,omitempty"`

	// Source identifies the specific field or parameter that caused the error.
	Source ErrorSource `json:"source,omitempty"`
}

Error represents a single error object returned when a request fails.

type ErrorSource

type ErrorSource struct {
	// Pointer is a JSON Pointer to the offending value in the request body.
	Body string `json:"body,omitempty"`

	// Parameter is the query string parameter that caused the error.
	Parameter string `json:"parameter,omitempty"`

	// Header is a string indicating the name of a single request header which caused the error.
	Header string `json:"header,omitempty"`

	// Cookie is a string indicating the name of a cookie which caused the error.
	Cookie string `json:"cookie,omitempty"`
}

ErrorSource identifies where an error originated in the request.

type GoogleClaims

type GoogleClaims struct {
	Sub           string `json:"sub"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Picture       string `json:"picture"`
}

type IDToken

type IDToken struct {
	Provider       Provider
	ProviderUserID string
	Email          string
	FullName       string
}
type Links struct {
	// Next is a link to the next page in a paginated response.
	Next string `json:"next,omitempty"`

	// Previous is a link to the previous page in a paginated response.
	Previous string `json:"previous,omitempty"`
}

Links contains URLs to other related pages.

type Match

type Match struct {
	PositionID  ULID      `json:"position_id"`
	Title       string    `json:"title"`
	Description string    `json:"description"`
	Company     string    `json:"company"`
	CreatedAt   time.Time `json:"created_at"`
}

type Meta

type Meta struct {
	// Page contains metadata about current page
	Page Page `json:"page,omitempty"`
}

Meta contains that contains data that does not belong to Data, Errors, or Links.

type Method

type Method string
const (
	MethodPost   Method = http.MethodPost
	MethodGet    Method = http.MethodGet
	MethodPatch  Method = http.MethodPatch
	MethodDelete Method = http.MethodDelete
)

type Middleware

type Middleware func(http.HandlerFunc) http.HandlerFunc

type OIDCConfig

type OIDCConfig struct {
	OAuth2Config *oauth2.Config
	Verifier     *oidc.IDTokenVerifier
}

type Page

type Page struct {
	Cursor  string `json:"cursor,omitempty"`
	Limit   int    `json:"limit"`
	Count   int    `json:"count"`
	HasNext bool   `json:"has_next"`
}

func GetPageFromQuery

func GetPageFromQuery(r *http.Request) Page

type Position

type Position struct {
	ID          ULID   `json:"id"`
	RecruiterID ULID   `json:"recruiter_id,omitempty"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Company     string `json:"company"`
	IsActive    bool   `json:"is_active"`
}

type Provider

type Provider string
const (
	ProviderApple  Provider = "apple"
	ProviderGoogle Provider = "google"
	ProviderEmail  Provider = "email"
)

func StringToProvider

func StringToProvider(str string) (Provider, error)

func (Provider) ToString

func (p Provider) ToString() string

type Reaction

type Reaction struct {
	RecommendationID ULID         `json:"recommendation_id"`
	ReactorType      ReactorType  `json:"-"`
	ReactorID        ULID         `json:"-"`
	ReactionType     ReactionType `json:"reaction_type"`
	ReactedAt        time.Time    `json:"reacted_at"`
}

type ReactionType

type ReactionType string
const (
	ReactionTypePositive ReactionType = "positive"
	ReactionTypeNegative ReactionType = "negative"
	ReactionTypeNeutral  ReactionType = "neutral"
)

func (ReactionType) IsValid

func (r ReactionType) IsValid() bool

type ReactorType

type ReactorType string
const (
	ReactorTypeCandidate ReactorType = "candidate"
	ReactorTypeRecruiter ReactorType = "recruiter"
)

func (ReactorType) IsValid

func (r ReactorType) IsValid() bool

type Recommendation

type Recommendation struct {
	ID          ULID `json:"id"`
	PositionID  ULID `json:"position_id"`
	CandidateID ULID `json:"candidate_id"`
}

type RecommendationForCandidate

type RecommendationForCandidate struct {
	RecommendationID ULID   `json:"recommendation_id"`
	PositionID       ULID   `json:"position_id"`
	Title            string `json:"title"`
	Company          string `json:"company"`
	Description      string `json:"description"`
}

type RecommendationForRecruiter

type RecommendationForRecruiter struct {
	RecommendationID ULID   `json:"recommendation_id"`
	PositionID       ULID   `json:"position_id"`
	PositionTitle    string `json:"position_title"`
	CandidateID      ULID   `json:"candidate_id"`
	FullName         string `json:"full_name"`
	About            string `json:"about"`
}

type Recruiter

type Recruiter struct {
	ID     ULID `json:"id"`
	UserID ULID `json:"user_id"`
}

type RefreshToken

type RefreshToken struct {
	RefreshToken string `json:"refresh_token"`
	ExpiresIn    uint32 `json:"expires_in"`
	UserID       ULID   `json:"user_id"`
}

type RefreshTokenClaims

type RefreshTokenClaims struct {
	UserID   ULID
	Provider Provider
	JTI      ULID
}

type RequestBodyCreateCandidate

type RequestBodyCreateCandidate struct {
	About string `json:"about"`
}

type RequestBodyCreatePosition

type RequestBodyCreatePosition struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Company     string `json:"company"`
}

type RequestBodyCreateUser

type RequestBodyCreateUser struct {
	Email    string `json:"email"`
	Password string `json:"password"`
	FullName string `json:"full_name"`
}

type RequestBodyLoginViaEmail

type RequestBodyLoginViaEmail struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

type RequestBodyPatchCandidate

type RequestBodyPatchCandidate struct {
	About *string `json:"about"`
}

type RequestBodyPatchPosition

type RequestBodyPatchPosition struct {
	Title       *string `json:"title"`
	Description *string `json:"description"`
	Company     *string `json:"company"`
	IsActive    *bool   `json:"is_active"`
}

type RequestBodyPatchUser

type RequestBodyPatchUser struct {
	FullName *string `json:"full_name"`
	UserName *string `json:"user_name"`
}

type RequestBodyRefresh

type RequestBodyRefresh struct {
	GrantType    string `json:"grant_type"`
	RefreshToken string `json:"refresh_token"`
}

type RequestCreateReaction

type RequestCreateReaction struct {
	RecommendationID ULID         `json:"recommendation_id"`
	ReactionType     ReactionType `json:"reaction_type"`
}

type ResponseDataHealth

type ResponseDataHealth struct {
	Status string `json:"status"`
}

type ResponseWriter

type ResponseWriter struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

func (*ResponseWriter) WriteHeader

func (rw *ResponseWriter) WriteHeader(code int)

type Role

type Role string
const (
	RoleCandidate Role = "candidate"
	RoleRecruiter Role = "recruiter"
)

type Route

type Route string
const (

	// Misc
	RouteHealth Route = "/health"

	// Authentication and authorization
	RouteCallback         Route = "/callback"
	RouteLoginViaEmail    Route = "/login"
	RouteLoginViaProvider Route = "/login/{provider}"
	RouteRefresh          Route = "/refresh"

	// Resources and collections
	RouteCandidate       Route = "/candidate"
	RouteCandidates      Route = "/candidates"
	RouteMatches         Route = "/matches"
	RoutePosition        Route = "/positions/{id}"
	RoutePositions       Route = "/positions"
	RouteReactions       Route = "/reactions"
	RouteRecommendations Route = "/recommendations"
	RouteRecruiter       Route = "/recruiter"
	RouteRecruiters      Route = "/recruiters"
	RouteUser            Route = "/user"
	RouteUsers           Route = "/users"
)

type RouteConfig

type RouteConfig struct {
	Method      Method
	Handler     http.HandlerFunc
	Middlewares []Middleware
	Route       Route
	Roles       []Role
}

type Scope

type Scope string
const (
	ScopeRoleCandidate Scope = "role:candidate"
	ScopeRoleRecruiter Scope = "role:recruiter"
)

type StateTokenClaims

type StateTokenClaims struct {
	CSRF string `json:"csrf"`
}

type Store

type Store struct {
	DatabaseProvider DatabaseProvider
	DB               *sql.DB
}

func NewStore

func NewStore(c StoreConfig) (Store, error)

func (Store) ClearAll

func (s Store) ClearAll(ctx context.Context) error

func (Store) CreateCandidate

func (s Store) CreateCandidate(userID ULID, about string) (ULID, error)

func (Store) CreatePosition

func (s Store) CreatePosition(recruiterID ULID, title string, description string, company string, isActive bool) (ULID, error)

func (Store) CreateReaction

func (s Store) CreateReaction(recommendationID ULID, reactorType ReactorType, reactorID ULID, reactionType ReactionType) error

func (Store) CreateRecommendation

func (s Store) CreateRecommendation(positionID ULID, candidateID ULID) (ULID, error)

func (Store) CreateRecruiter

func (s Store) CreateRecruiter(userID ULID) (ULID, error)

func (Store) CreateRefreshToken

func (s Store) CreateRefreshToken(userID ULID) (jti ULID, err error)

func (Store) CreateUser

func (s Store) CreateUser(provider Provider, providerUserID string, email string, fullName string, userName string, passwordHash string) (ULID, error)

func (Store) DeleteCandidate

func (s Store) DeleteCandidate(candidateID ULID) error

func (Store) DeletePosition

func (s Store) DeletePosition(positionID ULID) error

func (Store) DeleteRecruiter

func (s Store) DeleteRecruiter(recruiterID ULID) error

func (Store) DeleteUser

func (s Store) DeleteUser(userID ULID) error

func (Store) FetchPendingEmbeddingsMetadata

func (s Store) FetchPendingEmbeddingsMetadata(limit uint16) ([]ULID, []string, error)

func (Store) GetCandidate

func (s Store) GetCandidate(candidateID ULID) (Candidate, error)

func (Store) GetCandidates

func (s Store) GetCandidates(limit uint16, recommendationSpan time.Duration) ([]ULID, error)

func (Store) GetMatchesByCandidateID

func (s Store) GetMatchesByCandidateID(candidateID ULID, page Page) (matches []Match, nextCursor ULID, err error)

func (Store) GetPosition

func (s Store) GetPosition(positionID ULID) (Position, error)

func (Store) GetPositions

func (s Store) GetPositions(recruiterID ULID, page Page) (positions []Position, nextCursor ULID, err error)

func (Store) GetPositionsForCandidateViaEmbeddings

func (s Store) GetPositionsForCandidateViaEmbeddings(candidateID ULID, topPositions uint16) ([]ULID, error)

func (Store) GetPositionsForCandidateViaFTS

func (s Store) GetPositionsForCandidateViaFTS(candidateID ULID, topPositions uint16) ([]ULID, error)

func (Store) GetReactionsByCandidateID

func (s Store) GetReactionsByCandidateID(candidateID ULID, page Page) (reactions []Reaction, nextCursor ULID, err error)

func (Store) GetRecommendation

func (s Store) GetRecommendation(recommendationID ULID) (Recommendation, error)

func (Store) GetRecommendationsForCandidate

func (s Store) GetRecommendationsForCandidate(candidateID ULID, page Page, excludeReacted bool) (recommendations []RecommendationForCandidate, nextCursor ULID, err error)

func (Store) GetRecommendationsForRecruiter

func (s Store) GetRecommendationsForRecruiter(recruiterID ULID, page Page, excludeReacted bool) (recommendations []RecommendationForRecruiter, nextCursor ULID, err error)

func (Store) GetRecruiter

func (s Store) GetRecruiter(recruiterID ULID) (Recruiter, error)

func (Store) GetUser

func (s Store) GetUser(userID ULID) (User, error)

func (Store) GetUserAndRoles

func (s Store) GetUserAndRoles(userID ULID) (User, map[Role]ULID, error)

func (Store) GetUserAndRolesByEmail

func (s Store) GetUserAndRolesByEmail(email string, provider Provider) (User, map[Role]ULID, error)

func (Store) GetUserIDAndRolesByProvider

func (s Store) GetUserIDAndRolesByProvider(provider Provider, providerUserID string) (ULID, map[Role]ULID, error)

func (Store) GetUserRoles

func (s Store) GetUserRoles(userID ULID, provider Provider) (map[Role]ULID, error)

func (Store) IsRevokedRefreshToken

func (s Store) IsRevokedRefreshToken(jti ULID) (bool, error)

func (Store) MarkEmbeddingsStatus

func (s Store) MarkEmbeddingsStatus(entityIDs []ULID, status EmbeddingStatus) error

func (Store) MarkEmbeddingsStatusTx

func (s Store) MarkEmbeddingsStatusTx(tx *sql.Tx, entityIDs []ULID, status EmbeddingStatus) error

func (Store) RecruiterExists

func (s Store) RecruiterExists(recruiterID ULID) (bool, error)

func (Store) UpdateCandidate

func (s Store) UpdateCandidate(
	candidateID ULID,
	newAbout string,
) error

func (Store) UpdateCandidateAndReturn

func (s Store) UpdateCandidateAndReturn(
	candidateID ULID,
	newAbout string,
) (Candidate, error)

func (Store) UpdatePosition

func (s Store) UpdatePosition(
	positionID ULID,
	newTitle string,
	newDescription string,
	newCompany string,
	isActive bool,
) error

func (Store) UpdateUser

func (s Store) UpdateUser(
	userID ULID,
	newFullName string,
	newUserName string,
) error

func (Store) UpdateUserAndReturn

func (s Store) UpdateUserAndReturn(
	userID ULID,
	newFullName string,
	newUserName string,
) (User, error)

func (Store) UpsertEmbeddingsTx

func (s Store) UpsertEmbeddingsTx(tx *sql.Tx, embeddingIDs []ULID, embeddings []EmbeddingEntity) error

func (Store) UserExistsByEmail

func (s Store) UserExistsByEmail(email string, provider Provider) (bool, error)

type StoreConfig

type StoreConfig struct {
	DatabaseProvider      DatabaseProvider
	PostgreSQLDatabaseURL string
}

type TokenPair

type TokenPair struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    uint32 `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Scope        string `json:"scope"`
	UserID       ULID   `json:"user_id"`
}

type ULID

type ULID string

func NewCandidateULID

func NewCandidateULID() (ULID, error)

func NewJTIULID

func NewJTIULID() (ULID, error)

func NewPositionULID

func NewPositionULID() (ULID, error)

func NewRecommendationULID

func NewRecommendationULID() (ULID, error)

func NewRecruiterULID

func NewRecruiterULID() (ULID, error)

func NewULID

func NewULID(prefix string) (ULID, error)

func NewUserULID

func NewUserULID() (ULID, error)

func Rerank

func Rerank(c AIConfig, candidateID ULID, positions []ULID) ([]ULID, error)

type User

type User struct {
	ID             ULID      `json:"id"`
	Provider       Provider  `json:"-"`
	ProviderUserID string    `json:"-"`
	Email          string    `json:"email"`
	FullName       string    `json:"full_name"`
	UserName       string    `json:"user_name"`
	PasswordHash   string    `json:"-"`
	UpdatedAt      time.Time `json:"updated_at"`
}

type Vault

type Vault struct {
	AccessTokenParser      paseto.Parser
	RefreshTokenParser     paseto.Parser
	StateTokenParser       paseto.Parser
	V4AsymmetricPublicKey  paseto.V4AsymmetricPublicKey
	V4AsymmetricSecretKey  paseto.V4AsymmetricSecretKey
	V4SymmetricKey         paseto.V4SymmetricKey
	UseGoogleSSO           bool
	UseAppleSSO            bool
	GoogleOIDCConfig       *OIDCConfig
	AppleOIDCConfig        *OIDCConfig
	RefreshTokenExpiration time.Duration
	AccessTokenExpiration  time.Duration
	StateTokenExpiration   time.Duration
	VerifierExpiration     time.Duration
}

func NewVault

func NewVault(ctx context.Context, c VaultConfig) (Vault, error)

func (Vault) CreateAccessToken

func (v Vault) CreateAccessToken(userID ULID, provider Provider, roles map[Role]ULID) (AccessToken, error)

func (Vault) CreateAuthCodeURL

func (v Vault) CreateAuthCodeURL(state string, verifier string, provider Provider) (string, error)

func (Vault) CreateRefreshToken

func (v Vault) CreateRefreshToken(userID ULID, provider Provider, jti ULID) (RefreshToken, error)

func (Vault) CreateStateToken

func (v Vault) CreateStateToken(provider Provider) (string, error)

func (Vault) CreateTokenPair

func (v Vault) CreateTokenPair(userID ULID, provider Provider, jti ULID, roles map[Role]ULID) (TokenPair, error)

func (Vault) ExchangeAppleCodeForIDToken

func (v Vault) ExchangeAppleCodeForIDToken(ctx context.Context, code string, verifierCookie *http.Cookie) (string, error)

func (Vault) ExchangeGoogleCodeForIDToken

func (v Vault) ExchangeGoogleCodeForIDToken(ctx context.Context, code string, verifierCookie *http.Cookie) (string, error)

func (Vault) ParseAccessToken

func (v Vault) ParseAccessToken(tokenString string) (AccessTokenClaims, error)

func (Vault) ParseRefreshToken

func (v Vault) ParseRefreshToken(tokenString string) (RefreshTokenClaims, error)

func (Vault) ParseStateToken

func (v Vault) ParseStateToken(raw string) (StateTokenClaims, error)

func (Vault) VerifyAndParseAppleIDToken

func (v Vault) VerifyAndParseAppleIDToken(ctx context.Context, rawIDToken string, userJSON string) (IDToken, error)

func (Vault) VerifyAndParseGoogleIDToken

func (v Vault) VerifyAndParseGoogleIDToken(ctx context.Context, rawIDToken string) (IDToken, error)

type VaultConfig

type VaultConfig struct {
	ServerBaseURL          string
	SymmetricKey           string
	AsymmetricKey          string
	UseGoogleSSO           bool
	UseAppleSSO            bool
	GoogleClientID         string
	GoogleClientSecret     string
	AppleClientID          string
	AppleClientSecret      string
	RefreshTokenExpiration time.Duration
	AccessTokenExpiration  time.Duration
}

Directories

Path Synopsis
cmd
hvcli command
hvserver command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL