### Complete Prefab Configuration Example Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md This example demonstrates a recommended pattern for initializing Prefab, including setting defaults, loading configuration files, validating required settings, and starting application components. ```go package main import ( "log" "time" "github.com/dpup/prefab" ) func main() { // 1. Set application defaults prefab.LoadConfigDefaults(map[string]interface{}{ "myapp.database.host": "localhost", "myapp.database.port": 5432, "myapp.cacheRefreshInterval": "5m", "myapp.maxRetries": 3, }) // 2. Load environment-specific config prefab.LoadConfigFile("./config.yaml") // 3. Create server with plugins s := prefab.New( prefab.WithPlugin(auth.Plugin()), ) // 4. Validate required configuration on startup validateConfig() // 5. Use config throughout your application startBackgroundJobs() // 6. Start the server if err := s.Start(); err != nil { log.Fatal(err) } } func validateConfig() { required := []string{ "myapp.database.host", "myapp.database.name", } for _, key := range required { if !prefab.ConfigExists(key) { log.Fatalf("Required configuration missing: %s", key) } } } func startBackgroundJobs() { interval := prefab.ConfigDuration("myapp.cacheRefreshInterval") go func() { ticker := time.NewTicker(interval) for range ticker.C { refreshCache() } }() } func refreshCache() { // Implementation... } ``` -------------------------------- ### Basic Prefab Server Setup Source: https://github.com/dpup/prefab/blob/main/docs/getting-started.md Create a minimal Prefab server, register your gRPC service and gateway, and start the server. Ensure your service implementation and proto definitions are correctly imported. ```go package main import ( "fmt" "github.com/dpup/prefab" "yourpackage/yourservice" ) func main() { // Create a new server with default options s := prefab.New() // Register your gRPC service and gateway s.RegisterService( &yourservice.YourService_ServiceDesc, yourservice.RegisterYourServiceHandler, &yourServiceImpl{}, ) // Start the server if err := s.Start(); err != nil { fmt.Println(err) } } ``` -------------------------------- ### Create and Start a Prefab Server Source: https://github.com/dpup/prefab/blob/main/docs/README.md This snippet demonstrates the common pattern for initializing a Prefab server with plugins and static files, registering a service, and starting the server. Ensure you have the necessary Go modules imported. ```go package main import ( "fmt" "github.com/dpup/prefab" "github.com/dpup/prefab/plugins/auth" "yourpackage/yourservice" ) func main() { // Create server with desired plugins and options s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithStaticFiles("/", "./static/"), ) // Register your service s.RegisterService( &yourservice.YourService_ServiceDesc, yourservice.RegisterYourServiceHandler, &serviceImpl{}, ) // Start the server if err := s.Start(); err != nil { fmt.Println(err) } } ``` -------------------------------- ### Run OAuth Server Example Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Command to run a complete OAuth server example. This example includes various OAuth flows and an interactive web interface for testing. ```bash go run ./examples/oauthserver ``` -------------------------------- ### Install PostgreSQL Driver Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/README.md Install the Go PostgreSQL driver using go get. ```bash go get github.com/lib/pq ``` -------------------------------- ### Environment Variable Configuration Example Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md An example of setting various Prefab and application configuration options using environment variables. ```bash # Prefab configuration export PF__SERVER__PORT=9000 export PF__AUTH__SIGNING_KEY=secret-key export PF__AUTH__GOOGLE__ID=google-client-id export PF__AUTH__GOOGLE__SECRET=google-client-secret # Your application configuration export PF__MYAPP__DATABASE__HOST=prod-db.example.com export PF__MYAPP__CACHE_REFRESH_INTERVAL=10m ``` -------------------------------- ### Basic Server Setup with Fake Auth Source: https://github.com/dpup/prefab/blob/main/plugins/auth/fakeauth/README.md Demonstrates how to initialize a server with both the standard auth plugin and the fake auth plugin. ```go s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(fake.Plugin()), ) ``` -------------------------------- ### Real-World Composition Example Source: https://github.com/dpup/prefab/blob/main/docs/authz.md A comprehensive example composing ownership, static, membership, and global roles for document authorization. ```go authz.WithRoleDescriber("document", authz.Compose( // Grant owner role if user owns the document authz.OwnershipRole(authz.RoleOwner, func(doc *Document) string { return doc.OwnerID }), // Grant viewer role if document is published authz.StaticRole(authz.RoleViewer, func(_ context.Context, _ auth.Identity, doc *Document) bool { return doc.Published }), // Grant workspace roles based on membership authz.MembershipRoles( func(doc *Document) string { return doc.WorkspaceID }, func(ctx context.Context, workspaceID string, identity auth.Identity) ([]authz.Role, error) { workspace, err := fetchWorkspace(ctx, workspaceID) if err != nil { return nil, err } return workspace.GetUserRoles(ctx, identity.Subject) }, ), // Grant admin role to superusers authz.GlobalRole(authz.RoleAdmin, func(ctx context.Context, identity auth.Identity, _ authz.Scope) (bool, error) { return db.IsSuperuser(ctx, identity.Subject) }), )) ``` -------------------------------- ### Initialize Server with Storage Plugin (SQLite) Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of initializing a Prefab server with the storage plugin using a SQLite database. ```go s := prefab.New( // or prefab.WithPlugin(storage.Plugin(sqlite.New("data.db"))), ) ``` -------------------------------- ### Start SSE Server Source: https://github.com/dpup/prefab/blob/main/examples/ssestream/README.md Run the main Go application to start the SSE server. This command initiates the server process that will handle SSE connections. ```bash go run examples/ssestream/main.go ``` -------------------------------- ### Start Prefab Server Source: https://github.com/dpup/prefab/blob/main/docs/getting-started.md Start the Prefab server. This method blocks execution until the server is shut down. Handle any potential errors during startup. ```go if err := s.Start(); err != nil { // Handle error } ``` -------------------------------- ### Initialize Prefab Server with Options Source: https://github.com/dpup/prefab/blob/main/docs/getting-started.md Customize server behavior by providing options during initialization. This example shows how to set up a root HTTP handler and serve static files. ```go s := prefab.New( prefab.WithHTTPHandler("/", http.HandlerFunc(homeHandler)), prefab.WithStaticFiles("/static/", "./static/"), // Other options as needed ) ``` -------------------------------- ### Initialize Server with Templates Plugin Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of initializing a Prefab server with the templates plugin, used for generating content like emails. ```go s := prefab.New( prefab.WithPlugin(templates.Plugin()), ) ``` -------------------------------- ### PostgreSQL Connection String Example Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/README.md Example of a standard PostgreSQL connection string format. ```plaintext postgres://postgres:secret@localhost:5432/myapp?sslmode=disable ``` -------------------------------- ### Initialize Server with Storage Plugin (In-Memory) Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of initializing a Prefab server with the storage plugin using an in-memory store. ```go s := prefab.New( prefab.WithPlugin(storage.Plugin(memstore.New())), ) ``` -------------------------------- ### Install Goose Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/migrations/README.md Install the Goose migration tool using the Go package manager. ```bash go install github.com/pressly/goose/v3/cmd/goose@latest ``` -------------------------------- ### Initialize Server with Email Plugin Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of initializing a Prefab server with the email plugin, which is required for features like magic link authentication. ```go s := prefab.New( prefab.WithPlugin(email.Plugin()), ) ``` -------------------------------- ### Initialize Server with Storage and Auth Plugins Source: https://github.com/dpup/prefab/blob/main/README.md Use this to initialize the server with the storage plugin and the auth plugin. This setup is necessary for persisting blocked tokens. ```go s := prefab.New( ... prefab.WithPlugin(storage.Plugin(store)), prefab.WithPlugin(auth.Plugin()), ... ) ``` -------------------------------- ### Configuration Validation Failure Example Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Example error message when critical configuration values fail validation at server startup. ```text Configuration validation failed: - server.port: must be between 1 and 65535, got: 70000 - auth.expiration: must be positive, got: -1h Fix these errors in prefab.yaml or environment variables and try again. ``` -------------------------------- ### Initialize Server with Authentication Plugins Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of initializing a Prefab server with the core authentication plugin and specific providers like Google OAuth. Includes optional storage for token persistence. ```go s := prefab.New( prefab.WithPlugin(auth.Plugin()), // Add at least one authentication provider prefab.WithPlugin(google.Plugin()), // Optional storage for token persistence prefab.WithPlugin(storage.Plugin(sqlite.New("auth.db"))), ) ``` -------------------------------- ### Install PostgreSQL on macOS with Homebrew Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Installs PostgreSQL using Homebrew on macOS. ```bash # macOS with Homebrew brew install postgresql ``` -------------------------------- ### Start PostgreSQL Docker Container Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Use this command to start a PostgreSQL container for testing purposes. Ensure the ports and environment variables are set correctly. ```bash # Start a PostgreSQL container docker run --name prefab-postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=prefab -p 5432:5432 -d postgres:14 # Verify it's running docker ps ``` -------------------------------- ### Start gRPC Server with JSON REST Source: https://github.com/dpup/prefab/blob/main/README.md This snippet initializes a Prefab server, registers a gRPC service along with its handler and implementation, and starts the server. It's suitable for projects needing both gRPC and JSON REST endpoints. ```go package main import ( "fmt" "github.com/dpup/prefab" ) func main() { s := prefab.New() s.RegisterService( &FooBar_ServiceDesc, RegisterFooBarHandler, &foobarImpl{}, ) if err := s.Start(); err != nil { fmt.Println(err) } } ``` -------------------------------- ### Custom Configuration Validation Example Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Implement custom validation logic after loading configuration, checking existence and using validators. ```go func main() { // Validate your config after loading if !prefab.ConfigExists("myapp.apiKey") { panic("myapp.apiKey is required") } port := prefab.ConfigInt("myapp.database.port") if err := prefab.ValidatePort(port); err != nil { panic(fmt.Sprintf("myapp.database.port: %v", err)) } // Now create the server s := prefab.New() // ... } ``` -------------------------------- ### Complex Authorization Setup with Builder Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Use this pattern for intricate authorization setups requiring custom policies, role hierarchies, and object fetching logic. It allows for fine-grained control over access. ```go builder := authz.NewBuilder(). WithPolicy(authz.Allow, roleUser, authz.Action("documents.view")), WithPolicy(authz.Allow, roleOwner, authz.Action("documents.edit")), WithPolicy(authz.Allow, roleAdmin, authz.Action("*")), WithRoleHierarchy(roleAdmin, roleEditor, roleViewer, roleUser), WithObjectFetcher("document", authz.AsObjectFetcher( authz.Fetcher(db.GetDocumentByID), )). WithRoleDescriber("document", authz.Compose( authz.OwnershipRole(roleOwner, func(doc *Document) string { return doc.OwnerID }), )) s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(builder.Build()), ) ``` -------------------------------- ### Install PostgreSQL on Ubuntu/Debian Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Installs PostgreSQL using the system package manager on Ubuntu or Debian-based systems. ```bash # Ubuntu/Debian sudo apt-get install postgresql ``` -------------------------------- ### Quick Start: Initialize OAuth2 Plugin Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md This snippet shows the minimal configuration required to set up the OAuth2 plugin with a client and integrate it into a Prefab server. It requires the auth plugin to be present for user authentication. ```go import ( "github.com/dpup/prefab" "github.com/dpup/prefab/plugins/auth" "github.com/dpup/prefab/plugins/oauth" ) oauthPlugin := oauth.NewBuilder(). WithClient(oauth.Client{ ID: "my-app", Secret: "secret-key", Name: "My Application", RedirectURIs: []string{"https://myapp.com/callback"}, Scopes: []string{"read", "write"}, }). Build() server := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(oauthPlugin), ) ``` -------------------------------- ### Add Prefab to Go Project Source: https://github.com/dpup/prefab/blob/main/docs/quickstart.md Add the Prefab library to your Go project using go get. ```bash go get github.com/dpup/prefab ``` -------------------------------- ### Real-World Composition Example for Org Fetcher Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Demonstrates a complex composition of fetchers for 'org' resources, including cache, validated database fetch, and a remote API fallback. ```go authz.WithObjectFetcher("org", authz.AsObjectFetcher( authz.ComposeFetchers( // Try cache first authz.MapFetcher(cache), // Then validated database fetch authz.ValidatedFetcher( authz.Fetcher(db.GetOrgByID), func(org *Org) error { if org.Deleted { return errors.NewC("org deleted", codes.NotFound) } return nil }, ), // Finally try remote API authz.Fetcher(api.FetchOrg), ), )) ``` -------------------------------- ### Complete Authorization Setup in Go Source: https://github.com/dpup/prefab/blob/main/docs/authz.md This snippet shows how to initialize the prefab server with the authorization plugin, including defining roles, policies, role hierarchy, object fetching, and role description logic. It's used for setting up comprehensive access control rules. ```go package main import ( "context" "github.com/dpup/prefab" "github.com/dpup/prefab/plugins/auth" "github.com/dpup/prefab/plugins/authz" ) // Define roles const ( roleUser = authz.Role("user") roleOwner = authz.Role("owner") roleAdmin = authz.Role("admin") ) // Define your domain object type Document struct { ID string WorkspaceID string OwnerID string Published bool } func (d *Document) AuthzType() string { return "document" } func (d *Document) ScopeID() string { return d.WorkspaceID } func main() { s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(authz.Plugin( // Define policies authz.WithPolicy(authz.Allow, roleUser, authz.Action("documents.view")), authz.WithPolicy(authz.Allow, roleOwner, authz.Action("documents.edit")), authz.WithPolicy(authz.Allow, roleAdmin, authz.Action("*")), // Role hierarchy authz.WithRoleHierarchy(roleAdmin, roleOwner, roleUser), // Object fetcher authz.WithObjectFetcher("document", authz.AsObjectFetcher( authz.Fetcher(getDocument), )), // Role describer authz.WithRoleDescriber("document", authz.Compose( authz.OwnershipRole(roleOwner, func(doc *Document) string { return doc.OwnerID }), authz.StaticRole(roleUser, func(_ context.Context, _ auth.Identity, doc *Document) bool { return doc.Published }), )), )), ) // Register your service s.RegisterService(...) s.Start() } func getDocument(ctx context.Context, id string) (*Document, error) { // Fetch from database return db.GetDocumentByID(ctx, id) } ``` -------------------------------- ### Common CRUD Authorization Setup with Builder Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Define standard policies for common CRUD operations using predefined actions like `ActionRead`, `ActionCreate`, etc. This simplifies setup for typical applications. ```go builder := authz.NewBuilder(). // CRUD policies WithPolicy(authz.Allow, authz.RoleViewer, authz.ActionRead). WithPolicy(authz.Allow, authz.RoleEditor, authz.ActionCreate). WithPolicy(authz.Allow, authz.RoleEditor, authz.ActionRead). WithPolicy(authz.Allow, authz.RoleEditor, authz.ActionUpdate). WithPolicy(authz.Allow, authz.RoleAdmin, authz.ActionDelete). WithPolicy(authz.Allow, authz.RoleAdmin, authz.Action("*")). // Object fetcher and role describer WithObjectFetcher("document", authz.AsObjectFetcher( authz.Fetcher(db.GetDocumentByID), )). WithRoleDescriber("document", authz.Compose( authz.OwnershipRole(authz.RoleOwner, func(doc *Document) string { return doc.OwnerID }), )) s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(builder.Build()), ) ``` -------------------------------- ### New PostgreSQL Store (Manual Setup) Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/README.md Instantiate a new PostgreSQL store with automatic table creation disabled, intended for production environments using migrations. ```go store := postgres.New( "postgres://user:password@localhost/dbname?sslmode=disable", postgres.WithAutoCreateTables(false), ) ``` -------------------------------- ### Structured Logging Example Source: https://github.com/dpup/prefab/blob/main/docs/logging.md Use structured logging with `*w` variants for easier log aggregation and querying. Avoid formatted strings which hinder log analysis. ```go // Good: Structured fields logging.Infow(ctx, "Order created", "orderID", order.ID, "userID", user.ID, "amount", order.Total, "items", len(order.Items), ) // Avoid: Formatted strings make it harder to query logs logging.Infof(ctx, "Order %s created for user %s with %d items totaling $%.2f", order.ID, user.ID, len(order.Items), order.Total) ``` -------------------------------- ### Complete Proto Example with Authorization Annotations Source: https://github.com/dpup/prefab/blob/main/docs/authz.md A comprehensive proto file demonstrating the use of authorization annotations for RPC methods and request fields, including HTTP mapping. ```protobuf syntax = "proto3"; package docservice; import "google/api/annotations.proto"; import "plugins/authz/authz.proto"; service DocumentService { rpc ListDocuments(ListDocumentsRequest) returns (ListDocumentsResponse) { option (prefab.authz.action) = "documents.list"; option (prefab.authz.resource) = "workspace"; option (google.api.http) = { get: "/api/workspaces/{workspace_id}/documents" }; } rpc GetDocument(GetDocumentRequest) returns (GetDocumentResponse) { option (prefab.authz.action) = "documents.view"; option (prefab.authz.resource) = "document"; option (prefab.authz.default_effect) = "deny"; option (google.api.http) = { get: "/api/workspaces/{workspace_id}/documents/{document_id}" }; } rpc UpdateDocument(UpdateDocumentRequest) returns (UpdateDocumentResponse) { option (prefab.authz.action) = "documents.update"; option (prefab.authz.resource) = "document"; option (prefab.authz.default_effect) = "deny"; option (google.api.http) = { put: "/api/workspaces/{workspace_id}/documents/{document_id}" body: "*" }; } } message ListDocumentsRequest { string workspace_id = 1 [(prefab.authz.id) = true]; } message GetDocumentRequest { string workspace_id = 1 [(prefab.authz.scope) = true]; string document_id = 2 [(prefab.authz.id) = true]; } message UpdateDocumentRequest { string workspace_id = 1 [(prefab.authz.scope) = true]; string document_id = 2 [(prefab.authz.id) = true]; string title = 3; string content = 4; } ``` -------------------------------- ### Generate gRPC and Gateway Code Source: https://github.com/dpup/prefab/blob/main/docs/quickstart.md Install protoc plugins and generate Go code for gRPC and the gRPC-Gateway. ```bash # Install protoc plugins (if not already installed) go install google.golang.org/protobuf/cmd/protoc-gen-go go install google.golang.org/grpc/cmd/protoc-gen-go-grpc go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway # Generate code protoc -I./proto \ -I./proto/third_party/googleapis \ --go_out=. \ --go-grpc_out=. \ --grpc-gateway_out=. \ ./proto/helloservice/helloservice.proto ``` -------------------------------- ### Production Setup with PostgreSQL Migrations Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/migrations/README.md Configure the PostgreSQL store for production use, disabling auto-table creation and specifying custom schema and table prefix. ```go // Production setup using migrations store := postgres.New( "postgres://user:password@localhost/dbname?sslmode=disable", postgres.WithSchema("prefab"), postgres.WithPrefix("myapp_"), postgres.WithAutoCreateTables(false), // Disable auto-creation ) ``` -------------------------------- ### Load Configuration Defaults and Files Before Server Creation Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Load application defaults and configuration files before initializing the Prefab server. This ensures that custom settings are applied before the server starts. ```go // Load config first prefab.LoadConfigDefaults(map[string]interface{}{ "myapp.setting": "value", }) prefab.LoadConfigFile("./app.yaml") // Then create server s := prefab.New() // Config is available value := prefab.ConfigString("myapp.setting") ``` -------------------------------- ### Complete Handler Example with Contextual Logging Source: https://github.com/dpup/prefab/blob/main/docs/logging.md Demonstrates how to implement context-aware and structured logging within a Go handler function. It includes tracking request-specific fields, validating input, handling errors, and logging success messages. ```go func (s *orderService) CreateOrder(ctx context.Context, req *CreateOrderRequest) (*Order, error) { // Track request-specific fields logging.Track(ctx, "userID", req.UserId) logging.Infow(ctx, "Creating order", "items", len(req.Items)) // Validate if err := s.validator.Validate(req); err != nil { logging.Warnw(ctx, "Validation failed", "error", err) return nil, errors.NewC(codes.InvalidArgument, err) } // Create order order, err := s.db.CreateOrder(ctx, req) if err != nil { logging.Errorw(ctx, "Failed to create order", "error", err) return nil, err } // Track the created order logging.Track(ctx, "orderID", order.Id) logging.Infow(ctx, "Order created successfully", "total", order.Total) return order, nil } ``` -------------------------------- ### Example Role Hierarchy with Policies in Go Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Demonstrates how to combine role hierarchy with specific policies to manage permissions. Users with higher roles inherit permissions from lower roles, as shown in the comments. ```go authz.Plugin( authz.WithPolicy(authz.Allow, authz.RoleViewer, authz.Action("documents.view")), authz.WithPolicy(authz.Allow, authz.RoleEditor, authz.Action("documents.edit")), authz.WithRoleHierarchy(authz.RoleAdmin, authz.RoleEditor, authz.RoleViewer), ) // User with "editor" role can: // - documents.view (inherited from viewer) // - documents.edit (direct permission) // User with "admin" role can: // - documents.view (inherited from editor → viewer) // - documents.edit (inherited from editor) ``` -------------------------------- ### Setting up Authz Plugin with Policies and Fetchers Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Configure the Authz plugin by defining roles, policies (allow/deny), and registering object fetchers and role describers. This setup is crucial for the plugin to enforce access control rules. ```go import ( "github.com/dpup/prefab" "github.com/dpup/prefab/plugins/auth" "github.com/dpup/prefab/plugins/authz" ) // 1. Define roles const ( roleUser = authz.Role("user") roleOwner = authz.Role("owner") roleAdmin = authz.Role("admin") ) // 2. Set up authorization plugin s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(authz.Plugin( // Define policies (effect, role, action) authz.WithPolicy(authz.Allow, roleUser, authz.Action("documents.view")), authz.WithPolicy(authz.Allow, roleOwner, authz.Action("documents.edit")), authz.WithPolicy(authz.Allow, roleAdmin, authz.Action("*")), // Register object fetcher authz.WithObjectFetcher("document", authz.AsObjectFetcher( authz.Fetcher(db.GetDocumentByID), )), // Register role describer authz.WithRoleDescriber("document", authz.Compose( authz.OwnershipRole(roleOwner, func(doc *Document) string { return doc.OwnerID }), )), )), ) ``` -------------------------------- ### Register Custom Plugin Configuration Keys Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Example of registering configuration keys for a custom plugin in an init() function. This enables typo detection and validation by Prefab. ```go package myplugin import "github.com/dpup/prefab" const PluginName = "myplugin" func init() { prefab.RegisterConfigKeys( prefab.ConfigKeyInfo{ Key: "myplugin.apiKey", Description: "API key for external service", Type: "string", }, prefab.ConfigKeyInfo{ Key: "myplugin.timeout", Description: "Request timeout", Type: "duration", }, ) } func Plugin() *MyPlugin { return &MyPlugin{ apiKey: prefab.ConfigString("myplugin.apiKey"), timeout: prefab.ConfigDuration("myplugin.timeout"), } } ``` -------------------------------- ### MapFetcher - Static Maps Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Useful for tests, examples, or small static datasets. It creates a fetcher from a map of string IDs to objects. ```go staticDocuments := map[string]*Document{ "1": {ID: "1", Title: "Doc 1"}, "2": {ID: "2", Title: "Doc 2"}, } authz.MapFetcher(staticDocuments) ``` -------------------------------- ### Logging Scopes Example Source: https://github.com/dpup/prefab/blob/main/docs/logging.md Create logging scopes using `logging.With` and `logging.Named` for better organization, particularly within loops. Logs within a scope will automatically include the scope's identifier. ```go func processUsers(ctx context.Context, users []*User) { for _, u := range users { // Create a named scope for each user userCtx := logging.With(ctx, logging.FromContext(ctx).Named(u.ID)) processUser(userCtx, u) } } func processUser(ctx context.Context, user *User) { // All logs in this scope will include the user ID logging.Info(ctx, "Processing user") // Logs: "user123: Processing user" } ``` -------------------------------- ### Common Make Commands for Prefab Source: https://github.com/dpup/prefab/blob/main/CONTRIBUTING.md These commands are frequently used for testing, linting, code generation, and building tools within the Prefab project. Ensure you have Make installed. ```sh make test # staticcheck + go vet + unit tests make lint # golangci-lint make fix # golangci-lint --fix make gen-proto # regenerate protobuf code (requires protoc) make tools # build the pinned code-generation tools ``` ```sh go test ./path/to/package -run TestName ``` ```sh make test-coverage # human-readable summary make test-coverage TARGET=./plugins/auth make test-coverage PORCELAIN=1 # machine-readable ``` -------------------------------- ### Environment Variable Mapping Example Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Demonstrates the correct way to set environment variables for camelCase and all-lowercase keys in YAML. Using snake_case in YAML is recommended for consistency with environment variable naming. ```bash # If your YAML has camelCase keys: # myapp: # maxRetries: 3 # Then the env var needs underscores before capitals: export PF__MYAPP__MAX_RETRIES=5 # Correct - maps to myapp.maxRetries # If your YAML has all lowercase: # myapp: # maxretries: 3 # Then the env var has no underscores: export PF__MYAPP__MAXRETRIES=5 # Correct - maps to myapp.maxretries ``` ```yaml # Recommended - snake_case in YAML myapp: max_retries: 3 # Becomes maxRetries internally # Env var: PF__MYAPP__MAX_RETRIES # This way the YAML structure matches the env var structure ``` -------------------------------- ### Run PostgreSQL Storage Tests with Custom DSN Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Runs the PostgreSQL storage tests with a custom DSN, suitable for local installations requiring username and password authentication. Adjust 'username', 'password', and 'prefab' as needed. ```bash # For a custom installation with password authentication PG_TEST_DSN="postgres://username:password@localhost:5432/prefab?sslmode=disable" go test -v ./plugins/storage/postgres ``` -------------------------------- ### Initialize Server with Authorization Plugin Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Demonstrates initializing a Prefab server with authentication, password authentication, and the authorization plugin. Shows how to define policies, object fetchers, and role describers. ```go s := prefab.New( prefab.WithPlugin(auth.Plugin()), prefab.WithPlugin(pwdauth.Plugin(...)), prefab.WithPlugin(authz.Plugin( authz.WithPolicy(authz.Allow, roleUser, authz.Action("resources.read")), authz.WithObjectFetcher("resource", fetchResource), authz.WithRoleDescriber("*", roleDescriber), )), ) ``` -------------------------------- ### Build and Run Prefab Server Source: https://github.com/dpup/prefab/blob/main/docs/quickstart.md Build your application using go build and run the executable. ```bash go build ./yourapp ``` -------------------------------- ### Initialize Prefab with PostgreSQL Store Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/README.md Demonstrates initializing the Prefab application with the PostgreSQL storage plugin, including options for table prefix, schema, and auto-table creation. Shows both panic-on-error and safe error handling connection methods. ```go import ( "github.com/dpup/prefab" "github.com/dpup/prefab/plugins/storage" "github.com/dpup/prefab/plugins/storage/postgres" ) func main() { // Option 1: Simple connection with panic on error (for applications) store := postgres.New( "postgres://user:password@localhost/dbname?sslmode=disable", postgres.WithPrefix("prefab_"), postgres.WithSchema("myapp"), postgres.WithAutoCreateTables(false), // Use migrations in production ) // Option 2: Error handling connection (for tests or more controlled environments) store, err := postgres.SafeNew( "postgres://user:password@localhost/dbname?sslmode=disable", postgres.WithPrefix("prefab_"), postgres.WithSchema("myapp"), ) if err != nil { // Handle connection error log.Fatalf("Failed to connect to PostgreSQL: %v", err) } // Register the storage plugin s := prefab.New( prefab.WithPlugin(storage.Plugin(store)), // Other plugins... ) // Start your server if err := s.Start(); err != nil { panic(err) } } ``` -------------------------------- ### Create Prefab Server and Register Service Source: https://github.com/dpup/prefab/blob/main/docs/quickstart.md Set up the main Prefab server, configure its port, and register your gRPC service and its handler. ```go // main.go package main import ( "fmt" "log" "github.com/dpup/prefab" "yourmodule/helloservice" ) func main() { // Create a new Prefab server server := prefab.New( prefab.WithPort(8080), ) // Register the service and gateway server.RegisterService( &helloservice.HelloService_ServiceDesc, helloservice.RegisterHelloServiceHandler, helloservice.NewServer(), ) // Start the server fmt.Println("Server starting on :8080") if err := server.Start(); err != nil { log.Fatalf("Failed to start server: %v", err) } } ``` -------------------------------- ### Inactive Token Introspection Response Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Example JSON response when a token is inactive after introspection. ```json { "active": false } ``` -------------------------------- ### Active Token Introspection Response Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Example JSON response when a token is active after introspection. ```json { "active": true, "client_id": "my-app", "scope": "read write", "sub": "user123", "exp": 1234567890, "iat": 1234564290, "token_type": "Bearer", "iss": "https://api.example.com" } ``` -------------------------------- ### Example Structured Log Output Source: https://github.com/dpup/prefab/blob/main/docs/authz.md This JSON object demonstrates the fields included in structured authorization logs for debugging. ```json { "authz.action": "documents.write", "authz.resource": "document", "authz.objectID": "doc-123", "authz.roles": ["editor", "suspended"], "authz.evaluated_policies": [ {"role": "editor", "effect": "ALLOW"}, {"role": "suspended", "effect": "DENY"} ], "authz.effect": "DENY", "authz.reason": "denied by policy" } ``` -------------------------------- ### ValidatedFetcher - Add Validation Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Wraps a fetcher with additional validation logic. This example checks if a document is soft-deleted or archived before returning it. ```go authz.ValidatedFetcher( authz.Fetcher(db.GetDocumentByID), func(doc *Document) error { if doc.Deleted { return errors.NewC("document deleted", codes.NotFound) } if doc.Archived { return errors.NewC("document archived", codes.PermissionDenied) } return nil }, ) ``` -------------------------------- ### Initialize Server with Custom Auth Blocklist Source: https://github.com/dpup/prefab/blob/main/README.md This demonstrates how to initialize the server with a custom blocklist implementation for the auth plugin. It requires a storage plugin to persist blocked tokens. ```go s := prefab.New( ... prefab.WithPlugin(auth.Plugin( auth.WithBlocklist(auth.NewBlocklist(store)), )), ... ) ``` -------------------------------- ### Define a Custom Prefab Plugin Source: https://github.com/dpup/prefab/blob/main/docs/plugins.md Illustrates the structure of a custom Prefab plugin, including implementing the Name, Deps, ServerOptions, and Init methods. Shows how to access other plugins via the registry. ```go type myPlugin struct { // Plugin state } // Required: Plugin name for dependency resolution func (p *myPlugin) Name() string { return "myplugin" } // Optional: Specify required dependencies func (p *myPlugin) Deps() []string { return []string{"anotherplugin"} } // Optional: Add server options func (p *myPlugin) ServerOptions() []prefab.ServerOption { return []prefab.ServerOption{ prefab.WithGRPCInterceptor(p.interceptor), } } // Optional: Initialize plugin func (p *myPlugin) Init(ctx context.Context, r *prefab.Registry) error { // Access other plugins via registry otherPlugin := r.Get("anotherplugin").(AnotherPlugin) return nil } // Add to server s := prefab.New( prefab.WithPlugin(&myPlugin{}), ) ``` -------------------------------- ### OAuth Server Metadata Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Access OAuth server metadata per RFC 8414 using the GET /.well-known/oauth-authorization-server endpoint. ```APIDOC ## GET /.well-known/oauth-authorization-server ### Description Exposes OAuth server metadata. ### Method GET ### Endpoint /.well-known/oauth-authorization-server ### Response #### Success Response (200) Response includes: - Endpoint URLs (authorization, token, revocation, introspection) - Supported grant types and response types - Supported authentication methods - Supported PKCE methods ### Request Example ```bash curl http://localhost:8000/.well-known/oauth-authorization-server ``` ``` -------------------------------- ### Conditional Inclusion for Test Builds Source: https://github.com/dpup/prefab/blob/main/plugins/auth/fakeauth/README.md Provides an example of using build tags to conditionally include the fake auth plugin only in test builds. ```go // +build test package main import ( "github.com/dpup/prefab/plugins/auth/fake" ) func setupTestServer() { // Include fake auth plugin for tests } ``` -------------------------------- ### Authorization Code Flow - Token Response Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Example JSON response received after a successful token exchange, containing `access_token`, `token_type`, `expires_in`, and `refresh_token`. ```json { "access_token": "ACCESS_TOKEN", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "REFRESH_TOKEN" } ``` -------------------------------- ### TransformKey - Key Transformation Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Transforms the resource ID before it is used by the underlying fetcher. This example converts string IDs to integer IDs for a database fetcher. ```go // Convert string IDs to int IDs authz.TransformKey( func(id string) int { return parseID(id) }, authz.Fetcher(db.GetDocumentByNumericID), ) ``` -------------------------------- ### Validate Configuration on Startup Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Implement checks on application startup to validate the existence and values of critical configuration settings, preventing runtime errors. ```go if !prefab.ConfigExists("myapp.requiredSetting") { log.Fatal("myapp.requiredSetting is required") } if prefab.ConfigInt("myapp.maxRetries") < 1 { log.Fatal("myapp.maxRetries must be at least 1") } ``` -------------------------------- ### Drop PostgreSQL Test Database Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Drops the 'prefab' database. Use this command to clean up the test database after finishing tests with a local PostgreSQL installation. ```bash dropdb prefab ``` -------------------------------- ### Loading Additional Configuration Files in Go Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Use `prefab.LoadConfigFile()` to load custom configuration files before initializing the server. This is useful for separating different types of configurations or for environment-specific settings. ```go prefab.LoadConfigFile("./config.yaml") prefab.LoadConfigFile("./secrets.yaml") // Can load multiple files s := prefab.New() ``` -------------------------------- ### Create OAuth Client Using Client Store in Go Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Shows how to access the OAuth client store directly to create a new client. This method is an alternative to using AddClient and requires obtaining the store from the OAuth plugin. ```go store := oauthPlugin.GetClientStore() store.CreateClient(ctx, &oauth.Client{...}) ``` -------------------------------- ### Add OAuth Client Dynamically in Go Source: https://github.com/dpup/prefab/blob/main/plugins/oauth/README.md Demonstrates how to add a new OAuth client at runtime by retrieving the OAuth plugin from the registry and calling the AddClient method. Ensure the OAuth plugin is registered before use. ```go // Get OAuth plugin from registry oauthPlugin := registry.Get(oauth.PluginName).(*oauth.OAuthPlugin) // Add client dynamically if err := oauthPlugin.AddClient(oauth.Client{ ID: "new-client", Secret: "new-secret", RedirectURIs: []string{"https://new.com/callback"}, Scopes: []string{"read"}, CreatedBy: "user123", }); err != nil { return err } ``` -------------------------------- ### Manual Scope Checking in Custom Role Describer Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Example of a custom role describer function that manually checks if the request scope matches the object's scope before returning roles. ```go func describeRoles(ctx context.Context, identity auth.Identity, object any, scope authz.Scope) ([]authz.Role, error) { doc := object.(*Document) // Check scope matches if string(scope) != doc.WorkspaceID { return []authz.Role{}, nil } // Return roles... return roles, nil } ``` -------------------------------- ### Load Configuration Defaults Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Use `LoadConfigDefaults` to ensure your application can run with minimal configuration by providing sensible default values. ```go prefab.LoadConfigDefaults(map[string]interface{}{ "myapp.timeout": "30s", "myapp.maxConnections": 100, }) ``` -------------------------------- ### Fetch Config and Update Form Action Source: https://github.com/dpup/prefab/blob/main/examples/pwdauth/static/index.html Fetches configuration from '/api/meta/config' to get a CSRF token and updates the form's action attribute. Handles potential network or response errors. ```javascript fetch('/api/meta/config').then(response => { if (!response.ok) { console.log('Error response', response) return; } return response.json().then(data => { console.log(data) const form = document.querySelector('form'); form.action = form.action + "?csrf-token=${data.csrfToken}"; document.querySelector('button').disabled = false; }); }).catch(error => { console.log('Error', error) }); ``` -------------------------------- ### Defining Framework and Custom Roles Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Illustrates the definition of both framework-provided roles (like admin, editor) and custom roles (like reviewer, contributor) as strings. ```go const ( // Framework-provided roles roleAdmin = authz.RoleAdmin // "admin" roleEditor = authz.RoleEditor // "editor" roleViewer = authz.RoleViewer // "viewer" roleOwner = authz.RoleOwner // "owner" // Custom roles reviewer = authz.Role("reviewer") contributor = authz.Role("contributor") moderator = authz.Role("moderator") ) ``` -------------------------------- ### Authentication Configuration Options Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Set up authentication with JWT signing keys, token expiration, and Google OAuth credentials. ```yaml auth: signingKey: my-signing-key # Used for JWT tokens expiration: 24h # Token expiration time # Google OAuth settings google: id: your-google-client-id secret: your-google-client-secret redirectURI: https://your-app.com/auth/google/callback ``` -------------------------------- ### Policy Evaluation Example: Deny Wins Source: https://github.com/dpup/prefab/blob/main/docs/authz.md Demonstrates the AWS IAM-style policy precedence where an explicit Deny policy for any of the user's roles overrides an Allow policy, resulting in access denial. ```go // User has roles: [editor, suspended] authz.WithPolicy(authz.Allow, roleEditor, authz.Action("documents.write")) authz.WithPolicy(authz.Deny, roleSuspended, authz.Action("*" ``` -------------------------------- ### Configure Server Logging Source: https://github.com/dpup/prefab/blob/main/docs/logging.md Configure logging when creating your server using the `WithLogger` option. Choose between production JSON logging or development console logging. ```go import ( "github.com/dpup/prefab" "github.com/dpup/prefab/logging" ) func main() { // Production JSON logging server := prefab.New( prefab.WithLogger(logging.NewProdLogger()), prefab.WithPort(8080), // other options... ) // Development console logging server := prefab.New( prefab.WithLogger(logging.NewDevLogger()), prefab.WithPort(8080), // other options... ) } ``` -------------------------------- ### Create PostgreSQL Test Database Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Creates a database named 'prefab' for testing. This can be done using the 'createdb' command or the 'psql' client. ```bash # Create database createdb prefab # Or using psql psql -c "CREATE DATABASE prefab;" ``` -------------------------------- ### Run PostgreSQL Storage Tests with Docker DSN Source: https://github.com/dpup/prefab/blob/main/plugins/storage/postgres/TESTING.md Executes the PostgreSQL storage tests using the provided DSN for a Docker-based PostgreSQL setup. The 'sslmode=disable' is often used for local testing environments. ```bash # For the Docker setup PG_TEST_DSN="postgres://postgres:postgres@localhost:5432/prefab?sslmode=disable" go test -v ./plugins/storage/postgres ``` -------------------------------- ### Define Application Configuration Defaults Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Set default values for your application's configuration before initializing the Prefab server. This ensures essential settings are always present. ```go prefab.LoadConfigDefaults(map[string]interface{}{ "myapp.database.host": "localhost", "myapp.database.port": 5432, "myapp.database.name": "myapp_dev", "myapp.cacheRefreshInterval": "5m", "myapp.maxRetries": 3, "myapp.enableFeatureX": false, }) s := prefab.New() ``` -------------------------------- ### Configure CSRF Protection in Proto RPC Source: https://github.com/dpup/prefab/blob/main/README.md Set CSRF protection mode for RPC methods using proto options. 'auto' is the default, disabling protection for GET, HEAD, and OPTIONS requests. ```proto rpc Get(Request) returns (Response) { option (csrf_mode) = "on"; option (google.api.http) = { get: "/get" }; } ``` -------------------------------- ### Require String Configuration Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Use ConfigMustString to ensure a configuration value is a non-empty string, panicking if not found. ```go // Require a non-empty string apiKey := prefab.ConfigMustString("myapp.apiKey", "Set PF__MYAPP__API_KEY environment variable") ``` -------------------------------- ### Load Application Configuration from YAML Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Create a YAML file to store your application-specific configuration and load it using `prefab.LoadConfigFile()`. This allows for environment-specific settings. ```yaml # config.yaml or prefab.yaml server: port: 8080 # Your application configuration myapp: database: host: localhost port: 5432 name: myapp_production cacheRefreshInterval: 10m maxRetries: 5 enableFeatureX: true features: experimental: false betaAccess: true ``` ```go prefab.LoadConfigFile("./config.yaml") s := prefab.New() // Config is available dbHost := prefab.ConfigString("myapp.database.host") ``` -------------------------------- ### Prefab Configuration Access Methods Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Prefab offers a variety of helper functions to access configuration values as strings, integers, floats, booleans, durations, byte slices, and string maps. It also provides methods to check for key existence and retrieve all configuration. ```go // String values apiKey := prefab.ConfigString("myapp.apiKey") // Numeric values port := prefab.ConfigInt("myapp.port") timeout := prefab.ConfigInt64("myapp.timeout") ratio := prefab.ConfigFloat64("myapp.ratio") // Boolean values enabled := prefab.ConfigBool("myapp.featureEnabled") // Duration values (parses "5m", "1h", "30s", etc.) interval := prefab.ConfigDuration("myapp.refreshInterval") // Or require a duration (panics if missing or invalid) required := prefab.ConfigMustDuration("myapp.required") // String slices hosts := prefab.ConfigStrings("myapp.allowedHosts") // Byte slices secret := prefab.ConfigBytes("myapp.secret") // Maps and nested structures dbConfig := prefab.ConfigStringMap("myapp.database") // Check if a key exists if prefab.ConfigExists("myapp.optionalFeature") { // ... } // Get all config as a map allConfig := prefab.ConfigAll() // Advanced: Access the underlying koanf instance directly prefab.Config.Unmarshal("myapp.database", &myDatabaseConfig) ``` -------------------------------- ### Configure Prefab with Functional Options Source: https://github.com/dpup/prefab/blob/main/docs/configuration.md Use functional options with prefab.New() for programmatic configuration. These options override all other configuration sources and are ideal for testing scenarios or values that should never come from config files. ```go s := prefab.New( prefab.WithHost("0.0.0.0"), prefab.WithPort(8080), prefab.WithHTTPHandler("/custom", myHandler), prefab.WithStaticFiles("/static/", "./static/"), prefab.WithPlugin(myPlugin), ) ```