@pressbuddy/site-control
v1.0.1
Published
Drop-in React/Next.js package for site status control based on active plan status
Maintainers
Readme
@pressbuddy/site-control
A drop-in React/Next.js package for controlling site access based on active plan status. Provides seamless integration for checking site subscription status and conditionally rendering content.
Features
- 🚀 Drop-in integration for React and Next.js applications
- 🔒 Zero secrets - uses only public API endpoints
- ⚡ Smart caching with configurable TTL and background refresh
- 🎨 Customizable UI with built-in Material Design components
- 🛡️ Fail-safe - configurable behavior when API is unavailable
- 📱 SSR/SSG support - works with all Next.js rendering methods
- 🎯 TypeScript - fully typed for better developer experience
- 🪶 Lightweight - tree-shakeable with minimal dependencies
Installation
npm install @pressbuddy/site-controlPeer Dependencies
For React projects:
npm install react react-domFor Material-UI styling (optional):
npm install @mui/material @emotion/react @emotion/styledFor Next.js projects:
npm install nextQuick Start
1. Initialize Site Control
import { initSiteControl } from '@pressbuddy/site-control';
// Initialize once in your app (e.g., in _app.tsx or main.tsx)
initSiteControl({
baseUrl: 'https://api.yourserver.com',
siteId: 'your-site-id',
domain: 'example.com', // optional, defaults to window.location.hostname
mode: 'flag', // 'flag' for basic check, 'check' for detailed info
ttlMs: 60000, // Cache for 1 minute
failBehavior: 'fail-open', // Allow access if API fails
});2. Use in React Components
import { SiteGuard } from '@pressbuddy/site-control';
function App() {
return (
<SiteGuard>
<YourAppContent />
</SiteGuard>
);
}3. Custom Hook Usage
import { useSiteStatus } from '@pressbuddy/site-control';
function MyComponent() {
const { loading, active, details, refresh } = useSiteStatus();
if (loading) return <div>Checking site status...</div>;
if (!active) return <div>Site is inactive</div>;
return <div>Site is active! Plan: {details?.planName}</div>;
}API Reference
Core Functions
initSiteControl(options)
Initialize the global site control configuration.
interface InitOptions {
baseUrl: string; // API server URL
siteId: string; // Your site identifier
domain?: string; // Domain (auto-detected if not provided)
mode?: 'flag' | 'check'; // API mode (default: 'flag')
checkPath?: string; // Custom API endpoint path
ttlMs?: number; // Cache TTL (default: 60000)
failBehavior?: 'fail-open' | 'fail-closed'; // Error handling (default: 'fail-open')
renderStrategy?: 'package-view' | 'server-html'; // Render strategy (default: 'package-view')
inactiveHtmlEndpoint?: string; // Server HTML endpoint (for server-html strategy)
onStatus?: (status: SiteStatus) => void; // Status change callback
}fetchSiteStatus(options)
Manually fetch site status.
const status = await fetchSiteStatus({
siteId: 'your-site-id',
domain: 'example.com', // optional
});
console.log(status.active); // boolean
console.log(status.details); // additional info if mode: 'check'React Components
<SiteGuard>
Wrapper component that conditionally renders content based on site status.
<SiteGuard
loadingFallback={<CustomLoader />}
activeFallback={<CustomInactiveView />}
>
<YourApp />
</SiteGuard>Props:
children: Content to render when site is activeloadingFallback?: Custom loading componentactiveFallback?: Custom inactive state component
<InactiveView>
Default inactive state component with Material Design styling.
<InactiveView
title="Custom Title"
subtitle="Custom message"
ctaLabel="Contact Us"
ctaHref="/contact"
details={siteDetails}
/>React Hooks
useSiteStatus()
Hook for accessing site status in components.
const { loading, active, details, refresh } = useSiteStatus();Returns:
loading: boolean - Whether status check is in progressactive: boolean | null - Site active statusdetails: object - Additional site information (if available)refresh: function - Manually refresh status
Next.js Integration
Middleware
Protect routes with middleware:
// middleware.ts
import { nextMiddlewareGuard } from '@pressbuddy/site-control/next';
export default nextMiddlewareGuard({
siteId: 'your-site-id',
baseUrl: 'https://api.yourserver.com',
redirectUrl: '/inactive',
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|inactive).*)'],
};Pages Router (getServerSideProps)
import { withSiteGuardGSSP } from '@pressbuddy/site-control/next';
export const getServerSideProps = withSiteGuardGSSP(
async (context) => {
// Your original getServerSideProps logic
return { props: { data: 'your-data' } };
},
{
siteId: 'your-site-id',
baseUrl: 'https://api.yourserver.com',
inactiveReturn: 'notFound', // or 'redirect' or 'props'
}
);App Router
// app/page.tsx
import { appRouterServerCheck } from '@pressbuddy/site-control/next';
import { redirect, notFound } from 'next/navigation';
export default async function Page() {
const { active } = await appRouterServerCheck({
siteId: 'your-site-id',
baseUrl: 'https://api.yourserver.com',
});
if (!active) {
notFound(); // or redirect('/inactive')
}
return <YourPageContent />;
}Advanced Usage
Custom Inactive View
import { SiteGuard, useSiteStatus } from '@pressbuddy/site-control';
function CustomInactiveView() {
const { details } = useSiteStatus();
return (
<div className="custom-inactive">
<h1>Subscription Required</h1>
<p>Plan: {details?.planName}</p>
<p>Expired: {details?.planExpiry}</p>
<button onClick={() => window.location.href = '/billing'}>
Renew Subscription
</button>
</div>
);
}
function App() {
return (
<SiteGuard activeFallback={<CustomInactiveView />}>
<YourApp />
</SiteGuard>
);
}Server HTML Strategy
initSiteControl({
baseUrl: 'https://api.yourserver.com',
siteId: 'your-site-id',
renderStrategy: 'server-html',
inactiveHtmlEndpoint: '/api/site/inactive-html',
});This will fetch and render HTML from your server when the site is inactive.
Error Handling
import { fetchSiteStatus } from '@pressbuddy/site-control';
try {
const status = await fetchSiteStatus({ siteId: 'your-site-id' });
console.log('Site is active:', status.active);
} catch (error) {
if (error.type === 'NETWORK_ERROR') {
console.log('Network issue:', error.message);
} else if (error.type === 'TIMEOUT_ERROR') {
console.log('Request timed out');
}
}Cache Management
import { clearCachedStatus } from '@pressbuddy/site-control';
// Clear cache for a specific site
clearCachedStatus('your-site-id', 'example.com');
// Force refresh
const { refresh } = useSiteStatus();
await refresh();Configuration Options
API Modes
flag(default): Minimal endpoint that returns only active statuscheck: Detailed endpoint that returns additional site information
Fail Behaviors
fail-open(default): Allow access when API is unavailablefail-closed: Block access when API is unavailable
Render Strategies
package-view(default): Use built-in inactive componentsserver-html: Fetch and render HTML from server endpoint
TypeScript Support
The package is fully typed with TypeScript. All interfaces are exported:
import type {
SiteStatus,
InitOptions,
SiteGuardProps,
UseSiteStatusResult
} from '@pressbuddy/site-control';Browser Support
- Modern browsers (ES2020+)
- React 16.8+ (hooks support)
- Next.js 12+ (for Next.js features)
Security Considerations
- ✅ No secrets in client code
- ✅ Public API endpoints only
- ✅ HTML sanitization for server-html strategy
- ✅ Fail-safe defaults
- ⚠️ Consider using HTTPS for API endpoints
- ⚠️ Validate domain in your API endpoints
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see LICENSE file for details.
